lab: patch live updates in place (keyed DOM morph) so background refresh stops wiping typed input #57
Labels
No labels
bug
enhancement
in-progress
needs-info
needs-triage
p0
ready-for-agent
ready-for-human
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/nixos#57
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem Statement
When I open lab on my phone and start typing a name into a project's "New instance" label field, the page refreshes itself every few seconds and wipes out what I've typed — along with my keyboard focus, so the on-screen keyboard dismisses. I can never finish naming an instance.
More broadly, the project list visibly "reloads" on a timer: it flickers, the "connecting…" spinner resets, and I can lose my scroll position — because every background refresh rebuilds the whole live region from scratch. The same refresh also clobbers a half-typed login code. The filter box is the only field that survives, and only because it happens to live outside the rebuilt region.
Solution
Background refreshes update the page in place — patching only what actually changed — instead of rebuilding the live region. Typing into any field (instance label, login code), my caret, my scroll position, my filter, and the "connecting…" spinner all survive a refresh, even when real data changes underneath me. Nothing visibly reloads.
The list still updates live (an instance flipping from "connecting…" to an "Open" link appears on its own, badge counts stay accurate), but it never interrupts what I'm doing. This also lays the groundwork so a future per-project context menu can stay open seamlessly while the list refreshes behind it.
User Stories
Implementation Decisions
Approach. Replace the wholesale
innerHTMLswap of the live region with an in-place, keyed DOM morph that patches only what changed. Hand-rolled, vanilla, inline in the template — no library, no JS build step. The architecture stays server-rendered hypermedia; the server keeps returning the same HTML fragment.Deep module — the morph. A single function with a tiny, stable interface —
morph(liveContainer, incomingContainer)— that mutates the live container to match the incoming one and hides all reconciliation complexity:Input contract. The morph never writes
valueor selection on any input/textarea. These are always client-owned (the server renders them empty), so typing survives even a genuine data update. This is the crux of the fix.Key contract (server-rendered attributes the morph depends on).
data-name).data-instance).data-key), so the banner appearing/disappearing on a login-state change doesn't misalign positional matching.Live-update loop (glue around the morph).
Server changes are minimal. Add the
data-instanceattribute to instance rows anddata-keyto the top-level blocks in the live fragment template. No handler, route, or response-shape changes; no JSON API.Rejected alternatives (see Further Notes for rationale): a minimal pause-while-typing band-aid; adopting htmx + idiomorph; a client-rendered SPA with a JSON API. The morph was chosen as the smallest change that fully fixes the clobber and the flicker while preserving the dependency-free, no-build, no-JS-fallback shape.
Testing Decisions
data-name,data-instance,data-key). This guards against a future template edit silently dropping a key and degrading live updates.Out of Scope
Further Notes
fwpulls within ~5 minutes and the dev microvm rebuilds, at which point re-verify on the authenticated URL and on a phone.