diff --git a/.gitignore b/.gitignore index 0f44920..2b28f19 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ +.idea/ .cache dist node_modules .DS_Store +yarn.lock diff --git a/src/core/color.tsx b/src/core/color.tsx new file mode 100644 index 0000000..1d901ba --- /dev/null +++ b/src/core/color.tsx @@ -0,0 +1,97 @@ +export class Color { + hex: string; + + constructor( + hex: string + ) { + if (hex.length === 3) { + hex = hex + hex; + } + + this.hex = hex; + } + + lightenColor(color: string, percentage: number): string { + let r = Math.round(Math.min(255, parseInt(color.substring(1, 3), 16) * (1 + percentage))).toString(16); + let g = Math.round(Math.min(255, parseInt(color.substring(3, 5), 16) * (1 + percentage))).toString(16); + let b = Math.round(Math.min(255, parseInt(color.substring(5, 7), 16) * (1 + percentage))).toString(16); + return "#" + (r.length<2 ? "0" + r : r) + (g.length<2 ? "0" + g : g) + (b.length<2 ? "0" + b : b); + } + + hexAlpha(alpha: number): string { + alpha = parseInt((alpha * 255).toFixed(0)); + + if (alpha <= 0) { + alpha = 0; + } else if (alpha >= 255) { + alpha = 255; + } + + return this.hex + alpha.toString(16); + } + + hexLighter(): string { + return this.hexAlpha(.7); + } + + static default(): Color { + return new Color('#dedede'); + } + + static white(): Color { + return new Color('#ffffff'); + } + + static pink(): Color { + return new Color('#ffbefa'); + } + + static purple(): Color { + return new Color('#e2beff'); + } + + static brown(): Color { + return new Color('#c9a15f'); + } + + static red(): Color { + return new Color('#ffbebe'); + } + + static green(): Color { + return new Color('#c8ffc8'); + } + + static blue(): Color { + return new Color('#b6b6ff'); + } + + static cyan(): Color { + return new Color('#beffff'); + } + + static yellow(): Color { + return new Color('#ffffa1'); + } + + static magenta(): Color { + return new Color('#ffa6ff'); + } + + static orange(): Color { + return new Color('#ffcb99'); + } + + static black(): Color { + return new Color('#868686'); + } + + equals(color: Color): boolean { + return this.hex === color.hex; + } + + isDefault(): boolean { + return this.equals(Color.default()); + } + +} diff --git a/src/core/range.ts b/src/core/range.ts index 9724666..45b0169 100644 --- a/src/core/range.ts +++ b/src/core/range.ts @@ -15,6 +15,10 @@ export class ByteRange { this.byteLength = byteLength; } + offset(): number { + return this.byteStart; + } + size(): number { return this.byteLength; } @@ -153,6 +157,14 @@ export class BitRange { this.bitLength = bitLength; } + offset(): number { + return this.bitStart; + } + + size(): number { + return this.bitLength; + } + enclosingByteRange(): ByteRange { let byteStart = Math.floor(this.bitStart / 8); let byteEnd = Math.floor((this.bitStart + this.bitLength - 1) / 8) + 1; diff --git a/src/core/tree.tsx b/src/core/tree.tsx index 7a686c9..46daf65 100644 --- a/src/core/tree.tsx +++ b/src/core/tree.tsx @@ -1,10 +1,12 @@ import { ByteRange, BitRange } from "./range"; +import { Color } from "./color"; export class Tree { label: string; range: ByteRange | BitRange; children: Array; error?: Error; + color: Color; parent?: Tree; @@ -12,12 +14,14 @@ export class Tree { label: string, range: ByteRange | BitRange, children: Array = [], - error?: Error + error?: Error, + color?: Color ) { this.label = label; this.range = range; this.children = children; this.error = error; + this.color = color || Color.default(); // Set parent pointers for the children tree objects. for (let child of this.children) { @@ -38,4 +42,10 @@ export class Tree { isParentOf(other: Tree): boolean { return other.isChildOf(this); } + + withColor(color: Color): Tree { + this.color = color; + + return this; + } } diff --git a/src/inspectors/custom.tsx b/src/inspectors/custom.tsx index 676838b..e92da37 100644 --- a/src/inspectors/custom.tsx +++ b/src/inspectors/custom.tsx @@ -1,5 +1,6 @@ import { ByteRange, BitRange } from "../core/range"; import { Tree } from "../core/tree"; +import { Color } from "../core/color"; import { assert } from "chai"; import * as React from "react"; @@ -93,12 +94,13 @@ Object.assign(window, { Tree: Tree, ByteRange: ByteRange, BitRange: BitRange, + Color: Color, opus: require("../decoders/opus"), rtp: require("../decoders/rtp") }); const DEFAULT_CODE = `((range) => { - return new Tree("Example Tree", range, []); + return new Tree("Example Tree", range, []).withColor(Color.red()); })`; class CustomInspector extends React.Component<{}, CustomInspectorState> { diff --git a/src/inspectors/png.tsx b/src/inspectors/png.tsx index 95a1b36..26d91de 100644 --- a/src/inspectors/png.tsx +++ b/src/inspectors/png.tsx @@ -6,6 +6,7 @@ import { assert } from "chai"; import { ByteRange, BitRange } from "../core/range"; import { SimpleInspector } from "../ui/simpleinspector"; import { Tree } from "../core/tree"; +import { Color } from "../core/color"; import { hexEllipsis } from "../core/utils"; @@ -91,20 +92,20 @@ function inspect(range: ByteRange): Tree { `Chunk (${type.readUTF8()})`, ptr.bytes(0, chunkSize), [ - new Tree(`Length: ${byteLen}`, length), - new Tree(`Type: ${type.readUTF8()}`, type) + new Tree(`Length: ${byteLen}`, length).withColor(Color.blue()), + new Tree(`Type: ${type.readUTF8()}`, type).withColor(Color.orange()) ] .concat(chunkElements) - .concat([new Tree(`CRC: ${crc.readUIntBE()}`, crc)]) - ) + .concat([new Tree(`CRC: ${crc.readUIntBE()}`, crc).withColor(Color.brown())]) + ).withColor(Color.green()) ); ptr = ptr.bytes(chunkSize); } return new Tree("PNG Image", range, [ - new Tree(`Signature: ${signature.toHex()}`, signature), - new Tree(`Chunks (${chunks.length})`, range.bytes(8), chunks) + new Tree(`Signature: ${signature.toHex()}`, signature).withColor(Color.red()), + new Tree(`Chunks (${chunks.length})`, range.bytes(8), chunks).withColor(Color.magenta()) ]); } diff --git a/src/ui/binaryview.tsx b/src/ui/binaryview.tsx index a69e485..f878ef6 100644 --- a/src/ui/binaryview.tsx +++ b/src/ui/binaryview.tsx @@ -2,6 +2,8 @@ import * as React from "react"; import { Col, Form, Row } from "react-bootstrap"; import { ByteRange, BitRange } from "../core/range"; +import { Color } from "../core/color"; +import { Tree } from "../core/tree"; const HEX_BYTES_PER_ROW = 16; const HEX_BYTES_PER_GROUP = 8; @@ -11,12 +13,10 @@ const BINARY_BYTES_PER_GROUP = 4; const DEFAULT_MAX_ROWS = 1000; -const SELECTED_COLOR = "#dedede"; - type Format = "hex" | "binary"; export class BinaryView extends React.Component< - { data: ByteRange; selected?: ByteRange | BitRange; maxRows: number }, + { data: ByteRange; selected?: ByteRange | BitRange; maxRows: number; tree: Tree }, { format: Format } > { static defaultProps = { @@ -83,6 +83,28 @@ export class BinaryView extends React.Component< } } + byteColor(tree: Tree, byteNumber: number, color?: Color) { + if (tree === undefined) { + return Color.default(); + } + + for (const t of tree.children) { + if (t.range.offset() <= byteNumber && byteNumber < (t.range.offset() + t.range.size())) { + if (!t.color.isDefault()) { + color = t.color; + } + + const deepColor = this.byteColor(t, byteNumber, color); + + if (deepColor !== undefined) { + color = deepColor; + } + } + } + + return color; + } + renderHex() { let data = this.props.data; @@ -153,11 +175,12 @@ export class BinaryView extends React.Component< ))} + - {data.chunks(HEX_BYTES_PER_ROW).map((row, i) => ( -
- {row.chunks(HEX_BYTES_PER_GROUP).map((group, i) => ( - + {data.chunks(HEX_BYTES_PER_ROW).map((row, k) => ( +
+ {row.chunks(HEX_BYTES_PER_GROUP).map((group, j) => ( + {group.chunks(1).map((byte, i) => { let selected = false; if (this.props.selected) { @@ -170,11 +193,15 @@ export class BinaryView extends React.Component< } } + const byteNumber = i + (j * HEX_BYTES_PER_GROUP) + (k * HEX_BYTES_PER_ROW); + const color = this.byteColor(this.props.tree, byteNumber, undefined) || this.props.tree.color; + return ( @@ -206,8 +233,7 @@ export class BinaryView extends React.Component<
{row.chunks(BINARY_BYTES_PER_GROUP).map((group, i) => ( - {group.chunks(1).map((byte, i) => { - return ( + {group.chunks(1).map((byte, i) => ( @@ -242,8 +272,7 @@ export class BinaryView extends React.Component< ); })} - ); - })} + ))} ))}
diff --git a/src/ui/treebinaryview.tsx b/src/ui/treebinaryview.tsx index 49004f8..9e0827e 100644 --- a/src/ui/treebinaryview.tsx +++ b/src/ui/treebinaryview.tsx @@ -16,8 +16,8 @@ export class TreeBinaryView extends React.Component< super(props); this.state = {}; } - render() { + return ( <> @@ -34,7 +34,9 @@ export class TreeBinaryView extends React.Component<
+ 0) { @@ -83,7 +97,9 @@ export class TreeView extends React.Component< icon={this.state.open ? faCaretDown : faCaretRight} />
- {tree.label} + + {tree.label} +
{this.state.open && @@ -101,7 +117,7 @@ export class TreeView extends React.Component< ); } else { return ( -