@happyvertical/smrt-svelte 0.55.1 → 0.55.2

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.
Files changed (77) hide show
  1. package/README.md +29 -2
  2. package/agents/components.md +44 -1
  3. package/agents/workspace.md +77 -2
  4. package/dist/components/app/AppShell.svelte +121 -9
  5. package/dist/components/app/AppShell.svelte.d.ts +24 -2
  6. package/dist/components/app/AppShell.svelte.d.ts.map +1 -1
  7. package/dist/components/app/DockToggles.svelte +103 -0
  8. package/dist/components/app/DockToggles.svelte.d.ts +8 -0
  9. package/dist/components/app/DockToggles.svelte.d.ts.map +1 -0
  10. package/dist/components/app/dock-toggle.d.ts +14 -0
  11. package/dist/components/app/dock-toggle.d.ts.map +1 -0
  12. package/dist/components/app/dock-toggle.js +1 -0
  13. package/dist/components/app/index.d.ts +2 -0
  14. package/dist/components/app/index.d.ts.map +1 -1
  15. package/dist/components/app/index.js +1 -0
  16. package/dist/components/board/Board.svelte +92 -394
  17. package/dist/components/board/Board.svelte.d.ts.map +1 -1
  18. package/dist/components/forms/RelationInput.svelte +379 -0
  19. package/dist/components/forms/RelationInput.svelte.d.ts +75 -0
  20. package/dist/components/forms/RelationInput.svelte.d.ts.map +1 -0
  21. package/dist/components/forms/index.d.ts +2 -1
  22. package/dist/components/forms/index.d.ts.map +1 -1
  23. package/dist/components/forms/index.js +1 -0
  24. package/dist/components/forms/types.d.ts +9 -0
  25. package/dist/components/forms/types.d.ts.map +1 -1
  26. package/dist/components/sortable/Sortable.svelte +359 -0
  27. package/dist/components/sortable/Sortable.svelte.d.ts +26 -0
  28. package/dist/components/sortable/Sortable.svelte.d.ts.map +1 -0
  29. package/dist/components/sortable/controller.svelte.d.ts +152 -0
  30. package/dist/components/sortable/controller.svelte.d.ts.map +1 -0
  31. package/dist/components/sortable/controller.svelte.js +453 -0
  32. package/dist/components/sortable/index.d.ts +5 -0
  33. package/dist/components/sortable/index.d.ts.map +1 -0
  34. package/dist/components/sortable/index.js +2 -0
  35. package/dist/components/sortable/types.d.ts +80 -0
  36. package/dist/components/sortable/types.d.ts.map +1 -0
  37. package/dist/components/sortable/types.js +1 -0
  38. package/dist/components/workspace/admin-shell/AdminShell.svelte +49 -1
  39. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts.map +1 -1
  40. package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte +196 -0
  41. package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte.d.ts +17 -0
  42. package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte.d.ts.map +1 -0
  43. package/dist/components/workspace/admin-shell/TenantNav.svelte +69 -3
  44. package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts.map +1 -1
  45. package/dist/components/workspace/admin-shell/dock.d.ts +37 -0
  46. package/dist/components/workspace/admin-shell/dock.d.ts.map +1 -0
  47. package/dist/components/workspace/admin-shell/dock.js +25 -0
  48. package/dist/components/workspace/admin-shell/layout-context.d.ts +7 -0
  49. package/dist/components/workspace/admin-shell/layout-context.d.ts.map +1 -0
  50. package/dist/components/workspace/admin-shell/layout-context.js +17 -0
  51. package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts +69 -0
  52. package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts.map +1 -0
  53. package/dist/components/workspace/admin-shell/layout-controller.svelte.js +91 -0
  54. package/dist/components/workspace/admin-shell/layout.d.ts +120 -0
  55. package/dist/components/workspace/admin-shell/layout.d.ts.map +1 -0
  56. package/dist/components/workspace/admin-shell/layout.js +402 -0
  57. package/dist/components/workspace/admin-shell/settings.d.ts +6 -4
  58. package/dist/components/workspace/admin-shell/settings.d.ts.map +1 -1
  59. package/dist/components/workspace/admin-shell/settings.js +14 -4
  60. package/dist/components/workspace/admin-shell/state.svelte.d.ts +59 -1
  61. package/dist/components/workspace/admin-shell/state.svelte.d.ts.map +1 -1
  62. package/dist/components/workspace/admin-shell/state.svelte.js +146 -2
  63. package/dist/components/workspace/admin-shell/types.d.ts +52 -0
  64. package/dist/components/workspace/admin-shell/types.d.ts.map +1 -1
  65. package/dist/components/workspace/index.d.ts +6 -1
  66. package/dist/components/workspace/index.d.ts.map +1 -1
  67. package/dist/components/workspace/index.js +5 -0
  68. package/dist/i18n/server.d.ts +1 -0
  69. package/dist/i18n/server.d.ts.map +1 -1
  70. package/dist/i18n/server.js +1 -0
  71. package/dist/i18n/strings.sortable.d.ts +13 -0
  72. package/dist/i18n/strings.sortable.d.ts.map +1 -0
  73. package/dist/i18n/strings.sortable.js +13 -0
  74. package/dist/i18n/strings.workspace.d.ts +13 -0
  75. package/dist/i18n/strings.workspace.d.ts.map +1 -1
  76. package/dist/i18n/strings.workspace.js +13 -0
  77. package/package.json +26 -15
package/README.md CHANGED
@@ -32,6 +32,24 @@ pipeline (`opportunity.stage`) without importing `@happyvertical/smrt-projects`.
32
32
 
33
33
  Svelte 5 component library for the s-m-r-t framework. Provides UI components, browser AI integration (STT/TTS/LLM with warm cache), a theme system, permission-aware rendering, and module UI registry for agent admin panels.
34
34
 
35
+ ## Sortable and shell layout
36
+
37
+ `@happyvertical/smrt-svelte/sortable` provides `Sortable`, an accessible
38
+ (pointer, touch, keyboard, screen-reader announcements) list of containers and
39
+ items that share Board's drag engine. `AppShell` lets users customize their
40
+ shell: pass `layout` and `onlayoutchange` to own persistence (for example in an
41
+ exported app blueprint), or neither to store it in the user's settings. Mount
42
+ `ShellLayoutEditor` (from `/app` or `/workspace`) on a settings page, and call
43
+ `useShellLayout()` to make the same changes from code or an assistant. See
44
+ `agents/workspace.md`; the pure `ShellLayout` model is published without Svelte
45
+ as `@happyvertical/smrt-svelte/workspace/layout`.
46
+
47
+ ```svelte
48
+ <AppShell {nav} {navGroups} layout={stored} onlayoutchange={(next) => (stored = next)}>
49
+ <ShellLayoutEditor />
50
+ </AppShell>
51
+ ```
52
+
35
53
  ## Installation
36
54
 
37
55
  ```bash
@@ -486,6 +504,15 @@ keys the shell renders exactly as before.
486
504
  - **Nav attention**: a `ShellNavItem` with `attention: true` (or a string
487
505
  label) shows a dot in `TenantNav`, over the icon when collapsed, and
488
506
  announces the label ("Needs attention" by default) inside the link.
507
+ - **Nav action**: a `ShellNavItem` with `action: { href, label, icon?,
508
+ visibility? }` renders a separate icon-only link after the item's link
509
+ (`aria-label`/tooltip = `label`, default settings-gear icon,
510
+ `aria-current="page"` on its own page). `visibility: 'active'` omits the link
511
+ from the DOM unless the item's section is current: for a `ShellNavGroup`
512
+ item, any item of the group, their children, or any action href is current;
513
+ for a top-level item, the item, its children, or its action href. A collapsed
514
+ nav renders no actions (the action page stays reachable by its route).
515
+ `AppShell` passes `nav`/`navGroups` through unchanged.
489
516
  - **Public region ids** (`ADMIN_SHELL_REGION_IDS`): `smrt-admin-shell-header`,
490
517
  `smrt-admin-shell-{top,left,right,bottom}-panel`, and
491
518
  `smrt-admin-shell-main`, which is the page scroller.
@@ -510,7 +537,7 @@ importable, even if it appears in `dist/`.
510
537
  | `@happyvertical/smrt-svelte/forms` | Form inputs (TextInput, Select, MoneyInput, DateTimeInput, Toggle, etc.) |
511
538
  | `@happyvertical/smrt-svelte/settings` | Server-paged settings search, selection, and list/detail layout (`SettingsCatalog`, `paginateSettingsCatalog`) |
512
539
  | `@happyvertical/smrt-svelte/workspace` | AdminShell, ShellState, tenant nav, focus tools, settings, activities, and system/app panels |
513
- | `@happyvertical/smrt-svelte/app` | `AppShell` (Provider + themes + AdminShell + nav/dock slots), `OwnerSetupForm` (first-run owner setup), `ShellSettingsPage`, `RuntimeDiagnosticsWebMcp` |
540
+ | `@happyvertical/smrt-svelte/app` | `AppShell` (Provider + themes + AdminShell + nav/dock slots, `dockToggles` header buttons), `OwnerSetupForm` (first-run owner setup), `ShellSettingsPage`, `RuntimeDiagnosticsWebMcp` |
514
541
  | `@happyvertical/smrt-svelte/app/runtime-diagnostics` | Svelte-free diagnostics WebMCP registration and its tool name/endpoint constants, importable from server routes |
515
542
  | `@happyvertical/smrt-svelte/workspace/legacy` | Opt-in ToolsDock compatibility surface for applications migrating to AdminShell |
516
543
  | `@happyvertical/smrt-svelte/workspace/server` | Server-side workspace helpers (Node only) |
@@ -560,7 +587,7 @@ clears it.
560
587
 
561
588
  ### Components by Category
562
589
 
563
- **Forms**: `AddressInput`, `CheckboxInput`, `DateRangeInput`, `DateTimeInput`, `FileUpload`, `Form`, `FormGroup`, `FormMicButton`, `Input`, `MeasurementInput`, `MoneyInput`, `NumberInput`, `PhoneInput`, `SearchInput`, `Select`, `SelectInput`, `Textarea`, `TextareaInput`, `TextInput`, `Toggle`
590
+ **Forms**: `AddressInput`, `CheckboxInput`, `DateRangeInput`, `DateTimeInput`, `FileUpload`, `Form`, `FormGroup`, `FormMicButton`, `Input`, `MeasurementInput`, `MoneyInput`, `NumberInput`, `PhoneInput`, `RelationInput`, `SearchInput`, `Select`, `SelectInput`, `Textarea`, `TextareaInput`, `TextInput`, `Toggle`
564
591
 
565
592
  **Layout**: `Container`, `EmptyState`, `Footer`, `Grid`, `Header`, `Masthead`, `PageHeader`, `SummaryCard`
566
593
 
@@ -15,10 +15,11 @@ the top-of-stack, domain-aware pieces:
15
15
  | Category | Components |
16
16
  |----------|------------|
17
17
  | AI | `Provider`, `AILoadingOverlay`, `CapabilityGate`, `DownloadProgress`, `STTTest`, `VoiceInput` |
18
- | Forms (`/forms`) | `TextInput`, `Select`, `MoneyInput`, `DateTimeInput`, `Toggle`, `FileUpload`, `AddressInput`, + more (AI-wired inputs use the hooks/browser-ai here) |
18
+ | Forms (`/forms`) | `TextInput`, `Select`, `MoneyInput`, `DateTimeInput`, `Toggle`, `FileUpload`, `AddressInput`, `RelationInput`, + more (AI-wired inputs use the hooks/browser-ai here) |
19
19
  | Module | `ModulePanel` |
20
20
  | Settings (`/settings`) | `SettingsCatalog`, `paginateSettingsCatalog` |
21
21
  | Audit (root export) | `RecordHistory`, `AuditList` — authorized data props; [contract](../../../docs/content/audit-trail.md) |
22
+ | Board (`/board`) / Sortable (`/sortable`) | `Board` (kanban) and `Sortable` (nested lists) share one headless drag engine, `SortableController` — [details](workspace.md#sortable-and-the-board-engine) |
22
23
  | Workspace (`/workspace`) | `AdminShell`, `ShellState`, `TenantNav`, focus tools, settings, activities, and system/app panels |
23
24
  | Legacy workspace (`/workspace/legacy`) | First-generation `ToolsDock` compatibility surface during AdminShell migration |
24
25
 
@@ -49,6 +50,48 @@ migrate domain re-rolls *onto* them rather than build new primitives:
49
50
  flow.
50
51
  - **`Card`** (`./ui`) — the standard surface/container; retire local card CSS.
51
52
 
53
+ ### `RelationInput` (`./forms`, #3600)
54
+
55
+ Searchable single-select for relation (foreign-key) fields, a thin wrapper over
56
+ the smrt-ui `Combobox`. It is presentation only: the caller supplies the data,
57
+ so the same lookup an assistant uses to turn "Acme" into a record id backs the
58
+ picker. Option shape is `RelationOption` — `{ id, label, detail? }`.
59
+
60
+ - **Props.** `name`, `label`, `value` (bindable id, `''` = none), `required`,
61
+ `disabled`, `error`, `placeholder`, `description`, `search(query)`, optional
62
+ `resolve(id)` and `onCreate(query)`, plus `debounceMs` (250), `interaction`,
63
+ `onchange` and overridable text (`createLabel`, `clearLabel`, `loadingText`,
64
+ `emptyText`, `errorText`, `resultsText`).
65
+ - **`search`** runs with `''` when the list opens and then debounced per
66
+ keystroke; a response for a superseded query is dropped. A rejected search
67
+ shows `errorText` and the next keystroke retries.
68
+ - **`resolve`** supplies the label of a `value` that has not been searched yet
69
+ (initial value, parent rebind, form reset). Without it such a value shows an
70
+ empty field, never the raw id. A `null` result also leaves the field empty.
71
+ - **`onCreate`** adds a "New ..." button; it receives the text most recently
72
+ searched for and may return the created `RelationOption`, which is selected.
73
+ - **Form integration** is through `Combobox`'s control registration (rich `Form`
74
+ provides the registry), not a `FieldDefinition`: agents see a `combobox`
75
+ control whose value is the id. The form posts the id under `name`.
76
+ - **Known limit.** An agent staging a value through the registered control can
77
+ only choose among the options last listed (the registry matches against
78
+ `options`; `allowCustom` is off), so it cannot write an id it has not
79
+ searched. It fails closed. Writing a resolved id needs an async hook on
80
+ `Combobox` and is tracked separately.
81
+ - **Stale options.** Typing clears the listed options until the new search
82
+ answers, so Enter or a click can never pick a record for an older query.
83
+ - **Clear button** shows only when not `required`, not `disabled`, and a value
84
+ is set; it returns focus to the field.
85
+ - **Accessibility** is the combobox pattern (`role=combobox`, `aria-expanded`,
86
+ `aria-activedescendant`, listbox/option, Arrow/Enter/Escape) plus a polite
87
+ `role=status` live region announcing searching / "N results" / no matches,
88
+ `aria-busy` while fetching, and `aria-invalid` + `aria-describedby` for
89
+ `error` and `description`.
90
+ - Options render `detail` under the label, so the option's accessible name
91
+ includes it. The `Combobox` additions that make this possible (`filter`,
92
+ `onquery`, `status`, `busy`, `invalid`, `describedby`, `optionContent`) are
93
+ generic and default to the previous behaviour.
94
+
52
95
  ### Import convention (S10 #1415)
53
96
 
54
97
  Domain packages **consume** these primitives; they do not re-roll them. The
@@ -172,6 +172,64 @@ enforce permissions.
172
172
 
173
173
  `ActivityTicker` accepts optional `statuses` restricted to queued/running; running-only remains the default. Opting into queued work adds an explicit localized Queued label without progress. Terminal outcomes belong in expanded `ActivityList`, with retention controlled by the app-owned data source.
174
174
 
175
+ ## Shell layout customization
176
+
177
+ Users can show/hide the edge panels and reorder, hide, and move navigation
178
+ sections and items. Source: `workspace/admin-shell/layout.ts` (pure model),
179
+ `layout-controller.svelte.ts` (the API), `ShellLayoutEditor.svelte` (the
180
+ control panel), wired by `app/AppShell.svelte`.
181
+
182
+ - **`ShellLayout`** is a versioned (`version: 1`), JSON-serializable sparse
183
+ delta: `sectionOrder`, `itemOrder` (per section), `hidden` (section and item
184
+ ids), `moved` (item id to section id), `panels` (per edge `visible` /
185
+ `initial`). It is what a host stores or exports (smrt-planner bundles it in
186
+ its app blueprint). `normalizeShellLayout(unknown)` reads untrusted JSON and
187
+ never throws (unknown version yields an empty layout).
188
+ - **Ids.** Sections: `ShellNavGroup.id ?? heading`; items: `ShellNavItem.id ??
189
+ href`; one shared namespace (duplicates get `#2`, `#3`). Set explicit ids
190
+ when headings are translated or hrefs change. The flat `nav` items are the
191
+ implicit first section, `@root` (`SHELL_NAV_ROOT_SECTION_ID`): items can be
192
+ moved in and out, the section itself cannot move or hide.
193
+ - **`applyShellLayout(nav, groups, panels, layout)`** is pure and total. Ids the
194
+ host no longer has are ignored; new host items and sections keep their
195
+ default slots (listed ids permute among the slots they occupy). A hidden
196
+ section hides its items; a section the layout empties is dropped from
197
+ `groups`; an empty layout returns the inputs unchanged. A layout can hide a
198
+ panel but cannot bring back one the host removed (`false` / `initial:
199
+ 'hidden'`). Published from the browser-safe `./workspace/layout` subpath (no
200
+ Svelte) as well as `./workspace`.
201
+ - **Persistence is host-owned.** `AppShell` takes `layout` and `onlayoutchange`:
202
+ with `layout` the host is the single source of truth (pass `null` for none);
203
+ with only `onlayoutchange` the shell keeps the value in memory and reports
204
+ edits; with neither, the layout lives in the settings-core user tier
205
+ (`ShellSettingsDelta.layout`, via `storageKey`). `AppShell` now creates the
206
+ `ShellState` itself and passes it to `AdminShell`, so panel overrides reach
207
+ it (`ShellState.setLayoutPanels`); a layout-hidden edge ignores toggles,
208
+ hotkeys, viewport defaults, and the restore opener.
209
+ - **Layout API** (`useShellLayout()` / `tryUseShellLayout()` under an
210
+ `AppShell`, also `ShellLayoutController`): `moveSection(id, toIndex)`,
211
+ `moveItem(id, sectionId, toIndex?)`, `hide(id)`, `show(id)`, `setPanel(edge,
212
+ { visible?, initial? })`, `reset()`, each returning whether anything changed,
213
+ plus readers (`layout`, `sections`, `panels`, `applied`, `customized`,
214
+ `isHidden`). An assistant calls the same methods the editor does. The
215
+ context is separate from `useAdminShell()` because `AppShell`, not
216
+ `AdminShell`, owns the navigation.
217
+ - **`ShellLayoutEditor`**: `controller` (default: context), `preview` (default
218
+ true), `iconComponent`. Mount it on a settings page or in a dock tool.
219
+
220
+ ### Sortable and the Board engine
221
+
222
+ `components/sortable/controller.svelte.ts` is the headless engine extracted from
223
+ `Board`: keyboard pick-up (Space/Enter, arrows, Escape), pointer and native-drag
224
+ moves with a 6px threshold, one in-flight move at a time (a rejected `commit`
225
+ restores and announces), focus return, and announcements as typed events the
226
+ host localizes. `Board` (own markup and `ui.board.*` strings) and `Sortable`
227
+ (`ui.sortable.*`) both use it. `Sortable` renders stacked containers of items
228
+ with a move handle per item, optional `reorderContainers` (a second engine; `fixed`
229
+ containers lead and stay put), a drop marker, and cancel-on-blur; it is
230
+ controlled and never mutates `items`. Item moves report `target.index` after
231
+ removing the item from `source`.
232
+
175
233
  ## App shell and owner setup (`./app`)
176
234
 
177
235
  `@happyvertical/smrt-svelte/app` (`src/components/app/`) is browser-only. It
@@ -182,13 +240,30 @@ lives beside, not inside, `./workspace` so the AdminShell barrel stays free of
182
240
  unchanged), `ThemeProvider`, the theme CSS (`themes/styles/{all,fonts}.css`
183
241
  are imported by the component — forgetting them leaves `--smrt-*` unresolved),
184
242
  `AdminShell`, `TenantNav` from the consumer's `nav`/`navGroups`/`currentHref`,
185
- and an `AppScopePanel`. Panels start collapsed as in `AdminShell`; pass
243
+ and an `AppScopePanel`. `ShellNavItem.action` (trailing icon link, optional `visibility: 'active'` = only while the item's section is current; omitted when collapsed) is rendered by `TenantNav` and passed through `AppShell` unchanged. Panels start collapsed as in `AdminShell`; pass
186
244
  `config` to change that. `dock` is a snippet rendered inside the shell: a host
187
245
  places its assistant there in a `ShellDockTool`, so this package never
188
246
  imports `smrt-chat`. It receives the Provider's `DataSurfaceRegistry`
189
247
  (`{#snippet dock(registry)}`), the instance mounted routes register on
190
248
  when `webmcp` UI is on; pass it to `<AssistantDock {registry} />`. `runtimeDiagnostics` (default false) mounts the
191
- read-only `smrt.runtime.diagnostics.read` WebMCP tool. Server code that
249
+ read-only `smrt.runtime.diagnostics.read` WebMCP tool. `dockToggles`
250
+ (`{ tool, label, icon? }[]`) renders icon buttons at the right of the shell
251
+ header (the AdminShell `header` row, shown only when the prop is non-empty),
252
+ each toggling the `ShellDockTool` with that id: `aria-pressed` and
253
+ `aria-expanded` follow the dock, `aria-controls` is
254
+ `smrt-admin-shell-right-panel`, the label is the tooltip, and `assistant`
255
+ defaults to a chat-bubble icon (`icon: 'chat'`; other text is a glyph). A
256
+ toggle whose tool is not registered yet, or whose dock is not `available`
257
+ (the right edge removed by the host or hidden by the user's layout), is
258
+ `aria-disabled` and `open`/`toggle` return false. Hosts, routes and
259
+ assistants drive the same dock with `useShellDock()` from `./workspace`
260
+ (`{ active, tools, available, has, isOpen, open, close, toggle }`, throws outside a
261
+ shell). Opening moves focus into the dock (first focusable, else the panel);
262
+ closing by Escape, toggle or code returns it to the opener when focus was left
263
+ in the dock. `open(tool, { focus: false })` skips the focus move. The
264
+ dock-control state lives on `ShellState` (`openDockTool`, `toggleDockTool`,
265
+ `closeFocusTool`, `openFocusToolId`). Playground:
266
+ `playground/src/routes/app-shell-dock-toggles`. Server code that
192
267
  lists that tool (the diagnostics route's `toolNames`) imports
193
268
  `RUNTIME_DIAGNOSTICS_WEBMCP_TOOL_NAME` from `./app/runtime-diagnostics`,
194
269
  which is plain TypeScript; the `./app` barrel loads Svelte components and
@@ -9,20 +9,33 @@ import '@happyvertical/smrt-ui/themes/styles/base.css';
9
9
  import '@happyvertical/smrt-ui/themes/styles/fonts.css';
10
10
  import type { DataSurfaceRegistry } from '@happyvertical/smrt-ui/data';
11
11
  import { useI18n } from '@happyvertical/smrt-ui/i18n';
12
- import type { Snippet } from 'svelte';
12
+ import { type Snippet, untrack } from 'svelte';
13
13
  import { M } from '../../i18n/strings.workspace.js';
14
14
  import Provider from '../../Provider.svelte';
15
15
  import type { User } from '../../state/app-state.js';
16
16
  import type { WebMcpProviderConfig } from '../../web/webmcp-provider.js';
17
17
  import AdminShell from '../workspace/admin-shell/AdminShell.svelte';
18
18
  import AppScopePanel from '../workspace/admin-shell/AppScopePanel.svelte';
19
+ import {
20
+ applyShellLayout,
21
+ normalizeShellLayout,
22
+ type ShellLayout,
23
+ } from '../workspace/admin-shell/layout.js';
24
+ import { setShellLayout } from '../workspace/admin-shell/layout-context.js';
25
+ import { ShellLayoutController } from '../workspace/admin-shell/layout-controller.svelte.js';
26
+ import { resolveShellConfig } from '../workspace/admin-shell/settings.js';
27
+ import { createShellState } from '../workspace/admin-shell/state.svelte.js';
19
28
  import TenantNav from '../workspace/admin-shell/TenantNav.svelte';
20
- import type {
21
- ShellNavGroup,
22
- ShellNavItem,
23
- ShellPanelDefaults,
29
+ import {
30
+ PANEL_EDGES,
31
+ type PanelEdge,
32
+ type ShellNavGroup,
33
+ type ShellNavItem,
34
+ type ShellPanelDefaults,
24
35
  } from '../workspace/admin-shell/types.js';
25
36
  import DockSlot from './DockSlot.svelte';
37
+ import DockToggles from './DockToggles.svelte';
38
+ import type { DockToggle } from './dock-toggle.js';
26
39
  import RuntimeDiagnosticsWebMcp from './RuntimeDiagnosticsWebMcp.svelte';
27
40
 
28
41
  interface Props {
@@ -54,6 +67,17 @@ interface Props {
54
67
  runtimeDiagnostics?: boolean;
55
68
  /** Initial panel states and presentation; forwarded to AdminShell. */
56
69
  config?: ShellPanelDefaults;
70
+ /**
71
+ * The user's layout customization (panel visibility, navigation order and
72
+ * visibility), applied over `nav`, `navGroups` and `config`. Passing it
73
+ * makes the host the owner: the shell never stores it, and reports edits
74
+ * through `onlayoutchange` for the host to pass back. Pass `null` for "no
75
+ * customization yet". Omit both `layout` and `onlayoutchange` to let the
76
+ * shell keep the layout in the user's settings (`storageKey`).
77
+ */
78
+ layout?: ShellLayout | null;
79
+ /** Called with the next layout whenever the user (or an assistant) edits it. */
80
+ onlayoutchange?: (layout: ShellLayout) => void;
57
81
  /** Theme preset applied by the ThemeProvider. */
58
82
  preset?: ThemePreset;
59
83
  /** Light, dark or system color scheme; the user's persisted choice wins. */
@@ -69,6 +93,15 @@ interface Props {
69
93
  * register on it when the `webmcp` UI is enabled (`webmcp: true`).
70
94
  */
71
95
  dock?: Snippet<[DataSurfaceRegistry]>;
96
+ /**
97
+ * Icon buttons rendered at the right of the shell header, each toggling
98
+ * the `ShellDockTool` with the same id open and closed. `aria-pressed` and
99
+ * `aria-expanded` follow the dock, focus moves into the dock when it
100
+ * opens and back to the button when it closes (Escape included). The
101
+ * `assistant` tool defaults to a chat-bubble icon. Hosts and assistants can
102
+ * drive the same dock from code with `useShellDock()`.
103
+ */
104
+ dockToggles?: DockToggle[];
72
105
  children: Snippet;
73
106
  }
74
107
 
@@ -87,20 +120,99 @@ let {
87
120
  permissions = [],
88
121
  runtimeDiagnostics = false,
89
122
  config,
123
+ layout,
124
+ onlayoutchange,
90
125
  preset = 'smrt',
91
126
  colorScheme = 'system',
92
127
  appPanelDocs,
93
128
  dock,
129
+ dockToggles = [],
94
130
  children,
95
131
  }: Props = $props();
96
132
  const { t } = useI18n();
97
- const hasNav = $derived(nav.length > 0 || navGroups.length > 0);
133
+
134
+ // The shell state lives here (not inside AdminShell) so the layout can reach
135
+ // it: panel overrides apply to it, and the user's layout is stored in it when
136
+ // the host does not own persistence.
137
+ const shell = untrack(() =>
138
+ createShellState({
139
+ config,
140
+ storageKey,
141
+ layoutPanels: normalizeShellLayout(layout).panels,
142
+ }),
143
+ );
144
+ // Hosts that pass only `onlayoutchange` own persistence but not the value:
145
+ // keep it in memory so edits show at once.
146
+ let localLayout = $state<ShellLayout | undefined>();
147
+ const controlled = $derived(layout !== undefined);
148
+ const effectiveLayout = $derived(
149
+ normalizeShellLayout(
150
+ controlled ? layout : onlayoutchange ? localLayout : shell.settings.layout,
151
+ ),
152
+ );
153
+ const applied = $derived(
154
+ applyShellLayout(nav, navGroups, config, effectiveLayout),
155
+ );
156
+ const hasNav = $derived(applied.nav.length > 0 || applied.groups.length > 0);
157
+
158
+ // Starting-state edits made through the layout API, applied once the layout
159
+ // carrying them is in force (or dropped if the host answered differently).
160
+ const pendingStart = new Map<PanelEdge, 'collapsed' | 'expanded'>();
161
+ $effect(() => {
162
+ const panels = effectiveLayout.panels ?? {};
163
+ shell.setLayoutPanels(panels);
164
+ for (const [edge, state] of pendingStart) {
165
+ const initial =
166
+ panels[edge]?.initial ?? resolveShellConfig(config).panels[edge].initial;
167
+ if (initial === state) shell.setPanelStart(edge, state);
168
+ }
169
+ pendingStart.clear();
170
+ });
171
+
172
+ setShellLayout(
173
+ new ShellLayoutController({
174
+ nav: () => nav,
175
+ groups: () => navGroups,
176
+ panels: () => config,
177
+ layout: () => effectiveLayout,
178
+ commit(next) {
179
+ // A changed starting state is an explicit edit: show it now. It waits
180
+ // for the layout to actually change (the host may reject the edit) and
181
+ // is never done for a loaded layout (hydration, a late `layout` prop),
182
+ // so loading cannot erase the user's own open/closed toggle.
183
+ for (const edge of PANEL_EDGES) {
184
+ const before = effectiveLayout.panels?.[edge]?.initial;
185
+ const after = next.panels?.[edge]?.initial;
186
+ if (before === after) continue;
187
+ const state = after ?? resolveShellConfig(config).panels[edge].initial;
188
+ if (state === 'collapsed' || state === 'expanded') {
189
+ pendingStart.set(edge, state);
190
+ }
191
+ }
192
+ if (!controlled) {
193
+ if (onlayoutchange) localLayout = next;
194
+ else shell.setLayout(next);
195
+ }
196
+ onlayoutchange?.(next);
197
+ },
198
+ }),
199
+ );
98
200
  </script>
99
201
 
202
+ {#snippet dockHeader()}
203
+ <DockToggles toggles={dockToggles} />
204
+ {/snippet}
205
+
100
206
  <Provider {webmcp} {user} {permissions}>
101
207
  <RuntimeDiagnosticsWebMcp enabled={runtimeDiagnostics} />
102
208
  <ThemeProvider {preset} {colorScheme} persist={true}>
103
- <AdminShell {title} {subtitle} {storageKey} {config} path={currentHref}>
209
+ <AdminShell
210
+ {title}
211
+ {subtitle}
212
+ state={shell}
213
+ path={currentHref}
214
+ header={dockToggles.length > 0 ? dockHeader : undefined}
215
+ >
104
216
  {#snippet appPanel()}
105
217
  <AppScopePanel
106
218
  appName={title}
@@ -120,8 +232,8 @@ const hasNav = $derived(nav.length > 0 || navGroups.length > 0);
120
232
  {#snippet tenantPanel()}
121
233
  {#if hasNav}
122
234
  <TenantNav
123
- items={nav}
124
- groups={navGroups}
235
+ items={applied.nav}
236
+ groups={applied.groups}
125
237
  {currentHref}
126
238
  aria-label={t(M['ui.app_shell.navigation'])}
127
239
  />
@@ -3,10 +3,12 @@ import '@happyvertical/smrt-ui/themes/styles/all.css';
3
3
  import '@happyvertical/smrt-ui/themes/styles/base.css';
4
4
  import '@happyvertical/smrt-ui/themes/styles/fonts.css';
5
5
  import type { DataSurfaceRegistry } from '@happyvertical/smrt-ui/data';
6
- import type { Snippet } from 'svelte';
6
+ import { type Snippet } from 'svelte';
7
7
  import type { User } from '../../state/app-state.js';
8
8
  import type { WebMcpProviderConfig } from '../../web/webmcp-provider.js';
9
- import type { ShellNavGroup, ShellNavItem, ShellPanelDefaults } from '../workspace/admin-shell/types.js';
9
+ import { type ShellLayout } from '../workspace/admin-shell/layout.js';
10
+ import { type ShellNavGroup, type ShellNavItem, type ShellPanelDefaults } from '../workspace/admin-shell/types.js';
11
+ import type { DockToggle } from './dock-toggle.js';
10
12
  interface Props {
11
13
  /** Application name shown in the shell brand and app panel. */
12
14
  title?: string;
@@ -36,6 +38,17 @@ interface Props {
36
38
  runtimeDiagnostics?: boolean;
37
39
  /** Initial panel states and presentation; forwarded to AdminShell. */
38
40
  config?: ShellPanelDefaults;
41
+ /**
42
+ * The user's layout customization (panel visibility, navigation order and
43
+ * visibility), applied over `nav`, `navGroups` and `config`. Passing it
44
+ * makes the host the owner: the shell never stores it, and reports edits
45
+ * through `onlayoutchange` for the host to pass back. Pass `null` for "no
46
+ * customization yet". Omit both `layout` and `onlayoutchange` to let the
47
+ * shell keep the layout in the user's settings (`storageKey`).
48
+ */
49
+ layout?: ShellLayout | null;
50
+ /** Called with the next layout whenever the user (or an assistant) edits it. */
51
+ onlayoutchange?: (layout: ShellLayout) => void;
39
52
  /** Theme preset applied by the ThemeProvider. */
40
53
  preset?: ThemePreset;
41
54
  /** Light, dark or system color scheme; the user's persisted choice wins. */
@@ -51,6 +64,15 @@ interface Props {
51
64
  * register on it when the `webmcp` UI is enabled (`webmcp: true`).
52
65
  */
53
66
  dock?: Snippet<[DataSurfaceRegistry]>;
67
+ /**
68
+ * Icon buttons rendered at the right of the shell header, each toggling
69
+ * the `ShellDockTool` with the same id open and closed. `aria-pressed` and
70
+ * `aria-expanded` follow the dock, focus moves into the dock when it
71
+ * opens and back to the button when it closes (Escape included). The
72
+ * `assistant` tool defaults to a chat-bubble icon. Hosts and assistants can
73
+ * drive the same dock from code with `useShellDock()`.
74
+ */
75
+ dockToggles?: DockToggle[];
54
76
  children: Snippet;
55
77
  }
56
78
  declare const AppShell: import("svelte").Component<Props, {}, "">;
@@ -1 +1 @@
1
- {"version":3,"file":"AppShell.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/app/AppShell.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,WAAW,EAEjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,8CAA8C,CAAC;AACtD,OAAO,+CAA+C,CAAC;AACvD,OAAO,gDAAgD,CAAC;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAEvE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAIzE,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,kBAAkB,EACnB,MAAM,mCAAmC,CAAC;AAK3C,UAAU,KAAK;IACb,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,GAAG,CAAC,EAAE,YAAY,EAAE,CAAC;IACrB,uEAAuE;IACvE,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;IAC5B,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,GAAG,oBAAoB,CAAC;IACxC,yFAAyF;IACzF,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,+FAA+F;IAC/F,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,sEAAsE;IACtE,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,iDAAiD;IACjD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,4DAA4D;IAC5D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC;IACtC,QAAQ,EAAE,OAAO,CAAC;CACnB;AA+ED,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"AppShell.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/app/AppShell.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,WAAW,EAEjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,8CAA8C,CAAC;AACtD,OAAO,+CAA+C,CAAC;AACvD,OAAO,gDAAgD,CAAC;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAEvE,OAAO,EAAE,KAAK,OAAO,EAAW,MAAM,QAAQ,CAAC;AAG/C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AAGzE,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,oCAAoC,CAAC;AAM5C,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,kBAAkB,EACxB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,UAAU,KAAK;IACb,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,GAAG,CAAC,EAAE,YAAY,EAAE,CAAC;IACrB,uEAAuE;IACvE,SAAS,CAAC,EAAE,aAAa,EAAE,CAAC;IAC5B,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,GAAG,oBAAoB,CAAC;IACxC,yFAAyF;IACzF,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,+FAA+F;IAC/F,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,8DAA8D;IAC9D,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,sEAAsE;IACtE,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,gFAAgF;IAChF,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;IAC/C,iDAAiD;IACjD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,4EAA4E;IAC5E,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,4DAA4D;IAC5D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;OAOG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC;IACtC;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,QAAQ,EAAE,OAAO,CAAC;CACnB;AA+JD,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,103 @@
1
+ <script lang="ts">
2
+ import { useShellDock } from '../workspace/admin-shell/dock.js';
3
+ import type { DockToggle } from './dock-toggle.js';
4
+
5
+ interface Props {
6
+ toggles: DockToggle[];
7
+ }
8
+
9
+ let { toggles }: Props = $props();
10
+ const dock = useShellDock();
11
+
12
+ function iconFor(toggle: DockToggle): string | undefined {
13
+ return toggle.icon ?? (toggle.tool === 'assistant' ? 'chat' : undefined);
14
+ }
15
+
16
+ function press(event: MouseEvent, toggle: DockToggle): void {
17
+ if (!dock.has(toggle.tool) || !dock.available) return;
18
+ dock.toggle(toggle.tool, {
19
+ returnFocus: event.currentTarget as HTMLElement,
20
+ });
21
+ }
22
+ </script>
23
+
24
+ <div class="smrt-dock-toggles" data-testid="dock-toggles">
25
+ {#each toggles as toggle (toggle.tool)}
26
+ {@const registered = dock.has(toggle.tool) && dock.available}
27
+ {@const open = dock.isOpen(toggle.tool)}
28
+ {@const icon = iconFor(toggle)}
29
+ <!-- raw-primitive-allow: shell chrome toggle, not a content button -->
30
+ <button
31
+ type="button"
32
+ class="smrt-dock-toggles__button"
33
+ class:active={open}
34
+ data-dock-tool={toggle.tool}
35
+ aria-label={toggle.label}
36
+ title={toggle.label}
37
+ aria-pressed={open}
38
+ aria-expanded={open}
39
+ aria-controls="smrt-admin-shell-right-panel"
40
+ aria-disabled={registered ? undefined : true}
41
+ onclick={(event) => press(event, toggle)}
42
+ >
43
+ {#if icon === 'chat'}
44
+ <svg
45
+ viewBox="0 0 24 24"
46
+ width="20"
47
+ height="20"
48
+ fill="none"
49
+ stroke="currentColor"
50
+ stroke-width="2"
51
+ stroke-linecap="round"
52
+ stroke-linejoin="round"
53
+ aria-hidden="true"
54
+ >
55
+ <path d="M21 11.5a8.5 8.5 0 0 1-12.4 7.5L3 20.5l1.6-5A8.5 8.5 0 1 1 21 11.5z" />
56
+ </svg>
57
+ {:else}
58
+ <span aria-hidden="true">{icon ?? toggle.label.charAt(0)}</span>
59
+ {/if}
60
+ </button>
61
+ {/each}
62
+ </div>
63
+
64
+ <style>
65
+ .smrt-dock-toggles {
66
+ display: flex;
67
+ align-items: center;
68
+ gap: var(--smrt-spacing-1);
69
+ margin-inline-start: auto;
70
+ }
71
+
72
+ .smrt-dock-toggles__button {
73
+ display: inline-grid;
74
+ place-items: center;
75
+ inline-size: 2.25rem;
76
+ block-size: 2.25rem;
77
+ padding: 0;
78
+ border: 1px solid transparent;
79
+ border-radius: var(--smrt-radius-md, 0.5rem);
80
+ background: transparent;
81
+ color: var(--smrt-color-on-surface);
82
+ cursor: pointer;
83
+ }
84
+
85
+ .smrt-dock-toggles__button:hover {
86
+ background: var(--smrt-color-surface-container-high);
87
+ }
88
+
89
+ .smrt-dock-toggles__button:focus-visible {
90
+ outline: 2px solid var(--smrt-color-primary);
91
+ outline-offset: 2px;
92
+ }
93
+
94
+ .smrt-dock-toggles__button.active {
95
+ background: var(--smrt-color-primary-container, var(--smrt-color-surface-container-high));
96
+ color: var(--smrt-color-on-primary-container, var(--smrt-color-primary));
97
+ }
98
+
99
+ .smrt-dock-toggles__button[aria-disabled='true'] {
100
+ opacity: 0.5;
101
+ cursor: default;
102
+ }
103
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { DockToggle } from './dock-toggle.js';
2
+ interface Props {
3
+ toggles: DockToggle[];
4
+ }
5
+ declare const DockToggles: import("svelte").Component<Props, {}, "">;
6
+ type DockToggles = ReturnType<typeof DockToggles>;
7
+ export default DockToggles;
8
+ //# sourceMappingURL=DockToggles.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DockToggles.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/app/DockToggles.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,UAAU,KAAK;IACb,OAAO,EAAE,UAAU,EAAE,CAAC;CACvB;AA2CD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -0,0 +1,14 @@
1
+ /** A header button that toggles one dock tool. */
2
+ export interface DockToggle {
3
+ /** Id of the `ShellDockTool` to toggle. */
4
+ tool: string;
5
+ /** Accessible name and tooltip. */
6
+ label: string;
7
+ /**
8
+ * Icon name. `chat` (the default for the `assistant` tool) draws a
9
+ * chat bubble; any other text is shown as a glyph. Without an icon the
10
+ * label's first letter stands in.
11
+ */
12
+ icon?: string;
13
+ }
14
+ //# sourceMappingURL=dock-toggle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dock-toggle.d.ts","sourceRoot":"","sources":["../../../src/components/app/dock-toggle.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,MAAM,WAAW,UAAU;IACzB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf"}
@@ -0,0 +1 @@
1
+ export {};
@@ -5,7 +5,9 @@
5
5
  * server or `$app/*` imports. Pair with the server `load`/`actions` from
6
6
  * `@happyvertical/smrt-app-runtime/sveltekit`.
7
7
  */
8
+ export { type ShellLayout, ShellLayoutController, ShellLayoutEditor, tryUseShellLayout, useShellLayout, } from '../workspace/index.js';
8
9
  export { default as AppShell } from './AppShell.svelte';
10
+ export type { DockToggle } from './dock-toggle.js';
9
11
  export { default as OwnerSetupForm } from './OwnerSetupForm.svelte';
10
12
  export type { OwnerSetupData, OwnerSetupFormResult, } from './owner-setup-types.js';
11
13
  export { default as RuntimeDiagnosticsWebMcp } from './RuntimeDiagnosticsWebMcp.svelte';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/app/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,YAAY,EACV,cAAc,EACd,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EACL,kCAAkC,EAClC,KAAK,uCAAuC,EAC5C,4BAA4B,EAC5B,oCAAoC,EACpC,KAAK,6BAA6B,EAClC,gCAAgC,GACjC,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/app/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACL,KAAK,WAAW,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,YAAY,EACV,cAAc,EACd,oBAAoB,GACrB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EACL,kCAAkC,EAClC,KAAK,uCAAuC,EAC5C,4BAA4B,EAC5B,oCAAoC,EACpC,KAAK,6BAA6B,EAClC,gCAAgC,GACjC,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,4BAA4B,CAAC"}
@@ -5,6 +5,7 @@
5
5
  * server or `$app/*` imports. Pair with the server `load`/`actions` from
6
6
  * `@happyvertical/smrt-app-runtime/sveltekit`.
7
7
  */
8
+ export { ShellLayoutController, ShellLayoutEditor, tryUseShellLayout, useShellLayout, } from '../workspace/index.js';
8
9
  export { default as AppShell } from './AppShell.svelte';
9
10
  export { default as OwnerSetupForm } from './OwnerSetupForm.svelte';
10
11
  export { default as RuntimeDiagnosticsWebMcp } from './RuntimeDiagnosticsWebMcp.svelte';