@cyann/dash-lab 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +138 -0
- package/CHANGELOG.md +48 -0
- package/CONTRIBUTING.md +94 -0
- package/LICENSE +21 -0
- package/README.md +154 -2
- package/THIRD_PARTY_NOTICES.md +31 -0
- package/dist/commands/CommandContext.d.ts +19 -0
- package/dist/commands/CommandContext.d.ts.map +1 -0
- package/dist/commands/CommandPalette.d.ts +10 -0
- package/dist/commands/CommandPalette.d.ts.map +1 -0
- package/dist/commands/CommandProvider.d.ts +12 -0
- package/dist/commands/CommandProvider.d.ts.map +1 -0
- package/dist/commands/CommandRegistry.d.ts +40 -0
- package/dist/commands/CommandRegistry.d.ts.map +1 -0
- package/dist/commands/searchCommands.d.ts +3 -0
- package/dist/commands/searchCommands.d.ts.map +1 -0
- package/dist/commands/shortcuts.d.ts +7 -0
- package/dist/commands/shortcuts.d.ts.map +1 -0
- package/dist/dash-lab.css +2 -0
- package/dist/i18n/I18nProvider.d.ts +20 -0
- package/dist/i18n/I18nProvider.d.ts.map +1 -0
- package/dist/i18n/i18nStore.d.ts +31 -0
- package/dist/i18n/i18nStore.d.ts.map +1 -0
- package/dist/i18n/messages.d.ts +3 -0
- package/dist/i18n/messages.d.ts.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2561 -0
- package/dist/layout/DockWorkspace.d.ts +7 -0
- package/dist/layout/DockWorkspace.d.ts.map +1 -0
- package/dist/layout/LayoutControls.d.ts +5 -0
- package/dist/layout/LayoutControls.d.ts.map +1 -0
- package/dist/layout/Workbench.d.ts +40 -0
- package/dist/layout/Workbench.d.ts.map +1 -0
- package/dist/layout/layoutPersistence.d.ts +22 -0
- package/dist/layout/layoutPersistence.d.ts.map +1 -0
- package/dist/layout/layoutStore.d.ts +13 -0
- package/dist/layout/layoutStore.d.ts.map +1 -0
- package/dist/layout/panels.d.ts +21 -0
- package/dist/layout/panels.d.ts.map +1 -0
- package/dist/layout/useLayoutManagement.d.ts +21 -0
- package/dist/layout/useLayoutManagement.d.ts.map +1 -0
- package/dist/layout/useWorkbenchCommands.d.ts +14 -0
- package/dist/layout/useWorkbenchCommands.d.ts.map +1 -0
- package/dist/library.d.ts +3 -0
- package/dist/library.d.ts.map +1 -0
- package/dist/notifications/NotificationButton.d.ts +3 -0
- package/dist/notifications/NotificationButton.d.ts.map +1 -0
- package/dist/notifications/NotificationContext.d.ts +5 -0
- package/dist/notifications/NotificationContext.d.ts.map +1 -0
- package/dist/notifications/NotificationProvider.d.ts +11 -0
- package/dist/notifications/NotificationProvider.d.ts.map +1 -0
- package/dist/notifications/NotificationToasts.d.ts +2 -0
- package/dist/notifications/NotificationToasts.d.ts.map +1 -0
- package/dist/notifications/NotificationViewport.d.ts +2 -0
- package/dist/notifications/NotificationViewport.d.ts.map +1 -0
- package/dist/notifications/notificationStore.d.ts +56 -0
- package/dist/notifications/notificationStore.d.ts.map +1 -0
- package/dist/notifications/preferences.d.ts +10 -0
- package/dist/notifications/preferences.d.ts.map +1 -0
- package/dist/overlays/dialogMotion.d.ts +3 -0
- package/dist/overlays/dialogMotion.d.ts.map +1 -0
- package/dist/overlays/useDialogDismiss.d.ts +9 -0
- package/dist/overlays/useDialogDismiss.d.ts.map +1 -0
- package/dist/plugins/PluginContext.d.ts +5 -0
- package/dist/plugins/PluginContext.d.ts.map +1 -0
- package/dist/plugins/PluginRegistry.d.ts +98 -0
- package/dist/plugins/PluginRegistry.d.ts.map +1 -0
- package/dist/plugins/pluginState.d.ts +19 -0
- package/dist/plugins/pluginState.d.ts.map +1 -0
- package/dist/settings/ApplicationMenu.d.ts +2 -0
- package/dist/settings/ApplicationMenu.d.ts.map +1 -0
- package/dist/settings/SettingsDialog.d.ts +12 -0
- package/dist/settings/SettingsDialog.d.ts.map +1 -0
- package/dist/settings/configuration.d.ts +36 -0
- package/dist/settings/configuration.d.ts.map +1 -0
- package/dist/settings/useWorkbenchConfiguration.d.ts +15 -0
- package/dist/settings/useWorkbenchConfiguration.d.ts.map +1 -0
- package/dist/theme/ThemeProvider.d.ts +17 -0
- package/dist/theme/ThemeProvider.d.ts.map +1 -0
- package/dist/theme/ThemeToggle.d.ts +3 -0
- package/dist/theme/ThemeToggle.d.ts.map +1 -0
- package/dist/theme/palettes.d.ts +153 -0
- package/dist/theme/palettes.d.ts.map +1 -0
- package/dist/theme/themeStore.d.ts +31 -0
- package/dist/theme/themeStore.d.ts.map +1 -0
- package/docs/api.md +171 -0
- package/docs/application-guide.md +332 -0
- package/docs/architecture.md +59 -0
- package/docs/decisions/001-library-and-demo.md +9 -0
- package/docs/decisions/002-local-plugins-and-languages.md +17 -0
- package/docs/decisions/003-plugin-state-checkpoints.md +11 -0
- package/docs/dependencies.md +33 -0
- package/docs/validation.md +126 -0
- package/package.json +97 -4
- package/scripts/install-hooks.mjs +17 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# Build an application and create plugins
|
|
2
|
+
|
|
3
|
+
This guide is sufficient to assemble an application without reading implementation source. Use [API reference](api.md) for individual signatures and [architecture](architecture.md) for maintenance boundaries.
|
|
4
|
+
|
|
5
|
+
## Decide where your code belongs
|
|
6
|
+
|
|
7
|
+
`dash-lab` is a reusable library; `demo/` is a consuming application. Put domain-specific views, commands, and plugin directories in the application. The library owns chrome, docking, theme, language, notifications, commands, configuration, and contribution contracts. A router, backend, editor, or data model remains the application's responsibility.
|
|
8
|
+
|
|
9
|
+
Use Node.js 24 LTS. The framework package also declares Node 22.13+ in the 22.x line. React and React DOM 19 are peers. Existing framework dependency versions are locked; new dependencies require the owner's approval unless explicitly requested.
|
|
10
|
+
|
|
11
|
+
## Build and evaluate the framework
|
|
12
|
+
|
|
13
|
+
From the framework repository:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm ci
|
|
17
|
+
npm run dev
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The demo runs at http://127.0.0.1:5173. A single startup process watches the library bundle and declarations and serves the application with Vite HMR. `npm run start` checks/builds everything once and previews at http://127.0.0.1:4173. Neither command requires a manual build first. Use `npm.cmd` on Windows when PowerShell blocks `npm.ps1`.
|
|
21
|
+
|
|
22
|
+
To consume a local build elsewhere:
|
|
23
|
+
|
|
24
|
+
```sh
|
|
25
|
+
npm pack
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This produces `cyann-dash-lab-0.1.0.tgz` using the current package version. It contains ESM, declarations, CSS, license notices, and documentation. It does not contain the demo or require package publication.
|
|
29
|
+
|
|
30
|
+
## Create a separate Vite application
|
|
31
|
+
|
|
32
|
+
In a sibling `my-lab` directory, install the tarball and the same application stack. Adjust the tarball path for your checkout:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
npm init -y
|
|
36
|
+
npm install ../dash-lab/cyann-dash-lab-0.1.0.tgz react@19 react-dom@19
|
|
37
|
+
npm install --save-dev vite@8 @vitejs/plugin-react@6 typescript@6 @types/react@19 @types/react-dom@19
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Set `"type": "module"` and these scripts in `package.json`:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"dev": "vite --host 127.0.0.1",
|
|
45
|
+
"check": "tsc --noEmit",
|
|
46
|
+
"build": "npm run check && vite build",
|
|
47
|
+
"start": "npm run build && vite preview --host 127.0.0.1 --port 4173"
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Create `vite.config.ts`:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { defineConfig } from 'vite';
|
|
55
|
+
import react from '@vitejs/plugin-react';
|
|
56
|
+
|
|
57
|
+
export default defineConfig({ plugins: [react()] });
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Create `tsconfig.json`:
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"compilerOptions": {
|
|
65
|
+
"target": "ES2022",
|
|
66
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
67
|
+
"module": "ESNext",
|
|
68
|
+
"moduleResolution": "Bundler",
|
|
69
|
+
"jsx": "react-jsx",
|
|
70
|
+
"strict": true,
|
|
71
|
+
"noUncheckedIndexedAccess": true,
|
|
72
|
+
"exactOptionalPropertyTypes": true,
|
|
73
|
+
"skipLibCheck": true,
|
|
74
|
+
"noEmit": true,
|
|
75
|
+
"types": ["vite/client"]
|
|
76
|
+
},
|
|
77
|
+
"include": ["src"]
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Create `index.html`:
|
|
82
|
+
|
|
83
|
+
```html
|
|
84
|
+
<!doctype html>
|
|
85
|
+
<html lang="en">
|
|
86
|
+
<head>
|
|
87
|
+
<meta charset="UTF-8" />
|
|
88
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
89
|
+
<title>My Lab</title>
|
|
90
|
+
</head>
|
|
91
|
+
<body>
|
|
92
|
+
<div id="root"></div>
|
|
93
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
94
|
+
</body>
|
|
95
|
+
</html>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Create `src/main.tsx`:
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import { StrictMode } from 'react';
|
|
102
|
+
import { createRoot } from 'react-dom/client';
|
|
103
|
+
import { Workbench } from '@cyann/dash-lab';
|
|
104
|
+
import { plugins } from './plugins';
|
|
105
|
+
import '@cyann/dash-lab/style.css';
|
|
106
|
+
import './style.css';
|
|
107
|
+
|
|
108
|
+
const root = document.getElementById('root');
|
|
109
|
+
if (!root) throw new Error('Missing root element.');
|
|
110
|
+
createRoot(root).render(
|
|
111
|
+
<StrictMode>
|
|
112
|
+
<Workbench title="My Lab" plugins={plugins} footer="My application" />
|
|
113
|
+
</StrictMode>,
|
|
114
|
+
);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Create `src/style.css`:
|
|
118
|
+
|
|
119
|
+
```css
|
|
120
|
+
html,
|
|
121
|
+
body,
|
|
122
|
+
#root {
|
|
123
|
+
height: 100%;
|
|
124
|
+
margin: 0;
|
|
125
|
+
}
|
|
126
|
+
body {
|
|
127
|
+
font-family: system-ui, sans-serif;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Create `src/plugins.ts`:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import { registerLocalPlugins } from '@cyann/dash-lab';
|
|
135
|
+
import type { WorkbenchPlugin } from '@cyann/dash-lab';
|
|
136
|
+
|
|
137
|
+
const modules = import.meta.glob<{ default: WorkbenchPlugin }>('./plugins/*/index.ts', { eager: true });
|
|
138
|
+
export const plugins = registerLocalPlugins(modules);
|
|
139
|
+
if (import.meta.hot) import.meta.hot.accept(() => window.location.reload());
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Add a plugin as described below, then run `npm run dev`. The application now owns discovery while the framework owns registration and rendering. Installing a new tarball updates the framework build; source changes in the separate framework checkout are not watched by this consumer.
|
|
143
|
+
|
|
144
|
+
## Plugin directory and activation
|
|
145
|
+
|
|
146
|
+
Use this structure inside the application:
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
src/
|
|
150
|
+
plugins.ts
|
|
151
|
+
plugins/
|
|
152
|
+
notes/
|
|
153
|
+
index.ts
|
|
154
|
+
NotesPanel.tsx
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`index.ts` default-exports a `WorkbenchPlugin`; `activate(api)` runs once at application startup, synchronously. It cannot call React hooks. Components supplied as contributions can use hooks because the workbench renders them inside its providers. An activation failure rolls its registrations back. Optional activation cleanup and individual disposers can be used before the registry is sealed; runtime unloading is unsupported.
|
|
158
|
+
|
|
159
|
+
Create `src/plugins/notes/NotesPanel.tsx`:
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import { useState } from 'react';
|
|
163
|
+
import { useI18n, useNotifications } from '@cyann/dash-lab';
|
|
164
|
+
|
|
165
|
+
export function NotesPanel() {
|
|
166
|
+
const [note, setNote] = useState('');
|
|
167
|
+
const { notify } = useNotifications();
|
|
168
|
+
const { t } = useI18n();
|
|
169
|
+
return (
|
|
170
|
+
<section style={{ padding: 24 }}>
|
|
171
|
+
<h1>{t('notes.title')}</h1>
|
|
172
|
+
<label>
|
|
173
|
+
{t('notes.label')}
|
|
174
|
+
<textarea value={note} onChange={(event) => setNote(event.target.value)} />
|
|
175
|
+
</label>
|
|
176
|
+
<button type="button" onClick={() => notify({ title: t('notes.ready'), description: note, level: 'success' })}>
|
|
177
|
+
{t('notes.send')}
|
|
178
|
+
</button>
|
|
179
|
+
</section>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## All contribution types
|
|
185
|
+
|
|
186
|
+
Create `src/plugins/notes/index.ts`:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
import type { WorkbenchPlugin } from '@cyann/dash-lab';
|
|
190
|
+
import { NotesPanel } from './NotesPanel';
|
|
191
|
+
|
|
192
|
+
const plugin: WorkbenchPlugin = {
|
|
193
|
+
id: 'notes',
|
|
194
|
+
activate(api) {
|
|
195
|
+
api.registerPanel({ id: 'notes.panel', title: 'notes.title', component: NotesPanel });
|
|
196
|
+
api.registerCommand({
|
|
197
|
+
id: 'notes.notify',
|
|
198
|
+
title: 'notes.command',
|
|
199
|
+
shortcut: 'Mod+Alt+G',
|
|
200
|
+
execute: ({ notify }) => {
|
|
201
|
+
notify({ title: 'The notes plugin is ready.', level: 'info' });
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
api.registerMenu({ id: 'notes.menu', title: 'notes.command', commandId: 'notes.notify' });
|
|
205
|
+
api.registerStatusItem({ id: 'notes.status', component: () => 'Notes ready' });
|
|
206
|
+
api.registerMonitoringItem({ id: 'notes.monitoring', component: () => 'Notes plugin active' });
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
export default plugin;
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Plugin commands receive live `notify` and `executeCommand` services; handlers may return promises. Notification text is application-owned and retained as delivered. For localized notifications or access to current component state, register an application command in a React component with `useCommands()` and return the registration cleanup from an effect; use `useI18n()` in that component. Avoid registering the same command from both activation and a component.
|
|
213
|
+
|
|
214
|
+
IDs are unique within each contribution kind. Keep them stable across versions, display-title changes, and languages. `workbench.*` command IDs are reserved. Menu command IDs can reference plugin or built-in commands; unavailable actions stay disabled. Do not conflict with built-in shortcuts or an application's registrations.
|
|
215
|
+
|
|
216
|
+
## Add application and plugin translations
|
|
217
|
+
|
|
218
|
+
Update `src/main.tsx` to import `I18nProvider` from `@cyann/dash-lab` and wrap Workbench with this provider:
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
<I18nProvider
|
|
222
|
+
languages={[
|
|
223
|
+
{
|
|
224
|
+
id: 'en',
|
|
225
|
+
label: 'English',
|
|
226
|
+
messages: {
|
|
227
|
+
'notes.title': 'Notes',
|
|
228
|
+
'notes.label': 'Your note',
|
|
229
|
+
'notes.ready': 'Note ready',
|
|
230
|
+
'notes.send': 'Send note',
|
|
231
|
+
'notes.command': 'Notes: notify',
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
id: 'fr',
|
|
236
|
+
label: 'Français',
|
|
237
|
+
messages: {
|
|
238
|
+
'notes.title': 'Notes',
|
|
239
|
+
'notes.label': 'Votre note',
|
|
240
|
+
'notes.ready': 'Note prête',
|
|
241
|
+
'notes.send': 'Envoyer la note',
|
|
242
|
+
'notes.command': 'Notes : notifier',
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
]}
|
|
246
|
+
>
|
|
247
|
+
<Workbench title="My Lab" plugins={plugins} footer="My application" />
|
|
248
|
+
</I18nProvider>
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
English/French workbench catalogs are already included; these entries extend them. Set `initialLocale="fr"` to select French on first mount. A saved preference takes precedence. Add another `{ id, label, messages }` definition to extend the selector. `antdLocales` can supply that language's Ant Design catalog. `t` interpolates `{name}` parameters as plain text; missing translations fall back to English, then the key.
|
|
252
|
+
|
|
253
|
+
If a parent application component uses `useI18n()` to construct chrome props, wrap that component itself with `I18nProvider`. The automatic provider inside Workbench supplies its descendants, so it cannot translate values already constructed by its parent. Status and monitoring contributions can use `useI18n` directly in their React components.
|
|
254
|
+
|
|
255
|
+
## Position panels and preserve state
|
|
256
|
+
|
|
257
|
+
For two panels in one plugin, register the reference first:
|
|
258
|
+
|
|
259
|
+
```ts
|
|
260
|
+
api.registerPanel({ id: 'notes.primary', title: 'Notes', component: NotesPanel });
|
|
261
|
+
api.registerPanel({
|
|
262
|
+
id: 'notes.secondary',
|
|
263
|
+
title: 'Second notes',
|
|
264
|
+
component: NotesPanel,
|
|
265
|
+
position: { referencePanel: 'notes.primary', direction: 'right' },
|
|
266
|
+
initialWidth: 360,
|
|
267
|
+
});
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
The example is an alternative to the single `notes.panel` registration. Directions are left, right, above, below, or within. Vite modules activate in sorted path order, and application panels follow plugin panels. Cross-plugin references therefore require explicit, predictable directory order; leaving placement unspecified is the simplest default. Mounted panel collections are stable. Changing plugin membership requires a remount; the supplied HMR boundary reloads during development. Production requires rebuilding.
|
|
271
|
+
|
|
272
|
+
Each panel scrolls when its content exceeds the tab height. An editor/canvas may keep its own internal scroll behavior. Focus, View, theme, language, preset changes, and restore reuse existing panel instances. Reset restores initial layout/visibility and preserves the saved checkpoint.
|
|
273
|
+
|
|
274
|
+
## Save and transfer configuration
|
|
275
|
+
|
|
276
|
+
Rearranging, resizing, or selecting tabs automatically updates the custom layout after 200 ms of inactivity. The Custom layout button restores it after trying Application, Columns, or Rows; selecting those presets does not overwrite it. Header/footer settings save immediately and keep the custom arrangement. Focus, language/theme changes, and host-window resizing do not replace the custom layout. Preferences persist independently, including theme family, light/dark mode, language, motion, popup visibility, and default popup duration. The legacy Save/Restore/Reset commands and shortcuts remain available for compatibility; no manual save is required.
|
|
277
|
+
|
|
278
|
+
Choose stable application-specific `layoutStorageKey` and `preferencesStorageKey` values, or false for session-only behavior. Default keys derive from the workbench title; keep the title/keys stable when language changes. JSON export captures current layout, preferences, and opted-in plugin data; import validates the whole file, version, panel identities, and registered language before applying it. History and unregistered React panel content are excluded. A storage failure retains usable session state and reports incomplete persistence.
|
|
279
|
+
|
|
280
|
+
Adding/removing or renaming panel IDs makes old checkpoints incompatible. Keep IDs stable when only renaming a tab. An incompatible checkpoint is rejected and the default workspace remains usable. Version-1 configurations without a locale remain valid and select English. Files are limited to 1 MB, and popout windows are not restored.
|
|
281
|
+
|
|
282
|
+
### Persist a plugin's data
|
|
283
|
+
|
|
284
|
+
Register one optional handler from `activate(api)`. It is automatically keyed by your plugin's stable ID:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
api.registerState({
|
|
288
|
+
exportState: () => ({ version: 1, ...notesStore.getState() }),
|
|
289
|
+
importState(value) {
|
|
290
|
+
if (!value || typeof value !== 'object' || !('version' in value) || value.version !== 1 || !('text' in value) || typeof value.text !== 'string') {
|
|
291
|
+
throw new Error('Unsupported notes state.');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
notesStore.setState({ text: value.text });
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Here `notesStore` is your application's observable store; components must subscribe to it for restored data to appear. The existing Notes example uses component-local `useState`, which remains session-only until you move that data into such a store. See the complete, working implementation in [`demo/src/plugins/hello-world/state.ts`](../demo/src/plugins/hello-world/state.ts), its registration in `index.ts`, and its subscribed `HelloWorld` component. No additional persistence layer is needed inside a plugin.
|
|
300
|
+
|
|
301
|
+
Both callbacks are synchronous. Export only plain JSON values; import receives untrusted data and must validate its shape and plugin-specific version before mutation. The framework copies snapshots. A handler must be able to import its own exported data for rollback. Do not use these callbacks for asynchronous requests or external side effects.
|
|
302
|
+
|
|
303
|
+
Layout snapshots capture participating plugin data too; Custom layout and the next startup load that same checkpoint. Plugin-only data changes do not trigger a layout event; use JSON export or the retained Save layout command to capture them immediately. Preferences continue to save independently, and Reset/presets leave current plugin data alone. JSON export uses a top-level `plugins` object, for example `"plugins": { "notes": { "version": 1, "text": "Draft" } }`, without duplicating it inside `workspace`. JSON import also updates the local checkpoint when there is a docking workspace. Without panels, JSON transfer applies plugin data for the current session.
|
|
304
|
+
|
|
305
|
+
Old files without `plugins` leave plugin data untouched. Missing plugin entries do the same; unknown/nonparticipating plugin IDs are ignored and not retained on the next export. A failed importer triggers rollback of attempted plugins and prevents layout/preferences changes. A failed rollback is reported rather than treated as a successful import. The existing 1 MB file/checkpoint limits include plugin data.
|
|
306
|
+
|
|
307
|
+
## Validate and maintain
|
|
308
|
+
|
|
309
|
+
Framework checks are `npm run check`, `npm run lint`, `npm run format:check`, `npm test`, `npm run build`, `npm run test:package`, and `npm run build:storybook`. Browser CI checks run with `npm run test:browser:ci` and `npm run test:browser:ci -- --dev` after a build. The package check uses a fresh consumer and normal npm lifecycle handling. A separate consumer should also type-check/build its own application.
|
|
310
|
+
|
|
311
|
+
Update public JSDoc and [API reference](api.md) when contracts change. Update [release-node.md](../release-node.md), [CHANGELOG.md](../CHANGELOG.md), examples, and relevant architecture/validation notes. Keep the MIT license and required third-party notices. Commit each coherent feature; do not push or publish automatically.
|
|
312
|
+
|
|
313
|
+
## Troubleshooting
|
|
314
|
+
|
|
315
|
+
| Symptom | Check |
|
|
316
|
+
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
317
|
+
| Blank or tiny docking area | Import `@cyann/dash-lab/style.css` and give html/body/root and the workbench parent a height. |
|
|
318
|
+
| Plugin absent | The entry must match `src/plugins/*/index.ts`, default-export a plugin, and be included in the application's eager glob. |
|
|
319
|
+
| Registration closed | Finish registration before mounting; create a new registry and remount to change membership. |
|
|
320
|
+
| Panel reference error | Register the referenced ID first; consider sorted plugin path order. |
|
|
321
|
+
| Command unavailable | Inspect ID/shortcut conflicts, `isEnabled`, overlapping execution, and notification history. |
|
|
322
|
+
| Shortcut does nothing in a textarea | Application shortcuts protect editable controls unless `allowInEditable` is explicitly enabled. |
|
|
323
|
+
| Translation displays its key | Add that key to an application/plugin catalog for the active language or English fallback. |
|
|
324
|
+
| Hook provider error | Call hooks from a component inside Workbench or the matching explicit provider, never during activation. |
|
|
325
|
+
| Old saved layout rejected | The current panel identity set differs, or data is corrupt; use the default layout and rearrange a panel to refresh the custom layout. |
|
|
326
|
+
| Preferences/layout do not survive reload | Check the configured storage keys and notification feedback; browser storage may be blocked/full. |
|
|
327
|
+
| Port already used | Stop your existing server or choose another port for your separate application; framework defaults are strict. |
|
|
328
|
+
| PowerShell refuses npm | Use `npm.cmd`. |
|
|
329
|
+
|
|
330
|
+
Theme families are Azure (the original blue), High Contrast (presentation), Emerald (green), and Umber (brown). Each has independent light/dark variants. Select a family in the hamburger menu's Theme submenu or Preferences; the existing light/dark switch keeps that family. High Contrast uses stronger text/borders, larger controls and typography. Application-owned panel content should consume the shared tokens for matching readability.
|
|
331
|
+
|
|
332
|
+
`ThemeProvider` accepts optional `initialFamily` (`azure` by default). `useTheme()` exposes `family` and `setFamily(ThemeFamily)` alongside the existing mode controls. Workbench automatically persists the selected family in `preferences.themeFamily` and includes it in JSON transfers. Version-1 files without this field use Azure; unknown families are rejected before import.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
`src/` is the reusable library. `demo/` consumes its public API and contains application-specific content. The library must not import the demo.
|
|
4
|
+
|
|
5
|
+
```mermaid
|
|
6
|
+
flowchart LR
|
|
7
|
+
Demo[Hello World application] --> API[Public library exports]
|
|
8
|
+
Stories[Storybook] --> API
|
|
9
|
+
API --> Layout[Workbench]
|
|
10
|
+
API --> Theme[Scoped theme provider]
|
|
11
|
+
Theme --> AntD[Ant Design ConfigProvider]
|
|
12
|
+
Theme --> Tokens[CSS tokens and Dockview theme]
|
|
13
|
+
Layout --> Visibility[Instance-local visibility store]
|
|
14
|
+
Layout --> Docking[Dockview workspace]
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Vite builds the ESM package into `dist/`. TypeScript emits declarations there independently. The demo build consumes the built package and writes to `demo-dist/`. In development, Vite resolves the demo's library imports to source for hot reload while a separate Vite build watcher and TypeScript watcher keep package outputs current. Both watchers and the demo server run in one Node process and close on Ctrl+C.
|
|
18
|
+
|
|
19
|
+
The demo discovers `src/plugins/*/index.ts` with Vite's eager `import.meta.glob`. `registerLocalPlugins` activates default exports in path order. The registry rolls back failed activations and freezes its contribution collections when Workbench mounts. Plugins are trusted application code bundled at build time; no remote loading or runtime filesystem scanning occurs. Adding/removing a plugin requires restarting the mounted workbench; the demo's HMR boundary reloads on plugin module changes.
|
|
20
|
+
|
|
21
|
+
Plugin panels precede application panels, which may position themselves relative to a plugin panel. Commands are connected to the existing command/notification providers; menus invoke command IDs, and status/monitoring components render inside those same providers. The SDK model does not import the workbench UI. Runtime command conflicts report one notification and remove the attempted plugin-command batch, leaving panels usable. Plugin activation itself must be synchronous.
|
|
22
|
+
|
|
23
|
+
Storybook and Vitest run independently of the demo. Distribution includes only library artifacts, documentation, and license notices.
|
|
24
|
+
|
|
25
|
+
Each workbench owns a separate Zustand visibility store. Focus mode suppresses chrome without changing header/footer preferences or unmounting Dockview. Focus returns to the trigger when leaving focus mode. Keyboard dispatch, including Escape, is scoped to the focused workbench.
|
|
26
|
+
|
|
27
|
+
Panel definitions are validated before Dockview receives them. Application components are passed through stable renderers, and Dockview's `always` renderer retains inactive panel state. The demo uses only the MIT edition's docking features.
|
|
28
|
+
|
|
29
|
+
Each stable panel renderer includes its own full-height scroll container. Overflow scrolls within that panel, and tab changes retain its scroll position. View toggles header and footer together; individual visibility preferences and existing commands remain available.
|
|
30
|
+
|
|
31
|
+
Layout management captures the initial native Dockview layout before loading a saved checkpoint. Versioned checkpoints contain the native layout, header/footer preferences, and optional plugin data. Loading validates panel identities, components, group membership, grid structure, and JSON state before calling plugin importers and then `fromJSON` with `reuseExistingPanels`. Manual docking changes are coalesced and automatically persist the custom checkpoint after 200 ms, with pending writes flushed on page hide, unmount, or before applying another arrangement. Dockview events from programmatic applications, language/title updates, and host resizes are ignored so they do not overwrite the custom arrangement. Header/footer changes persist immediately against that checkpoint; a failed browser write keeps it in memory and reports a deduplicated warning. Reset and presets reuse existing panels, preserve current plugin data, and do not replace the saved checkpoint. Popout browser windows are not restored.
|
|
32
|
+
|
|
33
|
+
ThemeProvider owns another instance-local Zustand store. A single palette supplies CSS tokens and Ant Design tokens; Dockview keeps its native light/dark theme with CSS-variable overrides. A workbench inherits the nearest provider or creates a light provider automatically. Native controls and popup containers follow the local color scheme. Theme changes never recreate the workspace or its panels. The workbench configuration integration persists preferences separately from layout checkpoints.
|
|
34
|
+
|
|
35
|
+
Reduced-motion preferences are observed with a media query, including changes during a session. CSS transitions and Ant Design motion durations become zero. Ant Design's boolean motion flag stays stable because changing it can insert provider wrappers and remount the application subtree. Contrast tests independently compute luminance for primary, secondary, and primary-button text in both palettes.
|
|
36
|
+
|
|
37
|
+
NotificationProvider owns a bounded Zustand history, unread flags, and the active toast IDs. Dismissing a toast never removes or acknowledges its history entry. Toast components own and clean up their timers. A native modal dialog supplies keyboard focus containment and Escape handling for the history. Layout feedback is published once per action into the same center; the library does not have a second feedback system. Each workbench creates a provider unless an application supplies one.
|
|
38
|
+
|
|
39
|
+
CommandRegistry is independent of React and browser globals. It stores action definitions, checks canonical shortcut conflicts, blocks overlapping runs of the same registration, and provides disposable registrations and external-store snapshots. React's CommandProvider adapts it to notification error feedback. The workbench integration registers built-in actions over the existing theme, visibility, notification, and layout operations, and handles keyboard events at the workbench boundary. No lower-level service imports Workbench. Application actions follow the same lifecycle, demonstrated by the Playground commands.
|
|
40
|
+
|
|
41
|
+
CommandPalette consumes that registry without duplicating actions. A pure search function ranks title/ID matches and recent successful selections. The controlled native dialog keeps focus in a combobox, exposes a listbox with its active descendant, skips unavailable commands during keyboard navigation, and closes before executing an action that may open another dialog. Recent selections are local session state.
|
|
42
|
+
|
|
43
|
+
The Ant Design hamburger menu invokes registered commands to open Settings, Preferences, or the configuration file controls. The native settings dialog reuses the existing visibility and layout operations. Preferences keeps a draft until Apply. Theme and notification stores own their respective choices; neither imports the settings UI or the workbench.
|
|
44
|
+
|
|
45
|
+
Shared overlay styles slide and fade native dialogs on both entry and exit, fade their backdrops, and slide notifications from the right. CSS `@starting-style` defines entry values. When discrete `display` and `overlay` transitions work, native `close()` releases modal state and restores focus immediately while CSS retains the exit rendering. See [native top-layer transitions](https://developer.chrome.com/docs/modern-web-guidance/guides/ui-behaviors/animate-to-from-top-layer).
|
|
46
|
+
|
|
47
|
+
`dialogMotion` checks overlay support and probes real discrete-display behavior once per document. On browsers such as Firefox without that support, it marks the still-open dialog as closing, waits for its own transitions to settle, then calls native `close()` and restores focus. A bounded fallback handles stalled animations. Reopening cancels pending closure, and the palette awaits closure before executing a command that can open another modal. System and explicit reduced-motion choices close immediately when there is no animation to wait for.
|
|
48
|
+
|
|
49
|
+
Settings preserves its last displayed page during exit, and notification history retains its column layout. Shared dismissal handling routes backdrop clicks, cancel requests, and Escape through the same logic. Escape is intercepted before the browser's default action because Firefox can emit a non-cancelable `cancel` event after programmatic opening. IME composition and already handled keys are preserved. A backdrop click must start and end outside the dialog, preserving drags begun inside its content.
|
|
50
|
+
|
|
51
|
+
The pure configuration decoder validates the complete versioned JSON file before the workbench applies it. Optional `registerState` handlers own synchronous domain validation and live store updates; the framework owns JSON copying, plugin-ID scoping, and checkpoint orchestration. Plugin states are stored once at the JSON configuration's top level and alongside the layout in browser checkpoints. Before import, participating handlers export independent rollback snapshots. Failed imports restore attempted handlers in reverse order; native layout restoration reuses panels and rolls back a Dockview failure. Imports then apply validated preferences and explicitly save a layout/plugin checkpoint. Storage writes to the layout and preference keys are independent; if either fails, the imported configuration stays usable in memory and notification feedback states that persistence was incomplete. Export reads live layout/plugin data without changing the saved checkpoint. Data stays in the browser and local JSON files. See [the state checkpoint decision](decisions/003-plugin-state-checkpoints.md).
|
|
52
|
+
|
|
53
|
+
I18nProvider owns isolated Zustand language state, extensible message catalogs, plain-text interpolation, the subtree's language attribute, and Ant Design's locale provider. Workbench supplies it automatically when absent. English source messages serve as built-in keys; application catalogs can also use stable symbolic keys. Command/panel identities remain unchanged when their displayed titles are translated. The configuration codec accepts legacy files without a locale; the workbench checks registered-language availability before importing a layout. Notification history retains delivered content rather than translating user messages retrospectively.
|
|
54
|
+
|
|
55
|
+
Theme families are Azure (the original blue), High Contrast (presentation), Emerald (green), and Umber (brown). Each has independent light/dark variants. Select a family in the hamburger menu's Theme submenu or Preferences; the existing light/dark switch keeps that family. High Contrast uses stronger text/borders, larger controls and typography. Application-owned panel content should consume the shared tokens for matching readability.
|
|
56
|
+
|
|
57
|
+
`ThemeProvider` accepts optional `initialFamily` (`azure` by default). `useTheme()` exposes `family` and `setFamily(ThemeFamily)` alongside the existing mode controls. Workbench automatically persists the selected family in `preferences.themeFamily` and includes it in JSON transfers. Version-1 files without this field use Azure; unknown families are rejected before import.
|
|
58
|
+
|
|
59
|
+
NotificationToasts retains presentation-only snapshots of departing popups in their stack positions until CSS exit animations settle, with a bounded fallback and cleanup on unmount. Notification-store operations remain synchronous and unchanged, including removal and popup limits. Exit cancels expiry timers, disables interaction, removes live-region roles and hides the retained snapshot from assistive technology. System or explicit reduced motion disables exit animations and removes the snapshot before paint. This path is independent of native-dialog transition support and works in Firefox as well as Chromium.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# 001 — Separate the reusable workbench from its demo
|
|
2
|
+
|
|
3
|
+
The framework must support future applications while providing an immediately runnable Hello World example.
|
|
4
|
+
|
|
5
|
+
Keep one npm package with library source in `src/` and an application in `demo/`. Build them separately. The application imports the package's public API; only development resolves that API to source for hot reload. React stays a peer dependency, and Ant Design/Dockview/Zustand remain external in the ESM library. Ship CSS as an explicit stylesheet export.
|
|
6
|
+
|
|
7
|
+
A single application would make package reuse difficult to verify. A multi-package workspace would add release and development coordination before a second package is needed. The chosen structure keeps library consumption testable without that infrastructure.
|
|
8
|
+
|
|
9
|
+
Each workbench and theme provider owns its state. Shared global stores would couple multiple embedded applications; provider-scoped stores allow independent themes and visibility settings.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Local plugin startup and language catalogs
|
|
2
|
+
|
|
3
|
+
Status: accepted, 2026-11-03.
|
|
4
|
+
|
|
5
|
+
## Context
|
|
6
|
+
|
|
7
|
+
The framework must remain reusable while application plugins live in local directories and are compiled by Vite. Workbench panels already use stable startup definitions and persist native layouts by identity. English/French interfaces and future languages must preserve those identities and component state.
|
|
8
|
+
|
|
9
|
+
## Decision
|
|
10
|
+
|
|
11
|
+
Use Vite's eager glob in the consuming application, a synchronous transactional registry in the library, and a sealed contribution snapshot on mount. Connect contributions to existing commands/notifications and render status/monitoring components inside existing providers. Runtime membership changes require a remount; production changes require a build.
|
|
12
|
+
|
|
13
|
+
Use a small isolated catalog store and React provider with English fallback, plain-text placeholders, and Ant Design locale integration. Displayed titles can change while command/panel IDs remain stable. Keep locale optional in version-1 configuration and validate registered-language availability before importing a workspace.
|
|
14
|
+
|
|
15
|
+
## Alternatives and consequences
|
|
16
|
+
|
|
17
|
+
Runtime filesystem scanning and remote script loaders add deployment/security/lifecycle concerns outside the requested scope. Dynamic panel mutation would require reconciling saved layouts and mounted state. A full external translation framework would add a dependency for a small catalog requirement. The selected design uses the approved stack, makes startup deterministic, and leaves application services/content outside the core. Plugins remain trusted code, and language catalogs stay plain text.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Plugin state uses existing configuration checkpoints
|
|
2
|
+
|
|
3
|
+
## Decision
|
|
4
|
+
|
|
5
|
+
Plugins may register one synchronous `exportState`/`importState` pair during activation. The registry assigns its owning plugin ID. Plugin data joins automatic custom-layout checkpoints, explicit layout saves, restoration on startup, and JSON configuration import/export. Application stores remain plugin-owned; the framework does not serialize React components.
|
|
6
|
+
|
|
7
|
+
## Compatibility and failure handling
|
|
8
|
+
|
|
9
|
+
Version-1 files gain an optional `plugins` field. Absent entries leave live state unchanged. Unknown participants are ignored; plugin-specific schema versions remain inside their own data. Existing 1 MB limits include plugin data. Reset and layout presets preserve live plugin state.
|
|
10
|
+
|
|
11
|
+
Snapshots contain copied JSON data. Before applying plugin data and a layout, the framework captures participating stores for rollback. Failed imports restore attempted handlers in reverse order and report incomplete rollback. Callbacks must be synchronous and able to re-import their own snapshots; external side effects are outside this contract. This keeps the existing startup and mounted-panel lifecycles intact without a second persistence system.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
|
|
3
|
+
The stack follows the libraries explicitly selected in `prompt.md`. Versions are exact and the npm lockfile is committed. No additional UI framework, icon package, process manager, or test DOM is required.
|
|
4
|
+
|
|
5
|
+
| Role | Packages | License |
|
|
6
|
+
| ------------------ | ------------------------------------------- | --------------- |
|
|
7
|
+
| UI peers | React, React DOM | MIT |
|
|
8
|
+
| Workbench | Ant Design, Dockview React, Zustand | MIT |
|
|
9
|
+
| Demo routing | React Router | MIT |
|
|
10
|
+
| Build | Vite, its official React plugin | MIT |
|
|
11
|
+
| Types | TypeScript; Node/React declarations | Apache-2.0; MIT |
|
|
12
|
+
| Quality | ESLint, typescript-eslint, Prettier, Vitest | MIT |
|
|
13
|
+
| Component examples | Storybook, React/Vite integration | MIT |
|
|
14
|
+
|
|
15
|
+
TypeScript 6.0.3 is selected because typescript-eslint 8.72.1 declares support below 6.1; the current TypeScript 7 release is outside that range. Node.js 22.13+ satisfies the tooling engines; Node 24 LTS is the development baseline.
|
|
16
|
+
|
|
17
|
+
Runtime dependencies stay external in the library's JavaScript bundle; the demo bundles what it uses. The distribution must retain dependency copyright/license notices for bundled assets. Installed license files and transitive package metadata are checked as part of dependency validation. Security findings are reported from `npm audit`, independently of the license inventory.
|
|
18
|
+
|
|
19
|
+
The installed transitive inventory also includes ISC, BSD-2-Clause, BSD-3-Clause, 0BSD, BlueOak-1.0.0, CC-BY-4.0, and MPL-2.0 packages. Their own license texts and notices continue to apply when redistributing their code or data.
|
|
20
|
+
|
|
21
|
+
The non-MIT development dependencies include caniuse-lite data (CC-BY-4.0), glob/minimatch and their cache helpers (BlueOak-1.0.0), and Lightning CSS binaries (MPL-2.0). Their authoritative license files are included with the installed packages; these tools are not bundled into the library runtime. Dockview's installed `LICENCE.md` is reproduced in `THIRD_PARTY_NOTICES.md` because its CSS is bundled. Enterprise-only Dockview packages are not used.
|
|
22
|
+
|
|
23
|
+
On 2026-11-02, `npm audit --omit=dev` reports no runtime vulnerabilities. The full audit reports 14 moderate findings in development tooling, originating from glob and minimatch and propagated to ESLint/Storybook. npm proposes incompatible downgrades; no forced major override is applied. These findings remain a tooling limitation to revisit when upstream dependencies update.
|
|
24
|
+
|
|
25
|
+
The demo's bundled JavaScript is about 311 kB gzip with the current UI libraries. Vite reports its uncompressed chunk-size warning. Storybook also reports Ant Design's `use client` directive warnings while producing its browser-only preview; the build succeeds. Neither build is an assertion of React Server Component support.
|
|
26
|
+
|
|
27
|
+
## Git automation tools
|
|
28
|
+
|
|
29
|
+
Feature 13 explicitly requests Husky, lint-staged, and commitlint. Installed versions are Husky 9.1.7, lint-staged 16.4.0, and `@commitlint/cli`/`@commitlint/config-conventional` 21.2.4, all MIT. Their installed authoritative license files were inspected, alongside the [Husky license](https://github.com/typicode/husky/blob/main/LICENSE), [lint-staged license](https://github.com/lint-staged/lint-staged/blob/main/LICENSE), and [commitlint license](https://github.com/conventional-changelog/commitlint/blob/master/license.md). These are development tools and are not bundled into the framework runtime.
|
|
30
|
+
|
|
31
|
+
lint-staged 17 requires Node 22.22.1; 16.4.0 supports Node 20.17+ and preserves this package's advertised Node 22.13 minimum. The added tool dependencies include 75 MIT, ten ISC, one BSD-3-Clause (`fast-uri`), and one Python-2.0 (`argparse`) package. Their original notices remain in installed packages; the Python-derived license includes retention and change-summary conditions when redistributing modified derivatives. The framework does not modify or bundle these tool sources. Installed authoritative license texts were inspected for the non-MIT additions as well.
|
|
32
|
+
|
|
33
|
+
After installation on 2026-11-03, the runtime audit still reports zero findings. The full audit retains the previously documented 14 moderate development-tooling findings. The lockfile records exact resolutions. Hook setup follows [Husky](https://typicode.github.io/husky/get-started.html) and [commitlint](https://commitlint.js.org/guides/local-setup.html); GitHub workflow action versions follow the official [checkout](https://github.com/actions/checkout) and [setup-node](https://github.com/actions/setup-node) repositories.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Features 1–3 validation
|
|
2
|
+
|
|
3
|
+
Verified locally on Windows with Node.js 24.20.0 on 2026-11-02:
|
|
4
|
+
|
|
5
|
+
- TypeScript strict checking, ESLint, Prettier, and library/demo builds pass.
|
|
6
|
+
- Eleven Vitest tests cover panel contracts, independent workbench/theme state, focus visibility preferences, nested provider composition, and text contrast.
|
|
7
|
+
- Eleven headless Edge interaction checks pass against both `npm run start` and `npm run dev`: real greeting input, chrome visibility, focus exit and keyboard focus restoration, retained panel instances, both theme integrations, and reduced motion.
|
|
8
|
+
- Changing reduced-motion preferences during a session keeps the same panel instances and their state. Ant Design durations change without toggling its structural motion wrapper.
|
|
9
|
+
- The library build watcher rebuilds after an actual palette edit; Vite serves the changed source. The probe restores the original source and output.
|
|
10
|
+
- The packed npm tarball installs in a separate consumer. Its public API type-checks with NodeNext resolution, imports as ESM, renders with React DOM server, and resolves its CSS export.
|
|
11
|
+
- The static Storybook build succeeds with light, dark, hidden-chrome, and empty-workspace examples.
|
|
12
|
+
- Light/dark screenshots are visually reviewed at 1440 × 1000. They are generated under the ignored `test-results/` directory by `npm run test:browser`.
|
|
13
|
+
|
|
14
|
+
Browser verification currently covers Edge only. Firefox and Safari have not been tested. Dependency audit and bundling warnings are described in [dependency notes](dependencies.md).
|
|
15
|
+
|
|
16
|
+
## Feature 4
|
|
17
|
+
|
|
18
|
+
Seventeen unit tests and sixteen browser checks pass. Layout checks cover columns/rows presets, restoration and reset with the same panel instances, preservation of the explicit checkpoint, persistence across reload, corrupt saved data, and blocked storage with an in-memory fallback. The integration test waits for Dockview's asynchronous geometry update before comparing panel positions.
|
|
19
|
+
|
|
20
|
+
## Feature 5
|
|
21
|
+
|
|
22
|
+
Twenty-three unit tests and twenty-three browser checks pass. Notification checks cover bounded history, toast dismissal without losing unread entries, automatic toast expiry, sticky errors, per-item acknowledgment/removal, marking all read, clearing history, modal keyboard focus, and Escape. Layout actions publish into the same center. Warning/error text colors satisfy the same contrast tests as the other theme text.
|
|
23
|
+
|
|
24
|
+
## Feature 6
|
|
25
|
+
|
|
26
|
+
Thirty-two unit tests and thirty-two browser checks pass. The complete browser suite runs against development (including React StrictMode) and compiled demo servers. Command tests cover registration cleanup, ID and canonical shortcut conflicts, independent platform mappings, dynamic availability, asynchronous overlap prevention, error propagation, subscriptions, text-entry and contenteditable protection, IME/repeat/AltGraph filtering, scoped keyboard dispatch, modal boundaries, and built-in actions over layouts/themes/focus/notifications. A failing asynchronous demo action creates a notification without breaking the workspace.
|
|
27
|
+
|
|
28
|
+
The packaged library is installed into a fresh consumer after Features 4–6. Public types pass strict NodeNext checking; ESM execution, explicit provider composition, and the CSS export pass. Storybook includes interactive command and notification examples. The runtime audit reports zero vulnerabilities; the dependency versions are unchanged.
|
|
29
|
+
|
|
30
|
+
## Feature 7
|
|
31
|
+
|
|
32
|
+
Thirty-five unit tests and forty-one compiled-demo browser checks pass. Palette checks cover normalized and abbreviated search, empty results, live availability, unavailable actions, keyboard selection, recent successful commands, retained panel state, Escape focus restoration, Focus mode, and transfer to the notification dialog without stacking modals. Search ranking has separate unit coverage. No dependency was added.
|
|
33
|
+
|
|
34
|
+
## Feature 8
|
|
35
|
+
|
|
36
|
+
Forty-two unit tests and fifty-six compiled-demo browser checks pass. Coverage includes the File menu entirely by keyboard, Settings focus restoration and layout saving, preference drafts and cancellation, retained panel state, independent preference persistence, popup choices, real JSON download and file-picker imports, successful restoration after reload, rejection of incompatible versions/panels without partial application, repeated file selection, and blocked browser storage. Configuration validation also covers malformed preferences, missing workspaces, byte-size limits, and unknown fields.
|
|
37
|
+
|
|
38
|
+
The full browser suite also passes against the development server with React StrictMode. The packed package installs into a fresh consumer and passes strict NodeNext type checking, ESM execution, SSR provider composition, and CSS-export checks. Storybook builds with a settings example. The runtime dependency audit reports zero vulnerabilities; no dependency was added.
|
|
39
|
+
|
|
40
|
+
## Requested UI modifications
|
|
41
|
+
|
|
42
|
+
On 2026-11-03, forty-three unit tests and sixty-six compiled-demo browser checks pass. The seven changes cover the combined View control, larger modal close controls, blue/gray light and dark palettes aligned with the supplied references, larger notification text and panels, animated modal/toast entrances and fading backdrops, backdrop dismissal, and independent panel scrolling. Palette text contrasts remain covered by independent luminance calculations.
|
|
43
|
+
|
|
44
|
+
The same sixty-six checks pass on the development server with React StrictMode. Browser checks exercise all three modal backdrops, cancellation of a preference draft, focus restoration, preservation of a drag starting inside a modal, animation disabling for reduced motion, and real wheel scrolling inside overflowing content. Storybook includes an overflowing-content example. Screenshots capture settled overlays for visual review.
|
|
45
|
+
|
|
46
|
+
## Feature 9
|
|
47
|
+
|
|
48
|
+
Forty-nine unit tests cover plugin startup, all contribution kinds, copied/frozen definitions, transactional rollback, cleanup, stale APIs, duplicate IDs/shortcuts, reserved commands, deterministic module order, and sealing. The compiled demo discovers Hello World through Vite's eager directory glob. Browser checks exercise its panel, footer and monitoring items, and execute the contributed command from both its menu and the palette.
|
|
49
|
+
|
|
50
|
+
## Feature 10
|
|
51
|
+
|
|
52
|
+
Fifty-four unit tests and seventy-two compiled-demo browser checks pass. Language checks cover English defaults, French, catalog extension, fallback, plain-text interpolation, isolated providers, real Ant Design locale context, legacy preferences, and rejection of an unregistered language before applying a layout. The browser changes language without replacing the Hello World panel or its input, searches and executes a translated command, receives French feedback, and restores French menus/preferences after reload.
|
|
53
|
+
|
|
54
|
+
## Feature 11
|
|
55
|
+
|
|
56
|
+
Sixty Vitest tests pass, including public hook/provider contracts, nearest-provider isolation, missing-provider messages, independent stores, legacy language configuration, and a regression ensuring standalone English stays read-only during server rendering. Server snapshots use Zustand's initial state; browser interaction tests cover live updates and effect lifecycles.
|
|
57
|
+
|
|
58
|
+
`npm run test:package` builds and packs the library, rejects source/demo/test leakage, installs the tarball offline into a fresh application using npm's populated cache, type-checks its public API under strict NodeNext resolution, then executes real ESM/React server rendering, plugin status, French, legacy configuration, and CSS-export checks. Artifacts stay under ignored `test-results/`. The runtime install uses normal npm lifecycle handling, so package hook mistakes are observable.
|
|
59
|
+
|
|
60
|
+
## Feature 12
|
|
61
|
+
|
|
62
|
+
The API reference documents every public export group and its contracts. Contribution instructions, a changelog, project-specific agent guidance, and the plugin/language architecture decision accompany it. Storybook builds with plugin and language examples as well as the existing layout/theme/notification/command examples. The MIT license and third-party notices remain included in the package.
|
|
63
|
+
|
|
64
|
+
## Feature 13
|
|
65
|
+
|
|
66
|
+
CI and issue-form YAML structures are parsed locally; Conventional Commits validation accepts a valid message and rejects an invalid one. Husky is installed locally, with staged checks and commit-message validation enabled. The browser CI runner starts/closes its own server and passes all seventy-two checks against both compiled and development demos. Packed-consumer verification also passes with normal install lifecycle handling after hook setup.
|
|
67
|
+
|
|
68
|
+
GitHub-hosted workflow execution and the Linux/Node 22.13 matrix have not been run locally. They require pushing the repository; no push, deployment, package publication, or GitHub-side mutation is performed here.
|
|
69
|
+
|
|
70
|
+
## Feature 14
|
|
71
|
+
|
|
72
|
+
The README's Counter plugin and the complete application's Notes plugin, discovery module, language catalogs, HTML/CSS, and Vite/TypeScript configuration are extracted from their Markdown examples into a fresh packed-library consumer. They pass strict type checking and a real Vite build. Three additional browser checks verify local discovery, Counter state through View/theme changes, and its command's notification without state reset. The settled screenshot is `test-results/tutorial-counter.png`.
|
|
73
|
+
|
|
74
|
+
Documentation links resolve locally. `release-node.md` records all fourteen features, compatibility, verification, and publication status; AGENTS.md requires agents to update it in the same work/commit as future changes. Package contents include the guide, API docs, contribution/agent instructions, changelog, and release notes. The demo monitoring count derives from actual plugin/application panels rather than a fixed number.
|
|
75
|
+
|
|
76
|
+
Final visual review and the French browser regression also cover translated application-constructed header/footer/monitoring content and plugin status, with all toolbar controls remaining inside the desktop viewport.
|
|
77
|
+
|
|
78
|
+
## Plugin state serialization
|
|
79
|
+
|
|
80
|
+
On 2026-11-03, seventy-four Vitest tests pass. New tests cover optional/scoped registration and disposal, copied JSON snapshots, unsupported exports, missing/unknown IDs, null values, legacy configuration/checkpoints, domain failure rollback including a partially mutated failing plugin, incomplete rollback reporting, and rollback when native layout application fails. Strict TypeScript and ESLint pass.
|
|
81
|
+
|
|
82
|
+
All seventy-six Edge checks pass against both the compiled demo and the development server with React StrictMode. They verify explicit plugin checkpoint restoration, startup reload, export without duplicated state, import into the existing mounted panel, imported state surviving reload, rejected plugin versions leaving state/layout/preferences intact, and legacy JSON leaving live plugin data untouched. The demo's Hello World state uses the same registration mechanism as external plugins.
|
|
83
|
+
|
|
84
|
+
Library/demo and Storybook builds pass, as does the fresh packed-consumer check with the new public state handler/configuration types. Existing bundle-size and Storybook directive warnings remain. Modified files pass Prettier and `git diff --check`; the global format command flags the unrelated pre-existing `reviews/review-flash-2026-11-03-0938.md`, which is left unchanged. No dependency was added.
|
|
85
|
+
|
|
86
|
+
## Scoped package and npm publication script
|
|
87
|
+
|
|
88
|
+
The package is named `@cyann/dash-lab`, consistent with the adjacent libraries' manifests. Source imports, TypeScript/Vite aliases, documentation, and the packed-consumer fixtures use that name. Configuration JSON identifiers and browser-storage keys stay `dash-lab` for compatibility.
|
|
89
|
+
|
|
90
|
+
`npm run publish:npm -- --dry-run` succeeds with the real npm CLI. It runs strict TypeScript, ESLint, global Prettier, all seventy-four Vitest tests, seven script integration tests, and the packed-consumer check. It then packs `cyann-dash-lab-0.1.0.tgz` and successfully simulates publishing that exact artifact to npmjs with public access and the `latest` tag. Nothing is uploaded. The earlier review-format warning has been resolved by the intervening documentation commit; the global check now passes.
|
|
91
|
+
|
|
92
|
+
The seven script tests use temporary local Git repositories and a fake npm executable. They cover dirty-checkout rejection, account mismatch, early stop on failed validation, simulation without authentication, selection of the exact tarball, prerelease tags, invalid arguments, and package identity restrictions. Live account authentication and real publication remain untested; no account credentials are requested by these tests. No dependency was added.
|
|
93
|
+
|
|
94
|
+
Library/demo and Storybook builds also pass after the package rename. All seventy-six Edge checks pass against both compiled and development demos, confirming public package resolution, plugin state, configuration compatibility, and localization with the scoped imports.
|
|
95
|
+
|
|
96
|
+
## Git tags after npm publication
|
|
97
|
+
|
|
98
|
+
Twelve publication-script integration tests pass using real temporary Git repositories and a fake npm CLI. They verify annotated `version-<version>` tags on the validated commit, no tag before npm success or after npm failure, no Git tag during dry runs, unchanged reuse of existing tags, rejection of conflicting tags before npm contact, prerelease tag names, refusal when HEAD moves during checks, and explicit partial-success reporting when Git signing fails after publication.
|
|
99
|
+
|
|
100
|
+
The full real-npm dry-run pipeline passes with all seventy-four Vitest tests, the twelve script tests, strict TypeScript, ESLint, Prettier, library build, and packed-consumer validation. No actual npm publication or tag creation in the project repository is performed. Browser/Storybook checks are unchanged from the preceding package-scope verification; this change affects publication tooling only.
|
|
101
|
+
|
|
102
|
+
## Modal exit animations
|
|
103
|
+
|
|
104
|
+
Seventy-four Vitest tests, strict TypeScript, ESLint, Prettier, library/demo and Storybook builds, and packed-consumer validation pass. Eighty-seven Edge checks pass against both compiled and development demos, including eleven new checks for animated modal closure, focus handoff, rapid reopening, and immediate closure under system/explicit reduced motion.
|
|
105
|
+
|
|
106
|
+
Browser checks wait for entry to finish, trigger real dismissal, pause actual exit transitions halfway, and inspect opacity, slide direction, top-layer retention, backdrop fading, and the retained Preferences title. They finish the transitions and verify `display: none`. Escape, backdrop clicks, close buttons, Cancel, and Apply are exercised. Visual review of `test-results/demo-modal-exit.png` and `test-results/demo-notifications-exit.png` confirms the exit appearance and retained notification column layout. The palette still hands focus to Settings without stacking open modal dialogs.
|
|
107
|
+
|
|
108
|
+
The first browser run exposed the old entry keyframes masking exit opacity; replacing modal/backdrop entry keyframes with shared native CSS transitions resolved it on Edge. Toast entry remains unchanged. That initial verification did not include Firefox or Safari; the subsequently reported Firefox failure and its correction are recorded below. Existing build-size/Storybook warnings remain. No dependency or public API was added.
|
|
109
|
+
|
|
110
|
+
## Firefox notification exit regression
|
|
111
|
+
|
|
112
|
+
Reproduced on installed Firefox 159 using its actual WebDriver BiDi protocol and an isolated profile. The original notification dialog became `display: none` immediately after the close button, with no intermediate opacity or position. Firefox reports no native CSS overlay support. The new shared helper keeps the modal open during its fade/slide and calls `close()` only after its own transitions settle. Escape is intercepted before the browser default to cover non-cancelable cancel events after programmatic opening.
|
|
113
|
+
|
|
114
|
+
Six Firefox regressions pass against both the compiled demo and the development server: close-button motion with natural completion and focus restoration, real keyboard Escape, real pointer backdrop clicks, rapid reopening without delayed closure, palette-to-Settings handoff with only one open modal, and immediate closure with reduced motion. The tests observe intermediate opacity/position and backdrop opacity without advancing animation clocks. Run `npm run test:firefox` after building, or `npm run test:firefox -- --dev`; `FIREFOX_PATH` selects another installed executable.
|
|
115
|
+
|
|
116
|
+
The existing eighty-seven Edge checks pass in both compiled and development modes, retaining its native CSS exit path. Seventy-four Vitest tests, strict TypeScript, ESLint, formatting, library/demo and Storybook builds, and packed-consumer checks pass. No dependency or public API was added. Safari remains untested.
|
|
117
|
+
|
|
118
|
+
## Theme families and automatic custom layout
|
|
119
|
+
|
|
120
|
+
81 Vitest tests cover all eight palettes, independent family/mode/provider state, high-contrast text and border targets, additive preference round-trips, legacy compatibility and unknown-family rejection. Strict TypeScript, ESLint, Prettier, library/demo and Storybook builds, and packed-consumer validation pass.
|
|
121
|
+
|
|
122
|
+
100 Edge checks pass against each compiled and development demo. New interactions exercise each family in both modes without remounting panels, theme-family persistence and JSON export, real pointer-driven sash resizing, automatic custom/chrome persistence, restoring the exact custom width after trying presets, preserving the custom checkpoint during theme and host-window resizing, and storage failure fallback. Existing stateful plugin checkpoint, import rollback, localization, keyboard and overlay checks still pass. Six native Firefox modal regressions also pass against the compiled demo. No remote publication was performed.
|
|
123
|
+
|
|
124
|
+
## Notification popup exits
|
|
125
|
+
|
|
126
|
+
81 Vitest store/service/provider tests, strict TypeScript, ESLint, Prettier, library/demo and Storybook builds, and packed-consumer validation pass. Edge checks against the compiled and development demos now sample toast exit animations halfway through: opacity and horizontal position change, departing popups retain their stack position, become inert, stop live-region announcements and unmount when the animation finishes. Cases cover dismissal, expiry, clearing history, removing a middle entry, overflow replacement and immediate reduced-motion removal. Existing history/unread and plugin/layout checks remain intact. Nine Firefox checks pass with naturally progressing animations, including toast dismissal, expiry, immediate explicit reduced-motion removal and all six existing modal regressions.
|