Repository navigation
fix(grid): canvas render (blank + tab-switch blackout), motion/spinners, ERD readability, silent reconnect + perf - #41
Merged
Conversation
…errides Canvas grid rendered blank (only rgba-literal badges/icons visible) on macOS WKWebView: createColorReader reused one probe and reassigned style.color between synchronous getComputedStyle reads, but WKWebView returns a STALE computed color in that case — every token after the first froze at the first color's value, so text and grid lines painted the same color as the panel and vanished. Resolve each token on a throwaway node instead. Verified via pixel readback in a real WKWebView: foreground/grid/muted now resolve to distinct correct colors. Also remove the global motion additions from the connection-redesign PR: - the @media (prefers-reduced-motion: reduce) block forced animation-duration/transition-duration: 0.12s !important on every element, so spinners/loaders ran ~8x fast (broken) when macOS Reduce Motion is on. - the --ease-out/--ease-in-out/--ease-drawer token overrides silently repunched every ease-* utility app-wide (--ease-drawer was unused).
🦋 Changeset detectedLatest commit: 17c1995 The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
… layout Data grid render/reactivity/memory: - draw(): build the ~20-field per-row draw context ONCE per frame instead of a fresh literal per visible row (was thousands of short-lived objects/sec during scroll → GC jank); use geom.frozenWidth instead of re-summing pinned cols. - computeRowTops returns null when no row is expanded (the common case) and rowIndexAtY/rowAtContentY fall back to O(1) idx*rowHeight math — no 8 MB Float64Array reallocated per infinite-scroll page on 1M-row tables. Verified the null path is numerically identical to the array path. - Split the canvas effects: the resurface effect now tracks only true layout deps; selection/focus/edit/hover/staged-edits moved to the lightweight repaint effect, so arrow-key nav and drag-select no longer re-run resurface or touch the backing store. syncCanvasSurface() skips all DOM writes + setTransform when size/DPR are unchanged. - settings: in-memory cache so the zoom/pinch hot path no longer does localStorage getItem + JSON.parse twice per step. - LRU-cap _tabExpandCache (retained fetched FK sub-view rows) at 12 entries. ERD schema visualizer: - Much larger layout spacing (ranksep 220→340, nodesep 48→84, COL/ROW/RANK gaps 96/52/260 → 150/84/400, roomier orphan grid) so cards don't crowd and edges have room to fan — fixes the cramped/tangled look. - Bigger, more legible cards (NODE_W 230→268, ROW_H 22→28, HDR_H 34→42) with more padding, larger column/type fonts, and roomier PK/FK badges.
… layout The LR dagre layout of a deep schema is inherently very wide, so fit-to-view scaled the whole bounding box down to an unreadable horizontal sliver (all tables in one thin band). The previous "bump to 0.55 centred" landed the user in the middle of that band, still unreadable. - ensureFit: if the whole graph can't fit at a legible size, land at a fixed READABLE_ZOOM (0.62) anchored at the graph's top-left and let the user pan, instead of fitting everything into a sliver. Small graphs still fit-and-centre. - Moderate the layout gaps (ranksep 340→240, nodesep 84→56, COL/ROW/RANK 110/72/200) so the graph isn't stretched into an even wider sliver, and raise TARGET_H to 2600 so each rank (esp. a hub) stacks into a squarer, narrower block rather than one long line — more vertical breathing room, less horizontal sprawl.
…opup The 30s health-ping showed a persistent (duration: Infinity) "Connection lost / Reconnect" toast that never auto-dismissed even after recovery, and nothing reconnected on its own — the user had to click it. Now the app heals itself, no popup: - reconnectPool()/silentReconnect(): rebuild the pool for the CURRENT connection config in place — no UI teardown, no full-screen overlay, no toast. Rate-limited (3s) and re-entrancy-guarded so bursts of triggers / an unreachable host can't loop connect_*. - Health monitor: on ping failure, flag connectionLost (subtle StatusBar red dot) and silently reconnect instead of toasting. Pings every 30s when healthy, every 6s while lost, and clears the instant it recovers. - Auto-reconnect triggers: `online` / window focus / visibilitychange (network is back / window refocused), table switch, schema change, manual refresh, and the failing query itself (network-error catch paths). - Widen isNetworkError to also match pool-timeout / connection-closed / "cannot reach" / idle-termination so cloud-Postgres idle drops are treated as network blips (auto-reconnected) rather than surfaced as SQL errors. The StatusBar keeps a subtle red indicator while reconnecting; no modal/toast.
…ader createColorReader fell back to opaque rgb(0,0,0) if getComputedStyle ever returned an empty colour, and that value fills the ENTIRE canvas (the --panel background) — the worst-case failure for a full-canvas fill. Empty results are only really possible when the throwaway probe node is measured against a momentarily-detached subtree (e.g. mid tab remount). - Append the throwaway node to a guaranteed-connected host (probe's parent only when the probe itself isConnected, else <body>); theme tokens live on <html> so <body> inherits them identically. - On an empty computed colour, reuse the last successfully-resolved colour (or a neutral grey, never black) and do NOT cache it, so the next read resolves cleanly once the DOM settles. Re-verified in a real WKWebView: all tokens still resolve to correct distinct colours (panel [11,11,11], foreground [235,235,235], grid [39,39,39], …).
The earlier isNetworkError widening included bare 'timed out' and 'i/o error', which also match a Postgres statement/lock timeout (a slow query, not a dropped connection). That would flip connectionLost and make every following table tap tear down + rebuild the pool. Drop those two over-broad patterns; keep the connection-specific ones (pool timed out, connection/server closed, terminating connection, cannot reach, os errors).
…b-switch blank) Belt-and-suspenders for canvas painting across mounts / tab switches: - syncCanvasSurface() no longer short-circuits on unchanged dimensions; it always re-applies setTransform. (After the earlier effect split this is only called from the low-frequency layout effect, not on scroll/selection, so the cost is negligible.) A short-circuit here risked leaving a fresh/remounted canvas untransformed. - The layout effect now calls draw() synchronously whenever the surface is ok (not only on resize), so a fresh mount / tab switch can never composite an unpainted canvas (which would show the dark app background as a black grid). scheduleDraw() still coalesces concurrent updates. (isNetworkError over-match — 'timed out' / 'i/o error' — was already tightened in cccfbe4.)
…kout) Confirmed via on-device WKWebView instrumentation: after opening/switching tabs the grid went fully black while draw() ran with perfect state (correct size, rows, columns, colours). The context (_ctx) was still bound to a canvas WebKit had recreated on the tab switch (ctx.canvas detached, ctx.canvas !== canvasEl), so every draw painted an off-screen element and the visible canvas stayed black. Root cause was the c1114f3 perf change caching _ctx behind `if (!_ctx)`. - syncCanvasSurface() always re-fetches _ctx for the current canvasEl (getContext is idempotent per element, so it's cheap) — _ctx can never go stale. - New effect re-binds the context + re-applies size/transform + repaints whenever the <canvas> element identity changes, covering swaps not accompanied by a layout change. Adds a changeset so the release notes cover this PR's fixes (blank grid, tab-switch blackout, spinner/reduce-motion, ERD readability) + silent auto-reconnect.
broisnees
marked this pull request as ready for review
July 11, 2026 08:01
Collaborator
Author
|
lgtm. |
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
Fixes a set of issues introduced by the connection-redesign / grid-rewrite PR (#40), plus a memory/perf pass and silent auto-reconnect. Labeled
release:minor; a changeset (.changeset/canvas-render-motion-reconnect.md) drives the release notes.Bug fixes
getComputedStyle().colorfrom the shared canvas color-probe, collapsing every theme token to one value (text + grid lines invisible; only rgba-literal badges showed). Each color is now resolved on its own node. Verified in a real WKWebView via pixel readback.<canvas>WebKit had recreated on the switch, so draws hit an off-screen element._ctxnow always tracks the live canvas and re-binds on element change. Confirmed on-device (draw ran with perfect state butctx.canvas !== canvasEl).@media (prefers-reduced-motion)rule forcedanimation-duration: 0.12s !importanton everything. Removed the global motion/easing overrides.New
Performance / memory
Verification
npm run buildclean.