diff --git a/README.md b/README.md index 0174a55..1f886ac 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ The agent writes `.stackmap//diagram.json`, validates and repairs it, and

-- **Explore:** select a card for its details, connections and source evidence. **Trace** keeps a node's upstream and downstream; **Route** lights every path between two nodes. +- **Explore:** select a card to light its connections and dim the rest, with its details and source evidence in the inspector. **Trace** keeps a node's upstream and downstream; **Route** lights every path between two nodes. - **Find:** search titles, ids and types; the **lens** dims the node types you don't need. - **Guided views:** tabs the agent defines, like "Checkout path", that focus one part of the diagram. **Present** steps through them full screen. - **Flow:** **Play** sends pulses along whatever you're looking at: the whole diagram, a view, a trace or a route, hop by hop. diff --git a/packages/viewer/e2e/focus.spec.ts b/packages/viewer/e2e/focus.spec.ts index 1a24b3f..13a132e 100644 --- a/packages/viewer/e2e/focus.spec.ts +++ b/packages/viewer/e2e/focus.spec.ts @@ -37,12 +37,19 @@ test('the toolbar button enters focus, and the URL never keeps it', async ({ pag await expect(header(page)).toBeVisible(); }); -test('in focus, H hides the toolbar and shows the way back; leaving focus restores it', async ({ page }) => { +test('H hides the toolbar outside focus too', async ({ page }) => { await page.goto('/?page=release-delivery'); await expect(page.locator('.sm-card').first()).toBeVisible(); - // Outside focus H does nothing. + await page.keyboard.press('h'); + await expect(tool(page, 'Focus on the canvas (Z)')).toHaveCount(0); + await expect(header(page)).toBeVisible(); await page.keyboard.press('h'); await expect(tool(page, 'Focus on the canvas (Z)')).toBeVisible(); +}); + +test('in focus, H hides the toolbar and shows the way back; leaving focus restores it', async ({ page }) => { + await page.goto('/?page=release-delivery'); + await expect(page.locator('.sm-card').first()).toBeVisible(); await page.keyboard.press('z'); await page.keyboard.press('h'); await expect(tool(page, 'Leave focus (Z)')).toHaveCount(0); @@ -56,7 +63,6 @@ test('in focus, H hides the toolbar and shows the way back; leaving focus restor await page.keyboard.press('z'); await expect(header(page)).toBeVisible(); await expect(tool(page, /^Play the flow/)).toBeVisible(); - await expect(tool(page, 'Hide the toolbar (H)')).toHaveCount(0); }); test('Z and H are ignored while typing in search', async ({ page }) => { diff --git a/packages/viewer/e2e/visual.spec.ts-snapshots/grouped-dark-linux.png b/packages/viewer/e2e/visual.spec.ts-snapshots/grouped-dark-linux.png index fe9fe98..9810758 100644 Binary files a/packages/viewer/e2e/visual.spec.ts-snapshots/grouped-dark-linux.png and b/packages/viewer/e2e/visual.spec.ts-snapshots/grouped-dark-linux.png differ diff --git a/packages/viewer/e2e/visual.spec.ts-snapshots/grouped-light-linux.png b/packages/viewer/e2e/visual.spec.ts-snapshots/grouped-light-linux.png index bc9b0ee..3d17548 100644 Binary files a/packages/viewer/e2e/visual.spec.ts-snapshots/grouped-light-linux.png and b/packages/viewer/e2e/visual.spec.ts-snapshots/grouped-light-linux.png differ diff --git a/packages/viewer/e2e/visual.spec.ts-snapshots/lifecycle-dark-linux.png b/packages/viewer/e2e/visual.spec.ts-snapshots/lifecycle-dark-linux.png index 8d048eb..845f916 100644 Binary files a/packages/viewer/e2e/visual.spec.ts-snapshots/lifecycle-dark-linux.png and b/packages/viewer/e2e/visual.spec.ts-snapshots/lifecycle-dark-linux.png differ diff --git a/packages/viewer/e2e/visual.spec.ts-snapshots/lifecycle-light-linux.png b/packages/viewer/e2e/visual.spec.ts-snapshots/lifecycle-light-linux.png index 5a2359a..1fbf26e 100644 Binary files a/packages/viewer/e2e/visual.spec.ts-snapshots/lifecycle-light-linux.png and b/packages/viewer/e2e/visual.spec.ts-snapshots/lifecycle-light-linux.png differ diff --git a/packages/viewer/e2e/visual.spec.ts-snapshots/sample-dark-linux.png b/packages/viewer/e2e/visual.spec.ts-snapshots/sample-dark-linux.png index d3326f1..1c9e421 100644 Binary files a/packages/viewer/e2e/visual.spec.ts-snapshots/sample-dark-linux.png and b/packages/viewer/e2e/visual.spec.ts-snapshots/sample-dark-linux.png differ diff --git a/packages/viewer/e2e/visual.spec.ts-snapshots/sample-light-linux.png b/packages/viewer/e2e/visual.spec.ts-snapshots/sample-light-linux.png index 7727860..379c307 100644 Binary files a/packages/viewer/e2e/visual.spec.ts-snapshots/sample-light-linux.png and b/packages/viewer/e2e/visual.spec.ts-snapshots/sample-light-linux.png differ diff --git a/packages/viewer/e2e/visual.spec.ts-snapshots/sequence-dark-linux.png b/packages/viewer/e2e/visual.spec.ts-snapshots/sequence-dark-linux.png index 673a8dd..f3a3d45 100644 Binary files a/packages/viewer/e2e/visual.spec.ts-snapshots/sequence-dark-linux.png and b/packages/viewer/e2e/visual.spec.ts-snapshots/sequence-dark-linux.png differ diff --git a/packages/viewer/e2e/visual.spec.ts-snapshots/sequence-light-linux.png b/packages/viewer/e2e/visual.spec.ts-snapshots/sequence-light-linux.png index 62d6756..dca06dc 100644 Binary files a/packages/viewer/e2e/visual.spec.ts-snapshots/sequence-light-linux.png and b/packages/viewer/e2e/visual.spec.ts-snapshots/sequence-light-linux.png differ diff --git a/packages/viewer/e2e/visual.spec.ts-snapshots/stages-dark-linux.png b/packages/viewer/e2e/visual.spec.ts-snapshots/stages-dark-linux.png index 3faee80..b85d599 100644 Binary files a/packages/viewer/e2e/visual.spec.ts-snapshots/stages-dark-linux.png and b/packages/viewer/e2e/visual.spec.ts-snapshots/stages-dark-linux.png differ diff --git a/packages/viewer/e2e/visual.spec.ts-snapshots/stages-light-linux.png b/packages/viewer/e2e/visual.spec.ts-snapshots/stages-light-linux.png index 78fffb8..ddb35d0 100644 Binary files a/packages/viewer/e2e/visual.spec.ts-snapshots/stages-light-linux.png and b/packages/viewer/e2e/visual.spec.ts-snapshots/stages-light-linux.png differ diff --git a/packages/viewer/e2e/visual.spec.ts-snapshots/workflow-dark-linux.png b/packages/viewer/e2e/visual.spec.ts-snapshots/workflow-dark-linux.png index e36eff1..1a8d638 100644 Binary files a/packages/viewer/e2e/visual.spec.ts-snapshots/workflow-dark-linux.png and b/packages/viewer/e2e/visual.spec.ts-snapshots/workflow-dark-linux.png differ diff --git a/packages/viewer/e2e/visual.spec.ts-snapshots/workflow-light-linux.png b/packages/viewer/e2e/visual.spec.ts-snapshots/workflow-light-linux.png index a06aaf5..595f19f 100644 Binary files a/packages/viewer/e2e/visual.spec.ts-snapshots/workflow-light-linux.png and b/packages/viewer/e2e/visual.spec.ts-snapshots/workflow-light-linux.png differ diff --git a/packages/viewer/src/canvas/DiagramCanvas.test.tsx b/packages/viewer/src/canvas/DiagramCanvas.test.tsx index 034afa8..b8275c9 100644 --- a/packages/viewer/src/canvas/DiagramCanvas.test.tsx +++ b/packages/viewer/src/canvas/DiagramCanvas.test.tsx @@ -42,7 +42,8 @@ describe('DiagramCanvas', () => { ); nodeCardRenders.mockClear(); fireEvent.click(container.querySelector('.sm-card[data-card-id="orders"]')!); - // orders gains focus + the tab stop; edge (the default tab stop) loses it. The other four stay put. - expect([...new Set(nodeCardRenders.mock.calls.map(([id]) => id))].sort()).toEqual(['edge', 'orders']); + // orders gains focus + the tab stop; edge (the default tab stop) loses it; sessions, not a neighbour, dims. + // The other three stay put. + expect([...new Set(nodeCardRenders.mock.calls.map(([id]) => id))].sort()).toEqual(['edge', 'orders', 'sessions']); }); }); diff --git a/packages/viewer/src/chrome/IdentityCard.test.tsx b/packages/viewer/src/chrome/IdentityCard.test.tsx deleted file mode 100644 index b9b756a..0000000 --- a/packages/viewer/src/chrome/IdentityCard.test.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { render, screen } from '@testing-library/react'; -import { describe, expect, it } from 'vitest'; -import type { DiagramDraft } from '@stackmap/core'; -import { IdentityCard } from './IdentityCard'; - -const draft: DiagramDraft = { - kind: 'architecture', - title: 'A very long diagram title that will not fit in the identity card', - subtitle: 'A very long diagram subtitle that will not fit in the identity card either', - direction: 'RIGHT', - nodes: [], - edges: [], -}; - -describe('IdentityCard', () => { - it('carries title on the diagram title and subtitle', () => { - render(); - expect(screen.getByText(draft.title)).toHaveAttribute('title', draft.title); - expect(screen.getByText(draft.subtitle!)).toHaveAttribute('title', draft.subtitle); - }); -}); diff --git a/packages/viewer/src/chrome/IdentityCard.tsx b/packages/viewer/src/chrome/IdentityCard.tsx deleted file mode 100644 index 26ab797..0000000 --- a/packages/viewer/src/chrome/IdentityCard.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Ellipsis, Workflow } from 'lucide-react'; -import type { DiagramDraft } from '@stackmap/core'; -import { IconButton, PANEL_CLASS, PANEL_STYLE } from './ui'; - -export function IdentityCard({ draft, onDetails }: { draft: DiagramDraft; onDetails?: () => void }) { - return ( -
-
-
-
-
- {draft.title} -
- {draft.subtitle && ( -
- {draft.subtitle} -
- )} -
- {onDetails && ( - - - - )} -
- ); -} diff --git a/packages/viewer/src/chrome/Inspector.tsx b/packages/viewer/src/chrome/Inspector.tsx index b07b6bc..9bf71eb 100644 --- a/packages/viewer/src/chrome/Inspector.tsx +++ b/packages/viewer/src/chrome/Inspector.tsx @@ -38,12 +38,20 @@ const LINE_STYLES: { key: string; label: string; test: (e: DiagramEdge) => boole ]; function Legend({ draft }: { draft: DiagramDraft }) { + const { dispatch } = useExplore(); const lines = LINE_STYLES.filter((l) => draft.edges.some(l.test)); + // A row that unmounts under the pointer never gets its mouseleave. + useEffect(() => () => dispatch({ type: 'peek', nodeType: null }), [dispatch]); return ( <> -