Skip to content

Show a message's fields and bytes as separate panes - #16

Merged
DHUKK merged 1 commit into
mainfrom
improve-hex-dump
Aug 17, 2026
Merged

DHUKK merged 1 commit into
mainfrom
improve-hex-dump

Conversation

@DHUKK

@DHUKK DHUKK commented Aug 17, 2026

Copy link
Copy Markdown
Owner

The detail panel was one scroll area with the summary, the field tree and the hex dump stacked inside it. A message with many fields pushed the bytes out of sight, and reaching them scrolled the field tree away, so the two representations this view exists to link could not both be read at once.

Layout

.detail-panel
├── .panel-head    name, docs link, type char, the three facts, category chip
├── .doc-block     summary and detail, fixed
├── .fields-block  heading fixed, tree scrolls
└── .hex-block     heading fixed, ruler and dump in one framed box

The facts about the instance moved onto the head row, which had a wide empty middle while they sat in a boxed strip costing a line of the panel. The Fields heading now stays put while its tree scrolls, matching what the Raw bytes heading already did.

The bytes pane takes its content's height rather than a fixed share, since most messages are a handful of bytes: a ParameterStatus is 29, a Sync is 5. Four rows is the floor and 40% of the panel is the ceiling, so a short message does not hold open a large empty box and a large CopyData cannot crowd out the fields.

The column ruler

00 01 02 … 0f above the dump, the header every GUI hex editor has. It sits outside the scroll container and follows it sideways by transform rather than being position: sticky inside it. Sticky would sit in the flow above the sizer, which shifts every row's scroll position, so topInset would stop being the container's padding and the reveal arithmetic would have to reckon with a newly obscured top band. Outside costs one scrollLeft state variable.

It reuses .hex-offset, .hex-bytes and .hex-byte, so the 2.6ch cells and the wide 8n + 1 divider are defined once and used twice. A separate set of widths would have been a second contract to keep in step.

The box border, background and inset shadow moved from .hex-dump to .hex-dump-frame, so the ruler sits inside the same box as the bytes it labels.

Scrolling

Revealing a field scrolls to the row its first byte falls in and brings that row to the top. A field can span more rows than one, and resting it against the nearer edge put its first row at the last visible line with the rest below the fold.

The dump's own top padding is part of that arithmetic now, which it was not before. scrollTop is measured from inside the padding while a row's position is measured from the sizer's origin, so a revealed row came to rest with its lower edge clipped by exactly the padding, nearly half of a 20px row. visibleRowWindow had the same omission, which OVERSCAN_ROWS = 6 had been masking. PacketList already solved this the same way, and HexDump now measures the padding off the computed style in its existing ResizeObserver rather than hardcoding it, since it is set in rem.

Hovering a byte scrolls its field's row into view, the reverse of what hovering a field already did to the dump. Each side declines to scroll while it is the one being pointed at, which is the hoveredByte guard on one side and the existing reveal prop on the other. It scrolls the tree's own scrollTop by comparing rectangles rather than calling scrollIntoView, which would also scroll every scrollable ancestor: below the 1100px breakpoint .explorer-body is one of those, so the page itself would move.

The dump no longer scrolls smoothly. That distance is however far apart the bytes happen to sit, so on a large dump it was a long glide with nothing to see on the way. The packet list keeps its animation, where a keyboard step is always exactly one row, and it keeps its own nearest-edge scrollTopToReveal in packetWindow.ts for the same reason.

Notes for review

  • 6 new tests in hexWindow.test.ts, 223 total. The load-bearing ones pin the padding fix and the wrapped-field case.
  • .detail-panel .panel-head gains flex-wrap, scoped so the message list's head is untouched. The detail panel is only guaranteed 26rem and that row now carries a name, a docs link, a type char, three facts and a chip.
  • The 20px row height is now in three places: .hex-row's height, ROW_HEIGHT in HexDump.tsx, and the min-height calc. Commented, but a custom property would cut it to two.
  • On a very short viewport the four-row floor beats the 40% ceiling, so the bytes pane takes more than its share. Inherent to asking for a minimum.
  • .hex-offset is 4 hex digits with no fixed width, so a packet past 0xffff would render five and shift its rows. No shipped capture is near it (the largest packet is 206 bytes) but an uploaded COPY capture could be. Left alone as out of scope.
  • Verified: typecheck, 223 tests, build. Layout and scrolling checked by hand in the browser.

The detail panel was one scroll area with the summary, the field tree and
the hex dump stacked inside it. A message with many fields pushed the bytes
out of sight, and reaching them scrolled the field tree away, so the two
representations this view exists to link could not both be read at once.
Each is its own pane now, and what the message is stays above them both.

The facts about the instance move onto the head row, which had a wide empty
middle while they sat in a boxed strip costing a line of the panel. The
Fields heading stays put while its tree scrolls, the way the Raw bytes
heading already did.

The bytes pane takes its content's height rather than a fixed share, since
most messages are a handful of bytes. Four rows is the floor, so a five-byte
Sync still gets a box with some presence, and 40% of the panel is the
ceiling, so a large CopyData cannot crowd out the fields.

A ruler above the dump labels which column is which byte of the row. It sits
outside the scroll container and follows it sideways by transform, because
the dump is wider than its pane at most widths and a ruler that did not
follow would label the wrong columns. It reuses the cells' own widths, so
the two cannot drift apart.

Revealing a field scrolls to the row its first byte falls in and brings that
row to the top. A field can span more rows than one, and resting it against
the nearer edge left the rest of it below the fold. The dump's own top
padding is part of that arithmetic now, which it was not before: a revealed
row came to rest with its lower edge clipped by exactly the padding, nearly
half of a 20px row.

Hovering a byte scrolls its field's row into view, the reverse of what
hovering a field already did to the dump. Each side declines to scroll while
it is the one being pointed at.

The dump no longer scrolls smoothly. That distance is however far apart the
bytes happen to sit, so on a large dump it was a long glide with nothing to
see on the way.
@DHUKK
DHUKK merged commit d7363e7 into main Aug 17, 2026
2 checks passed
@DHUKK
DHUKK deleted the improve-hex-dump branch August 29, 2026 23:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant