diff --git a/media/editor/dataDisplay.css b/media/editor/dataDisplay.css
index 75e2904f..48139076 100644
--- a/media/editor/dataDisplay.css
+++ b/media/editor/dataDisplay.css
@@ -129,3 +129,317 @@
.data-cell-char {
width: calc(var(--cell-size) * 0.7) !important;
}
+
+/*
+ * Rainbow palettes (dark and light)
+ * Inspired by "your hex editor should color-code bytes" blogpost by Alice Pellerin
+ * (https://simonomi.dev/blog/color-code-your-bytes/)
+ */
+.colorSchemeRainbowDark00 { color: gray; }
+.colorSchemeRainbowDark0 { color: oklch(75% .18 360); } /* */
+.colorSchemeRainbowDark1 { color: oklch(75% .18 23); } /* */
+.colorSchemeRainbowDark2 { color: oklch(75% .18 50); } /* */
+.colorSchemeRainbowDark3 { color: oklch(75% .18 65); }
+.colorSchemeRainbowDark4 { color: oklch(75% .18 77); }
+.colorSchemeRainbowDark5 { color: oklch(75% .18 103); }
+.colorSchemeRainbowDark6 { color: oklch(75% .18 130); }
+.colorSchemeRainbowDark7 { color: oklch(75% .18 142); } /* */
+.colorSchemeRainbowDark8 { color: oklch(75% .18 150); } /* */
+.colorSchemeRainbowDark9 { color: oklch(75% .18 163); }
+.colorSchemeRainbowDarkA { color: oklch(75% .18 184); }
+.colorSchemeRainbowDarkB { color: oklch(75% .18 209); }
+.colorSchemeRainbowDarkC { color: oklch(75% .18 232); }
+.colorSchemeRainbowDarkD { color: oklch(75% .18 254); }
+.colorSchemeRainbowDarkE { color: oklch(75% .18 294); }
+.colorSchemeRainbowDarkF { color: oklch(75% .18 328); } /* */
+.colorSchemeRainbowDarkFf { color: white; }
+
+.colorSchemeRainbowLight00 { color: gray; }
+.colorSchemeRainbowLight0 { color: oklch(67% .18 360); } /* */
+.colorSchemeRainbowLight1 { color: oklch(67% .18 23); } /* */
+.colorSchemeRainbowLight2 { color: oklch(67% .18 50); } /* */
+.colorSchemeRainbowLight3 { color: oklch(67% .18 65); }
+.colorSchemeRainbowLight4 { color: oklch(67% .18 77); }
+.colorSchemeRainbowLight5 { color: oklch(67% .18 103); }
+.colorSchemeRainbowLight6 { color: oklch(67% .18 130); }
+.colorSchemeRainbowLight7 { color: oklch(67% .18 142); } /* */
+.colorSchemeRainbowLight8 { color: oklch(67% .18 150); } /* */
+.colorSchemeRainbowLight9 { color: oklch(67% .18 163); }
+.colorSchemeRainbowLightA { color: oklch(67% .18 184); }
+.colorSchemeRainbowLightB { color: oklch(67% .18 209); }
+.colorSchemeRainbowLightC { color: oklch(67% .18 232); }
+.colorSchemeRainbowLightD { color: oklch(67% .18 254); }
+.colorSchemeRainbowLightE { color: oklch(67% .18 294); }
+.colorSchemeRainbowLightF { color: oklch(67% .18 328); } /* */
+.colorSchemeRainbowLightFf { color: black; }
+
+.colorSchemeCategoriesDark00 { color: gray; }
+.colorSchemeCategoriesDarkAsciiNonGraphic { color: #50fa7b; }
+.colorSchemeCategoriesDarkAscii { color: #8be9fd; }
+.colorSchemeCategoriesDarkNonAscii { color: #f1fa8c; }
+
+.colorSchemeCategoriesLight00 { color: gray; }
+.colorSchemeCategoriesLightAsciiNonGraphic { color: #54ad45; }
+.colorSchemeCategoriesLightAscii { color: #0773b2; }
+.colorSchemeCategoriesLightNonAscii { color: #bbb31e; }
+
+/*
+ * Color gradients inspired by aticu's contribution to Hexyl
+ * (see https://github.com/sharkdp/hexyl/pull/247)
+ */
+.colorSchemeGradient00 { color: #646464; } /* NULL */
+.colorSchemeGradient01 { color: #ff00ff; } /* */
+.colorSchemeGradient02 { color: #fb00ff; }
+.colorSchemeGradient03 { color: #f700ff; }
+.colorSchemeGradient04 { color: #f200ff; }
+.colorSchemeGradient05 { color: #ee00ff; }
+.colorSchemeGradient06 { color: #ea00ff; }
+.colorSchemeGradient07 { color: #e600ff; }
+.colorSchemeGradient08 { color: #e100ff; }
+.colorSchemeGradient09 { color: #dd00ff; }
+.colorSchemeGradient0A { color: #d900ff; }
+.colorSchemeGradient0B { color: #d500ff; }
+.colorSchemeGradient0C { color: #d000ff; }
+.colorSchemeGradient0D { color: #cc00ff; }
+.colorSchemeGradient0E { color: #c800ff; }
+.colorSchemeGradient0F { color: #c400ff; }
+.colorSchemeGradient10 { color: #c000ff; }
+.colorSchemeGradient11 { color: #bb00ff; }
+.colorSchemeGradient12 { color: #b700ff; }
+.colorSchemeGradient13 { color: #b300ff; }
+.colorSchemeGradient14 { color: #af00ff; }
+.colorSchemeGradient15 { color: #aa00ff; }
+.colorSchemeGradient16 { color: #a600ff; }
+.colorSchemeGradient17 { color: #a200ff; }
+.colorSchemeGradient18 { color: #9e00ff; }
+.colorSchemeGradient19 { color: #9900ff; }
+.colorSchemeGradient1A { color: #9500ff; }
+.colorSchemeGradient1B { color: #9100ff; }
+.colorSchemeGradient1C { color: #8d00ff; }
+.colorSchemeGradient1D { color: #8800ff; }
+.colorSchemeGradient1E { color: #8400ff; }
+.colorSchemeGradient1F { color: #8000ff; } /* */
+.colorSchemeGradient20 { color: #0080ff; } /* */
+.colorSchemeGradient21 { color: #0081fe; }
+.colorSchemeGradient22 { color: #0083fd; }
+.colorSchemeGradient23 { color: #0084fc; }
+.colorSchemeGradient24 { color: #0085fb; }
+.colorSchemeGradient25 { color: #0087fa; }
+.colorSchemeGradient26 { color: #0088f9; }
+.colorSchemeGradient27 { color: #0089f8; }
+.colorSchemeGradient28 { color: #008af7; }
+.colorSchemeGradient29 { color: #008cf6; }
+.colorSchemeGradient2A { color: #008df5; }
+.colorSchemeGradient2B { color: #008ef4; }
+.colorSchemeGradient2C { color: #008ff3; }
+.colorSchemeGradient2D { color: #0091f2; }
+.colorSchemeGradient2E { color: #0092f1; }
+.colorSchemeGradient2F { color: #0093f0; }
+.colorSchemeGradient30 { color: #0094ef; }
+.colorSchemeGradient31 { color: #0095ee; }
+.colorSchemeGradient32 { color: #0097ed; }
+.colorSchemeGradient33 { color: #0098ec; }
+.colorSchemeGradient34 { color: #0099eb; }
+.colorSchemeGradient35 { color: #009aea; }
+.colorSchemeGradient36 { color: #009be9; }
+.colorSchemeGradient37 { color: #009de8; }
+.colorSchemeGradient38 { color: #009ee7; }
+.colorSchemeGradient39 { color: #009fe6; }
+.colorSchemeGradient3A { color: #00a0e5; }
+.colorSchemeGradient3B { color: #00a2e4; }
+.colorSchemeGradient3C { color: #00a3e3; }
+.colorSchemeGradient3D { color: #00a4e2; }
+.colorSchemeGradient3E { color: #00a5e1; }
+.colorSchemeGradient3F { color: #00a6e0; }
+.colorSchemeGradient40 { color: #00a8df; }
+.colorSchemeGradient41 { color: #00a9de; }
+.colorSchemeGradient42 { color: #00aadd; }
+.colorSchemeGradient43 { color: #00abdc; }
+.colorSchemeGradient44 { color: #00acdb; }
+.colorSchemeGradient45 { color: #00aeda; }
+.colorSchemeGradient46 { color: #00afd9; }
+.colorSchemeGradient47 { color: #00b0d8; }
+.colorSchemeGradient48 { color: #00b1d7; }
+.colorSchemeGradient49 { color: #00b3d6; }
+.colorSchemeGradient4A { color: #00b4d5; }
+.colorSchemeGradient4B { color: #00b5d4; }
+.colorSchemeGradient4C { color: #00b6d3; }
+.colorSchemeGradient4D { color: #00b7d2; }
+.colorSchemeGradient4E { color: #00b9d1; }
+.colorSchemeGradient4F { color: #00bad0; }
+.colorSchemeGradient50 { color: #00bbcf; }
+.colorSchemeGradient51 { color: #00bcce; }
+.colorSchemeGradient52 { color: #00bdcd; }
+.colorSchemeGradient53 { color: #00bfcc; }
+.colorSchemeGradient54 { color: #00c0cb; }
+.colorSchemeGradient55 { color: #00c1ca; }
+.colorSchemeGradient56 { color: #00c2c9; }
+.colorSchemeGradient57 { color: #00c3c8; }
+.colorSchemeGradient58 { color: #00c5c7; }
+.colorSchemeGradient59 { color: #00c6c6; }
+.colorSchemeGradient5A { color: #00c7c5; }
+.colorSchemeGradient5B { color: #00c8c4; }
+.colorSchemeGradient5C { color: #00cac3; }
+.colorSchemeGradient5D { color: #00cbc2; }
+.colorSchemeGradient5E { color: #00ccc1; }
+.colorSchemeGradient5F { color: #00cdc0; }
+.colorSchemeGradient60 { color: #00cebf; }
+.colorSchemeGradient61 { color: #00d0be; }
+.colorSchemeGradient62 { color: #00d1bd; }
+.colorSchemeGradient63 { color: #00d2bc; }
+.colorSchemeGradient64 { color: #00d3bb; }
+.colorSchemeGradient65 { color: #00d4ba; }
+.colorSchemeGradient66 { color: #00d6b9; }
+.colorSchemeGradient67 { color: #00d7b8; }
+.colorSchemeGradient68 { color: #00d8b7; }
+.colorSchemeGradient69 { color: #00d9b6; }
+.colorSchemeGradient6A { color: #00dab5; }
+.colorSchemeGradient6B { color: #00dcb4; }
+.colorSchemeGradient6C { color: #00ddb3; }
+.colorSchemeGradient6D { color: #00deb2; }
+.colorSchemeGradient6E { color: #00dfb1; }
+.colorSchemeGradient6F { color: #00e0b0; }
+.colorSchemeGradient70 { color: #00e2af; }
+.colorSchemeGradient71 { color: #00e3ae; }
+.colorSchemeGradient72 { color: #00e4ad; }
+.colorSchemeGradient73 { color: #00e5ac; }
+.colorSchemeGradient74 { color: #00e6ab; }
+.colorSchemeGradient75 { color: #00e8aa; }
+.colorSchemeGradient76 { color: #00e9a9; }
+.colorSchemeGradient77 { color: #00eaa8; }
+.colorSchemeGradient78 { color: #00eba7; }
+.colorSchemeGradient79 { color: #00eca6; }
+.colorSchemeGradient7A { color: #00eea5; }
+.colorSchemeGradient7B { color: #00efa4; }
+.colorSchemeGradient7C { color: #00f0a3; }
+.colorSchemeGradient7D { color: #00f1a2; }
+.colorSchemeGradient7E { color: #00ff80; } /* */
+.colorSchemeGradient7F { color: #408000; } /* DEL */
+.colorSchemeGradient80 { color: #ff0000; } /* */
+.colorSchemeGradient81 { color: #ff0300; }
+.colorSchemeGradient82 { color: #ff0600; }
+.colorSchemeGradient83 { color: #ff0900; }
+.colorSchemeGradient84 { color: #ff0c00; }
+.colorSchemeGradient85 { color: #ff0f00; }
+.colorSchemeGradient86 { color: #ff1200; }
+.colorSchemeGradient87 { color: #ff1500; }
+.colorSchemeGradient88 { color: #ff1800; }
+.colorSchemeGradient89 { color: #ff1b00; }
+.colorSchemeGradient8A { color: #ff1e00; }
+.colorSchemeGradient8B { color: #ff2100; }
+.colorSchemeGradient8C { color: #ff2400; }
+.colorSchemeGradient8D { color: #ff2700; }
+.colorSchemeGradient8E { color: #ff2a00; }
+.colorSchemeGradient8F { color: #ff2d00; }
+.colorSchemeGradient90 { color: #ff3000; }
+.colorSchemeGradient91 { color: #ff3300; }
+.colorSchemeGradient92 { color: #ff3600; }
+.colorSchemeGradient93 { color: #ff3900; }
+.colorSchemeGradient94 { color: #ff3c00; }
+.colorSchemeGradient95 { color: #ff3f00; }
+.colorSchemeGradient96 { color: #ff4200; }
+.colorSchemeGradient97 { color: #ff4500; }
+.colorSchemeGradient98 { color: #ff4800; }
+.colorSchemeGradient99 { color: #ff4b00; }
+.colorSchemeGradient9A { color: #ff4e00; }
+.colorSchemeGradient9B { color: #ff5100; }
+.colorSchemeGradient9C { color: #ff5400; }
+.colorSchemeGradient9D { color: #ff5700; }
+.colorSchemeGradient9E { color: #ff5a00; }
+.colorSchemeGradient9F { color: #ff5d00; }
+.colorSchemeGradientA0 { color: #ff6000; }
+.colorSchemeGradientA1 { color: #ff6300; }
+.colorSchemeGradientA2 { color: #ff6600; }
+.colorSchemeGradientA3 { color: #ff6900; }
+.colorSchemeGradientA4 { color: #ff6c00; }
+.colorSchemeGradientA5 { color: #ff6f00; }
+.colorSchemeGradientA6 { color: #ff7200; }
+.colorSchemeGradientA7 { color: #ff7500; }
+.colorSchemeGradientA8 { color: #ff7800; }
+.colorSchemeGradientA9 { color: #ff7b00; }
+.colorSchemeGradientAa { color: #ff7e00; }
+.colorSchemeGradientAb { color: #ff8100; }
+.colorSchemeGradientAc { color: #ff8400; }
+.colorSchemeGradientAd { color: #ff8700; }
+.colorSchemeGradientAe { color: #ff8a00; }
+.colorSchemeGradientAf { color: #ff8d00; }
+.colorSchemeGradientB0 { color: #ff9000; }
+.colorSchemeGradientB1 { color: #ff9300; }
+.colorSchemeGradientB2 { color: #ff9600; }
+.colorSchemeGradientB3 { color: #ff9900; }
+.colorSchemeGradientB4 { color: #ff9c00; }
+.colorSchemeGradientB5 { color: #ff9f00; }
+.colorSchemeGradientB6 { color: #ffa200; }
+.colorSchemeGradientB7 { color: #ffa500; }
+.colorSchemeGradientB8 { color: #ffa800; }
+.colorSchemeGradientB9 { color: #ffab00; }
+.colorSchemeGradientBa { color: #ffae00; }
+.colorSchemeGradientBb { color: #ffb100; }
+.colorSchemeGradientBc { color: #ffb400; }
+.colorSchemeGradientBd { color: #ffb700; }
+.colorSchemeGradientBe { color: #ffba00; }
+.colorSchemeGradientBf { color: #ffbd00; }
+.colorSchemeGradientC0 { color: #ffc000; }
+.colorSchemeGradientC1 { color: #ffc300; }
+.colorSchemeGradientC2 { color: #ffc600; }
+.colorSchemeGradientC3 { color: #ffc900; }
+.colorSchemeGradientC4 { color: #ffcc00; }
+.colorSchemeGradientC5 { color: #ffcf00; }
+.colorSchemeGradientC6 { color: #ffd200; }
+.colorSchemeGradientC7 { color: #ffd500; }
+.colorSchemeGradientC8 { color: #ffd800; }
+.colorSchemeGradientC9 { color: #ffdb00; }
+.colorSchemeGradientCa { color: #ffde00; }
+.colorSchemeGradientCb { color: #ffe100; }
+.colorSchemeGradientCc { color: #ffe400; }
+.colorSchemeGradientCd { color: #ffe700; }
+.colorSchemeGradientCe { color: #ffea00; }
+.colorSchemeGradientCf { color: #ffed00; }
+.colorSchemeGradientD0 { color: #fff000; }
+.colorSchemeGradientD1 { color: #fff300; }
+.colorSchemeGradientD2 { color: #fff600; }
+.colorSchemeGradientD3 { color: #fff900; }
+.colorSchemeGradientD4 { color: #ffff00; }
+.colorSchemeGradientD5 { color: #ffff06; }
+.colorSchemeGradientD6 { color: #ffff0c; }
+.colorSchemeGradientD7 { color: #ffff12; }
+.colorSchemeGradientD8 { color: #ffff18; }
+.colorSchemeGradientD9 { color: #ffff1e; }
+.colorSchemeGradientDa { color: #ffff24; }
+.colorSchemeGradientDb { color: #ffff2a; }
+.colorSchemeGradientDc { color: #ffff30; }
+.colorSchemeGradientDd { color: #ffff36; }
+.colorSchemeGradientDe { color: #ffff3c; }
+.colorSchemeGradientDf { color: #ffff42; }
+.colorSchemeGradientE0 { color: #ffff48; }
+.colorSchemeGradientE1 { color: #ffff4e; }
+.colorSchemeGradientE2 { color: #ffff54; }
+.colorSchemeGradientE3 { color: #ffff5a; }
+.colorSchemeGradientE4 { color: #ffff60; }
+.colorSchemeGradientE5 { color: #ffff66; }
+.colorSchemeGradientE6 { color: #ffff6c; }
+.colorSchemeGradientE7 { color: #ffff72; }
+.colorSchemeGradientE8 { color: #ffff78; }
+.colorSchemeGradientE9 { color: #ffff7e; }
+.colorSchemeGradientEa { color: #ffff84; }
+.colorSchemeGradientEb { color: #ffff8a; }
+.colorSchemeGradientEc { color: #ffff90; }
+.colorSchemeGradientEd { color: #ffff96; }
+.colorSchemeGradientEe { color: #ffff9c; }
+.colorSchemeGradientEf { color: #ffffa2; }
+.colorSchemeGradientF0 { color: #ffffa8; }
+.colorSchemeGradientF1 { color: #ffffae; }
+.colorSchemeGradientF2 { color: #ffffb4; }
+.colorSchemeGradientF3 { color: #ffffba; }
+.colorSchemeGradientF4 { color: #ffffc0; }
+.colorSchemeGradientF5 { color: #ffffc6; }
+.colorSchemeGradientF6 { color: #ffffcc; }
+.colorSchemeGradientF7 { color: #ffffd2; }
+.colorSchemeGradientF8 { color: #ffffd8; }
+.colorSchemeGradientF9 { color: #ffffde; }
+.colorSchemeGradientFa { color: #ffffe4; }
+.colorSchemeGradientFb { color: #ffffea; }
+.colorSchemeGradientFc { color: #fffff0; }
+.colorSchemeGradientFd { color: #fffff6; }
+.colorSchemeGradientFe { color: #fffffc; }
+.colorSchemeGradientFf { color: #ffffff; } /* */
diff --git a/media/editor/dataDisplay.tsx b/media/editor/dataDisplay.tsx
index fe81526d..1e51bf76 100644
--- a/media/editor/dataDisplay.tsx
+++ b/media/editor/dataDisplay.tsx
@@ -6,10 +6,15 @@ import { useRecoilValue, useSetRecoilState } from "recoil";
import { HexDecorator } from "../../shared/decorators";
import { EditRangeOp, HexDocumentEditOp } from "../../shared/hexDocumentModel";
import {
+ ColorScheme,
CopyFormat,
DeleteAcceptedMessage,
InspectorLocation,
+ IStyleSettings,
MessageType,
+ NonAsciiStyleCharacter,
+ NonPrintableAsciiStyleCharacter,
+ PrintableAsciiStyleCharacter
} from "../../shared/protocol";
import { binarySearch } from "../../shared/util/binarySearch";
import { Range } from "../../shared/util/range";
@@ -31,7 +36,6 @@ import { strings } from "./strings";
import {
clamp,
clsx,
- getAsciiCharacter,
getScrollDimensions,
HexDecoratorStyles,
parseHexDigit,
@@ -699,6 +703,7 @@ const DataRowContents: React.FC<{
}> = ({ offset, width, showDecodedText, rawBytes, isRowWithInsertDataCell, decorators }) => {
let memoValue = "";
const ctx = useDisplayContext();
+ const styleSettings = useRecoilValue(select.editorSettings).style;
for (const byte of rawBytes) {
memoValue += "," + byte;
}
@@ -745,7 +750,10 @@ const DataRowContents: React.FC<{
bytes.push(
- {char === undefined ? "." : char}
+ {computeDecodedTextCharacter(value, styleSettings)}
,
);
}
@@ -785,3 +792,184 @@ const DataRowContents: React.FC<{
>
);
};
+
+const computeDecodedTextCharacter = (byte: number, styleSettings: IStyleSettings): string => {
+
+ // Non-printable ASCII
+ if ((byte <= 0x1F) || (byte === 0x7F)) {
+ switch (styleSettings.nonPrintableAsciiCharacter) {
+
+ case NonPrintableAsciiStyleCharacter.Dot:
+ return dedicatedGlyphOrElse(byte, '.');
+
+ // U+00B7 MIDDLE DOT
+ case NonPrintableAsciiStyleCharacter.Middot:
+ return dedicatedGlyphOrElse(byte, `\u00B7`);
+
+ // U+2022 BULLET
+ case NonPrintableAsciiStyleCharacter.Bullet:
+ return dedicatedGlyphOrElse(byte, '\u2022');
+
+ // U+00D7 MULTIPLICATION SIGN
+ case NonPrintableAsciiStyleCharacter.Cross:
+ return dedicatedGlyphOrElse(byte, '\u00D7');
+
+ // Control Pictures (U+2400..U+243F)
+ case NonPrintableAsciiStyleCharacter.Symbol:
+ return (byte === 0x7F) ? '\u2421' : String.fromCharCode(0x2400 + byte);
+
+ // Braille Patterns (U+2800..U+28FF)
+ case NonPrintableAsciiStyleCharacter.Braille:
+ return asBraillePattern(byte);
+ }
+ }
+
+ // Printable ASCII
+ if ((byte >= 0x20) && (byte < 0x7F)) {
+ switch (styleSettings.printableAsciiCharacter) {
+
+ case PrintableAsciiStyleCharacter.Ascii:
+ return String.fromCharCode(byte);
+
+ // Braille Patterns (U+2800..U+28FF)
+ case PrintableAsciiStyleCharacter.Braille:
+ return asBraillePattern(byte);
+ }
+ }
+
+ // Non-ASCII
+ switch (styleSettings.nonAsciiCharacter) {
+
+ case NonAsciiStyleCharacter.Dot:
+ return '.';
+
+ // U+00B7 MIDDLE DOT
+ case NonAsciiStyleCharacter.Middot:
+ return `\u00B7`;
+
+ // U+2022 BULLET
+ case NonAsciiStyleCharacter.Bullet:
+ return '\u2022';
+
+ // U+00D7 MULTIPLICATION SIGN
+ case NonAsciiStyleCharacter.Cross:
+ return '\u00D7';
+
+ // Braille Patterns (U+2800..U+28FF)
+ case NonAsciiStyleCharacter.Braille:
+ return asBraillePattern(byte);
+ }
+}
+
+function dedicatedGlyphOrElse(byte: number, defaultCharacter: string): string {
+ switch (byte) {
+
+ // NUL: U+22C4 Diamond Operator
+ case 0x00:
+ return '\u22C4';
+
+ // TAB "\t": U+21E5 (rightwards arrow to bar)
+ case 0x09:
+ return '\u21E5';
+
+ // LF "\n": U+2190 (leftwards arrow)
+ // DEL: U+2190 (leftwards arrow)
+ case 0x0A:
+ case 0x7F:
+ return '\u2190';
+
+ // CR "\r": U+21B5 (downwards arrow corner lefwards)
+ case 0x0D:
+ return '\u21B5';
+
+ // " ": U+00B7 Middle Dot
+ case 0x20:
+ return defaultCharacter !== `\u00B7` ? '\u00B7' : ' ';
+
+ default:
+ return defaultCharacter;
+ }
+}
+
+/*
+ * Braille glyph rendering inspired by aticu's contribution to Hexyl
+ * (see https://github.com/sharkdp/hexyl/pull/247)
+ */
+function asBraillePattern(byte: number): string {
+ // Braille bit positions <=> byte bit positions:
+ // 0 3 <=> 0 1
+ // 1 4 <=> 2 3
+ // 2 5 <=> 4 5
+ // 6 7 <=> 6 7
+ const brailleBits: number[] = [0, 3, 1, 4, 2, 5, 6, 7];
+
+ let brailleCharCode = 0x2800;
+ for (let bit = 0; bit < 8; bit++) {
+ brailleCharCode |= (byte >> bit & 1) << brailleBits[bit];
+ }
+ return String.fromCharCode(brailleCharCode);
+}
+
+const computeByteColorClass = (byte: number, styleSettings: IStyleSettings): string | undefined => {
+
+ return computeColorClass(byte, styleSettings.byteColorScheme);
+}
+
+const computeDecodedTextColorClass = (byte: number, styleSettings: IStyleSettings): string | undefined => {
+
+ return computeColorClass(byte, styleSettings.decodedTextColorScheme)
+ // Falls back to legacy decoded text color style
+ ?? ((byte < 0x20) || (byte >= 0x7F) ? style.nonGraphicChar : undefined);
+}
+
+const computeColorClass = (byte: number, colorScheme: ColorScheme): string | undefined => {
+
+ // CategoriesDark, CategoriesLight
+ if (colorScheme === ColorScheme.CategoriesDark
+ || colorScheme === ColorScheme.CategoriesLight) {
+
+ const prefix = colorScheme === ColorScheme.CategoriesDark
+ ? "colorSchemeCategoriesDark"
+ : "colorSchemeCategoriesLight";
+
+ return (byte === 0x00) ? style[`${prefix}00` as keyof typeof style]
+ : (byte <= 0x1F) ? style[`${prefix}AsciiNonGraphic` as keyof typeof style]
+ : (byte <= 0x7E) ? style[`${prefix}Ascii` as keyof typeof style]
+ : style[`${prefix}NonAscii` as keyof typeof style];
+ }
+
+ // RainbowDark, RainbowLight
+ if (colorScheme === ColorScheme.RainbowDark
+ || colorScheme === ColorScheme.RainbowLight) {
+
+ const prefix = colorScheme === ColorScheme.RainbowDark
+ ? "colorSchemeRainbowDark"
+ : "colorSchemeRainbowLight";
+
+ switch (byte) {
+ case 0x00:
+ return style[`${prefix}00` as keyof typeof style];
+ case 0xff:
+ return style[`${prefix}Ff` as keyof typeof style];
+ default: {
+ const highNibble = (byte >> 4).toString(16).toUpperCase();
+ return style[`${prefix}${highNibble}` as keyof typeof style];
+ }
+ }
+ }
+
+ // Gradient
+ if (colorScheme === ColorScheme.Gradient) {
+
+ const highNibble = (byte >> 4).toString(16);
+ const lowNibble = (byte & 0x0f).toString(16);
+ const suffix = (highNibble >= '0' && highNibble <= '9')
+ ? highNibble + lowNibble.toUpperCase()
+ : highNibble.toUpperCase() + lowNibble.toLowerCase();
+ return style[`colorSchemeGradient${suffix}` as keyof typeof style];
+ }
+
+ // (Default)
+ return undefined;
+};
+
diff --git a/media/editor/util.ts b/media/editor/util.ts
index 79d2cea2..1efa6e7b 100644
--- a/media/editor/util.ts
+++ b/media/editor/util.ts
@@ -4,7 +4,6 @@
// Assorted helper functions
import { HexDecoratorType } from "../../shared/decorators";
-import { Range } from "../../shared/util/range";
import _style from "./util.css";
/**
@@ -44,46 +43,6 @@ export const clsx = (...classes: (string | false | undefined | null)[]): string
return out;
};
-/**
- * @description Checks if the given number is in any of the ranges
- * @param {number} num The number to use when checking the ranges
- * @param {Range[]} ranges The ranges to check the number against
- * @returns {boolean} True if the number is in any of the ranges, false otherwise
- */
-export function withinAnyRange(num: number, ranges: Range[]): boolean {
- for (const range of ranges) {
- if (range.includes(num)) {
- return true;
- }
- }
- return false;
-}
-
-/**
- * @description Creates a list of ranges containing the non renderable 8 bit char codes
- * @returns {Range[]} The ranges which represent the non renderable 8 bit char codes
- */
-export function generateCharacterRanges(): Range[] {
- const ranges: Range[] = [];
- ranges.push(new Range(0, 32));
- ranges.push(new Range(127));
- return ranges;
-}
-
-const nonPrintableAsciiRange = generateCharacterRanges();
-
-/**
- * Gets the ascii character for the byte, if it's printable.
- * @returns
- */
-export const getAsciiCharacter = (byte: number): string | undefined => {
- if (withinAnyRange(byte, nonPrintableAsciiRange)) {
- return undefined;
- } else {
- return String.fromCharCode(byte);
- }
-};
-
/**
* Returns `x` clamped between the provided lower and upper bounds.
*/
diff --git a/package.json b/package.json
index 1acf4546..9a5a1c86 100644
--- a/package.json
+++ b/package.json
@@ -97,6 +97,98 @@
"type": "boolean",
"default": false,
"description": "%hexeditor.showOpenFileButton%"
+ },
+ "hexeditor.style.byteColorScheme": {
+ "type": "string",
+ "enum": [
+ "default",
+ "rainbow",
+ "rainbowLight",
+ "categories",
+ "categoriesLight",
+ "gradient"
+ ],
+ "default": "gradient",
+ "description": "%hexeditor.style.byteColorScheme%",
+ "enumDescriptions": [
+ "%hexeditor.style.colorscheme.default%",
+ "%hexeditor.style.colorscheme.rainbow%",
+ "%hexeditor.style.colorscheme.rainbowLight%",
+ "%hexeditor.style.colorscheme.categories%",
+ "%hexeditor.style.colorscheme.categoriesLight%",
+ "%hexeditor.style.colorscheme.gradient%"
+ ]
+ },
+ "hexeditor.style.decodedTextColorScheme": {
+ "type": "string",
+ "enum": [
+ "default",
+ "rainbow",
+ "rainbowLight",
+ "categories",
+ "categoriesLight",
+ "gradient"
+ ],
+ "default": "default",
+ "description": "%hexeditor.style.decodedTextColorScheme%",
+ "enumDescriptions": [
+ "%hexeditor.style.colorscheme.default%",
+ "%hexeditor.style.colorscheme.rainbow%",
+ "%hexeditor.style.colorscheme.rainbowLight%",
+ "%hexeditor.style.colorscheme.categories%",
+ "%hexeditor.style.colorscheme.categoriesLight%",
+ "%hexeditor.style.colorscheme.gradient%"
+ ]
+ },
+ "hexeditor.style.nonPrintableAsciiCharacter": {
+ "type": "string",
+ "enum": [
+ "dot",
+ "middot",
+ "bullet",
+ "cross",
+ "symbol",
+ "braille"
+ ],
+ "default": "bullet",
+ "enumDescriptions": [
+ "%hexeditor.style.character.dot%",
+ "%hexeditor.style.character.middot%",
+ "%hexeditor.style.character.bullet%",
+ "%hexeditor.style.character.cross%",
+ "%hexeditor.style.character.symbol%",
+ "%hexeditor.style.character.braille%"
+ ]
+ },
+ "hexeditor.style.printableAsciiCharacter": {
+ "type": "string",
+ "enum": [
+ "ascii",
+ "braille"
+ ],
+ "default": "ascii",
+ "enumDescriptions": [
+ "%hexeditor.style.character.ascii%",
+ "%hexeditor.style.character.braille%"
+ ]
+ },
+ "hexeditor.style.nonAsciiCharacter": {
+ "type": "string",
+ "enum": [
+ "dot",
+ "middot",
+ "bullet",
+ "cross",
+ "braille"
+ ],
+ "default": "cross",
+ "enumDescriptions": [
+ "%hexeditor.style.character.dot%",
+ "%hexeditor.style.character.middot%",
+ "%hexeditor.style.character.bullet%",
+ "%hexeditor.style.character.cross%",
+ "%hexeditor.style.character.braille%"
+ ]
}
}
}
diff --git a/package.nls.json b/package.nls.json
index 84ef5ed8..81b1937c 100644
--- a/package.nls.json
+++ b/package.nls.json
@@ -9,6 +9,24 @@
"hexeditor.columnWidth": "The number of bytes per row to show in the editor.",
"hexeditor.showDecodedText": "Whether decoded text should be shown in the editor.",
"hexeditor.showOpenFileButton": "Show Hex Editor button in editor menu.",
+ "hexeditor.style.byteColorScheme": "Color style for bytes",
+ "hexeditor.style.decodedTextColorScheme": "Color style for decoded text",
+ "hexeditor.style.nonPrintableAsciiCharacter": "Rendering style for non-printable ASCII within the decoded text",
+ "hexeditor.style.printableAsciiCharacter": "Rendering style for printable ASCII within the decoded text",
+ "hexeditor.style.nonAsciiCharacter": "Rendering style for non-ASCII within the decoded text",
+ "hexeditor.style.colorscheme.default": "Default (monochrome)",
+ "hexeditor.style.colorscheme.rainbow": "Color choosen by high nibble",
+ "hexeditor.style.colorscheme.rainbowLight": "Color choosen by high nibble (light background)",
+ "hexeditor.style.colorscheme.categories": "Color choosen by category (non-printable ASCII, printable ASCII, non-ASCII)",
+ "hexeditor.style.colorscheme.categoriesLight": "Color choosen by category (non-printable ASCII, printable ASCII, non-ASCII) (light background)",
+ "hexeditor.style.colorscheme.gradient": "Color gradients",
+ "hexeditor.style.character.dot": "Renders a regular dot",
+ "hexeditor.style.character.middot": "Renders a middle dot (lighter than a regular dot)",
+ "hexeditor.style.character.bullet": "Renders a bullet",
+ "hexeditor.style.character.cross": "Renders a cross (a multiplication sign)",
+ "hexeditor.style.character.symbol": "Renders a glyph that represents the control code",
+ "hexeditor.style.character.ascii": "Renders the actual ASCII character",
+ "hexeditor.style.character.braille": "Renders a braille glyph based on the byte value",
"hexEditor.openFile": "Open Active File in Hex Editor",
"hexEditor.goToOffset": "Go To Offset",
"hexEditor.selectBetweenOffsets": "Select Between Offsets",
diff --git a/shared/protocol.ts b/shared/protocol.ts
index 86c46100..84cbf9a3 100644
--- a/shared/protocol.ts
+++ b/shared/protocol.ts
@@ -58,12 +58,52 @@ export const enum InspectorLocation {
Sidebar = "sidebar",
}
+export const enum ColorScheme {
+ Default = "default",
+ RainbowDark = "rainbow",
+ RainbowLight = "rainbowLight",
+ CategoriesDark = "categories",
+ CategoriesLight = "categoriesLight",
+ Gradient = "gradient"
+}
+
+export const enum NonPrintableAsciiStyleCharacter {
+ Dot = "dot",
+ Middot = "middot",
+ Bullet = "bullet",
+ Cross = "cross",
+ Symbol = "symbol",
+ Braille = "braille"
+}
+
+export const enum PrintableAsciiStyleCharacter {
+ Ascii = "ascii",
+ Braille = "braille"
+}
+
+export const enum NonAsciiStyleCharacter {
+ Dot = "dot",
+ Middot = "middot",
+ Bullet = "bullet",
+ Cross = "cross",
+ Braille = "braille"
+}
+
+export interface IStyleSettings {
+ byteColorScheme: ColorScheme;
+ decodedTextColorScheme: ColorScheme;
+ nonPrintableAsciiCharacter: NonPrintableAsciiStyleCharacter;
+ printableAsciiCharacter: PrintableAsciiStyleCharacter;
+ nonAsciiCharacter: NonAsciiStyleCharacter;
+}
+
export interface IEditorSettings {
copyType: CopyFormat;
showDecodedText: boolean;
columnWidth: number;
inspectorType: InspectorLocation;
defaultEndianness: Endianness;
+ style: IStyleSettings;
}
export interface ICodeSettings {
diff --git a/src/hexEditorProvider.ts b/src/hexEditorProvider.ts
index 140d7a54..aba4aecc 100644
--- a/src/hexEditorProvider.ts
+++ b/src/hexEditorProvider.ts
@@ -9,6 +9,7 @@ import {
HexDocumentEditReference,
} from "../shared/hexDocumentModel";
import {
+ ColorScheme,
CopyFormat,
Endianness,
ExtensionHostMessageHandler,
@@ -16,10 +17,14 @@ import {
ICodeSettings,
IEditorSettings,
InspectorLocation,
+ IStyleSettings,
MessageHandler,
MessageType,
+ NonAsciiStyleCharacter,
+ NonPrintableAsciiStyleCharacter,
PasteMode,
- ToWebviewMessage,
+ PrintableAsciiStyleCharacter,
+ ToWebviewMessage
} from "../shared/protocol";
import { deserializeEdits, serializeEdits } from "../shared/serialization";
import { ILocalizedStrings, placeholder1 } from "../shared/strings";
@@ -31,11 +36,20 @@ import { HexEditorRegistry } from "./hexEditorRegistry";
import { ISearchRequest, LiteralSearchRequest, RegexSearchRequest } from "./searchRequest";
import { flattenBuffers, getBaseName, getCorrectArrayBuffer, randomString } from "./util";
+const defaultStyleSettings: Readonly = {
+ byteColorScheme: ColorScheme.Default,
+ decodedTextColorScheme: ColorScheme.Default,
+ nonPrintableAsciiCharacter: NonPrintableAsciiStyleCharacter.Dot,
+ printableAsciiCharacter: PrintableAsciiStyleCharacter.Ascii,
+ nonAsciiCharacter: NonAsciiStyleCharacter.Dot
+};
+
const defaultEditorSettings: Readonly = {
columnWidth: 16,
copyType: CopyFormat.HexOctets,
showDecodedText: true,
defaultEndianness: Endianness.Little,
+ style: defaultStyleSettings,
inspectorType: InspectorLocation.Aside,
};