Commit Graph
8 Commits
Author SHA1 Message Date
amosblomqvist 9fac2a0471 Fix merged layout: right sidebar is a full-height sibling; plan graph renders
The weave had nested the right sidebar inside the transcript column
(below the header), so the phase chip sat above the panel instead of left
of it and the panel was bounded by the scroller area. The column is now
a true sibling of the middle column under flex h-dvh, per the UI stream's
original design; TreePanel's aside gets flex-1 min-h-0 so it shares the
column with the fleet panel instead of overflowing it. The plan-graph
viewport (ResizeObserver-measured) now has real size, so the mermaid
view draws.

Headless-verified at 1280x800: panel spans 0..800 top-to-bottom in both
modes, chip right edge < panel left edge in both modes, graph SVG = 9
boxes + 8 beziers, console clean.
2026-09-02 17:09:10 +03:00
amosblomqvist fe88fa87ec Tree panel mode lives in the root layout: no open-then-close on session open
The persisted mode (lh:tree-panel-mode) was read inside TreePanel — a
per-page component that remounts on every session navigation. First
paint rendered the SSR-safe "list" default, then the mount effect
applied the stored mode: with the animated frame, opening a session
visibly opened the panel and then closed it. The mode now lives in a
root-layout provider (the SidebarProvider pattern) and survives
navigations untouched — the panel mounts already in the right mode.
The default stays "list" so SSR and first client render agree; the
stored preference applies once, on provider mount.
2026-09-02 02:43:28 +03:00
amosblomqvist 8dda21ce1b Tree panel: animated width toggle, no reflow flash; steady chat header
- The three panel modes were three separate fixed-width asides, so a
  mode switch jumped the whole transcript column width in one frame:
  scrollbar flash, overshoot, re-correct. Now ONE aside animates its
  width exactly like the left sidebar (reusing the sidebar-motion
  hook, so it inherits the provider's mount-gated transition), with
  a FIXED-width inner layer clipped by overflow-hidden — content
  never reflows mid-transition.
- The chat header was auto-height: the sidebar toggle (a size-9 icon
  button) renders only while the sidebar is closed, so opening it
  collapsed the row and slid the title + phase chip up. The header is
  now a constant h-10.
2026-09-02 02:39:49 +03:00
Amos 475726a976 Layout + graph interaction fixes
- Chat page: the tree panel is now a full-height sibling of the whole
  transcript column (header + scroller), exactly like the left sidebar —
  the session header (name + phase chip) spans from the left sidebar's
  edge to the panel's edge instead of riding over the panel.
- Plan graph: the zoom controls sit inside the pan surface, so their
  pointerdown started a capture drag and the click never reached the
  button (felt dead). startDrag now ignores presses on buttons.
- Fully zoomed out IS the fit view: zooming to the floor snaps to the
  centered fit (pure zoomedGraphView), pan is locked there (no drag
  begins, cursor-default instead of grab), and sitting at the floor
  re-arms auto-fit so later tree edits keep the graph fitting.
- Fit padding 48px → 16px: the default view fits more graph.
2026-09-01 19:02:18 +03:00
Amos 826de071dc Plan graph: the expanded mermaid-style view of the plan tree
The nested list stays as the default/collapsed view; the new expand
toggle (list header) opens the graph — a hand-built SVG diagram
(mermaid STYLE, no mermaid renderer: full control of the styling so it
speaks this app's design language):

- Top-down layered layout (topoLayout depth = rows, authoring order
  within a row, rows centered on the widest) — vertical space is the
  cheap axis; boxes + vertical bezier arrows with arrowheads.
- Status coloring matches the list marks: done nodes are colored in
  (teach-phase green tint — the graph fills in as the session
  progresses), current = the highlighted position, next = unlocked
  ring, upcoming = quiet.
- Drag pan + wheel zoom (to the cursor, non-passive), +/- and
  fit-to-panel buttons. The zoom FLOOR is fit: fully zoomed out, the
  whole diagram always fits the panel — it can never disappear into
  nothing. Auto-fits on measure/layout change until the learner takes
  manual control; fit re-enables it.
- Panel widens to w-96 in graph mode; mode persists
  (lh:tree-panel-mode: rail|list|graph, legacy key migrated).
- wrapTitle + graphLayout pure, unit-tested; TreeGraph SSR-render
  tested (statuses, arrows, wrapping, data-next).
2026-09-01 18:34:58 +03:00
Amos fbe4f16305 In-session phase presentation: header chip + dotted boundary dividers
- components/phase-divider.tsx: PhaseChip (header, next to the session
  title: phase-colored dot + Probing/Planning/Teaching) and PhaseDivider
  (the transcript boundary: color-coded dotted rules + a small chip —
  'Plan · N nodes' at the plan seam, 'Teaching' at the teach seam; probe
  never renders, phases only advance). planNodeCount reads the seam
  message's first plan tree.
- chat.tsx: dividers render at MESSAGE boundaries (phaseTimeline, one
  linear fold) — the seam is the transition to the message's phase, so
  the seam message's prose sits below its divider. requiresReveal now
  uses phaseAt3 teach (an occupied node), so probe- AND plan-phase
  questions auto-open.
- Retired the part-level plan-divider (artifact registry): the boundary
  is message-level, not part-level. Its tests replaced by
  render-phase-divider tests; core/phases gains tested phaseTimeline.
2026-09-01 18:31:01 +03:00
Amos e92140f993 Shell/core import law: public API barrels + ESLint enforcement (stage 2)
The shell now imports through exactly two barrels, enforced by
import/no-restricted-paths (npm run lint):

  components/**  ->  "@/src/core"      (pure core API, browser-safe)
  app/**         ->  "@/src/core" + "@/src/server" (server entries)

Zone violations are lint errors with architecture-pointer messages; the
pure-core modules (contracts, question-core, plan-tree, phases, pending,
math-delims, ui-message) may never import agent/server/store.

Supporting moves (direction: IO layers -> core):
- core/index.ts: the core public API barrel
- core/ui-message.ts: moved from src/agent (it was shell-facing all along)
- quiz/ask input zod schemas moved agent/tools -> core/question-core
  (the question contract, alongside the grading logic)
- ChatRecord/ChatSummary moved store/chats -> core/contracts (single schema)
- GraphSnapshot/NodeStateClass moved core/graph -> core/contracts (the
  /api/graph payload IS a contract; the pure renderer needs only these)
- src/server/{chats,graph,jobs}.ts + index.ts: thin server entries for the
  transport layer; GET /api/graph's fold side-effect now lives behind
  graphView() instead of inline in the route

Gates: tsc clean, 190/190 tests, eslint clean (law verified to fire on
store/agent/subpath violations), production build clean.

(Repo repair en route: a git object (app/chat/[id] tree) was missing from
the object store — reconstructed byte-exactly via mktree from its blob;
the broken node_modules that stalled the build was cleanly reinstalled.)
2026-08-31 19:51:17 +03:00
Amos 53be8a2834 W7: persistent right-panel plan tree 2026-08-22 11:37:28 +03:00