fix(log-viewer): mark every row an inspector pick names, not just the rendered ones - #975
Merged
lcottercertinia merged 5 commits intoAug 28, 2026
Merged
Conversation
… its mark Picking an inspector row moves the grid, and the move re-renders the rows the mark sits on, so the mark has to go on after it. The Call Tree applied the mark it had read before the move, so dropping the pick while the move ran cleared the mark and the move then put it back with nothing picked. Hovering another row landed the same way, with the hover's own mark stripped and never re-applied. The mark now reads what the inspector is pointing at when the move settles, which is the emphasis the view already keeps, so there is no second copy of that truth to fall out of step. Analysis had the same defect and its own copy of the handler; the Database grids and the flame chart held a third and fourth copy of the shape, without the move. All four now share `inspectorLocateHandler`, so whether a pick moves a view is an argument rather than a property of which handler was copied last. A move that fails no longer leaves an unhandled rejection.
The mark was a one-shot DOM sweep, adding a class to the rows rendered at that instant and remembering those elements. The row formatter stamps the row id but never re-applied the class, and the renderer de-initialises a row scrolled out of view and builds it again on the way back, so scrolling a grid while a picked inspector row was lit lost the mark. The mark now belongs to the table rather than to those elements: `LocatedRowMarker` records the ids it wants, and the stamp lights a row as it enters the DOM. The sweep stays, for the rows already rendered when the mark is set, and it now un-lights as well as lights so a re-used element cannot arrive carrying an old mark.
The mark had to wait for a pick's move to settle, because the move re-rendered the rows it sat on and stripped the class. Nothing has to wait now: the table holds what it wants lit, and a row the move renders lights itself. That drops the ordering the handler was built around, and with it the read-back of the emphasis, the awaits, and the three tests that only covered which report won a race. A move that fails is still answered, so a view that cannot reach a frame leaves no unhandled rejection.
…w-mark # Conflicts: # log-viewer/src/components/__tests__/inspectorLocate.test.ts # log-viewer/src/components/inspectorLocate.ts
lcottercertinia
previously approved these changes
Aug 28, 2026
The comment claimed the renderer rebuilds a row scrolled out of view and drops its class. It does not: Row.create() is guarded, Row.initialize() re-uses the element, and our renderer only detaches it. The gap is a row that has never been rendered, so the sweep finds no element to mark.
15 tasks
lcottercertinia
pushed a commit
that referenced
this pull request
Aug 28, 2026
# 📝 PR Overview #975 explained the row mark with a mechanism that does not exist: that the renderer de-initialises a row scrolled out of view and rebuilds it, dropping the class. A reader who trusts that comment would expect an ordinary scroll to lose a mark, and would look in the wrong place when the mark misbehaves. What actually happens: `Row.create()` is guarded by `this.created`, `Row.initialize()` deletes cells but re-uses the element, `RowManager.styleRow` adds and removes parity classes rather than assigning `className`, and our renderer only detaches and re-attaches the element. A class on a row element survives scrolling. The gap the declarative mark closes is the **first** render: a row that has never been on screen has no element, so a sweep of what is rendered cannot reach it. ## 🛠️ Changes made - Correct the `wantedByHost` comment to name the real gap: a row below the viewport, or a tree child built after the mark was set. - Rename one test from "as a scroll back brings one" to "as scrolling to a new one does", and correct its helper comment, so the test says which case it guards. ## 🧩 Type of change (check all applicable) - [ ] 🐛 Bug fix - something not working as expected - [ ] ✨ New feature – adds new functionality - [ ] ♻️ Refactor - internal changes with no user impact - [ ] ⚡ Performance Improvement - [x] 📝 Documentation - README or documentation site changes - [ ] 🔧 Chore - dev tooling, CI, config - [ ] 💥 Breaking change ## 📷 Screenshots / gifs / video [optional] N/A. ## 🔗 Related Issues Corrects comments added in #975. ## ✅ Tests added? - [ ] 👍 yes - [x] 🙅 no, not needed - [ ] 🙋 no, I need help Comments and one test name. The nine `LocatedRowMarker` tests still pass unchanged. ## 📚 Docs updated? - [ ] 🔖 README.md - [ ] 🔖 CHANGELOG.md - [ ] 📖 help site - [ ] 🧪 Marked any pre-release-only features - [x] 🙅 not needed Nothing user-visible changes. ## Anything else we need to know? [optional] No code changes: 6 insertions and 6 deletions, all inside comments and one `it` title.
15 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📝 PR Overview
The mark that shows where an inspector row's frames sit was a one-shot DOM sweep over the rows a table had rendered. Tabulator builds a row's element on its first render, so the sweep could not reach a row that had never been on screen. Pick a row, then scroll down to a marked row below the viewport or expand a tree row whose children are built afterwards, and those rows arrive unmarked.
The mark now belongs to the table rather than to a list of elements: the marker records the ids it wants, and the row formatter lights a row as it stamps it. That also removes the reason a pick's mark had to wait for the view to finish moving, so the handler #974 added loses its ordering, its awaits and three of its tests.
🛠️ Changes made
LocatedRowMarkerrecords the wanted ids per table host, androwIndexStamper/stampRowPathlight a row as they stamp it. EveryrowFormatterin the app already routes through one of those two, so no table factory needed a marker plumbed into it.inspectorLocateHandlermarks, then asks the view to move, and waits for nothing. A row the move renders lights itself. Gone with the ordering: the async body, the emphasis read-back, and the three tests that only covered which report won a race.🧩 Type of change (check all applicable)
📷 Screenshots / gifs / video [optional]
N/A. What changes is whether a row that was never on screen is marked when you reach it.
🔗 Related Issues
Follows #974, which added the shared handler this simplifies.
✅ Tests added?
Four new
LocatedRowMarkercases: a row arriving after the mark lights itself, a re-used element loses a stale mark, a table nothing has marked is left alone, and a table the mark has left stops lighting rows.📚 Docs updated?
No entry: the Inspector is unreleased, so this belongs to its existing entry. Ticked to record that it was considered.
Anything else we need to know? [optional]
Where to start.
log-viewer/src/components/locatedRow.ts—wantedByHost,stampandsweepare the whole mechanism.inspectorLocate.tsis what falls out of it, now 16 lines with no async.What does not happen, since it reads as though it should. A class on a row element survives ordinary scrolling:
Row.create()is guarded bythis.created,Row.initialize()deletes cells but re-uses the element,RowManager.styleRowadds and removes parity classes rather than assigningclassName, and our renderer only detaches and re-attaches the element. The gap is the first render, not a later one.Why a walk up the DOM.
stampfinds its table by walking parents until it meets a marked host. The alternative was threading the view's marker through four table factories to reach the formatters. The walk is a handful of nodes per row per render, against cell rendering that is orders of magnitude more, and it keeps the change inside one file.Test plan.
Known unrelated failure locally. `lana/src/services/tests/servicesRuntime.test.ts` cannot resolve `effect` in a worktree not installed since #951. No `lana` file is touched here, and it passes in CI.
Follow-ups, not in this PR.