@happyvertical/smrt-svelte 0.55.18 → 0.55.20

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 (70) hide show
  1. package/agents/cookbook-shell.md +26 -2
  2. package/agents/overview-surfaces.md +13 -3
  3. package/agents/workspace.md +46 -0
  4. package/dist/components/app/AppShell.svelte +29 -7
  5. package/dist/components/app/AppShell.svelte.d.ts +13 -5
  6. package/dist/components/app/AppShell.svelte.d.ts.map +1 -1
  7. package/dist/components/app/DockToggles.svelte +89 -47
  8. package/dist/components/app/DockToggles.svelte.d.ts +1 -1
  9. package/dist/components/app/DockToggles.svelte.d.ts.map +1 -1
  10. package/dist/components/app/PhoneTabBar.svelte +37 -12
  11. package/dist/components/app/PhoneTabBar.svelte.d.ts +1 -1
  12. package/dist/components/app/PhoneTabBar.svelte.d.ts.map +1 -1
  13. package/dist/components/app/ShellSettingsPage.svelte +2 -2
  14. package/dist/components/app/ShellSettingsPage.svelte.d.ts +2 -2
  15. package/dist/components/app/ShellSettingsPage.svelte.d.ts.map +1 -1
  16. package/dist/components/app/dock-toggle.d.ts +45 -0
  17. package/dist/components/app/dock-toggle.d.ts.map +1 -1
  18. package/dist/components/app/dock-toggle.js +26 -1
  19. package/dist/components/app/index.d.ts +1 -0
  20. package/dist/components/app/index.d.ts.map +1 -1
  21. package/dist/components/app/shell-user.d.ts +14 -0
  22. package/dist/components/app/shell-user.d.ts.map +1 -0
  23. package/dist/components/app/shell-user.js +1 -0
  24. package/dist/components/cookbook/CookbookSectionOverview.svelte +22 -3
  25. package/dist/components/cookbook/CookbookSectionOverview.svelte.d.ts +11 -2
  26. package/dist/components/cookbook/CookbookSectionOverview.svelte.d.ts.map +1 -1
  27. package/dist/components/cookbook/CookbookSectionPage.svelte +27 -9
  28. package/dist/components/cookbook/CookbookSectionPage.svelte.d.ts.map +1 -1
  29. package/dist/components/cookbook/index.d.ts +3 -1
  30. package/dist/components/cookbook/index.d.ts.map +1 -1
  31. package/dist/components/cookbook/index.js +2 -1
  32. package/dist/components/cookbook/registry.d.ts +18 -4
  33. package/dist/components/cookbook/registry.d.ts.map +1 -1
  34. package/dist/components/cookbook/registry.js +40 -2
  35. package/dist/components/cookbook/types.d.ts +39 -4
  36. package/dist/components/cookbook/types.d.ts.map +1 -1
  37. package/dist/components/overview/OverviewGrid.svelte +1 -0
  38. package/dist/components/overview/index.d.ts +1 -1
  39. package/dist/components/overview/index.d.ts.map +1 -1
  40. package/dist/components/overview/types.d.ts +6 -0
  41. package/dist/components/overview/types.d.ts.map +1 -1
  42. package/dist/components/overview/widgets/ChartWidget.svelte +158 -18
  43. package/dist/components/overview/widgets/ChartWidget.svelte.d.ts.map +1 -1
  44. package/dist/components/overview/widgets/ShortcutsWidget.svelte +32 -3
  45. package/dist/components/overview/widgets/ShortcutsWidget.svelte.d.ts.map +1 -1
  46. package/dist/components/overview/widgets/core.d.ts +9 -1
  47. package/dist/components/overview/widgets/core.d.ts.map +1 -1
  48. package/dist/components/overview/widgets/core.js +20 -1
  49. package/dist/components/overview/widgets/data.d.ts +16 -6
  50. package/dist/components/overview/widgets/data.d.ts.map +1 -1
  51. package/dist/components/workspace/admin-shell/AdminShell.svelte +13 -0
  52. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts.map +1 -1
  53. package/dist/components/workspace/admin-shell/ShellSettingsPanel.svelte +52 -7
  54. package/dist/components/workspace/admin-shell/ShellSettingsPanel.svelte.d.ts +6 -2
  55. package/dist/components/workspace/admin-shell/ShellSettingsPanel.svelte.d.ts.map +1 -1
  56. package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte +54 -5
  57. package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.d.ts +7 -2
  58. package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.d.ts.map +1 -1
  59. package/dist/components/workspace/admin-shell/mobile-shell.d.ts +1 -1
  60. package/dist/components/workspace/admin-shell/mobile-shell.d.ts.map +1 -1
  61. package/dist/components/workspace/admin-shell/state.svelte.d.ts +9 -0
  62. package/dist/components/workspace/admin-shell/state.svelte.d.ts.map +1 -1
  63. package/dist/components/workspace/admin-shell/state.svelte.js +14 -0
  64. package/dist/i18n/strings.overview.d.ts +5 -0
  65. package/dist/i18n/strings.overview.d.ts.map +1 -1
  66. package/dist/i18n/strings.overview.js +5 -0
  67. package/dist/i18n/strings.workspace.d.ts +2 -0
  68. package/dist/i18n/strings.workspace.d.ts.map +1 -1
  69. package/dist/i18n/strings.workspace.js +3 -0
  70. package/package.json +16 -16
@@ -18,7 +18,7 @@ sample data source are not part of it.
18
18
  | Entry | Contents |
19
19
  |---|---|
20
20
  | `./cookbook/server` | Svelte-free and Node-safe. `loadCookbookApp` (file + manifests + validation), `readInstalledManifests`, `hasCookbook`, and the pure builder/catalog/theme functions and types. Use it in `+layout.server.ts`. |
21
- | `./cookbook` | Everything above that is browser-safe, plus `CookbookApp`, `CookbookSectionPage`, `applyCookbookTheme`, `createCookbookRegistry`, `createSectionOverview`. |
21
+ | `./cookbook` | Everything above that is browser-safe, plus `CookbookApp`, `CookbookSectionPage`, `CookbookSectionOverview`, `applyCookbookTheme`, `createCookbookRegistry`, `createSectionOverview`. |
22
22
 
23
23
  ## Pure core (no DOM, Node-testable)
24
24
 
@@ -84,7 +84,8 @@ sample data source are not part of it.
84
84
  `slotItems`, `settingsHref`, ...), and:
85
85
 
86
86
  - `loaders` (`metric` / `records` / `chart` data loaders; with `metric` and
87
- `records` the section pages add the count and latest records),
87
+ `records` the section pages add the count and latest records; `shortcuts`
88
+ is a `CookbookShortcutsLoader`, below),
88
89
  - `widgetContext` (host capabilities for browser-side widget loads),
89
90
  - `resolveExport` (a static map of dynamic imports; turns a recipe export
90
91
  reference into a component, used for `shell-widget` slot items and recipe
@@ -107,6 +108,19 @@ registry, recipe widgets and slot items are resolved in effects (a bundled
107
108
  `resolveExport` is async), so a section page's overview appears after hydration,
108
109
  not in the server HTML.
109
110
 
111
+ ## Host shortcuts loader (#3888)
112
+
113
+ `CookbookApp loaders.shortcuts` is `(ctx, defaults: ShortcutEntry[]) =>
114
+ Promise<ShortcutEntry[]>` (`ShortcutEntry = { href, label, icon?, description?,
115
+ count? }`, exported from `./cookbook` and `./overview`).
116
+ `createCookbookRegistry` computes the default entries from the shell's resolved
117
+ sections, then, when the host supplies `shortcuts`, passes them through it
118
+ (`composeShortcutsLoader`); with no host loader the registry's loader is
119
+ unchanged. The host returns the entries to show (add `count`, reorder, add or
120
+ drop). If it throws, that load keeps the defaults (the page never empties
121
+ because a count query failed) unless the load itself was aborted. The shortcuts
122
+ widget renders `count` as "N records" (plural-aware i18n, tabular figures).
123
+
110
124
  ## `CookbookSectionPage`
111
125
 
112
126
  A section's own page: title, icon and description (the user's renames win over
@@ -117,6 +131,16 @@ route: `s/[slug]/+page.svelte` renders
117
131
  `<CookbookSectionPage slug={page.params.slug} />`. It must be inside
118
132
  `CookbookApp`.
119
133
 
134
+ Props (also on `CookbookSectionOverview`, #3888): `presentation?: 'plain' | 'card'`
135
+ (default `'plain'`) is passed to `OverviewGrid`, so the default section body
136
+ lists shortcut entries as unframed rows on the panel surface (the shortcuts
137
+ widget receives the grid's `presentation` and drops its per-entry frame); `'card'`
138
+ keeps the framed look, and editing always keeps its frame.
139
+ `heading?: Snippet<[{ section: CookbookSectionHeadingInfo }]>` replaces the
140
+ default title block (`{ id, title, description?, icon }`) on the page; on a
141
+ standalone `CookbookSectionOverview` it renders above the grid and needs the
142
+ `section` prop.
143
+
120
144
  ## Using it in an app
121
145
 
122
146
  ```ts
@@ -198,16 +198,26 @@ shape. Data shapes are in `widgets/data.ts`.
198
198
  | Type | Options | Data |
199
199
  |---|---|---|
200
200
  | `metric` | `title`, `model`*, `measure`, `field`, `filter` | `{ value, format?, currency?, label?, change?, href? }` (money = integer minor units) |
201
- | `chart` | `title`, `model`*, `groupBy`, `measure`, `field`, `period`, `style` (bar/line), `filter` | `{ points: [{ label, value }], format?, currency?, href? }`, one measure of a runtime report |
201
+ | `chart` | `title`, `model`*, `groupBy`, `measure`, `field`, `period`, `style` (bar/line/table/pie/stat), `filter` | `{ points: [{ label, value }], format?, currency?, href?, delta? }`, one measure of a runtime report |
202
202
  | `records` | `title`, `model`*, `sort`, `direction`, `limit`, `filter` | `{ rows: [{ id, title, subtitle?, meta?, href? }], total?, href? }`; the loader chooses the title field and the visible records |
203
- | `shortcuts` | `title`, `section` | `{ items: [{ id, label, href, icon?, description? }] }`; `shortcutsFromNav(sections, sectionId)` maps `resolveShellNavModel` / `useShellLayout().sections` (visible entries, user renames) |
203
+ | `shortcuts` | `title`, `section` | `{ items: [{ id, label, href, icon?, description?, count? }] }` (`count` draws as "N records"; widgets also receive the grid's `presentation` and the shortcuts widget drops its entry frame under `plain`); `shortcutsFromNav(sections, sectionId)` maps `resolveShellNavModel` / `useShellLayout().sections` (visible entries, user renames) |
204
204
  | `note` | `title`, `body` (Markdown) | none |
205
205
 
206
206
  `note` renders `parseMarkdown` output as elements (headings, paragraphs, lists,
207
207
  quotes, fenced code, bold/italic/code/links). There is no `{@html}`; raw HTML is
208
208
  literal text and `safeHref` limits links to http(s), mailto, same-site paths and
209
209
  fragments. Record and shortcut hrefs pass `safeHref` too. Charts carry their
210
- numbers as text (inline for bars, a visually hidden table for the line).
210
+ numbers as text (inline for bars, a visually hidden table for the line and the
211
+ donut, a visible table for `table`, the value itself for `stat`).
212
+
213
+ Chart styles (#3888, the renderer half of #3878's runtime-report chart hints):
214
+ `table` is a plain table of the series (tabular figures); `pie` is an SVG donut
215
+ with a legend (value and share) and a visually hidden data table, slices cycling
216
+ through the primary/secondary/tertiary/info tokens then their containers;
217
+ `stat` is the last point as a headline value with its change (`delta`, percent,
218
+ else derived from the last two points). Motion honours `prefers-reduced-motion`.
219
+ The chart option schema is **version 2**: v1 (`style` bar or line, or no version)
220
+ migrates by identity (`migrateChartOptions`), so saved overviews keep validating.
211
221
 
212
222
  ## Recipe widgets (`recipe-widgets.ts`)
213
223
 
@@ -226,6 +226,52 @@ dialog and the global shortcut, so it mounts once. Planner-style hosts that
226
226
  rendered their own `header.end` assistant toggle should pass `dockToggles`
227
227
  (`dock:assistant`) and `user` instead.
228
228
 
229
+ Async sign-out (#3860): `onSignOut?: () => void | Promise<void>` (AppShell,
230
+ `WorkspaceAccountMenu`, `ShellSettingsPage`/`ShellSettingsPanel`) is awaited.
231
+ While pending the control is busy (`aria-busy`) and disabled, so a second
232
+ activation never fires the callback again; on success the account menu closes;
233
+ on rejection the menu (or Account section) stays as it was and shows an inline
234
+ `role="alert"` "Couldn't sign out. Try again." (`ui.shell_settings.sign_out_failed`).
235
+ A rejection is caught, never an unhandled rejection; the control is usable again
236
+ for a retry. `signOutHref` links are unchanged.
237
+
238
+ Unrendered regions in settings (#3860): `ShellState.regionsRendered:
239
+ Record<PanelEdge, boolean>` (read-only for consumers; `AdminShell` writes it
240
+ from the same `topRendered`/`bottomRendered`/`rightRendered`/`isEdgeShown('left')`
241
+ logic that collapses empty bands and edges, default all `true`) says whether a
242
+ region is actually on screen, distinct from the user's visibility choice. The
243
+ Layout rows in `ShellSettingsPanel` list a region when it is rendered, or when
244
+ the user hid it (so Show can always restore it); a shown region that is not
245
+ rendered (empty band, edge not presented at this width) has no row. The
246
+ "cannot hide the last region" guard still counts every shown region. In edit
247
+ mode every band renders, so every available region is listed.
248
+
249
+ `user` prop type (#3860): `AppShell`'s `user` is `ShellUser` (exported from
250
+ `./app`): `null` or `{ id: string | null | undefined; email?; name?; avatarUrl?
251
+ (string | null) }`, all readonly. The runtime's `SessionLayoutUser`
252
+ (`data.session.user`) and a full `User` both assign without a cast
253
+ (`__tests__/shell-user.test.ts` proves it with `expectTypeOf`). `id` stays
254
+ required but accepts `null`/`undefined` because an unsaved `User` carries
255
+ that. The shell reads `name`, `email` and `avatarUrl` for the account menu
256
+ (the avatar now shows) and forwards the same object to `Provider` untouched
257
+ (one internal cast; the Provider is typed for the full `User`), so nothing is
258
+ added or invented on the way.
259
+
260
+ DockToggle fallback (#3860): a `dockToggles` entry also takes `href?`, `ready?`
261
+ (reactive, default `true`), `labelNotReady?` and `onpress?(event, { registered,
262
+ ready }) => boolean | void`. Precedence on activation: (1) `onpress` runs first;
263
+ returning `false` or calling `event.preventDefault()` stops everything (no
264
+ toggle, no navigation). (2) If the tool is registered with `useShellDock()`
265
+ (and the dock is available) and `ready !== false`, the dock toggles as before.
266
+ (3) Otherwise `href` is followed, else nothing happens (the inert
267
+ `aria-disabled` button, unchanged). With an `href` in state 3 the toggle renders
268
+ as an `<a>` (middle-click and open-in-new-tab work) with no `aria-pressed` /
269
+ `aria-expanded`. Its name and tooltip are `labelNotReady`, else "Set up {label}"
270
+ (`ui.dock_toggles.set_up`), whenever it is not ready or links. The phone tab
271
+ bar's dock tab (the first toggle) follows the same resolution through the shared
272
+ `resolveDockToggle` / `runDockTogglePress` helpers in `dock-toggle.ts`; that tab
273
+ becomes a link entry. `PhoneBottomBarItem.onclick` now receives the click event.
274
+
229
275
  `ShellLayout.placements?: Record<itemId, ShellSlot>` (additive, still version
230
276
  1) overrides default slots. Pure helpers in `layout.ts` (also `./workspace/layout`):
231
277
  `placeShellItem(layout, id, slot, defaultSlot?)` (passing the default drops a
@@ -70,6 +70,7 @@ import {
70
70
  resolvePhoneTabBar,
71
71
  } from './phone-tabs.js';
72
72
  import RuntimeDiagnosticsWebMcp from './RuntimeDiagnosticsWebMcp.svelte';
73
+ import type { ShellUser } from './shell-user.js';
73
74
  import type {
74
75
  AppShellAccountMenu,
75
76
  AppShellCommandPalette,
@@ -112,8 +113,12 @@ interface Props {
112
113
  * default slot (users can still move it in the layout editor).
113
114
  */
114
115
  accountMenu?: AppShellAccountMenu;
115
- /** Called when the account menu's Sign out is chosen (wins over `signOutHref`). */
116
- onSignOut?: () => void;
116
+ /**
117
+ * Called when the account menu's Sign out is chosen (wins over
118
+ * `signOutHref`). May be async: the menu awaits it, shows a busy state, and
119
+ * on rejection stays open with an inline error.
120
+ */
121
+ onSignOut?: () => unknown;
117
122
  /**
118
123
  * Where the account menu's Sign out link goes when there is no `onSignOut`.
119
124
  * A link is a GET: never point it at the runtime's POST-only `/sign-out`
@@ -136,8 +141,12 @@ interface Props {
136
141
  commandPalette?: AppShellCommandPalette;
137
142
  /** Options for the Provider's WebMCP integration; passed through unchanged. */
138
143
  webmcp?: boolean | WebMcpProviderConfig;
139
- /** Authenticated user forwarded to the Provider; `null` renders the shell signed out. */
140
- user?: User | null;
144
+ /**
145
+ * The signed-in identity (`ShellUser`): the runtime's `SessionLayoutUser`
146
+ * (`data.session.user`) or a full `User` both fit without a cast. It is
147
+ * forwarded to the Provider unchanged; `null` renders the shell signed out.
148
+ */
149
+ user?: ShellUser;
141
150
  /** The user's resolved permission slugs, forwarded to the Provider for permission-gated UI. */
142
151
  permissions?: string[];
143
152
  /** Register the read-only runtime diagnostics WebMCP tool. */
@@ -571,11 +580,23 @@ function accountPlacement(
571
580
  return alignStart ? 'bottom-start' : 'bottom-end';
572
581
  }
573
582
  const accountIdentity = $derived.by(() => {
574
- const record = (user ?? {}) as { name?: unknown; email?: unknown };
583
+ const record = (user ?? {}) as {
584
+ name?: unknown;
585
+ email?: unknown;
586
+ avatarUrl?: unknown;
587
+ };
575
588
  const name = typeof record.name === 'string' ? record.name.trim() : '';
576
589
  const email = typeof record.email === 'string' ? record.email.trim() : '';
577
- return { name, email };
590
+ const avatarUrl =
591
+ typeof record.avatarUrl === 'string' && record.avatarUrl
592
+ ? record.avatarUrl
593
+ : undefined;
594
+ return { name, email, avatarUrl };
578
595
  });
596
+ // The Provider is typed for the full `User`. The shell hands it exactly the
597
+ // object the host passed (no fields added or invented), so a minimal session
598
+ // projection reaches it just as it did when the prop was typed as `User`.
599
+ const providerUser = $derived(user as unknown as User | null);
579
600
  // The floating toggle sits over the header's end (header visible), the top of
580
601
  // the right sidebar (header hidden) or the top of main (neither); reserve that room so it covers no
581
602
  // control. AdminShell reads these custom properties.
@@ -867,6 +888,7 @@ const EDGE_OF_REGION: Record<ShellRegion, PanelEdge> = {
867
888
  <WorkspaceAccountMenu
868
889
  userName={accountIdentity.name || accountIdentity.email}
869
890
  email={accountIdentity.email}
891
+ avatarUrl={accountIdentity.avatarUrl}
870
892
  {compact}
871
893
  {tenantLabel}
872
894
  {onSignOut}
@@ -949,7 +971,7 @@ const EDGE_OF_REGION: Record<ShellRegion, PanelEdge> = {
949
971
  />
950
972
  {/snippet}
951
973
 
952
- <Provider {webmcp} {user} {permissions}>
974
+ <Provider {webmcp} user={providerUser} {permissions}>
953
975
  <RuntimeDiagnosticsWebMcp enabled={runtimeDiagnostics} />
954
976
  <ThemeProvider {preset} {colorScheme} persist={true}>
955
977
  {#if !chrome}
@@ -4,13 +4,13 @@ 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
6
  import { type Component, type Snippet } from 'svelte';
7
- import type { User } from '../../state/app-state.js';
8
7
  import type { WebMcpProviderConfig } from '../../web/webmcp-provider.js';
9
8
  import { type ShellLayout } from '../workspace/admin-shell/layout.js';
10
9
  import { type ShellSlot } from '../workspace/admin-shell/slots.js';
11
10
  import { type AdminShellProps, type ShellNavGroup, type ShellNavItem, type ShellPanelDefaults, type ShellSectionActionsContext } from '../workspace/admin-shell/types.js';
12
11
  import type { DockToggle } from './dock-toggle.js';
13
12
  import { type PhoneTabBarOptions } from './phone-tabs.js';
13
+ import type { ShellUser } from './shell-user.js';
14
14
  import type { AppShellAccountMenu, AppShellCommandPalette, ShellSlotItem } from './slot-item.js';
15
15
  interface Props {
16
16
  /** Application name shown in the shell brand and app panel. */
@@ -47,8 +47,12 @@ interface Props {
47
47
  * default slot (users can still move it in the layout editor).
48
48
  */
49
49
  accountMenu?: AppShellAccountMenu;
50
- /** Called when the account menu's Sign out is chosen (wins over `signOutHref`). */
51
- onSignOut?: () => void;
50
+ /**
51
+ * Called when the account menu's Sign out is chosen (wins over
52
+ * `signOutHref`). May be async: the menu awaits it, shows a busy state, and
53
+ * on rejection stays open with an inline error.
54
+ */
55
+ onSignOut?: () => unknown;
52
56
  /**
53
57
  * Where the account menu's Sign out link goes when there is no `onSignOut`.
54
58
  * A link is a GET: never point it at the runtime's POST-only `/sign-out`
@@ -73,8 +77,12 @@ interface Props {
73
77
  commandPalette?: AppShellCommandPalette;
74
78
  /** Options for the Provider's WebMCP integration; passed through unchanged. */
75
79
  webmcp?: boolean | WebMcpProviderConfig;
76
- /** Authenticated user forwarded to the Provider; `null` renders the shell signed out. */
77
- user?: User | null;
80
+ /**
81
+ * The signed-in identity (`ShellUser`): the runtime's `SessionLayoutUser`
82
+ * (`data.session.user`) or a full `User` both fit without a cast. It is
83
+ * forwarded to the Provider unchanged; `null` renders the shell signed out.
84
+ */
85
+ user?: ShellUser;
78
86
  /** The user's resolved permission slugs, forwarded to the Provider for permission-gated UI. */
79
87
  permissions?: string[];
80
88
  /** Register the read-only runtime diagnostics WebMCP tool. */
@@ -1 +1 @@
1
- {"version":3,"file":"AppShell.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/app/AppShell.svelte.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AppShell.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/app/AppShell.svelte.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,18 @@
1
1
  <script lang="ts">
2
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
3
+ import { M } from '../../i18n/strings.workspace.js';
2
4
  import { useShellDock } from '../workspace/admin-shell/dock.js';
3
5
  import {
4
6
  isShellIconName,
5
7
  SHELL_DEFAULT_TOOL_ICON,
6
8
  SHELL_ICON_PATHS,
7
9
  } from '../workspace/admin-shell/shell-icons.js';
8
- import type { DockToggle } from './dock-toggle.js';
10
+ import {
11
+ type DockToggle,
12
+ type DockToggleResolution,
13
+ resolveDockToggle,
14
+ runDockTogglePress,
15
+ } from './dock-toggle.js';
9
16
 
10
17
  interface Props {
11
18
  toggles: DockToggle[];
@@ -13,67 +20,101 @@ interface Props {
13
20
 
14
21
  let { toggles }: Props = $props();
15
22
  const dock = useShellDock();
23
+ const { t } = useI18n();
16
24
 
17
25
  function iconFor(toggle: DockToggle): string | undefined {
18
26
  return toggle.icon ?? (toggle.tool === 'assistant' ? 'chat' : undefined);
19
27
  }
20
28
 
29
+ function resolve(toggle: DockToggle): DockToggleResolution {
30
+ return resolveDockToggle(toggle, dock.has(toggle.tool) && dock.available);
31
+ }
32
+
33
+ function nameFor(toggle: DockToggle, notReady: boolean): string {
34
+ if (!notReady) return toggle.label;
35
+ return (
36
+ toggle.labelNotReady ??
37
+ t(M['ui.dock_toggles.set_up'], { label: toggle.label })
38
+ );
39
+ }
40
+
21
41
  function press(event: MouseEvent, toggle: DockToggle): void {
22
- if (!dock.has(toggle.tool) || !dock.available) return;
42
+ const resolution = resolve(toggle);
43
+ if (!runDockTogglePress(toggle, event, resolution)) return;
44
+ if (resolution.mode !== 'toggle') return;
23
45
  dock.toggle(toggle.tool, {
24
46
  returnFocus: event.currentTarget as HTMLElement,
25
47
  });
26
48
  }
27
49
  </script>
28
50
 
51
+ {#snippet glyph(icon: string | undefined)}
52
+ {#if icon === 'chat'}
53
+ <svg
54
+ viewBox="0 0 24 24"
55
+ width="20"
56
+ height="20"
57
+ fill="none"
58
+ stroke="currentColor"
59
+ stroke-width="2"
60
+ stroke-linecap="round"
61
+ stroke-linejoin="round"
62
+ aria-hidden="true"
63
+ >
64
+ <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" />
65
+ </svg>
66
+ {:else if icon === undefined || isShellIconName(icon)}
67
+ <svg
68
+ viewBox="0 0 24 24"
69
+ width="20"
70
+ height="20"
71
+ fill="currentColor"
72
+ aria-hidden="true"
73
+ focusable="false"
74
+ >
75
+ <path d={SHELL_ICON_PATHS[icon === undefined ? SHELL_DEFAULT_TOOL_ICON : icon]} />
76
+ </svg>
77
+ {:else}
78
+ <span aria-hidden="true">{icon}</span>
79
+ {/if}
80
+ {/snippet}
81
+
29
82
  <div class="smrt-dock-toggles" data-testid="dock-toggles">
30
83
  {#each toggles as toggle (toggle.tool)}
31
- {@const registered = dock.has(toggle.tool) && dock.available}
32
- {@const open = dock.isOpen(toggle.tool)}
84
+ {@const resolution = resolve(toggle)}
85
+ {@const registered = resolution.mode === 'toggle'}
86
+ {@const open = registered && dock.isOpen(toggle.tool)}
33
87
  {@const icon = iconFor(toggle)}
34
- <!-- raw-primitive-allow: shell chrome toggle, not a content button -->
35
- <button
36
- type="button"
37
- class="smrt-dock-toggles__button"
38
- class:active={open}
39
- data-dock-tool={toggle.tool}
40
- aria-label={toggle.label}
41
- title={toggle.label}
42
- aria-pressed={open}
43
- aria-expanded={open}
44
- aria-controls="smrt-admin-shell-right-panel"
45
- aria-disabled={registered ? undefined : true}
46
- onclick={(event) => press(event, toggle)}
47
- >
48
- {#if icon === 'chat'}
49
- <svg
50
- viewBox="0 0 24 24"
51
- width="20"
52
- height="20"
53
- fill="none"
54
- stroke="currentColor"
55
- stroke-width="2"
56
- stroke-linecap="round"
57
- stroke-linejoin="round"
58
- aria-hidden="true"
59
- >
60
- <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" />
61
- </svg>
62
- {:else if icon === undefined || isShellIconName(icon)}
63
- <svg
64
- viewBox="0 0 24 24"
65
- width="20"
66
- height="20"
67
- fill="currentColor"
68
- aria-hidden="true"
69
- focusable="false"
70
- >
71
- <path d={SHELL_ICON_PATHS[icon === undefined ? SHELL_DEFAULT_TOOL_ICON : icon]} />
72
- </svg>
73
- {:else}
74
- <span aria-hidden="true">{icon}</span>
75
- {/if}
76
- </button>
88
+ {@const name = nameFor(toggle, resolution.notReady)}
89
+ {#if resolution.mode === 'link'}
90
+ <a
91
+ class="smrt-dock-toggles__button"
92
+ href={toggle.href}
93
+ data-dock-tool={toggle.tool}
94
+ aria-label={name}
95
+ title={name}
96
+ onclick={(event) => press(event, toggle)}
97
+ >
98
+ {@render glyph(icon)}
99
+ </a>
100
+ {:else}
101
+ <!-- raw-primitive-allow: shell chrome toggle, not a content button -->
102
+ <button
103
+ type="button"
104
+ class="smrt-dock-toggles__button"
105
+ class:active={open}
106
+ data-dock-tool={toggle.tool}
107
+ aria-label={name}
108
+ title={name}
109
+ aria-pressed={open}
110
+ aria-expanded={open}
111
+ aria-controls="smrt-admin-shell-right-panel"
112
+ aria-disabled={registered ? undefined : true}
113
+ onclick={(event) => press(event, toggle)}
114
+ >
115
+ {@render glyph(icon)}
116
+ </button>
117
+ {/if}
77
118
  {/each}
78
119
  </div>
79
120
 
@@ -88,6 +129,7 @@ function press(event: MouseEvent, toggle: DockToggle): void {
88
129
 
89
130
  .smrt-dock-toggles__button {
90
131
  display: inline-grid;
132
+ text-decoration: none;
91
133
  place-items: center;
92
134
  position: relative;
93
135
  inline-size: var(--smrt-control-height-small);
@@ -1,4 +1,4 @@
1
- import type { DockToggle } from './dock-toggle.js';
1
+ import { type DockToggle } from './dock-toggle.js';
2
2
  interface Props {
3
3
  toggles: DockToggle[];
4
4
  }
@@ -1 +1 @@
1
- {"version":3,"file":"DockToggles.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/app/DockToggles.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGnD,UAAU,KAAK;IACb,OAAO,EAAE,UAAU,EAAE,CAAC;CACvB;AAgDD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"DockToggles.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/app/DockToggles.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,KAAK,UAAU,EAIhB,MAAM,kBAAkB,CAAC;AAG1B,UAAU,KAAK;IACb,OAAO,EAAE,UAAU,EAAE,CAAC;CACvB;AA6ED,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -18,7 +18,11 @@ import {
18
18
  SHELL_PLACEHOLDER_ICON,
19
19
  } from '../workspace/admin-shell/shell-icons.js';
20
20
  import type { PhoneBottomBarEntry } from '../workspace/admin-shell/types.js';
21
- import type { DockToggle } from './dock-toggle.js';
21
+ import {
22
+ type DockToggle,
23
+ resolveDockToggle,
24
+ runDockTogglePress,
25
+ } from './dock-toggle.js';
22
26
  import {
23
27
  isTabCurrent,
24
28
  type PhoneTabNavEntry,
@@ -93,17 +97,38 @@ const items = $derived.by(() => {
93
97
  }));
94
98
  if (dockToggle) {
95
99
  const tool = dockToggle.tool;
96
- out.push({
97
- id: `dock:${tool}`,
98
- label: dockToggle.label,
99
- iconName: dockIcon(dockToggle),
100
- active: dock.isOpen(tool),
101
- expanded: dock.isOpen(tool),
102
- controls: 'smrt-admin-shell-right-panel',
103
- onclick: () => {
104
- if (dock.has(tool) && dock.available) dock.toggle(tool);
105
- },
106
- });
100
+ const resolution = resolveDockToggle(
101
+ dockToggle,
102
+ dock.has(tool) && dock.available,
103
+ );
104
+ const toggling = resolution.mode === 'toggle';
105
+ const label = resolution.notReady
106
+ ? (dockToggle.labelNotReady ??
107
+ t(M['ui.dock_toggles.set_up'], { label: dockToggle.label }))
108
+ : dockToggle.label;
109
+ const press = (event: MouseEvent) => {
110
+ if (!runDockTogglePress(dockToggle, event, resolution)) return;
111
+ if (toggling) dock.toggle(tool);
112
+ };
113
+ out.push(
114
+ resolution.mode === 'link'
115
+ ? {
116
+ id: `dock:${tool}`,
117
+ label,
118
+ iconName: dockIcon(dockToggle),
119
+ href: dockToggle.href,
120
+ onclick: press,
121
+ }
122
+ : {
123
+ id: `dock:${tool}`,
124
+ label,
125
+ iconName: dockIcon(dockToggle),
126
+ active: toggling && dock.isOpen(tool),
127
+ expanded: toggling ? dock.isOpen(tool) : undefined,
128
+ controls: toggling ? 'smrt-admin-shell-right-panel' : undefined,
129
+ onclick: press,
130
+ },
131
+ );
107
132
  } else if (toolsTab) {
108
133
  out.push({
109
134
  id: 'tools',
@@ -1,5 +1,5 @@
1
1
  import type { Component } from 'svelte';
2
- import type { DockToggle } from './dock-toggle.js';
2
+ import { type DockToggle } from './dock-toggle.js';
3
3
  import { type PhoneTabNavEntry } from './phone-tabs.js';
4
4
  interface Props {
5
5
  /** Navigation destinations in order (see `phoneTabNavEntries`). */
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneTabBar.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/app/PhoneTabBar.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAWxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAEL,KAAK,gBAAgB,EAEtB,MAAM,iBAAiB,CAAC;AAGzB,UAAU,KAAK;IACb,mEAAmE;IACnE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,yGAAyG;IACzG,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,+BAA+B;IAC/B,aAAa,CAAC,EAAE,SAAS,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5D;AA8GD,QAAA,MAAM,WAAW,0BAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
1
+ {"version":3,"file":"PhoneTabBar.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/app/PhoneTabBar.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAWxC,OAAO,EACL,KAAK,UAAU,EAGhB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAEL,KAAK,gBAAgB,EAEtB,MAAM,iBAAiB,CAAC;AAGzB,UAAU,KAAK;IACb,mEAAmE;IACnE,OAAO,EAAE,gBAAgB,EAAE,CAAC;IAC5B,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,yGAAyG;IACzG,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,+BAA+B;IAC/B,aAAa,CAAC,EAAE,SAAS,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC5D;AAmID,QAAA,MAAM,WAAW,0BAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -10,8 +10,8 @@ interface Props {
10
10
  * `onSignOut` for that.
11
11
  */
12
12
  signOutHref?: string;
13
- /** Called by the sign-out control instead of following `signOutHref`. */
14
- onSignOut?: () => void;
13
+ /** Called by the sign-out control instead of following `signOutHref`; may be async (busy while pending, inline error on rejection). */
14
+ onSignOut?: () => unknown;
15
15
  }
16
16
 
17
17
  const { signOutHref, onSignOut }: Props = $props();
@@ -5,8 +5,8 @@ interface Props {
5
5
  * `onSignOut` for that.
6
6
  */
7
7
  signOutHref?: string;
8
- /** Called by the sign-out control instead of following `signOutHref`. */
9
- onSignOut?: () => void;
8
+ /** Called by the sign-out control instead of following `signOutHref`; may be async (busy while pending, inline error on rejection). */
9
+ onSignOut?: () => unknown;
10
10
  }
11
11
  declare const ShellSettingsPage: import("svelte").Component<Props, {}, "">;
12
12
  type ShellSettingsPage = ReturnType<typeof ShellSettingsPage>;
@@ -1 +1 @@
1
- {"version":3,"file":"ShellSettingsPage.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/app/ShellSettingsPage.svelte.ts"],"names":[],"mappings":"AAQA,UAAU,KAAK;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAuBD,QAAA,MAAM,iBAAiB,2CAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
1
+ {"version":3,"file":"ShellSettingsPage.svelte.d.ts","sourceRoot":"","sources":["../../../src/components/app/ShellSettingsPage.svelte.ts"],"names":[],"mappings":"AAQA,UAAU,KAAK;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uIAAuI;IACvI,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;CAC3B;AAuBD,QAAA,MAAM,iBAAiB,2CAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
@@ -17,5 +17,50 @@ export interface DockToggle {
17
17
  * region is hidden the shell moves it along the slot fallback chain.
18
18
  */
19
19
  slot?: ShellSlot;
20
+ /**
21
+ * Where to go when the toggle cannot toggle the dock: the tool is not
22
+ * registered with `useShellDock()`, or `ready` is `false`. The button then
23
+ * renders as a link (so middle-click and open-in-new-tab work) and drops
24
+ * `aria-pressed` / `aria-expanded`. Without it that state stays inert.
25
+ */
26
+ href?: string;
27
+ /**
28
+ * Whether the tool is usable right now (default `true`). Reactive: a host
29
+ * passes e.g. `aiState.assistantReady`. While `false` the toggle behaves as
30
+ * the unregistered state: `href` if given, else inert.
31
+ */
32
+ ready?: boolean;
33
+ /** Accessible name and tooltip in the not-ready state. Default "Set up {label}". */
34
+ labelNotReady?: string;
35
+ /**
36
+ * Called first on activation with the click event and the resolved state.
37
+ * Returning `false`, or calling `event.preventDefault()`, stops everything
38
+ * else (no dock toggle, no navigation).
39
+ */
40
+ onpress?: (event: MouseEvent, state: {
41
+ registered: boolean;
42
+ ready: boolean;
43
+ }) => boolean | void;
44
+ }
45
+ /** What activating a `DockToggle` does, from the live dock and `ready`. */
46
+ export type DockToggleMode = 'toggle' | 'link' | 'inert';
47
+ export interface DockToggleResolution {
48
+ mode: DockToggleMode;
49
+ registered: boolean;
50
+ ready: boolean;
51
+ /** True in the not-ready state (unregistered with an `href`, or `ready === false`). */
52
+ notReady: boolean;
20
53
  }
54
+ /**
55
+ * Precedence: `onpress` runs first and can cancel. Then, when the tool is
56
+ * registered and `ready !== false`, the dock toggles. Otherwise `href` is
57
+ * followed when given, else nothing happens.
58
+ */
59
+ export declare function resolveDockToggle(toggle: DockToggle, registered: boolean): DockToggleResolution;
60
+ /**
61
+ * Run `onpress` for a click. Returns `true` when the rest of the press (dock
62
+ * toggle or navigation) should go ahead. Stopping also calls
63
+ * `event.preventDefault()` so a link does not navigate.
64
+ */
65
+ export declare function runDockTogglePress(toggle: DockToggle, event: MouseEvent, resolution: DockToggleResolution): boolean;
21
66
  //# sourceMappingURL=dock-toggle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dock-toggle.d.ts","sourceRoot":"","sources":["../../../src/components/app/dock-toggle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAEnE,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IACzB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB"}
1
+ {"version":3,"file":"dock-toggle.d.ts","sourceRoot":"","sources":["../../../src/components/app/dock-toggle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAEnE,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IACzB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,oFAAoF;IACpF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,OAAO,CAAC,EAAE,CACR,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,KAC3C,OAAO,GAAG,IAAI,CAAC;CACrB;AAED,2EAA2E;AAC3E,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAEzD,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,cAAc,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,uFAAuF;IACvF,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,OAAO,GAClB,oBAAoB,CAKtB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,UAAU,EACjB,UAAU,EAAE,oBAAoB,GAC/B,OAAO,CAUT"}