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
15 changes: 15 additions & 0 deletions .cspell.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
version: '0.2'
language: en
words:
- APFS
- Argb
- axaml
- BGRA
Expand All @@ -21,12 +22,15 @@ words:
- Dema
- doctitle
- docversion
- Dedupe
- dedupe
- Dockable
- Dockables
- fileassert
- Hmmssfff
- LOCALAPPDATA
- Mvvm
- ordinally
- Pandoc
- pagetitle
- pasteable
Expand All @@ -46,8 +50,19 @@ words:
- sonar
- sonarmark
- Stdlib
- Subfolders
- subfolders
- sysml
- timeslice
- TOCTOU
- uncontended
- unioned
- Unwatch
- unwatch
- Unwatches
- unwatches
- Unwatching
- unwatching
- versionmark
- Weasyprint
- xunit
Expand Down
30 changes: 30 additions & 0 deletions .reviewmark.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,20 @@ reviews:
- docs/verification/sysml2-workbench/workspace-subsystem/diagnostics-aggregator.md
- src/**/WorkspaceSubsystem/DiagnosticsAggregator.cs
- test/**/WorkspaceSubsystem/DiagnosticsAggregatorTests.cs
- id: SysML2Workbench-WorkspaceSubsystem-WorkspaceSourceSet
title: Review that SysML2Workbench WorkspaceSubsystem WorkspaceSourceSet Implementation
is Correct
context:
- docs/design/sysml2-workbench.md
- docs/reqstream/sysml2-workbench.yaml
- docs/design/sysml2-workbench/workspace-subsystem.md
- docs/reqstream/sysml2-workbench/workspace-subsystem.yaml
paths:
- docs/reqstream/sysml2-workbench/workspace-subsystem/workspace-source-set.yaml
- docs/design/sysml2-workbench/workspace-subsystem/workspace-source-set.md
- docs/verification/sysml2-workbench/workspace-subsystem/workspace-source-set.md
- src/**/WorkspaceSubsystem/WorkspaceSourceSet.cs
- test/**/WorkspaceSubsystem/WorkspaceSourceSetTests.cs
- id: SysML2Workbench-ViewCatalogSubsystem
title: Review that SysML2Workbench ViewCatalogSubsystem Satisfies Subsystem Requirements
context:
Expand Down Expand Up @@ -292,6 +306,22 @@ reviews:
- docs/verification/sysml2-workbench/app-shell-subsystem/main-window-shell.md
- src/**/AppShellSubsystem/MainWindowShell.cs
- test/**/AppShellSubsystem/MainWindowShellTests.cs
- id: SysML2Workbench-AppShellSubsystem-WorkspacePanel
title: Review that SysML2Workbench AppShellSubsystem WorkspacePanel Implementation
is Correct
context:
- docs/design/sysml2-workbench.md
- docs/reqstream/sysml2-workbench.yaml
- docs/design/sysml2-workbench/app-shell-subsystem.md
- docs/reqstream/sysml2-workbench/app-shell-subsystem.yaml
paths:
- docs/reqstream/sysml2-workbench/app-shell-subsystem/workspace-panel.yaml
- docs/design/sysml2-workbench/app-shell-subsystem/workspace-panel.md
- docs/verification/sysml2-workbench/app-shell-subsystem/workspace-panel.md
- src/**/AppShellSubsystem/WorkspacePanelToolViewModel.cs
- src/**/AppShellSubsystem/WorkspacePanelToolView.axaml
- src/**/AppShellSubsystem/WorkspacePanelToolView.axaml.cs
- test/**/AppShellSubsystem/WorkspacePanelToolViewModelTests.cs
- id: OTS-DemaConsultingSysML2Tools
title: Review that DemaConsulting.SysML2Tools Provides Required Functionality
paths:
Expand Down
47 changes: 29 additions & 18 deletions docs/design/sysml2-workbench/app-shell-subsystem.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@

AppShellSubsystem is the composition root for the desktop application. It owns
the main window, navigation flow, and tabbed viewing experience that tie the
other subsystems into one coherent user workflow. Its only documented unit is
MainWindowShell. The thin desktop platform head remains outside the documented
unit set because it is only a bootstrap and lifetime host.
other subsystems into one coherent user workflow. Its documented units are
MainWindowShell and WorkspacePanel. The thin desktop platform head remains
outside the documented unit set because it is only a bootstrap and lifetime
host.

### Interfaces

Expand Down Expand Up @@ -51,18 +52,28 @@ environment.
user workflow without pushing UI-specific code into those subsystems.
5. The desktop platform head initializes the shell and application lifetime but
remains a bootstrap concern rather than a separately documented unit.
6. The Avalonia UI layer presents MainWindowShell's panels (predefined
views, custom view builder, diagnostics, and diagram) as a resizable,
floatable Dock layout, composed by a Dock factory from thin panel views
and view models that hold no logic beyond forwarding to MainWindowShell
and binding its state. The three Tool panels (predefined views, custom
view builder, diagnostics) are closable and restorable via a View menu,
which reuses the same long-lived panel view model instance so its
in-progress state survives the close/restore cycle. The diagram area
hosts zero or more independently closable diagram Documents - one per
open `WorkbenchTab`, each bound to its own `SvgCanvasHost` - and the
Dock `DocumentDock` container itself remains visibly present even when
no diagram tab is open, so an empty diagram area is a normal, supported
state rather than a dead end. MainWindowShell itself has no dependency
on Avalonia or Dock and is unaware of how its panels are arranged on
screen.
6. The Avalonia UI layer presents MainWindowShell's panels (workspace,
predefined views, custom view builder, diagnostics, and diagram) as a
resizable, floatable Dock layout, composed by a Dock factory from thin
panel views and view models that hold no logic beyond forwarding to
MainWindowShell and binding its state. The four Tool panels (workspace,
predefined views, custom view builder, diagnostics) are closable and
restorable via a View menu, which reuses the same long-lived panel view
model instance so its in-progress state survives the close/restore
cycle. The diagram area hosts zero or more independently closable diagram
Documents - one per open `WorkbenchTab`, each bound to its own
`SvgCanvasHost` - and the Dock `DocumentDock` container itself remains
visibly present even when no diagram tab is open, so an empty diagram
area is a normal, supported state rather than a dead end. MainWindowShell
itself has no dependency on Avalonia or Dock and is unaware of how its
panels are arranged on screen.
7. A workspace with zero sources open is a first-class, non-error state, not
a precondition failure: `MainWindowShell.CurrentWorkspace` is never null
and starts as a valid empty snapshot at construction. Every panel that
depends on workspace content - the workspace panel, predefined views,
custom view builder, and diagnostics - presents a distinct, friendly
empty-state message when `Sources.Count == 0` rather than rendering
against an empty `SysmlWorkspace` or leaving a blank pane; the diagnostics
panel in particular distinguishes "no diagnostics because the workspace is
empty" from "no diagnostics because everything is clean," since those are
different facts the user needs to tell apart.
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ snippet export within a single windowed user experience.

#### Data Model

**CurrentWorkspace**: `WorkspaceSnapshot?` — currently loaded workspace and its
revision metadata.
**CurrentWorkspace**: `WorkspaceSnapshot` — currently loaded workspace and its
revision metadata. Never `null`: initialized at construction to a valid empty
snapshot (zero sources, zero files, stdlib-only) rather than left unset until
the first source is added, so every consumer can safely read
`CurrentWorkspace.Sources` without a null guard.

**ActivePredefinedView**: `ViewDescriptor?` — selected catalog view, if the
user is in predefined-view mode.
Expand All @@ -29,27 +32,55 @@ active/focused, or `null` when no diagram tab is open. Updated both by shell
operations that open or focus a tab, and by the UI layer forwarding Dock's
own focus-change signal via `NotifyActiveDiagramTab`.

#### Key Methods
**CurrentSourceIdToFiles**: `IReadOnlyDictionary<string, IReadOnlyList<string>>`
— per-source file lists from the most recent workspace resolution, used by
WorkspacePanel to build its tree without owning its own `WorkspaceSourceSet`
instance.

**OpenWorkspace**: Loads a new workspace into the shell.
#### Key Methods

- *Parameters*: `string rootPath` — user-selected folder.
- *Returns*: `void` — shell state updates in place.
- *Preconditions*: `rootPath` exists and is readable.
- *Postconditions*: WorkspaceSubsystem is initialized, the view catalog is
refreshed, diagnostics are displayed, and every open tab is closed
(`ActiveTabId` becomes `null`) since a reloaded workspace invalidates every
currently-rendered diagram. The file watcher is retargeted to `rootPath` on
every call, not only the first: any previously watched root is torn down and
its pending change state discarded, so a second or later workspace open
never leaves the watcher bound to a stale folder.
**AddFileSource**: Adds a single file as a new workspace source.

- *Parameters*: `string path` — file to add.
- *Returns*: `WorkspaceSnapshot` — the reloaded workspace snapshot.
- *Postconditions*: The path is registered on the shell's owned
`WorkspaceSourceSet`, the source set is re-resolved, `WorkspaceModel` is
reloaded against the new resolution, the file watcher begins watching the
new source (a non-recursive, filtered watcher for a file source), the
resulting snapshot is applied (existing tabs are cleared, since a reload
invalidates every currently-rendered diagram), and `SourcesChanged` is
raised. Idempotent: adding the same path twice does not duplicate the
source.

**AddFolderSource**: Adds a folder as a new workspace source.

- *Parameters*: `string path` — folder to add.
- *Returns*: `WorkspaceSnapshot` — the reloaded workspace snapshot.
- *Postconditions*: Identical to `AddFileSource`, except the source is a
recursively watched folder and its files are discovered via
`WorkspaceSourceSet.Resolve()`'s default glob options.

**RemoveSource**: Removes a previously registered source.

- *Parameters*: `string sourceId` — id of the source to remove.
- *Returns*: `WorkspaceSnapshot` — the reloaded workspace snapshot.
- *Postconditions*: The source is removed from the source set (a no-op for
an unknown id), the set is re-resolved, `WorkspaceModel` is reloaded, the
now-unwatched source's watcher is disposed, the resulting snapshot is
applied, and `SourcesChanged` is raised. Removing the last remaining source
produces a valid empty snapshot (zero sources, zero files) - the same
first-class empty state the shell starts in at construction - and still
correctly clears every open tab, not only when shrinking to a smaller
nonzero file set.

#### Additional Key Methods

**SelectPredefinedView**: Renders a predefined view selected by the user.

- *Parameters*: `string viewId` — identifier from ViewCatalogPresenter.
- *Returns*: `void` — active diagram state updates in place.
- *Preconditions*: A workspace is loaded and the view identifier is present in
the current catalog.
- *Preconditions*: `CurrentWorkspace.Sources.Count > 0` and the view
identifier is present in the current catalog.
- *Postconditions*: `ActivePredefinedView` is updated; a tab identified by the
view's qualified name is opened (or, if already open, reused) and rendered
into; that tab becomes `ActiveTabId` either way, so selecting an
Expand All @@ -61,7 +92,8 @@ own focus-change signal via `NotifyActiveDiagramTab`.
- *Parameters*: `ViewDefinitionModel definition` — normalized custom-view
state.
- *Returns*: `void` — active diagram state updates in place.
- *Preconditions*: The definition validates against the current workspace.
- *Preconditions*: `CurrentWorkspace.Sources.Count > 0` and the definition
validates against the current workspace.
- *Postconditions*: `ActiveCustomView` is updated. If the currently active tab
is itself a custom-view-preview tab, it is re-rendered in place (same tab
identity and canvas); otherwise a brand-new custom-view-preview tab is
Expand Down Expand Up @@ -116,23 +148,33 @@ workspace is reloaded (which clears every tab). The Avalonia-aware UI layer
add or remove Dock dockables directly. The notification is raised through an
injected `IUiDispatcher`, so subscribers are guaranteed to observe it on the
dispatcher's target thread even though methods that trigger it - most notably
`OpenWorkspace`, which awaits workspace loading with `ConfigureAwait(false)` -
may themselves resume on a background thread pool thread once the load
completes.
`AddFolderSourceAsync`, which awaits workspace loading with
`ConfigureAwait(false)` - may themselves resume on a background thread pool
thread once the load completes.

**SourcesChanged**: Raised whenever the set of workspace sources changes - a
source is added or removed and the resulting resolution has been applied.
Parallel in structure and marshaling behavior to `TabsChanged`: raised through
the same injected `IUiDispatcher`. `WorkspacePanelToolViewModel` subscribes to
this to rebuild its tree, and `CurrentSourceIdToFiles` reflects the change by
the time subscribers observe the notification.

#### Dependencies

- **WorkspaceSubsystem** — loads and refreshes the workspace state.
- **WorkspaceSourceSet** — owned by the shell; mutated by
`AddFileSourceAsync`/`AddFolderSourceAsync`/`RemoveSourceAsync` and
re-resolved before every `WorkspaceModel` load, reload, and watcher diff.
- **ViewCatalogPresenter** — supplies predefined view choices.
- **ViewDefinitionModel** — captures custom-view authoring state.
- **SysmlSnippetGenerator** — exports custom-view definitions as SysML text.
- **LayoutInvoker** — renders predefined and custom views.
- **SvgCanvasHost** — displays the active diagram.
- **DiagnosticsListView** — displays workspace diagnostics.
- **RollingFileLogger** — records shell-level operational failures.
- **IUiDispatcher** — marshals `TabsChanged` notifications onto the thread
required by UI-facing subscribers; defaults to an immediate, synchronous
dispatcher when none is supplied.
- **IUiDispatcher** — marshals `TabsChanged` and `SourcesChanged`
notifications onto the thread required by UI-facing subscribers; defaults
to an immediate, synchronous dispatcher when none is supplied.
- **Avalonia** — provides the window, tab, and application-lifetime framework.

#### Callers
Expand Down
Loading