diff --git a/docs/README.md b/docs/README.md index 3841369d..ffe47edd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -21,6 +21,7 @@ Welcome to the GraphDone documentation! This directory contains comprehensive gu ### [Developer Guides](./guides/) - [Getting Started](./guides/getting-started.md) - Setup and first steps - [Architecture Overview](./guides/architecture-overview.md) - System design and technical decisions +- [Web / UI Architecture](./guides/web-ui-architecture.md) - View system, graph canvas, mobile shell, node inspector - [Testing Guide](../tests/README.md) - **E2E testing with robust authentication system** - [SQLite Deployment Modes](./guides/sqlite-deployment-modes.md) - Local dev vs Docker authentication storage - [User Flows](./guides/user-flows.md) - How teams actually use GraphDone diff --git a/docs/guides/architecture-overview.md b/docs/guides/architecture-overview.md index 1bf6f774..65e87687 100644 --- a/docs/guides/architecture-overview.md +++ b/docs/guides/architecture-overview.md @@ -8,6 +8,11 @@ GraphDone is architected around three core principles: 2. **Real-Time First**: Changes propagate immediately to all participants 3. **Democratic Coordination**: Priority emerges from community validation, not top-down assignment +> **Client / UI layer:** for the web app's structure (view system, graph canvas, +> mobile shell, node inspector) see [web-ui-architecture.md](./web-ui-architecture.md). +> Some sections below describe the forward-looking server/infra target, not all of +> which is wired up today. + ## Current Architecture (v0.3.1-alpha) ```mermaid diff --git a/docs/guides/web-ui-architecture.md b/docs/guides/web-ui-architecture.md new file mode 100644 index 00000000..ef275135 --- /dev/null +++ b/docs/guides/web-ui-architecture.md @@ -0,0 +1,54 @@ +# Web / UI Architecture + +How the client (`packages/web`, React 18 + Vite + Tailwind + D3) is structured. +This is the companion to [architecture-overview.md](./architecture-overview.md), +which covers the server / graph engine / data layer. + +```mermaid +graph TD + App["App.tsx
GraphProvider + ViewModeProvider"] + App --> Layout["Layout
sidebar · header · MobileBottomNav"] + Layout --> WS["Workspace
graph selector · data queries"] + WS --> VM["ViewManager
renders the active view mode"] + WS --> Insp["NodeInspector (docked)
+ on-canvas expand peek"] + + VM -->|list / cards| Card["CardView"] + VM -->|graph| SGV["SafeGraphVisualization
→ InteractiveGraphVisualization (D3)"] + VM -->|table · kanban · gantt
calendar · dashboard · activity| Other["other views"] + + VMC["ViewModeContext
active mode + persistence"] -.-> VM + VMC -.-> Layout + Insp --> Modes["Card · Contents (lazy markdown) · Diagram (sub-graph)"] + Audit["mobile-audit tests (CI)
layout · contrast · dialogs"] -.->|gate every screen| VM +``` + +## Pieces + +- **App shell** — `App.tsx` wraps the tree in `GraphProvider` (current graph + + drill-in/ascend) and **`ViewModeContext`** (`contexts/ViewModeContext.tsx`), the + single source of truth for the active view, persisted to `localStorage`. `Layout` + draws the chrome. +- **View system** — `ViewManager` renders one of 8 modes: `cards`, `graph`, + `table`, `kanban`, `gantt`, `calendar`, `dashboard`, `activity`. Phones default to + **`cards`** (a readable list); desktop defaults to `graph`. +- **Graph** — `SafeGraphVisualization` error-boundary-wraps + `InteractiveGraphVisualization`, the D3 force-directed canvas (one-shot physics, + viewport culling, LOD by zoom — see `LOD_THRESHOLDS`). +- **Node inspector** — a docked `NodeInspector` plus an on-canvas **expand-in-place** + peek, each with a **Card / Contents / Diagram** toggle readable at any zoom + (`NodeContentRenderer` lazy-loads markdown/Prism; `NodeSubgraphPreview` draws a + capped static sub-graph). In-canvas card titles have a zoom-decoupled + **legibility floor**. + +## Responsive tiers (boundary: Tailwind `md`, 768px) + +- **Phone (` { try { diff --git a/tests/diagnostics/node-expand-legibility.spec.ts b/tests/diagnostics/node-expand-legibility.spec.ts index 0cae3874..5eff8775 100644 --- a/tests/diagnostics/node-expand-legibility.spec.ts +++ b/tests/diagnostics/node-expand-legibility.spec.ts @@ -111,9 +111,6 @@ test.describe('node expand-in-place + legibility floor @geometry', () => { await expect(panel, 'peek stays anchored through zoom').toBeVisible(); await page.keyboard.press('Escape'); await expect(panel, 'Esc closes the peek').toBeHidden({ timeout: 5000 }); - - // eslint-disable-next-line no-console - console.log('[expand] ok — anchored Card/Contents/Diagram peek verified'); }); test('PR-4: title stays above the on-screen legibility floor when zoomed out', async ({ page }) => { @@ -125,9 +122,7 @@ test.describe('node expand-in-place + legibility floor @geometry', () => { // Zoom OUT into the band where the native (un-counter-scaled) title would be // sub-readable (k < ~0.857) but the label is still on screen. const k = await zoomOutInto(page, 0.45, 0.7); - // eslint-disable-next-line no-console - console.log('[legibility] zoomed to k=' + k.toFixed(3)); - expect(k, 'reached the counter-scale band (k < 0.857)').toBeLessThan(0.857); + expect(k, `reached the counter-scale band (k=${k.toFixed(3)} < 0.857)`).toBeLessThan(0.857); const probe = await page.evaluate(() => { const texts = [...document.querySelectorAll('.graph-container svg .node-title-text')] as SVGTextElement[]; @@ -151,9 +146,6 @@ test.describe('node expand-in-place + legibility floor @geometry', () => { // (e.g. 14px * 0.5 = 7px). expect(probe.screenHeight, `title on-screen height >= floor (${LEGIBLE_FLOOR_PX}px)`).toBeGreaterThanOrEqual(LEGIBLE_FLOOR_PX - 3); // Zoomed into the band, the counter-scale should be actively boosting (> 1). - expect(probe.groupScale, 'legibility counter-scale is engaged when zoomed out').toBeGreaterThan(1); - - // eslint-disable-next-line no-console - console.log('[legibility] title screenHeight=' + Math.round(probe.screenHeight) + 'px, groupScale=' + probe.groupScale); + expect(probe.groupScale, `legibility counter-scale engaged when zoomed out (h=${Math.round(probe.screenHeight)}px, scale=${probe.groupScale})`).toBeGreaterThan(1); }); });