Skip to content

feat(schema): full lineage view in a live new tab — drag-move, undo/redo, detail pane#55

Merged
BorisTyshkevich merged 4 commits into
mainfrom
feat/schema-view-new-tab
Jun 27, 2026
Merged

feat(schema): full lineage view in a live new tab — drag-move, undo/redo, detail pane#55
BorisTyshkevich merged 4 commits into
mainfrom
feat/schema-view-new-tab

Conversation

@BorisTyshkevich

Copy link
Copy Markdown
Collaborator

Summary

Reworks the fullscreen schema-lineage view from an in-app modal overlay into a real browser tab driven by the opener, with an in-app overlay fallback when pop-ups are blocked or COOP severs the opener.

New-tab, live via opener

  • window.open('', '_blank') is same-origin (about:blank), so the opener builds + drives the child DOM directly — no second JS bundle, no postMessage. The opener keeps the OAuth token + ch-client, so click-to-detail still fetches live and writes results into the child document.
  • The page CSS + data-theme/data-density are copied into the tab.
  • New injected seams on createApp(env): openWindow, stylesText. h()/s() gained a withDocument(doc, fn) seam so the same builders populate the child realm. Cross-realm fix: node detection duck-types on nodeType instead of instanceof Node (a child window has its own Node constructor, which silently stringified elements to [object HTMLDivElement]).
  • Opens synchronously in the click gesture (an await-then-open would be pop-up-blocked); the lineage is fetched, then render()/fail() fills the tab.

Headline

Schema: <db> · engine colour legend (incl. Buffer/Merge) · day/night switcher · right-aligned actions cluster. No ✕ in the tab (the browser tab's own close serves that); the overlay fallback keeps it.

Mouse model — three distinct cursors

  • pointer (finger) over a card → click opens the detail pane (columns / keys / partitions / DDL)
  • move ✛ when ⌘/Ctrl is held over a card → ⌘/Ctrl-drag relocates the node; only its incident edges re-route (straight, box-clipped)
  • grab over empty canvas → plain drag pans; wheel pans, ⌘/Ctrl+wheel zooms, double-click fits
  • Esc closes the detail pane (overlay: pane first, then overlay)

Node move: undo/redo + persistence

  • ⌘/Ctrl+Z undo, ⌘/Ctrl+Shift+Z or ⌘/Ctrl+Y redo, plus Undo/Redo buttons in the headline (disabled when nothing to undo/redo). Keyboard and buttons share one history.
  • Manually-moved positions persist per result (pinned to the clicked tab's result, captured before the fetch).

Other

  • Enum/long-type truncation so a giant Enum8(...) column can't blow out a card's (and the graph's) width; the full type still shows in the detail pane.

Architecture / conventions

Pure geometry, move-history, and card math live in src/core/ (graph-layout.js new, schema-cards.js) at 100% coverage; DOM glue stays in src/ui/; side effects are injected seams. No new runtime deps.

Tests

985 pass, per-file coverage gate green (pure/render layers 100; app.js glue above its floor).

Verification

Built and smoke-tested live on the github.demo cluster (agent Chrome): real tab renders with full legend, ⌘-drag move + edge re-route, click→detail (live opener fetch), Esc-close, theme toggle, and undo/redo via both keyboard and buttons.

🤖 Generated with Claude Code

BorisTyshkevich and others added 4 commits June 27, 2026 13:46
…edo, detail pane

Reworks the fullscreen schema-lineage view from an in-app modal overlay into a
real browser tab driven by the opener (same-origin about:blank), with an
in-app overlay fallback when pop-ups are blocked / COOP severs the opener.

- New tab kept live by the opener: the page CSS + theme are copied in, and the
  opener (which holds the OAuth token + ch-client) fetches node detail on demand
  and writes it into the child document. `window.open`/`stylesText` are injected
  seams on createApp(env); h()/s() gained a `withDocument` seam so the same
  builders populate the child realm (duck-typed node check fixes cross-realm
  `instanceof Node`).
- Headline: "Schema: <db>", the engine colour legend (incl. Buffer/Merge), a
  day/night switcher, and a right-aligned actions cluster; no ✕ in the tab.
- Mouse model with three distinct cursors: pointer (click → detail pane),
  move ✛ (⌘/Ctrl-drag → relocate node, incident edges re-route straight),
  grab (plain drag → pan). Esc closes the detail pane.
- Node-move undo/redo (⌘Z / ⌘⇧Z / ⌘Y + headline buttons), per-result position
  persistence, and an Enum/long-type truncation so a card can't blow out the
  layout width.

Pure geometry/history/card math lives in src/core (graph-layout.js,
schema-cards.js) at 100% coverage; the DOM glue stays in src/ui. 985 tests pass,
per-file coverage gate green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CNaybba6T2qFDDEYWqzXh1
…a:<db>"

The full view opens in a fresh tab with no focused element, so real ⌘/Ctrl
keystrokes never reached the document listener — the cursor stayed in click
mode and node drag (and undo/redo) didn't work until you clicked in first.

- Make the canvas focusable (tabindex=-1) and focus it after render, and call
  win.focus() on open so the tab is foregrounded — keydown now lands without a
  prior click. Verified live: activeElement === canvas, a real ⌘ keydown toggles
  the .modkey cursor state.
- Name the browser tab "Schema:<db>" (e.g. "Schema:eth") instead of the generic
  "Schema graph"; the in-app overlay fallback never touches the main app title.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CNaybba6T2qFDDEYWqzXh1
…olling)

The canvas.focus() added for keyboard handling scrolled the focused canvas into
view, pushing the headline bar above the viewport on first load — it only
reappeared after scrolling up.

- focus({ preventScroll: true }) so focusing never scrolls the header off.
- overflow: hidden on body.schema-tab — the full-height view never scrolls (the
  graph pans within the canvas), so there's nothing to scroll the bar away.

Verified live: barTop 0, scrollTop 0, body not scrollable, canvas still focused
(⌘/Ctrl keys still drive cursor mode + undo/redo).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CNaybba6T2qFDDEYWqzXh1
Review follow-ups for the full schema view (PR #55):

- Overlay fallback: guard render()/fail() once the view is closed so a
  late-arriving fetch can't attach (and leak) ⌘Z/⌘Y handlers on the main
  document that would swallow the editor's own undo/redo.
- expandSchemaGraph: wrap the whole post-open flow in try/catch → view.fail,
  so a token-refresh rejection or build throw can't strand the tab on
  "Loading…".
- Node ⌘-drag: end the drag on a buttons===0 move (release off-window no
  longer sticks the node to the cursor); re-read the viewBox each move so a
  ⌘/wheel zoom mid-drag keeps tracking the pointer.
- Re-route edge LABELS with their edges on a move (not just the paths), and
  put a straightened 2-point edge's label on the segment midpoint instead of
  the target arrowhead.
- Grow the layout bounds on a move so Fit/double-click can reframe a node
  dragged past dagre's original extent.
- Refit the full view + overlays on window resize (scoped opt; inline panes
  unchanged) so drag/pan keep tracking after a resize.
- Overlay theme toggle routes through app.toggleTheme (state + saved pref +
  header icon stay in sync); the tab toggle stays local. Icon rebuilt inside
  withDocument so it's created in the view's own realm.
- Clear the latched .modkey cursor on window blur.
- Reuse: panBy + the new-tab drag share dragDeltaToSvg; precompute each node's
  incident edges once instead of rescanning per mousemove frame.

Default theme is now light (day) — state fallback + the template's initial
paint, so there's no dark flash before JS runs.

Tests added for every new branch; 994 pass, per-file coverage gate green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G1pC6hkksFXLogTCVgNSgK
@BorisTyshkevich BorisTyshkevich merged commit 41277ae into main Jun 27, 2026
4 checks passed
@BorisTyshkevich BorisTyshkevich deleted the feat/schema-view-new-tab branch June 27, 2026 13:18
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