@happyvertical/smrt-svelte 0.55.6 → 0.55.7

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 (124) hide show
  1. package/AGENTS.md +17 -2
  2. package/README.md +83 -4
  3. package/agents/workspace.md +202 -5
  4. package/dist/browser-ai/adapters/stt/dictation-source.d.ts.map +1 -1
  5. package/dist/browser-ai/adapters/stt/dictation-source.js +8 -2
  6. package/dist/browser-ai/adapters/stt/factory.d.ts +6 -3
  7. package/dist/browser-ai/adapters/stt/factory.d.ts.map +1 -1
  8. package/dist/browser-ai/adapters/stt/factory.js +19 -9
  9. package/dist/browser-ai/adapters/stt/index.d.ts +3 -1
  10. package/dist/browser-ai/adapters/stt/index.d.ts.map +1 -1
  11. package/dist/browser-ai/adapters/stt/index.js +3 -1
  12. package/dist/browser-ai/adapters/stt/local-speech-model.d.ts +111 -0
  13. package/dist/browser-ai/adapters/stt/local-speech-model.d.ts.map +1 -0
  14. package/dist/browser-ai/adapters/stt/local-speech-model.js +377 -0
  15. package/dist/browser-ai/adapters/stt/local-speech.d.ts +82 -0
  16. package/dist/browser-ai/adapters/stt/local-speech.d.ts.map +1 -0
  17. package/dist/browser-ai/adapters/stt/local-speech.js +317 -0
  18. package/dist/browser-ai/adapters/stt/local-speech.worker.d.ts +2 -0
  19. package/dist/browser-ai/adapters/stt/local-speech.worker.d.ts.map +1 -0
  20. package/dist/browser-ai/adapters/stt/local-speech.worker.js +32 -0
  21. package/dist/browser-ai/adapters/stt/transformers-env.d.ts +8 -0
  22. package/dist/browser-ai/adapters/stt/transformers-env.d.ts.map +1 -0
  23. package/dist/browser-ai/adapters/stt/transformers-env.js +17 -0
  24. package/dist/browser-ai/adapters/stt/types.d.ts +54 -9
  25. package/dist/browser-ai/adapters/stt/types.d.ts.map +1 -1
  26. package/dist/browser-ai/capabilities/speech-probe.d.ts +29 -0
  27. package/dist/browser-ai/capabilities/speech-probe.d.ts.map +1 -0
  28. package/dist/browser-ai/capabilities/speech-probe.js +19 -0
  29. package/dist/browser-ai/core/errors.d.ts +9 -0
  30. package/dist/browser-ai/core/errors.d.ts.map +1 -1
  31. package/dist/browser-ai/core/errors.js +12 -0
  32. package/dist/browser-ai/index.d.ts +1 -0
  33. package/dist/browser-ai/index.d.ts.map +1 -1
  34. package/dist/browser-ai/index.js +1 -0
  35. package/dist/components/app/AppShell.svelte +587 -33
  36. package/dist/components/app/AppShell.svelte.d.ts +75 -4
  37. package/dist/components/app/AppShell.svelte.d.ts.map +1 -1
  38. package/dist/components/app/DockToggles.svelte +0 -1
  39. package/dist/components/app/dock-toggle.d.ts +7 -1
  40. package/dist/components/app/dock-toggle.d.ts.map +1 -1
  41. package/dist/components/app/index.d.ts +2 -1
  42. package/dist/components/app/index.d.ts.map +1 -1
  43. package/dist/components/app/slot-item.d.ts +21 -0
  44. package/dist/components/app/slot-item.d.ts.map +1 -0
  45. package/dist/components/app/slot-item.js +1 -0
  46. package/dist/components/forms/AddressInput.svelte +2 -15
  47. package/dist/components/forms/AddressInput.svelte.d.ts.map +1 -1
  48. package/dist/components/forms/DateRangeInput.svelte +2 -15
  49. package/dist/components/forms/DateRangeInput.svelte.d.ts.map +1 -1
  50. package/dist/components/forms/DateTimeInput.svelte +8 -15
  51. package/dist/components/forms/DateTimeInput.svelte.d.ts.map +1 -1
  52. package/dist/components/forms/MeasurementInput.svelte +2 -15
  53. package/dist/components/forms/MeasurementInput.svelte.d.ts.map +1 -1
  54. package/dist/components/forms/MoneyInput.svelte +8 -25
  55. package/dist/components/forms/MoneyInput.svelte.d.ts.map +1 -1
  56. package/dist/components/forms/NumberInput.svelte +7 -15
  57. package/dist/components/forms/NumberInput.svelte.d.ts.map +1 -1
  58. package/dist/components/forms/PhoneInput.svelte +7 -15
  59. package/dist/components/forms/PhoneInput.svelte.d.ts.map +1 -1
  60. package/dist/components/forms/SelectInput.svelte +34 -40
  61. package/dist/components/forms/SelectInput.svelte.d.ts.map +1 -1
  62. package/dist/components/forms/TextInput.svelte +20 -37
  63. package/dist/components/forms/TextInput.svelte.d.ts.map +1 -1
  64. package/dist/components/forms/TextareaInput.svelte +19 -35
  65. package/dist/components/forms/TextareaInput.svelte.d.ts.map +1 -1
  66. package/dist/components/sortable/Sortable.svelte +6 -33
  67. package/dist/components/sortable/Sortable.svelte.d.ts.map +1 -1
  68. package/dist/components/sortable/announce.d.ts +5 -0
  69. package/dist/components/sortable/announce.d.ts.map +1 -0
  70. package/dist/components/sortable/announce.js +33 -0
  71. package/dist/components/workspace/admin-shell/AdminShell.svelte +424 -58
  72. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts +27 -2
  73. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts.map +1 -1
  74. package/dist/components/workspace/admin-shell/ShellBrand.svelte +86 -0
  75. package/dist/components/workspace/admin-shell/ShellBrand.svelte.d.ts +18 -0
  76. package/dist/components/workspace/admin-shell/ShellBrand.svelte.d.ts.map +1 -0
  77. package/dist/components/workspace/admin-shell/ShellIconButton.svelte +77 -0
  78. package/dist/components/workspace/admin-shell/ShellIconButton.svelte.d.ts +26 -0
  79. package/dist/components/workspace/admin-shell/ShellIconButton.svelte.d.ts.map +1 -0
  80. package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte +88 -16
  81. package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte.d.ts.map +1 -1
  82. package/dist/components/workspace/admin-shell/ShellNavEditor.svelte +531 -0
  83. package/dist/components/workspace/admin-shell/ShellNavEditor.svelte.d.ts +26 -0
  84. package/dist/components/workspace/admin-shell/ShellNavEditor.svelte.d.ts.map +1 -0
  85. package/dist/components/workspace/admin-shell/ShellSectionIcon.svelte +57 -0
  86. package/dist/components/workspace/admin-shell/ShellSectionIcon.svelte.d.ts +22 -0
  87. package/dist/components/workspace/admin-shell/ShellSectionIcon.svelte.d.ts.map +1 -0
  88. package/dist/components/workspace/admin-shell/ShellSectionMenu.svelte +290 -0
  89. package/dist/components/workspace/admin-shell/ShellSectionMenu.svelte.d.ts +32 -0
  90. package/dist/components/workspace/admin-shell/ShellSectionMenu.svelte.d.ts.map +1 -0
  91. package/dist/components/workspace/admin-shell/TenantNav.svelte +67 -2
  92. package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts +8 -0
  93. package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts.map +1 -1
  94. package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts +67 -0
  95. package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts.map +1 -1
  96. package/dist/components/workspace/admin-shell/layout-controller.svelte.js +129 -1
  97. package/dist/components/workspace/admin-shell/layout.d.ts +122 -0
  98. package/dist/components/workspace/admin-shell/layout.d.ts.map +1 -1
  99. package/dist/components/workspace/admin-shell/layout.js +410 -10
  100. package/dist/components/workspace/admin-shell/settings.js +4 -4
  101. package/dist/components/workspace/admin-shell/shell-icons.d.ts +45 -0
  102. package/dist/components/workspace/admin-shell/shell-icons.d.ts.map +1 -0
  103. package/dist/components/workspace/admin-shell/shell-icons.js +66 -0
  104. package/dist/components/workspace/admin-shell/slot-labels.d.ts +33 -0
  105. package/dist/components/workspace/admin-shell/slot-labels.d.ts.map +1 -0
  106. package/dist/components/workspace/admin-shell/slot-labels.js +33 -0
  107. package/dist/components/workspace/admin-shell/slots.d.ts +50 -0
  108. package/dist/components/workspace/admin-shell/slots.d.ts.map +1 -0
  109. package/dist/components/workspace/admin-shell/slots.js +77 -0
  110. package/dist/components/workspace/admin-shell/state.svelte.d.ts +35 -0
  111. package/dist/components/workspace/admin-shell/state.svelte.d.ts.map +1 -1
  112. package/dist/components/workspace/admin-shell/state.svelte.js +111 -0
  113. package/dist/components/workspace/admin-shell/types.d.ts +101 -1
  114. package/dist/components/workspace/admin-shell/types.d.ts.map +1 -1
  115. package/dist/components/workspace/index.d.ts +9 -2
  116. package/dist/components/workspace/index.d.ts.map +1 -1
  117. package/dist/components/workspace/index.js +6 -0
  118. package/dist/i18n/strings.workspace.d.ts +49 -0
  119. package/dist/i18n/strings.workspace.d.ts.map +1 -1
  120. package/dist/i18n/strings.workspace.js +49 -0
  121. package/package.json +30 -19
  122. package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts +0 -51
  123. package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts.map +0 -1
  124. package/dist/browser-ai/adapters/stt/whisper-wasm.js +0 -380
package/AGENTS.md CHANGED
@@ -205,7 +205,22 @@ for a data-surface one — for exactly the component's lifetime.
205
205
 
206
206
  - **Preload strategies**: `none`, `eager`, `idle` (recommended), `on-visible`
207
207
  - **Warm client cache**: module-level Map survives navigation/remounts -- avoids re-downloading WASM/models
208
- - **Adapters**: STT (browser-speech, whisper-cpp, whisper-wasm), TTS (browser-synthesis), LLM (webllm, transformers-llm)
208
+ - **Adapters**: STT (browser-speech, whisper-cpp, whisper-local = alias whisper-wasm), TTS (browser-synthesis), LLM (webllm, transformers-llm)
209
+ - **On-device speech** (`whisper-local` = alias `whisper-wasm`, and `moonshine`): thin adapters over
210
+ `@happyvertical/speech/local` (`LocalTranscriber`, or its worker host `serveLocalTranscriber`).
211
+ Push-to-talk (record on `start()`, transcribe on `stop()`, one final result) plus `transcribePcm()`
212
+ for smrt-ui's hands-free mode. Default `onnx-community/whisper-tiny.en` q8 (~45 MB, Cache
213
+ Storage); `model: 'moonshine-tiny'` (~32 MB) / `'moonshine-base'` (~67 MB) or any HF id
214
+ (`LOCAL_SPEECH_MODELS`); WebGPU else single-thread WASM (no COOP/COEP needed). The package never
215
+ imports the peers: a host passes `createWorker` (build it from
216
+ `@happyvertical/smrt-svelte/browser-ai/whisper-worker?worker`, alias `speech-worker`, the one module
217
+ that imports `@happyvertical/speech/local` and `@huggingface/transformers` statically) plus
218
+ `loadSpeech: () => import('@happyvertical/speech/local')`, or `loadModule` for the page thread.
219
+ `createLocalSpeechModel()` (alias `createWhisperLocalModel()`) / `adapter.model` is the consent API
220
+ (`estimateSize`, `isCached`, `load({ onProgress, signal })` with `downloading` -> `extracting` ->
221
+ `complete`); share it via `modelHandle`. English-only handling of `language`/`task` lives in the speech
222
+ package. `probeBrowserSpeech()` -> `works | missing | unreliable` (no network, no mic prompt)
223
+ decides whether to offer it. Sources set `stopTimeoutMs` so `Dictation` waits for the transcription.
209
224
  - Cache API: `getCachedSTT()`, `getCachedTTS()`, `getCachedLLM()`, `getCacheStats()`, `clearAllCaches()`
210
225
  - **Inference backends (browser-ai)**: two `@happyvertical/smrt-web/ai` `InferenceBackend`s, both
211
226
  `kind: 'local'`, both refusing `tool`/`function` messages, non-string content, `tool_calls` and
@@ -258,4 +273,4 @@ for a data-surface one — for exactly the component's lifetime.
258
273
  - `@happyvertical/smrt-ui` (UI runtime: primitives, theme system, i18n client, module registry) is a hard `dependency`. The agent-admin shells that used to type against `@happyvertical/smrt-agents/ui` moved to `@happyvertical/smrt-agents/svelte` (#1589), so `smrt-agents` is no longer a dependency here — this drops smrt-svelte below smrt-agents in the package DAG.
259
274
  - `@happyvertical/smrt-languages` is a hard `dependency` (not an optional peer): the Node-only `/i18n/server` subpath imports its resolver. The browser bundle still excludes it — the client `/i18n` layer never imports the languages root, so it tree-shakes out.
260
275
  - `@happyvertical/logger` (SDK) is a `dependency` — the console logger used for voice/AI error reporting in the form components. Consume it **only** through `src/internal/logger.ts`, never `createLogger()` at module scope: `createLogger()` reads `HAVE_LOGGER_LEVEL` from `process.env`, so a top-level call throws `ReferenceError: process is not defined` in the browser and kills client-side hydration under `vite dev` (prod builds tree-shake/define it away, so this only bites in dev). The `internal/logger` wrapper constructs the logger lazily and falls back to a bare `ConsoleLogger` when `process.env` is absent, keeping this browser-reachable module (imported by `Provider` + the form primitives) safe.
261
- - Peer (all optional): `svelte` >=5.18.2, plus the browser-AI engines (`@huggingface/transformers`, `@mlc-ai/web-llm`, `@remotion/whisper-web`, `@xenova/transformers`) and `chrono-node`.
276
+ - Peer (all optional): `svelte` >=5.18.2, plus the browser-AI engines (`@happyvertical/speech`, `@huggingface/transformers`, `@mlc-ai/web-llm`, `@remotion/whisper-web`) and `chrono-node`.
package/README.md CHANGED
@@ -40,7 +40,7 @@ items that share Board's drag engine. `AppShell` lets users customize their
40
40
  shell: pass `layout` and `onlayoutchange` to own persistence (for example in an
41
41
  exported app blueprint), or neither to store it in the user's settings. Mount
42
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
43
+ `useShellLayout()` to make the same changes from code or an assistant. Users can rename sections and individual navigation entries (`items`, `renameItem`), hide section titles, and create or delete their own sections. See
44
44
  `agents/workspace.md`; the pure `ShellLayout` model is published without Svelte
45
45
  as `@happyvertical/smrt-svelte/workspace/layout`.
46
46
 
@@ -50,6 +50,16 @@ as `@happyvertical/smrt-svelte/workspace/layout`.
50
50
  </AppShell>
51
51
  ```
52
52
 
53
+ Opt in to editing the real shell in place with `layoutEditing` on `AppShell`: a
54
+ pencil toggle ("Edit layout", highlighted while editing; press again or Escape to exit) joins the header (as does the app title, a movable
55
+ `item:brand` in `header.start`), and while on, every slot is a
56
+ labelled drop zone, shell items and navigation get grips (drag, or Space and
57
+ arrow keys), section headings get icon overlays and a floating toolbar, and
58
+ hidden regions show as strips. Apps add per-section icon buttons (an Options
59
+ gear, Help) with `sectionActions`; pass `layoutEditing={{ floating: true }}` to pin the
60
+ toggle in the top-right corner instead of the header; `useShellLayout()` exposes `editing` and
61
+ `setEditing`. See `agents/workspace.md`.
62
+
53
63
  ## Installation
54
64
 
55
65
  ```bash
@@ -288,6 +298,60 @@ bind legacy name-based cleanup to registrations made by that caller, so
288
298
  overlapping same-name fields can unmount in either order without retaining a
289
299
  detached control.
290
300
 
301
+ ### Voice typing where the browser cannot (on-device Whisper and Moonshine)
302
+
303
+ Browsers differ on speech recognition: Chrome and Safari have it, Firefox does
304
+ not, and Brave has the API without a speech service behind it. `probeBrowserSpeech()`
305
+ says which you have (`'works' | 'missing' | 'unreliable'`) without a network
306
+ call or a microphone prompt. For the last two, offer a one-time model download
307
+ and dictate with a speech model running in the browser (WebGPU where available,
308
+ single-thread WASM otherwise; no cross-origin isolation needed). The engine is
309
+ `@happyvertical/speech/local` (an optional peer, `>=0.102.5`, as is
310
+ `@huggingface/transformers`).
311
+
312
+ | `model` | Size | Notes |
313
+ | --- | --- | --- |
314
+ | `moonshine-tiny` (`onnx-community/moonshine-tiny-ONNX`) | ~32 MB | English, fastest: best for live dictation |
315
+ | `moonshine-base` (`onnx-community/moonshine-base-ONNX`) | ~67 MB | English, more accurate |
316
+ | `whisper-tiny.en` (default) | ~45 MB | English |
317
+ | `whisper-base.en` / `whisper-small.en` | ~85 / ~260 MB | English |
318
+ | any Hugging Face id, e.g. `onnx-community/whisper-base` | varies | multilingual Whisper |
319
+
320
+ ```ts
321
+ import {
322
+ createLocalSpeechModel,
323
+ createSttDictationSource,
324
+ probeBrowserSpeech,
325
+ } from '@happyvertical/smrt-svelte/browser-ai';
326
+ // The worker is the one module that imports the optional peers statically,
327
+ // so only apps that build it bundle them.
328
+ import SpeechWorker from '@happyvertical/smrt-svelte/browser-ai/whisper-worker?worker';
329
+
330
+ if ((await probeBrowserSpeech()) !== 'works') {
331
+ const model = createLocalSpeechModel({
332
+ model: 'moonshine-tiny',
333
+ createWorker: () => new SpeechWorker(),
334
+ loadSpeech: () => import('@happyvertical/speech/local'),
335
+ });
336
+ model.estimateSize(); // ~32 MB, for the consent text
337
+ await model.isCached(); // already downloaded on this device?
338
+ await model.load({ onProgress, signal }); // downloading -> extracting ("Getting ready") -> complete; abort() cancels
339
+ const dictation = createSttDictationSource({ type: 'moonshine', modelHandle: model });
340
+ }
341
+ ```
342
+
343
+ `createWhisperLocalModel`, `{ type: 'whisper-local' }` and the `whisper-wasm`
344
+ alias keep working. Without `createWorker` the model runs on the page's thread
345
+ and `loadModule: () => import('@huggingface/transformers')` supplies the
346
+ runtime. With a worker, `device` and `dtype` are the worker entry's (`auto`
347
+ and `q8`).
348
+
349
+ Push-to-talk: the microphone records until stopped, then one final result is
350
+ emitted. For hands-free dictation (each sentence written down when the speaker
351
+ pauses) give smrt-ui's `Dictation` `mode: 'hands-free'` and
352
+ `createHandsFreeCapture`; these adapters' `transcribePcm()` writes down each
353
+ utterance. English-only models (`*.en`, Moonshine) ignore a requested language.
354
+
291
355
  ### Form Components
292
356
 
293
357
  ```svelte
@@ -496,6 +560,12 @@ keys the shell renders exactly as before.
496
560
  focus (unless something inside already has it) and returns it on close;
497
561
  Escape or a scrim click closes it, sliding it back out as the scrim fades
498
562
  (no motion under `prefers-reduced-motion`). Resizing applies only while docked.
563
+ - **Railless edges**: `rail: false` gives a side edge no rail (no tool buttons, open or closed); closed, it takes
564
+ no space and renders nothing to see or tab to, and opens from elsewhere (a
565
+ header dock toggle, `useShellDock()`). Pair it with `presentation: 'overlay'`
566
+ for a chat that slides over the page. Layout edit mode leaves a railless edge
567
+ as it is (closed stays closed) and, while editing, shows a small "Open
568
+ <tool>" tab on the shell's right edge when it has content.
499
569
  - **Kept panels**: `keepMounted: true` keeps a collapsed edge's panel content
500
570
  (`appPanel`, `tenantPanel`, the focus panel, `systemPanel`) mounted with the
501
571
  `hidden` attribute instead of unmounting it, so component state (a chat
@@ -537,7 +607,7 @@ importable, even if it appears in `dist/`.
537
607
  | `@happyvertical/smrt-svelte/forms` | Form inputs (TextInput, Select, MoneyInput, DateTimeInput, Toggle, etc.) |
538
608
  | `@happyvertical/smrt-svelte/settings` | Server-paged settings search, selection, and list/detail layout (`SettingsCatalog`, `paginateSettingsCatalog`) |
539
609
  | `@happyvertical/smrt-svelte/workspace` | AdminShell, ShellState, tenant nav, focus tools, settings, activities, and system/app panels |
540
- | `@happyvertical/smrt-svelte/app` | `AppShell` (Provider + themes + AdminShell + nav/dock slots, `dockToggles` header buttons), `OwnerSetupForm` (first-run owner setup), `ShellSettingsPage`, `RuntimeDiagnosticsWebMcp` |
610
+ | `@happyvertical/smrt-svelte/app` | `AppShell` (Provider + themes + AdminShell + nav/dock slots, `dockToggles` buttons and host `slots` for the header/footer/sidebar regions), `OwnerSetupForm` (first-run owner setup), `ShellSettingsPage`, `RuntimeDiagnosticsWebMcp` |
541
611
  | `@happyvertical/smrt-svelte/app/runtime-diagnostics` | Svelte-free diagnostics WebMCP registration and its tool name/endpoint constants, importable from server routes |
542
612
  | `@happyvertical/smrt-svelte/workspace/legacy` | Opt-in ToolsDock compatibility surface for applications migrating to AdminShell |
543
613
  | `@happyvertical/smrt-svelte/workspace/server` | Server-side workspace helpers (Node only) |
@@ -671,8 +741,10 @@ updates retain their existing behavior.
671
741
 
672
742
  Use `shell.setPanelState(edge, 'hidden' | 'collapsed' | 'expanded')` to persist
673
743
  runtime panel preferences; app-configured hidden edges remain unavailable.
674
- AdminShell keeps a discoverable tenant collapse control with supplied navigation
675
- (`showTenantToggle={false}` opts out), a Menu opener in narrow layouts, and the
744
+ AdminShell's edge toggle buttons and WASD hotkeys are opt-in via `edgeToggles`
745
+ (default `false`: regions lay out inline; pass `true`, or a per-edge map, to
746
+ keep the drop-down behaviour; `showTenantToggle` is a deprecated alias for
747
+ `edgeToggles.left`), a Menu opener in narrow layouts, and the
676
748
  system toggle alongside a custom `systemBar` that has a `systemPanel` to open
677
749
  (a `systemBar` with no `systemPanel` owns the bottom band and draws no toggle). Closed narrow drawers are inert;
678
750
  opening focuses the first control, and closing or Escape restores the opener.
@@ -688,6 +760,13 @@ canonical 48px target; omit density to inherit the theme, or choose
688
760
  `"comfortable"`. Account menus in the shell app bar open below the bar so the
689
761
  existing footer's default placement stays reachable on narrow screens.
690
762
 
763
+ Sections-only navigation: pass `navMode="sections"` to `AppShell` and give
764
+ each `ShellNavGroup` an `icon` and `href`; the sidebar then lists only the
765
+ sections, and the section's page renders its entries with `ShellSectionMenu`
766
+ (`meta`/`actions` snippets for counts and "New ..." links). In layout edit mode
767
+ the menu rows get grip, rename and hide controls, and the sidebar's section
768
+ toolbar gets an icon picker (`useShellLayout().setSectionIcon`).
769
+
691
770
  `TenantNav` accepts optional `groups: ShellNavGroup[]` (`{ heading, items }`)
692
771
  next to its existing flat `items`. Each group has a labelled `role="group"` and
693
772
  native disclosure summary; keyboard and touch users can collapse it even in the
@@ -83,6 +83,140 @@ and touch input across mobile widths and desktop panel-state combinations.
83
83
 
84
84
  See `src/components/workspace/MIGRATION.md` for the old-to-new concept map.
85
85
 
86
+ ### Regions and slots
87
+
88
+ Four regions map to the existing edges (ids and `ShellLayout.panels` keys are
89
+ unchanged): Header (`top`), Left sidebar (`left`), Right sidebar (`right`),
90
+ Footer (`bottom`). Default labels are those names; override via `config`.
91
+ `AdminShell`/`AppShell` take `slots?: Partial<Record<ShellSlot, Snippet>>`
92
+ (`ShellSlot` from `./workspace`): `header.start|center|end`,
93
+ `footer.start|center|end`, `leftSidebar.header|footer`,
94
+ `rightSidebar.header|footer`. Header/footer slots sit in the bar (start after
95
+ the brand/menu opener, end after the account), sidebar header sits above and
96
+ footer below the sidebar's main area. Defaults: brand/title/menu opener/account
97
+ stay where they were (`header.start`/`header.end` areas), `tenantFooter` stays
98
+ the left sidebar footer, `dockToggles` default to `header.end`. There is no
99
+ separate header row.
100
+
101
+ Fallback when a slot's region is hidden (or a sidebar is collapsed), first
102
+ visible wins (`slotFallbackChain`): header.X -> leftSidebar.header ->
103
+ rightSidebar.header -> footer.X; footer.X -> leftSidebar.footer ->
104
+ rightSidebar.footer -> header.X; leftSidebar.header -> header.start ->
105
+ rightSidebar.header -> footer.start; leftSidebar.footer -> footer.start ->
106
+ header.start -> rightSidebar.footer; rightSidebar.header -> header.end ->
107
+ leftSidebar.header -> footer.end; rightSidebar.footer -> footer.end ->
108
+ header.end -> leftSidebar.footer. The legacy `tenantFooter` prop keeps its own
109
+ `footerInHeader` fallback.
110
+
111
+ ### Edge toggles and hotkeys are opt-in (#3661)
112
+
113
+ `AdminShell`/`AppShell` `edgeToggles?: boolean | Partial<Record<PanelEdge,
114
+ boolean>>` defaults to `false`: no edge toggle buttons, no hotkey hints, and
115
+ WASD/`?` are inert. Regions are laid out inline: the left sidebar is docked
116
+ open off phones (`ShellState.setInlineEdges`/`isInline`; viewport defaults and
117
+ `overlayMedia` do not apply), and the header/footer are plain bands without a
118
+ drop-down drawer (so `appPanel`/`systemPanel` are not reachable). Visibility
119
+ comes from `ShellLayout.panels.visible` and edit mode. The right edge is the
120
+ dock: dock toggles and focus tools still open it, it just loses its button.
121
+ `hotkeys` (default: follows the toggles) forces shortcuts on (`true`, panels
122
+ behave as drop-downs without buttons) or off. Phones keep the Menu opener and
123
+ the left drawer's close toggle. `showTenantToggle` is a deprecated alias for
124
+ `edgeToggles.left`. Migration: pass `edgeToggles` (true) to keep the previous
125
+ behaviour.
126
+
127
+ ### Moving items between slots
128
+
129
+ Movable items have stable ids: dock toggles `dock:<tool>`, each host `slots`
130
+ snippet `slot:<slot>`, and host `slotItems` (`AppShell`, `{ id, label, slot,
131
+ render }[]`, exported type `ShellSlotItem`; keep ids stable, they are stored in
132
+ layouts). `AppShell` also supplies the built-in item `item:brand` (the shell
133
+ `logoSrc`/`logoAlt`, title and subtitle, linked to `homeHref` when set, default slot `header.start`, movable, same fallback chain
134
+ when the header is hidden); it sets `AdminShell` `brandInSlot` so the top band
135
+ draws no brand of its own (standalone `AdminShell` still does). The menu
136
+ opener and account content are not items yet (#3656 step 3).
137
+
138
+ `ShellLayout.placements?: Record<itemId, ShellSlot>` (additive, still version
139
+ 1) overrides default slots. Pure helpers in `layout.ts` (also `./workspace/layout`):
140
+ `placeShellItem(layout, id, slot, defaultSlot?)` (passing the default drops a
141
+ no-op override; re-placing puts the item last), `resetShellItemPlacement`,
142
+ `resolveShellPlacements(items, layout)` (default order first, then placement
143
+ order; unknown ids and slots ignored) and `resolveShellVisiblePlacements(items,
144
+ layout, visible)` (placement first, then the hidden-region fallback above).
145
+ `useShellLayout()` adds `placeItem(id, slot)`, `resetItem(id)`, and readers
146
+ `placementItems`, `placements` (per slot) and `isRegionVisible(region)`.
147
+
148
+ ### Editing the layout in place
149
+
150
+ `AppShell` `layoutEditing?: boolean | { slot?; floating? }` opts in (off: no toggle,
151
+ `setEditing(true)` refused, existing apps unchanged). It adds the built-in item
152
+ `item:layout-edit` (default slot `header.end`, normal hidden-region fallback):
153
+ a pencil `ShellIconButton` named "Edit layout" with `aria-pressed`; the same
154
+ button turns edit mode off (it stays a pencil, highlighted while on). Escape
155
+ also exits when no section toolbar is open (an open toolbar, or a keyboard move,
156
+ consumes Escape first).
157
+ `{ floating: true }` instead renders the same button fixed in the top-right
158
+ corner (round, elevated, safe-area aware, z-index 45): not a slot item, not
159
+ movable, never displaced by hidden regions. The header reserves end padding for
160
+ it, or, when the header is hidden, the right sidebar reserves top padding, or main
161
+ does when no right sidebar is shown
162
+ (`--smrt-shell-floating-reserve-inline|block|main`, set by `AppShell`).
163
+ A polite live region announces the mode. `useShellLayout()` has
164
+ `editable`, `editing` and `setEditing(boolean)` for hosts and assistants.
165
+
166
+ While `editing`:
167
+
168
+ - `AdminShell` `layoutEdit` (`ShellLayoutEditSurface`, set by `AppShell`) renders
169
+ every slot of a visible region as a dashed drop zone (entering edit mode
170
+ expands a collapsed left/right sidebar so its header/footer zones exist, and
171
+ leaving restores it; a railless `rail: false` edge is never revealed: it
172
+ stays as the user left it, and while editing a closed railless right edge
173
+ with focus/dock tools shows a slim "Open <tool>" indicator tab
174
+ (`data-edit-indicator`, z 21, no layout shift) that opens it and closes it
175
+ again on exit) (`data-smrt-edit-zone`)
176
+ labelled "Header · Left", empty ones included, highlighted
177
+ (`data-drop-target`) while a drag targets it. Movable items render exactly as
178
+ on the site (inside an `inert` wrapper) with a grip; the toggle itself is
179
+ fixed. One headless `createSortable` engine (confined to the shell root,
180
+ `allowSameContainerReorder: false`) drives pointer, native-drag and keyboard
181
+ moves (Space, Up/Down across zones in visual order, Enter drops, Escape
182
+ cancels) with the same announcements as `Sortable`
183
+ (`sortable/announce.ts`), committing `placeItem(id, slot)`. Items whose region
184
+ is hidden show in the zone they fall back to.
185
+ - Nothing in the header band renders outside the drop zones except each
186
+ region's edit control: the brand is an item in `header.start`, and the Menu
187
+ opener is hidden off phones. Every region, a collapsed sidebar rail included, gets an icon-only eye-off
188
+ "Hide <region>" button (`ShellLayoutEditSurface.regionControl`, end of the
189
+ header/footer band, top of a sidebar) that writes
190
+ `setPanel(edge, { visible: false })`; the strip's "Show" restores it. The
191
+ last visible region's button is disabled with an explanatory tooltip. Items
192
+ of a hidden region (the edit toggle included) follow the fallback chain.
193
+ Hiding the right sidebar (the dock) makes `useShellDock().available` false:
194
+ dock toggles render unregistered and `open` is refused until it is shown.
195
+ - Hidden, available regions render as thin dashed strips ("Right sidebar ·
196
+ hidden") with an eye "Show <region>" button (`setPanel(edge, { visible })`).
197
+ - `ShellNavEditor` replaces `TenantNav`: the real rows plus grips on every
198
+ heading and item (existing reorder/move-across-sections), icon-only
199
+ `ShellIconButton`s (eye `aria-pressed` = shown in navigation, heading
200
+ `aria-pressed` = title shown, host actions) on each section and an eye per
201
+ item, and a "New section" button at the end.
202
+ - Clicking a section heading opens its floating toolbar (rename input focused,
203
+ title, hide, delete for custom sections with a confirm when non-empty, host
204
+ actions). With `(hover: hover) and (pointer: fine)` hovering also opens it
205
+ (closes on leave unless a click pinned it); touch or no hover is click/tap
206
+ only. One toolbar at a time; Escape closes and returns focus to the heading;
207
+ a pinned toolbar closes on outside pointer or focus leaving.
208
+
209
+ Host extension point: `AppShell` `sectionActions?: Snippet<[ShellSectionActionsContext]>`
210
+ (`{ sectionId, label, custom, editing }`) renders icon buttons in both the
211
+ section overlay and its toolbar (e.g. the planner's Options gear and Help);
212
+ use `ShellIconButton` (from `./workspace`) for matching looks. It renders only
213
+ while editing.
214
+
215
+ `ShellLayoutEditor` no longer has a Placement section (superseded by dragging in
216
+ place); it keeps panels, the navigation sort (icon toggles `aria-pressed` for
217
+ "Show <name> in navigation" / "Show title of <name>"), the preview and Reset.
218
+ The layout helpers and `placeItem`/`resetItem` stay.
219
+
86
220
  ### Activity ticker
87
221
 
88
222
  `ActivityTicker` accepts app-owned `activities: ReadonlyArray<ShellActivity>` plus
@@ -219,11 +353,75 @@ control panel), wired by `app/AppShell.svelte`.
219
353
  `moveItem(id, sectionId, toIndex?)`, `hide(id)`, `show(id)`, `setPanel(edge,
220
354
  { visible?, initial? })`, `reset()`, each returning whether anything changed,
221
355
  plus readers (`layout`, `sections`, `panels`, `applied`, `customized`,
222
- `isHidden`). An assistant calls the same methods the editor does. The
356
+ `isHidden`; item placement is in "Moving items between slots", edit mode in
357
+ "Editing the layout in place"). An assistant calls the same methods the editor does. The
223
358
  context is separate from `useAdminShell()` because `AppShell`, not
224
359
  `AdminShell`, owns the navigation.
360
+ - **Sections belong to the app.** Hosts (and recipes, via the host) suggest
361
+ sections as `ShellNavGroup`s; the user overrides them in the same additive
362
+ version-1 layout: `sections[id] = { label?, showTitle? }` (rename; hide the
363
+ title) and `customSections: { id: 'custom:...', label }[]` (user-created).
364
+ Custom sections join `sectionOrder`, receive items through `moved`, show in
365
+ the editor when empty but not in the nav, and get a unique `custom:` id.
366
+ `showTitle: false` (also settable by a host on `ShellNavGroup.showTitle`)
367
+ renders the items flat in a `role="group"` named by the heading, no
368
+ `<details>`/`<summary>`. `applyShellLayout` keeps the section's original id
369
+ on renamed output groups. Helpers: `renameShellSection`,
370
+ `setShellSectionTitleVisible`, `createShellSection`, `deleteShellSection`
371
+ (custom only; items return to their default sections). Old stored layouts
372
+ need no migration; new host sections and items land in their suggested slot.
373
+ Controller: `renameSection`, `setSectionTitleVisible`, `createSection(label)`
374
+ (returns the id), `deleteSection`.
375
+ - **Item labels.** The same layout renames single navigation entries:
376
+ `items[itemId] = { label? }` (additive; trimmed, blank removed; ids as above,
377
+ top-level items of `nav` and groups). `resolveShellNavModel` items carry
378
+ `label` / `defaultLabel`; `applyShellLayout` emits the renamed item with
379
+ `label` = the user's and `defaultLabel` = the host's (so nav, breadcrumbs and
380
+ titles that read `nav` follow the rename). Helper `renameShellItem(nav, groups,
381
+ layout, itemId, label | null)`; controller `renameItem(itemId, label | null)`
382
+ (null or blank resets). Edit mode: each `ShellNavEditor` row has an icon-only
383
+ "Rename <label>" button opening an inline field (Enter saves, Escape cancels,
384
+ blur saves) and, once renamed, "Reset <label> to <original>"; the original shows
385
+ as the row tooltip. Presets ("cookbooks") ship it in the stored layout.
386
+ - **Sections-only navigation (`navMode: 'sections'`).** `AppShell` (and
387
+ `TenantNav` / `ShellNavEditor` directly) take `navMode?: 'items' | 'sections'`
388
+ (default `'items'`, unchanged). In `'sections'` the left sidebar shows one
389
+ link per section (icon + label), no entries; a collapsed rail (`tenantRail`)
390
+ shows the icons with tooltips. The link goes to `ShellNavGroup.href`, else
391
+ `AppShell` `sectionHref(sectionId)` (user-created sections), else the first
392
+ entry; it is `aria-current="page"` on that page and `"true"` on any of the
393
+ section's entry pages. `AppShell` also takes `iconComponent` for host icon
394
+ names. `ShellNavGroup.icon` / `.href` are additive; the user override is
395
+ `sections[id].icon` (trimmed; blank or the host's icon removes it), helper
396
+ `setShellSectionIcon(nav, groups, layout, id, icon | null)`, controller
397
+ `setSectionIcon(id, icon | null)`; `resolveShellNavModel` sections carry
398
+ `icon` / `defaultIcon` and `applyShellLayout` emits the effective `icon`.
399
+ Icons are shell icon names (`SHELL_SECTION_ICONS` is the picker set, drawn
400
+ from Material icons like the rest of `SHELL_ICON_PATHS`; `SHELL_DEFAULT_SECTION_ICON`
401
+ is `folder`) or host names rendered by `iconComponent` (`ShellSectionIcon`
402
+ tries the built-in set first). Edit mode in this mode: sections keep grip,
403
+ rename, hide, delete; the title toggle is replaced by an "Icon of <label>"
404
+ popover grid ("Use <icon> icon", `aria-pressed` on the current one; Escape
405
+ closes the grid before the toolbar). Entries are not in the sidebar.
406
+ - **`ShellSectionMenu`** (`sectionId`, `controller?`, `meta(entry)`,
407
+ `actions(entry)`, `iconComponent`, `aria-label`, `layout`): the section's page body.
408
+ `layout="cards"` renders an auto-fill grid (min 16rem) of cards: 32px icon in a
409
+ tinted circle, title, `ShellNavItem.description` (2 lines), then `meta` and
410
+ `actions` at the bottom; one stretched link, actions stay separate controls;
411
+ edit mode keeps grip/rename/hide in the grid. Unknown icon names draw the
412
+ default glyph (dev warning), never the name. `layout.items[id].description` (host/preset-set, not editable in the UI)
413
+ overrides an item's `description`. Default `list`: A
414
+ `<ul>` of rows (icon, label link, host `meta`, host `actions`, chevron; the
415
+ row is one stretched link, actions sit above it) in applied order with
416
+ renames; hidden entries are omitted. While editing, the same rows become the
417
+ nav editor's chrome (single-container `Sortable` grip with keyboard moves,
418
+ inline rename/reset, show/hide toggle) driving `moveItem`/`renameItem`/
419
+ `hide`/`show`; rows are not links then. `ShellSectionMenuEntry` is the snippet
420
+ argument (`id, href, label, defaultLabel, icon, hidden, item`).
225
421
  - **`ShellLayoutEditor`**: `controller` (default: context), `preview` (default
226
- true), `iconComponent`. Mount it on a settings page or in a dock tool.
422
+ true), `iconComponent`. Each section has an inline name input, icon toggles
423
+ for visibility and title, and (custom only) a trash button, which confirms
424
+ when non-empty; a New section button creates one. Mount it on a settings page or in a dock tool.
227
425
 
228
426
  ### Sortable and the Board engine
229
427
 
@@ -255,9 +453,8 @@ lives beside, not inside, `./workspace` so the AdminShell barrel stays free of
255
453
  (`{#snippet dock(registry)}`), the instance mounted routes register on
256
454
  when `webmcp` UI is on; pass it to `<AssistantDock {registry} />`. `runtimeDiagnostics` (default false) mounts the
257
455
  read-only `smrt.runtime.diagnostics.read` WebMCP tool. `dockToggles`
258
- (`{ tool, label, icon? }[]`) renders icon buttons at the right of the shell
259
- header (the AdminShell `header` row, shown only when the prop is non-empty),
260
- each toggling the `ShellDockTool` with that id: `aria-pressed` and
456
+ (`{ tool, label, icon?, slot? }[]`) renders icon buttons in the shell slot
457
+ named by `slot` (default `header.end`; see "Regions and slots"), each toggling the `ShellDockTool` with that id: `aria-pressed` and
261
458
  `aria-expanded` follow the dock, `aria-controls` is
262
459
  `smrt-admin-shell-right-panel`, the label is the tooltip, and `assistant`
263
460
  defaults to a chat-bubble icon (`icon: 'chat'`; other text is a glyph). A
@@ -1 +1 @@
1
- {"version":3,"file":"dictation-source.d.ts","sourceRoot":"","sources":["../../../../src/browser-ai/adapters/stt/dictation-source.ts"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5D,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,aAA0C,GAClD,MAAM,OAAO,CAAC,UAAU,CAAC,CAc3B"}
1
+ {"version":3,"file":"dictation-source.d.ts","sourceRoot":"","sources":["../../../../src/browser-ai/adapters/stt/dictation-source.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE5D,wBAAgB,wBAAwB,CACtC,OAAO,GAAE,aAA0C,GAClD,MAAM,OAAO,CAAC,UAAU,CAAC,CAmB3B"}
@@ -7,7 +7,8 @@
7
7
  * adapter lazily, once, the first time someone starts dictating: nothing
8
8
  * loads and no microphone prompt appears until then. Defaults to the
9
9
  * browser's own speech recognition (`browser-speech`), which needs no
10
- * download; an unsupported browser rejects with
10
+ * download; an on-device model is never downloaded by dictation (see
11
+ * `LocalSpeechSTTAdapter.prepare`). An unsupported browser rejects with
11
12
  * `CapabilityNotAvailableError`, which `Dictation` reports in plain words.
12
13
  *
13
14
  * @example
@@ -26,7 +27,12 @@ export function createSttDictationSource(options = { type: 'browser-speech' }) {
26
27
  return () => {
27
28
  adapter ??= (async () => {
28
29
  const created = await getSTT(options);
29
- await created.ensureInitialized();
30
+ // An adapter that needs a download is never loaded here: dictation must
31
+ // not start one. It checks its own model when started, and the host
32
+ // loads it explicitly (after asking).
33
+ if (!created.getCapabilities().requiresDownload) {
34
+ await created.ensureInitialized();
35
+ }
30
36
  return created;
31
37
  })().catch((error) => {
32
38
  // Let a later tap try again (for example after a permission change).
@@ -20,8 +20,11 @@ import type { GetSTTOptions, STTAdapter } from './types.js';
20
20
  * // Whisper.cpp (recommended for accuracy)
21
21
  * const stt = await getSTT({ type: 'whisper-cpp', model: 'tiny.en' });
22
22
  *
23
- * // Whisper WASM (transformers.js - has known issues)
24
- * const stt = await getSTT({ type: 'whisper-wasm', modelSize: 'tiny' });
23
+ * // Local Whisper (transformers.js; WebGPU or WASM, one-time ~45 MB download)
24
+ * const stt = await getSTT({ type: 'whisper-local' });
25
+ *
26
+ * // Moonshine (fastest for live dictation, one-time ~32 MB download)
27
+ * const stt = await getSTT({ type: 'moonshine' });
25
28
  * ```
26
29
  */
27
30
  export declare function getSTT(options?: GetSTTOptions): Promise<STTAdapter>;
@@ -45,5 +48,5 @@ export declare function getSTTAuto(options?: Partial<Omit<GetSTTOptions, 'type'>
45
48
  /**
46
49
  * Check if a specific STT backend is available
47
50
  */
48
- export declare function isSTTAvailable(type: 'browser-speech' | 'whisper-wasm'): boolean;
51
+ export declare function isSTTAvailable(type: 'browser-speech' | 'whisper-wasm' | 'whisper-local' | 'moonshine'): boolean;
49
52
  //# sourceMappingURL=factory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../src/browser-ai/adapters/stt/factory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAEV,aAAa,EACb,UAAU,EAGX,MAAM,YAAY,CAAC;AA6BpB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAqB7E;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,UAAU,CAC9B,OAAO,GAAE,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAM,GACjD,OAAO,CAAC,UAAU,CAAC,CAmBrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,gBAAgB,GAAG,cAAc,GACtC,OAAO,CAYT"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../../src/browser-ai/adapters/stt/factory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,OAAO,KAAK,EAEV,aAAa,EAEb,UAAU,EAEX,MAAM,YAAY,CAAC;AAkCpB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAuB7E;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,UAAU,CAC9B,OAAO,GAAE,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAM,GACjD,OAAO,CAAC,UAAU,CAAC,CAmBrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,gBAAgB,GAAG,cAAc,GAAG,eAAe,GAAG,WAAW,GACtE,OAAO,CAgBT"}
@@ -12,10 +12,13 @@ function isBrowserSpeechOptions(options) {
12
12
  return !options.type || options.type === 'browser-speech';
13
13
  }
14
14
  /**
15
- * Type guard for Whisper WASM options (transformers.js)
15
+ * Type guard for on-device speech options (transformers.js): Whisper
16
+ * (`whisper-wasm` is the older name for `whisper-local`) and Moonshine.
16
17
  */
17
- function isWhisperWasmOptions(options) {
18
- return options.type === 'whisper-wasm';
18
+ function isLocalSpeechOptions(options) {
19
+ return (options.type === 'whisper-local' ||
20
+ options.type === 'whisper-wasm' ||
21
+ options.type === 'moonshine');
19
22
  }
20
23
  /**
21
24
  * Type guard for Whisper.cpp options (@remotion/whisper-web)
@@ -39,8 +42,11 @@ function isWhisperCppOptions(options) {
39
42
  * // Whisper.cpp (recommended for accuracy)
40
43
  * const stt = await getSTT({ type: 'whisper-cpp', model: 'tiny.en' });
41
44
  *
42
- * // Whisper WASM (transformers.js - has known issues)
43
- * const stt = await getSTT({ type: 'whisper-wasm', modelSize: 'tiny' });
45
+ * // Local Whisper (transformers.js; WebGPU or WASM, one-time ~45 MB download)
46
+ * const stt = await getSTT({ type: 'whisper-local' });
47
+ *
48
+ * // Moonshine (fastest for live dictation, one-time ~32 MB download)
49
+ * const stt = await getSTT({ type: 'moonshine' });
44
50
  * ```
45
51
  */
46
52
  export async function getSTT(options = {}) {
@@ -52,14 +58,16 @@ export async function getSTT(options = {}) {
52
58
  const { WhisperCppSTTAdapter } = await import('./whisper-cpp.js');
53
59
  return new WhisperCppSTTAdapter(options);
54
60
  }
55
- if (isWhisperWasmOptions(options)) {
56
- const { WhisperWasmSTTAdapter } = await import('./whisper-wasm.js');
57
- return new WhisperWasmSTTAdapter(options);
61
+ if (isLocalSpeechOptions(options)) {
62
+ const { LocalSpeechSTTAdapter } = await import('./local-speech.js');
63
+ return new LocalSpeechSTTAdapter(options);
58
64
  }
59
65
  throw new UnsupportedAdapterError(String(options.type), [
60
66
  'browser-speech',
61
67
  'whisper-cpp',
68
+ 'whisper-local',
62
69
  'whisper-wasm',
70
+ 'moonshine',
63
71
  ]);
64
72
  }
65
73
  /**
@@ -103,7 +111,9 @@ export function isSTTAvailable(type) {
103
111
  if (type === 'browser-speech') {
104
112
  return caps.stt.browserSpeechAPI;
105
113
  }
106
- if (type === 'whisper-wasm') {
114
+ if (type === 'whisper-wasm' ||
115
+ type === 'whisper-local' ||
116
+ type === 'moonshine') {
107
117
  return caps.stt.whisperWasm;
108
118
  }
109
119
  return false;
@@ -4,7 +4,9 @@
4
4
  export { type BrowserSpeechError, BrowserSpeechSTTAdapter, } from './browser-speech.js';
5
5
  export { createSttDictationSource } from './dictation-source.js';
6
6
  export * from './factory.js';
7
+ export { LocalSpeechSTTAdapter, WhisperLocalSTTAdapter, WhisperWasmSTTAdapter, } from './local-speech.js';
8
+ export { createLocalSpeechModel, createWhisperLocalModel, isEnglishOnlyLocalSpeechModel, LOCAL_SPEECH_MODELS, type LocalSpeechDevice, type LocalSpeechLoadOptions, LocalSpeechModel, type LocalSpeechModelOptions, type LocalSpeechModelPreset, type LocalSpeechModelState, resolveLocalSpeechModelId, WHISPER_LOCAL_DEFAULT_MODEL, WHISPER_LOCAL_DEFAULT_SIZE_BYTES, type WhisperLocalDevice, type WhisperLocalLoadOptions, WhisperLocalModel, type WhisperLocalModelOptions, type WhisperLocalModelState, } from './local-speech-model.js';
9
+ export { configureTransformersEnv } from './transformers-env.js';
7
10
  export * from './types.js';
8
11
  export { WhisperCppSTTAdapter } from './whisper-cpp.js';
9
- export { WhisperWasmSTTAdapter } from './whisper-wasm.js';
10
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/browser-ai/adapters/stt/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,KAAK,kBAAkB,EACvB,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/browser-ai/adapters/stt/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,KAAK,kBAAkB,EACvB,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,cAAc,cAAc,CAAC;AAC7B,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,sBAAsB,EACtB,uBAAuB,EACvB,6BAA6B,EAC7B,mBAAmB,EACnB,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,yBAAyB,EACzB,2BAA2B,EAC3B,gCAAgC,EAChC,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,GAC5B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -4,6 +4,8 @@
4
4
  export { BrowserSpeechSTTAdapter, } from './browser-speech.js';
5
5
  export { createSttDictationSource } from './dictation-source.js';
6
6
  export * from './factory.js';
7
+ export { LocalSpeechSTTAdapter, WhisperLocalSTTAdapter, WhisperWasmSTTAdapter, } from './local-speech.js';
8
+ export { createLocalSpeechModel, createWhisperLocalModel, isEnglishOnlyLocalSpeechModel, LOCAL_SPEECH_MODELS, LocalSpeechModel, resolveLocalSpeechModelId, WHISPER_LOCAL_DEFAULT_MODEL, WHISPER_LOCAL_DEFAULT_SIZE_BYTES, WhisperLocalModel, } from './local-speech-model.js';
9
+ export { configureTransformersEnv } from './transformers-env.js';
7
10
  export * from './types.js';
8
11
  export { WhisperCppSTTAdapter } from './whisper-cpp.js';
9
- export { WhisperWasmSTTAdapter } from './whisper-wasm.js';