Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 3 additions & 1 deletion docs/capabilities.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
4 changes: 3 additions & 1 deletion docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
18 changes: 18 additions & 0 deletions docs/verification.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
34 changes: 24 additions & 10 deletions src/lib/ui/diagram.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
}
3 changes: 0 additions & 3 deletions src/lib/workbench/diagrams/Diagram.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ import type {
Point,
} from '$lib/workspace/views';
interface Props {
guide?: string;
exploration?: ExplorationRequest;
elements?: readonly ModelElement[];
explorationFocus?: 'scope' | 'representation';
Expand All @@ -104,7 +103,6 @@ interface Props {
layoutMode: 'code' | 'split' | 'diagram';
}
export function Diagram({
guide,
exploration,
explorationFocus,
elements = [],
Expand Down Expand Up @@ -494,7 +492,6 @@ export function Diagram({
</div>
)}
</header>
{guide && <p className="example-guide">{guide}</p>}
{!view ? (
<div className="empty">
<h2>One model. Focused views.</h2>
Expand Down
126 changes: 126 additions & 0 deletions src/lib/workbench/shell/EditorTour.tsx
Original file line number Diff line number Diff line change
@@ -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<HTMLButtonElement>(null);
const anchor = useRef<HTMLElement | null>(null);
const [target, setTarget] = useState<HTMLElement | null>(null);
useEffect(() => {
if (!open) return;
const candidate = document.querySelector<HTMLElement>(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 (
<Popover
open={open}
onOpenChange={(value) => {
setOpen(value);
if (value) setIndex(0);
}}
>
<PopoverTrigger asChild>
<Button ref={trigger} variant="ghost" aria-label="Guide to the editor">
<CircleHelp aria-hidden="true" />
<span className="button-label">Guide</span>
</Button>
</PopoverTrigger>
{target && <PopoverAnchor virtualRef={anchor} />}
<PopoverContent
aria-label="Editor guide"
aria-describedby={descriptionId}
side="bottom"
align="center"
collisionPadding={12}
>
<div className="editor-tour">
<div
id={descriptionId}
className="editor-tour"
aria-live="polite"
aria-atomic="true"
>
<p className="editor-tour-progress">
{index + 1} of {steps.length}
</p>
<h2>{step.title}</h2>
<p>{step.text}</p>
{hint && <p>{hint}</p>}
</div>
<div className="editor-tour-actions">
<Button variant="ghost" size="sm" onClick={() => setOpen(false)}>
Close
</Button>
<Button
variant="outline"
size="sm"
disabled={index === 0}
onClick={() => setIndex(index - 1)}
>
Back
</Button>
<Button
size="sm"
onClick={() =>
index === steps.length - 1
? setOpen(false)
: setIndex(index + 1)
}
>
{index === steps.length - 1 ? 'Done' : 'Next'}
</Button>
</div>
</div>
</PopoverContent>
</Popover>
);
}
3 changes: 3 additions & 0 deletions src/lib/workbench/shell/ProjectHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -48,6 +49,7 @@ export function ProjectHeader({
<small className="project-origin">{origin}</small>
</div>
<nav aria-label="Project actions">
<EditorTour />
<AppearanceSettings active={active} />
{recovery}
<Button
Expand All @@ -70,6 +72,7 @@ export function ProjectHeader({
</Button>
<Button
variant="default"
data-tour-download
disabled={!state.project || state.busy}
onClick={onDownload}
>
Expand Down
7 changes: 1 addition & 6 deletions src/lib/workbench/shell/Workbench.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from './WorkbenchPanels';
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { createBrowserSession } from './session';
import { exampleProject, exampleView, roverGuide } from './example';
import { exampleProject, exampleView } from './example';
import type { Workspace, WorkspaceState } from '$lib/workspace/createWorkspace';
import type { ProjectBundle } from '$lib/workspace/persistence';
import type { EditProposal, SemanticCommand } from '$lib/workspace/editing';
Expand Down Expand Up @@ -745,11 +745,6 @@ export function Workbench({
layoutMode={mode}
key={(diagramIdentity ?? 'none') + ':' + state.revision}
view={displayedView}
guide={
origin === 'Built-in example' && displayedView
? roverGuide[displayedView.view]
: undefined
}
exploration={state.exploration?.request}
elements={state.model?.elements ?? []}
explorationFocus={explorationFocus}
Expand Down
Loading
Loading