The build story
A log explorer that doesn’t lose your place.
A working prototype, and the story of building it. I went looking for a papercut worth fixing — a small, real, unglamorous problem — and built the fix one decision at a time. The demo is the argument; this is the reasoning behind it.
The problem
Chasing an ID scatters the investigation across tabs
You’re troubleshooting a live incident. Logs are tailing. You’ve filtered to a single request ID — the failing one — and the picture is finally narrowing. You click a line to see the context around it: the calls before, the calls after.
A new tab opens.
The context is there, but nothing else is — no filter, no live tail, just a slice. Your narrowed view is waiting back in the first tab, so you flip over, find the next interesting line, and click again. Another tab. By the time you’ve worked through the incident you have a fan of tabs, each holding one slice of the story, and you’re piecing the timeline back together by switching between them.
The work of narrowing in doesn’t follow you to where you look at it.
The idea
Open context where the line lives
Click a line — call it the anchor — and the rows around it expand inline, dimmed for contrast so the matching lines stay bright. Open a second context without losing the first. The filter doesn’t reset. The position doesn’t reset. The view stays in place; the application carries the work it should have been carrying all along.
See it for yourself
The whole argument is in the interaction — two minutes, live.
What I built
A small prototype built around one incident
The investigation moment is the whole subject. The logs are mocked, filtering is three preset chips, and there’s no search, no virtualization, no loading states. Scope is narrow to keep the subject in frame.
The mock data got real work, though. It tells one tight incident — a config reload quietly shrinks a database pool, checkout starts failing, a reverse reload recovers it — and the cause line carries no request ID, so the trace filter can never surface it. Only context can. Every chip points at a moment in that story, and the debrief that closes the demo reads straight off the lines.
The cut
The animation looked great but wasn’t doing the work
My first instinct was to make context appear with motion: click an anchor, and the rows above and below grow out of it smoothly. I built it, and at a few dozen lines it looked great. At real log volumes it wouldn’t have — virtualization mounts and unmounts rows as you scroll, and the animation breaks with them.
But that wasn’t the real reason it went. The animation wasn’t doing work the prototype needed: the fan of tabs collapses the moment context opens in place, and a smooth reveal doesn’t close that gap any further. What in-place context does open up is a different problem — with several contexts in one long list, navigation and orientation become the design work. And since logs are read by developers, keyboard parity with the mouse isn’t a nice-to-have. So the animation went, and the next problem came forward.
The Legend
A static hint grew into the primary surface
The Legend wasn’t designed up front. It started as a static ? hint in the corner and became the prototype’s primary interaction surface — adaptive, clickable, and alive to its own firings. Each piece arrived as the answer to a problem the last one left behind.
The first design put More and Less buttons on each anchor. They worked until you scrolled; the anchor left the screen and took the buttons with it. The keyboard bindings — Shift+E to expand the most recent context, Esc to close it — kept working from anywhere, and with one path strictly worse, the buttons had to go. That left mouse users with a gap.
The corner hint closed it. It grew into a small toolbar that surfaces a binding only while pressing it would do something: Shift+E appears when there’s a context to expand, Esc when there’s one to close, E for the focused line. Each entry is also a button, so a click and a key press fire exactly the same action. When nothing applies, it falls back to ? + shortcuts — the surface is never empty.
Only show a binding when pressing it would do something.
That’s the rule, and three entries turned out to be the cap before the bar felt busy — [ and ] for hopping between anchors stayed in the shortcut sheet instead. One quieter problem remained: if you’d scrolled away from the context Shift+E was expanding, the press did nothing you could see. Auto-scrolling to the result has no right answer — a context grows in two directions at once — so the acknowledgement lives with the press itself. The entry pulses: a brief fade, gated behind prefers-reduced-motion, catching the eye in the periphery. Something happened.
What’s next
The pulse says it happened; a mini-map would say where
What I have in mind is a slim strip mirroring the list in miniature: anchors as marks, open contexts as highlighted bands, the viewport as a sliding frame. When a context expands fifty lines below your view, the mark pulses there. And with several contexts open, the strip carries orientation passively — a glance, not a read. The pulse handles this happened; the mini-map handles and here.
Closing
The application carries the work
The fan of tabs collapses. The filter persists, several contexts share one view, the bindings tell you what they’d do before you commit, and the actions you can’t see still announce themselves. None of it is dramatic alone. Together they make the application carry the work it should have been carrying all along.
If one rule survives the build, it’s the one the Legend taught me: build the small thing, watch where it falls short, and build the next piece to answer that. Everything that isn’t doing work gets cut — even when it demos well.
See it for yourself
The whole argument is in the interaction — two minutes, live.