From c2bb80eb522b5c0dfd8789c94e3c8b62a3556417 Mon Sep 17 00:00:00 2001 From: Rohan Date: Tue, 10 Nov 2026 20:08:37 +0530 Subject: [PATCH] feat: add optional accessible editor guide --- README.md | 2 +- docs/capabilities.md | 4 +- docs/testing.md | 4 +- docs/verification.md | 18 +++ src/lib/ui/diagram.css | 34 +++-- src/lib/workbench/diagrams/Diagram.tsx | 3 - src/lib/workbench/shell/EditorTour.tsx | 126 +++++++++++++++++ src/lib/workbench/shell/ProjectHeader.tsx | 3 + src/lib/workbench/shell/Workbench.tsx | 7 +- src/lib/workbench/shell/WorkbenchPanels.tsx | 4 +- src/lib/workbench/shell/example.ts | 13 -- tests/e2e/editor-tour.spec.ts | 129 ++++++++++++++++++ tests/e2e/visual.spec.ts | 21 +++ ...ration-overview-dark-1440-visual-linux.png | Bin 103335 -> 104332 bytes ...oration-overview-dark-768-visual-linux.png | Bin 65330 -> 65774 bytes ...ation-overview-light-1440-visual-linux.png | Bin 97209 -> 98250 bytes ...ration-overview-light-768-visual-linux.png | Bin 63606 -> 64032 bytes ...ation-structure-dark-1440-visual-linux.png | Bin 91422 -> 92455 bytes ...ration-structure-dark-768-visual-linux.png | Bin 65061 -> 65497 bytes ...tion-structure-light-1440-visual-linux.png | Bin 89955 -> 90894 bytes ...ation-structure-light-768-visual-linux.png | Bin 64076 -> 64493 bytes ...ver-architecture-dark-768-visual-linux.png | Bin 67624 -> 62491 bytes ...er-architecture-light-768-visual-linux.png | Bin 59093 -> 53238 bytes .../diagnostics-dark-narrow-visual-linux.png | Bin 81961 -> 82364 bytes .../diagnostics-dark-visual-linux.png | Bin 109544 -> 110596 bytes .../diagnostics-light-narrow-visual-linux.png | Bin 80376 -> 80799 bytes .../diagnostics-light-visual-linux.png | Bin 106785 -> 107762 bytes .../editor-guide-dark-1440-visual-linux.png | Bin 0 -> 238484 bytes .../editor-guide-dark-768-visual-linux.png | Bin 0 -> 142307 bytes .../editor-guide-light-1440-visual-linux.png | Bin 0 -> 227494 bytes .../editor-guide-light-768-visual-linux.png | Bin 0 -> 136857 bytes ...ration-overview-dark-1440-visual-linux.png | Bin 99652 -> 100647 bytes ...oration-overview-dark-768-visual-linux.png | Bin 66923 -> 67386 bytes ...ation-overview-light-1440-visual-linux.png | Bin 97124 -> 98089 bytes ...ration-overview-light-768-visual-linux.png | Bin 62363 -> 62780 bytes ...ation-structure-dark-1440-visual-linux.png | Bin 76707 -> 77705 bytes ...ration-structure-dark-768-visual-linux.png | Bin 48928 -> 49385 bytes ...tion-structure-light-1440-visual-linux.png | Bin 75282 -> 76272 bytes ...ation-structure-light-768-visual-linux.png | Bin 48146 -> 48563 bytes ...ploration-retry-dark-1440-visual-linux.png | Bin 71186 -> 72161 bytes ...xploration-retry-dark-768-visual-linux.png | Bin 43690 -> 44117 bytes ...loration-retry-light-1440-visual-linux.png | Bin 70197 -> 71191 bytes ...ploration-retry-light-768-visual-linux.png | Bin 42745 -> 43185 bytes .../mobile-header-dark-390-visual-linux.png | Bin 8860 -> 9286 bytes .../mobile-header-dark-600-visual-linux.png | Bin 9030 -> 9463 bytes .../mobile-header-light-390-visual-linux.png | Bin 8831 -> 9236 bytes .../mobile-header-light-600-visual-linux.png | Bin 9010 -> 9430 bytes .../panels-dark-narrow-visual-linux.png | Bin 93005 -> 93386 bytes .../panels-dark-visual-linux.png | Bin 185092 -> 186143 bytes .../panels-light-narrow-visual-linux.png | Bin 90031 -> 90488 bytes .../panels-light-visual-linux.png | Bin 178510 -> 179481 bytes .../requirements-dark-1440-visual-linux.png | Bin 141306 -> 142308 bytes .../requirements-dark-768-visual-linux.png | Bin 87125 -> 87553 bytes .../requirements-light-1440-visual-linux.png | Bin 135412 -> 136403 bytes .../requirements-light-768-visual-linux.png | Bin 83750 -> 84160 bytes ...equirements-overview-dark-visual-linux.png | Bin 154412 -> 155415 bytes ...quirements-overview-light-visual-linux.png | Bin 141283 -> 142281 bytes ...uirements-table-dark-1440-visual-linux.png | Bin 142292 -> 143293 bytes ...quirements-table-dark-768-visual-linux.png | Bin 99828 -> 100258 bytes ...irements-table-light-1440-visual-linux.png | Bin 139113 -> 140095 bytes ...uirements-table-light-768-visual-linux.png | Bin 97613 -> 98022 bytes ...rover-lifecycle-dark-1440-visual-linux.png | Bin 127050 -> 107644 bytes .../rover-lifecycle-dark-768-visual-linux.png | Bin 59327 -> 61984 bytes ...over-lifecycle-light-1440-visual-linux.png | Bin 114986 -> 102813 bytes ...rover-lifecycle-light-768-visual-linux.png | Bin 51753 -> 54286 bytes .../rover-mission-dark-1440-visual-linux.png | Bin 114652 -> 122935 bytes .../rover-mission-dark-768-visual-linux.png | Bin 52601 -> 54908 bytes .../rover-mission-light-1440-visual-linux.png | Bin 103202 -> 110651 bytes .../rover-mission-light-768-visual-linux.png | Bin 48411 -> 44682 bytes ...er-requirements-dark-1440-visual-linux.png | Bin 152686 -> 151275 bytes ...ver-requirements-dark-768-visual-linux.png | Bin 88494 -> 84080 bytes ...r-requirements-light-1440-visual-linux.png | Bin 145729 -> 144397 bytes ...er-requirements-light-768-visual-linux.png | Bin 83713 -> 78705 bytes ...source-editor-dark-narrow-visual-linux.png | Bin 175248 -> 175622 bytes .../source-editor-dark-visual-linux.png | Bin 217697 -> 218652 bytes ...ource-editor-light-narrow-visual-linux.png | Bin 171874 -> 172310 bytes .../source-editor-light-visual-linux.png | Bin 214405 -> 215364 bytes ...l-information-dark-narrow-visual-linux.png | Bin 73085 -> 73463 bytes .../symbol-information-dark-visual-linux.png | Bin 92284 -> 93463 bytes ...-information-light-narrow-visual-linux.png | Bin 71133 -> 71567 bytes .../symbol-information-light-visual-linux.png | Bin 90414 -> 91374 bytes ...eability-matrix-dark-1440-visual-linux.png | Bin 141196 -> 142204 bytes ...ceability-matrix-dark-768-visual-linux.png | Bin 99590 -> 100018 bytes ...ability-matrix-light-1440-visual-linux.png | Bin 137365 -> 138322 bytes ...eability-matrix-light-768-visual-linux.png | Bin 97395 -> 97803 bytes .../workbench-dark-visual-linux.png | Bin 129013 -> 126453 bytes .../workbench-light-visual-linux.png | Bin 116785 -> 113226 bytes .../workbench-narrow-visual-linux.png | Bin 81220 -> 81654 bytes 88 files changed, 332 insertions(+), 36 deletions(-) create mode 100644 src/lib/workbench/shell/EditorTour.tsx create mode 100644 tests/e2e/editor-tour.spec.ts create mode 100644 tests/e2e/visual.spec.ts-snapshots/editor-guide-dark-1440-visual-linux.png create mode 100644 tests/e2e/visual.spec.ts-snapshots/editor-guide-dark-768-visual-linux.png create mode 100644 tests/e2e/visual.spec.ts-snapshots/editor-guide-light-1440-visual-linux.png create mode 100644 tests/e2e/visual.spec.ts-snapshots/editor-guide-light-768-visual-linux.png diff --git a/README.md b/README.md index c0beb19..95d5988 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ The landing page offers **Open app** and **Try an example**. `/app` opens a proj Choose **Load example** to open the **Inspection rover** in Diagram mode. Six source files describe four connected assemblies, declared component values, an inspection mission, operating states and quantitative requirements. -Follow the in-app guide through **architecture → mission → lifecycle → requirements → definitions**. The opening diagram includes a 480 Wh battery, two distinct drives, lidar, controller, camera and radio. Declared values, successions and satisfaction links are model content; the app does not execute or simulate them. +Explore the named views **architecture → mission → lifecycle → requirements → definitions**. Choose **Guide** in the project header for a tour of the editor controls. The opening diagram includes a 480 Wh battery, two distinct drives, lidar, controller, camera and radio. Declared values, successions and satisfaction links are model content; the app does not execute or simulate them. 1. Select another named view in Explorer or the diagram’s **Named view** selector. 2. Choose **Split** to see source; selecting a file keeps the active diagram open. diff --git a/docs/capabilities.md b/docs/capabilities.md index 8ea12a5..ec30035 100644 --- a/docs/capabilities.md +++ b/docs/capabilities.md @@ -29,8 +29,10 @@ Use **New part** and **Connect ports** in supported structural diagrams, or the Layout reconciliation checks every saved view, including inactive views. Verified commands use source-bound UTF-8 splices; free-form edits use conservative replacement ranges and require unchanged engine-recorded declaration headers, complete usage contexts and compatible resolved bindings. Unknown or ambiguous mappings are invalidated explicitly. This is bounded preservation, not a general persistent-identity guarantee. See [the implemented policy](architecture/identity-reconciliation.md) and [authoring contracts](architecture/graphical-authoring.md). -The explicit **Load example** project is [Inspection rover](../examples/inspection-rover/README.md): six source files and five guided views covering architecture, mission, lifecycle, requirements and component definitions. All five render bounded projections; their catalog entries retain limits. Inspection rover is the only bundled example; the legacy [Smart air workshop](../examples/air-compressor/README.md) remains available for explicit source import and regression coverage. Declared attributes show effective source members, with shorter labels for simple scalar defaults and exact declaration text on hover. Larger named structural diagrams use narrower cards and tighter spacing. Values are not evaluated. Action/state graphs show authored behavior, not execution. +The explicit **Load example** project is [Inspection rover](../examples/inspection-rover/README.md): six source files and five views covering architecture, mission, lifecycle, requirements and component definitions. All five render bounded projections; their catalog entries retain limits. Inspection rover is the only bundled example; the legacy [Smart air workshop](../examples/air-compressor/README.md) remains available for explicit source import and regression coverage. Declared attributes show effective source members, with shorter labels for simple scalar defaults and exact declaration text on hover. Larger named structural diagrams use narrower cards and tighter spacing. Values are not evaluated. Action/state graphs show authored behavior, not execution. The [visualization example library](../examples/README.md) records representative models, named views, intended visual quality and current gaps. Its checks distinguish successful engine evaluation from application rendering; use-case, package-graph and other unsupported renderer fixtures remain explicit development targets. The read-only requirements table inspects all applied requirement declarations, identifiers, owned documentation and declared satisfaction/verification links. It supports search, name sorting and CSV export. The traceability matrix uses requirements as rows and linked satisfiers/verifiers as columns, retaining exact declaration identities and negation. It does not infer missing relationships, compute verification verdicts or edit model content. Unknown/incomplete extraction is unavailable rather than an empty result. Source reveal requires that file's draft to match applied source. [Requirements data contract](architecture/requirements-data.md). + +The optional **Guide** in the project header introduces Explorer, layout, source application, model representations and project download for any open project. It can be dismissed and restarted, and does not alter project data. diff --git a/docs/testing.md b/docs/testing.md index 96f6a9a..710e1bc 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -109,7 +109,7 @@ For a critical regression, demonstrate that the assertion detects a deliberate, The initial-exploration failure regression lets the actual parse worker load, fails the following exploration WASM request, then retries with the asset restored. It checks unchanged source/presentations/history and keyboard focus on Scope. Four visual cases retain the retry state at desktop/narrow widths in both themes. -`rover.spec.ts` checks the actual default example, inherited source-declared attribute values, exact architecture connections, all six source files, the five-view guide, declared requirement links and source/geometry preservation for both native behavior families through portable reopen. `startExample()` explicitly chooses the retained air-workshop fixture for existing regressions; `waitForRoverStartup()` asserts the product default independently. Behavior captures cover desktop/narrow widths and both themes. +`rover.spec.ts` checks the actual default example, inherited source-declared attribute values, exact architecture connections, all six source files, all five named views, declared requirement links and source/geometry preservation for both native behavior families through portable reopen. `startExample()` explicitly chooses the retained air-workshop fixture for existing regressions; `waitForRoverStartup()` asserts the product default independently. Behavior captures cover desktop/narrow widths and both themes. ## OpenSysML 0.12 and requirements data @@ -122,3 +122,5 @@ The language-assistance suite additionally checks real Toolkit references and ex Drag helpers wait for stable heading and viewport geometry across animation frames before issuing one pointer gesture. This avoids reading a screen coordinate while a mode change is still fitting the camera; it does not retry a failed drag. The legacy workspace reopen scenario uses the same browser-normalized rendered-position helper as the newer preservation tests, while separately requiring the moved symbols and exact portable position doubles to survive reopen. The 0.12 assembly overview fixture records thirteen source-backed edges (ownership/reference plus typing) rather than the old six type-collapsed edges. Its routing regression still requires every route/label to avoid cards and reveals each exact declaration or type reference by keyboard. Changed example tree expectations retain explicit source targets/gaps and exact endpoint/declaration fixtures; structural boundary connection counts are not relaxed. + +`editor-tour.spec.ts` exercises the optional guide with an imported project and an unapplied draft, verifies keyboard dismissal/focus return and checks every popup stays in the viewport at phone width with Explorer collapsed. It also checks that collapsed Explorer and Code-only layouts receive instructions for revealing hidden controls without changing the layout, that each step has an accessible description and a polite live region, and that dismissing by choosing another control preserves focus on that control. The visual project captures the first step at desktop/narrow widths in both themes. Screen-reader announcements still require manual assistive-technology verification. diff --git a/docs/verification.md b/docs/verification.md index c00cad4..81aa51f 100644 --- a/docs/verification.md +++ b/docs/verification.md @@ -1,5 +1,23 @@ # Verification record +## Editor guide review + +Recorded 2026-11-10. The guide now explains how to reveal collapsed Explorer and model representation controls hidden by Code layout. Each step supplies the dialog's accessible description and a polite, atomic live region. Removing the custom close-focus override lets outside clicks keep focus on the chosen control; Escape, Close and Done still return focus to Guide. These changes do not switch layout or modify project data. + +`npm run check` passed formatting, architecture, TypeScript, 160 Vitest tests and ten Node checks (`sandbox/guide-review-check.log`). `npm run test:e2e -- --project=functional editor-tour.spec.ts` passed all four real-engine browser scenarios after a fresh production build (`sandbox/guide-review-functional.log`): draft/project preservation and keyboard return, all five steps at 390px, hidden-control hints with unchanged layout and restart, and outside-click focus. Before the fix, the accessible-description and hidden-control assertions failed against the original implementation (`sandbox/guide-review-before.log`); the existing phone scenario passed. + +`npm run test:e2e -- --project=visual` completed with 80 passes and one screenshot mismatch (`sandbox/guide-review-visual.log`). All four editor-guide comparisons passed without updating baselines. The failed `requirement hierarchy and declared satisfaction in light at 768` comparison differed by 1,907 pixels, confined to diagram text/edges; its original trace, actual and diff are preserved in `sandbox/guide-review-requirements-failure/`. An isolated fresh-build rerun of that exact scenario passed without code or baseline changes (`sandbox/guide-review-requirements-recheck.log`). This is an intermittent visual mismatch with unresolved cause, not an entirely green full-suite run. The complete functional suite was not rerun. + +The guide was inspected manually in light/dark themes at desktop and phone widths. Automated accessibility assertions check the exposed description/live-region attributes, not actual screen-reader announcements; manual assistive-technology verification and Firefox/WebKit remain unperformed. These are local results, not hosted CI. + +## Optional editor guide + +Recorded 2026-11-10. The rover-specific inline view descriptions were removed. An optional five-step Guide in the project header introduces Explorer, layout, applying source, model representations and downloading for any open project. It uses the existing popover primitives; there are no new dependencies or project format changes. + +`npm run check` passed formatting, architecture, TypeScript, 160 Vitest tests and ten Node checks (`sandbox/editor-tour-check-final.log`). Fresh builds passed through Playwright. The initial five-case functional run covered the guide and real-engine rover workflows (`sandbox/editor-tour-functional.log`); two final guide cases passed with an unapplied draft, keyboard dismissal/focus return, and all popup bounds at 390px with Explorer collapsed (`sandbox/editor-tour-draft.log`). The guide regression failed against the retained previous build because its Guide button was absent (`sandbox/editor-tour-before.log`). An initial phone test failed in fixture setup at a hidden Explorer view; after starting the project before resizing, it exposed an animation-timing measurement, corrected with a retrying bounds assertion (`sandbox/editor-tour-final.log`, `sandbox/editor-tour-final2.log`). + +The visual suite generated 81 baselines (`sandbox/editor-tour-visual-all.log`). The changed header, removal of rover notes and guide captures were inspected in both themes and widths via `sandbox/editor-tour-review/`; initial desktop guide placement was clipped and was corrected before the final review. Normal comparison then passed all 81 without updates (`sandbox/editor-tour-visual-final.log`, output `sandbox/playwright/editor-tour-visual-final`). These are local Chromium results, not hosted CI or a complete accessibility audit. + ## OpenSysML 0.12 and requirements/source assistance Recorded 2026-11-06. OpenSysML is now pinned to 0.12.0 with the local overlays ported, verified source/toolchain checksums and retained provenance. SysML Toolkit remains at 0.10.10. Requirements table/matrix inspection is read-only; references navigate project source and formatting creates an undoable draft requiring explicit Apply. No production dependencies or saved-format changes. diff --git a/src/lib/ui/diagram.css b/src/lib/ui/diagram.css index a6a9da9..7f85ffd 100644 --- a/src/lib/ui/diagram.css +++ b/src/lib/ui/diagram.css @@ -431,16 +431,6 @@ box-shadow: inset 0 0 0 4px var(--sysml-surface); } -.example-guide { - margin: 0; - padding: 10px 16px; - border-bottom: 1px solid var(--sysml-border); - font-size: 12px; - line-height: 1.6; - color: var(--sysml-muted); - background: var(--sysml-surface); -} - .react-flow__node.structure-node.behavior-node { border-radius: 16px; } @@ -455,3 +445,27 @@ background: var(--sysml-text); border: 3px solid var(--sysml-text); } + +.tour-highlight { + outline: 3px solid var(--sysml-accent); + outline-offset: 2px; +} +.editor-tour { + display: grid; + gap: 10px; +} +.editor-tour h2 { + font-weight: 600; +} +.editor-tour p { + margin: 0; + font-size: 13px; +} +.editor-tour-progress { + color: var(--sysml-muted); +} +.editor-tour-actions { + display: flex; + justify-content: flex-end; + gap: 6px; +} diff --git a/src/lib/workbench/diagrams/Diagram.tsx b/src/lib/workbench/diagrams/Diagram.tsx index df7cf28..a6aedbe 100644 --- a/src/lib/workbench/diagrams/Diagram.tsx +++ b/src/lib/workbench/diagrams/Diagram.tsx @@ -83,7 +83,6 @@ import type { Point, } from '$lib/workspace/views'; interface Props { - guide?: string; exploration?: ExplorationRequest; elements?: readonly ModelElement[]; explorationFocus?: 'scope' | 'representation'; @@ -104,7 +103,6 @@ interface Props { layoutMode: 'code' | 'split' | 'diagram'; } export function Diagram({ - guide, exploration, explorationFocus, elements = [], @@ -494,7 +492,6 @@ export function Diagram({ )} - {guide &&

{guide}

} {!view ? (

One model. Focused views.

diff --git a/src/lib/workbench/shell/EditorTour.tsx b/src/lib/workbench/shell/EditorTour.tsx new file mode 100644 index 0000000..f9d509d --- /dev/null +++ b/src/lib/workbench/shell/EditorTour.tsx @@ -0,0 +1,126 @@ +import { useEffect, useId, useRef, useState } from 'react'; +import { CircleHelp } from 'lucide-react'; +import { Button } from '$lib/ui/primitives/button'; +import { + Popover, + PopoverAnchor, + PopoverContent, + PopoverTrigger, +} from '$lib/ui/primitives/popover'; + +const steps = [ + { + title: 'Find your files and views', + text: 'Use Explorer to open source files, choose named views and browse model elements.', + target: '[data-tour-explorer]', + hidden: 'Choose Show Explorer to reopen the panel.', + }, + { + title: 'Choose a layout', + text: 'Switch between source, a split view and the diagram using these controls.', + target: '[aria-label="Editor layout"]', + }, + { + title: 'Apply source changes', + text: 'Source edits stay in a draft until you choose Apply source. Issues explain errors while your draft is retained.', + target: '[aria-label="Source actions"]', + }, + { + title: 'Inspect your model', + text: 'Choose Diagram view, Requirements table or Traceability matrix to inspect the applied model.', + target: '[aria-label="Model representation"]', + hidden: 'Choose Split or Diagram to reveal these controls.', + }, + { + title: 'Save your project', + text: 'Download project saves your source, drafts and diagram positions in a portable project file.', + target: 'button[data-tour-download]', + }, +] as const; + +export function EditorTour() { + const descriptionId = useId(); + const [open, setOpen] = useState(false); + const [index, setIndex] = useState(0); + const trigger = useRef(null); + const anchor = useRef(null); + const [target, setTarget] = useState(null); + useEffect(() => { + if (!open) return; + const candidate = document.querySelector(steps[index]!.target); + const visible = candidate && candidate.getClientRects().length > 0; + const next = visible ? candidate : trigger.current; + anchor.current = next; + setTarget(next); + if (next !== trigger.current) next?.classList.add('tour-highlight'); + return () => { + if (next !== trigger.current) next?.classList.remove('tour-highlight'); + }; + }, [open, index]); + const step = steps[index]!; + const hint = + target === trigger.current && 'hidden' in step ? step.hidden : undefined; + return ( + { + setOpen(value); + if (value) setIndex(0); + }} + > + + + + {target && } + +
+
+

+ {index + 1} of {steps.length} +

+

{step.title}

+

{step.text}

+ {hint &&

{hint}

} +
+
+ + + +
+
+
+
+ ); +} diff --git a/src/lib/workbench/shell/ProjectHeader.tsx b/src/lib/workbench/shell/ProjectHeader.tsx index c9a0b20..2bad2d1 100644 --- a/src/lib/workbench/shell/ProjectHeader.tsx +++ b/src/lib/workbench/shell/ProjectHeader.tsx @@ -4,6 +4,7 @@ import { Button } from '$lib/ui/primitives/button'; import { AppearanceSettings } from '$lib/ui/AppearanceSettings'; import type { WorkspaceState } from '$lib/workspace/createWorkspace'; import { Brand } from '../entry/Brand'; +import { EditorTour } from './EditorTour'; export function ProjectHeader({ state, @@ -48,6 +49,7 @@ export function ProjectHeader({ {origin}