@happyvertical/smrt-svelte 0.51.39 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +3 -0
- package/README.md +214 -1
- package/agents/components.md +8 -1
- package/agents/workspace.md +24 -0
- package/dist/Provider.svelte +19 -15
- package/dist/Provider.svelte.d.ts +2 -2
- package/dist/Provider.svelte.d.ts.map +1 -1
- package/dist/__tests__/provider-mode-harness.svelte +14 -0
- package/dist/__tests__/provider-mode-harness.svelte.d.ts +11 -0
- package/dist/__tests__/provider-mode-harness.svelte.d.ts.map +1 -0
- package/dist/__tests__/provider-mode-probe.svelte +13 -0
- package/dist/__tests__/provider-mode-probe.svelte.d.ts +8 -0
- package/dist/__tests__/provider-mode-probe.svelte.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/browser-speech.d.ts +11 -0
- package/dist/browser-ai/adapters/stt/browser-speech.d.ts.map +1 -1
- package/dist/browser-ai/adapters/stt/browser-speech.js +20 -4
- package/dist/browser-ai/adapters/stt/browser-speech.test.js +113 -0
- package/dist/browser-ai/adapters/stt/dictation-source.d.ts +3 -0
- package/dist/browser-ai/adapters/stt/dictation-source.d.ts.map +1 -0
- package/dist/browser-ai/adapters/stt/dictation-source.js +38 -0
- package/dist/browser-ai/adapters/stt/dictation-source.test.js +52 -0
- package/dist/browser-ai/adapters/stt/index.d.ts +2 -1
- package/dist/browser-ai/adapters/stt/index.d.ts.map +1 -1
- package/dist/browser-ai/adapters/stt/index.js +2 -1
- package/dist/components/forms/FileUpload.svelte +120 -16
- package/dist/components/forms/FileUpload.svelte.d.ts +7 -1
- package/dist/components/forms/FileUpload.svelte.d.ts.map +1 -1
- package/dist/components/forms/Form.svelte +118 -8
- package/dist/components/forms/Form.svelte.d.ts +18 -2
- package/dist/components/forms/Form.svelte.d.ts.map +1 -1
- package/dist/components/forms/FormScope.svelte +202 -0
- package/dist/components/forms/FormScope.svelte.d.ts +22 -0
- package/dist/components/forms/FormScope.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/FileUpload.native-form.test.js +249 -0
- package/dist/components/forms/__tests__/FileUploadReset.fixture.svelte +24 -0
- package/dist/components/forms/__tests__/FileUploadReset.fixture.svelte.d.ts +10 -0
- package/dist/components/forms/__tests__/FileUploadReset.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/Form.ui-controls.test.js +119 -0
- package/dist/components/forms/__tests__/Form.webmcp.test.js +5 -3
- package/dist/components/forms/__tests__/FormScope.test.js +39 -0
- package/dist/components/forms/__tests__/form-scope.fixture.svelte +30 -0
- package/dist/components/forms/__tests__/form-scope.fixture.svelte.d.ts +10 -0
- package/dist/components/forms/__tests__/form-scope.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/form-with-ui-controls.fixture.svelte +84 -0
- package/dist/components/forms/__tests__/form-with-ui-controls.fixture.svelte.d.ts +14 -0
- package/dist/components/forms/__tests__/form-with-ui-controls.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/index.d.ts +1 -0
- package/dist/components/forms/index.d.ts.map +1 -1
- package/dist/components/forms/index.js +1 -0
- package/dist/components/workspace/README.md +11 -0
- package/dist/components/workspace/__tests__/AdminShell.responsive.test.js +597 -0
- package/dist/components/workspace/__tests__/AdminShell.test.js +257 -1
- package/dist/components/workspace/__tests__/TenantNav.test.js +115 -0
- package/dist/components/workspace/__tests__/WorkspaceAccountMenu.test.js +6 -0
- package/dist/components/workspace/__tests__/admin-shell-header-overflow.test.js +25 -0
- package/dist/components/workspace/__tests__/admin-shell-mobile.test.js +217 -0
- package/dist/components/workspace/__tests__/admin-shell-page-contracts.test.js +30 -0
- package/dist/components/workspace/__tests__/admin-shell-responsive-state.test.js +184 -0
- package/dist/components/workspace/__tests__/admin-shell-state.test.js +25 -0
- package/dist/components/workspace/__tests__/admin-shell-theme-hooks.test.js +36 -0
- package/dist/components/workspace/__tests__/shell-chrome-components.test.js +108 -0
- package/dist/components/workspace/admin-shell/AdminShell.svelte +1099 -59
- package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts +44 -1
- package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/PhoneBottomBar.svelte +170 -0
- package/dist/components/workspace/admin-shell/PhoneBottomBar.svelte.d.ts +11 -0
- package/dist/components/workspace/admin-shell/PhoneBottomBar.svelte.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/PhoneTopBar.svelte +93 -0
- package/dist/components/workspace/admin-shell/PhoneTopBar.svelte.d.ts +11 -0
- package/dist/components/workspace/admin-shell/PhoneTopBar.svelte.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/ShellNavToggle.svelte +88 -0
- package/dist/components/workspace/admin-shell/ShellNavToggle.svelte.d.ts +14 -0
- package/dist/components/workspace/admin-shell/ShellNavToggle.svelte.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/ShellTitle.svelte +42 -0
- package/dist/components/workspace/admin-shell/ShellTitle.svelte.d.ts +11 -0
- package/dist/components/workspace/admin-shell/ShellTitle.svelte.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/TenantNav.svelte +116 -13
- package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts +8 -2
- package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte +16 -2
- package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.d.ts +2 -0
- package/dist/components/workspace/admin-shell/WorkspaceAccountMenu.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/mobile-shell-dom.d.ts +37 -0
- package/dist/components/workspace/admin-shell/mobile-shell-dom.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/mobile-shell-dom.js +150 -0
- package/dist/components/workspace/admin-shell/mobile-shell.d.ts +191 -0
- package/dist/components/workspace/admin-shell/mobile-shell.d.ts.map +1 -0
- package/dist/components/workspace/admin-shell/mobile-shell.js +165 -0
- package/dist/components/workspace/admin-shell/settings.d.ts +27 -2
- package/dist/components/workspace/admin-shell/settings.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/settings.js +84 -2
- package/dist/components/workspace/admin-shell/state.svelte.d.ts +57 -1
- package/dist/components/workspace/admin-shell/state.svelte.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/state.svelte.js +156 -6
- package/dist/components/workspace/admin-shell/types.d.ts +115 -1
- package/dist/components/workspace/admin-shell/types.d.ts.map +1 -1
- package/dist/components/workspace/admin-shell/types.js +13 -0
- package/dist/components/workspace/index.d.ts +11 -4
- package/dist/components/workspace/index.d.ts.map +1 -1
- package/dist/components/workspace/index.js +12 -3
- package/dist/i18n/strings.forms.d.ts +3 -0
- package/dist/i18n/strings.forms.d.ts.map +1 -1
- package/dist/i18n/strings.forms.js +3 -0
- package/dist/i18n/strings.workspace.d.ts +14 -0
- package/dist/i18n/strings.workspace.d.ts.map +1 -1
- package/dist/i18n/strings.workspace.js +14 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/provider-mode.test.js +86 -0
- package/dist/state/app-state.svelte.d.ts.map +1 -1
- package/dist/state/app-state.svelte.js +8 -8
- package/dist/web/__tests__/assistant-dock.integration.svelte.test.js +2 -2
- package/dist/web/__tests__/list-sort-surface.fixture.svelte +41 -0
- package/dist/web/__tests__/list-sort-surface.fixture.svelte.d.ts +15 -0
- package/dist/web/__tests__/list-sort-surface.fixture.svelte.d.ts.map +1 -0
- package/dist/web/__tests__/list-sort-surface.svelte.test.js +59 -0
- package/dist/web/__tests__/surfaces.child.fixture.svelte +69 -0
- package/dist/web/__tests__/surfaces.child.fixture.svelte.d.ts +16 -0
- package/dist/web/__tests__/surfaces.child.fixture.svelte.d.ts.map +1 -0
- package/dist/web/__tests__/surfaces.fixture.svelte +26 -0
- package/dist/web/__tests__/surfaces.fixture.svelte.d.ts +18 -0
- package/dist/web/__tests__/surfaces.fixture.svelte.d.ts.map +1 -0
- package/dist/web/__tests__/surfaces.svelte.test.js +140 -0
- package/dist/web/__tests__/webmcp-ui.test.js +18 -0
- package/dist/web/index.d.ts +4 -1
- package/dist/web/index.d.ts.map +1 -1
- package/dist/web/index.js +4 -1
- package/dist/web/link-surface.svelte.d.ts +14 -0
- package/dist/web/link-surface.svelte.d.ts.map +1 -0
- package/dist/web/link-surface.svelte.js +49 -0
- package/dist/web/list-surface.svelte.d.ts +80 -0
- package/dist/web/list-surface.svelte.d.ts.map +1 -0
- package/dist/web/list-surface.svelte.js +309 -0
- package/dist/web/step-surface.svelte.d.ts +17 -0
- package/dist/web/step-surface.svelte.d.ts.map +1 -0
- package/dist/web/step-surface.svelte.js +56 -0
- package/dist/web/webmcp-ui-context.d.ts +7 -0
- package/dist/web/webmcp-ui-context.d.ts.map +1 -1
- package/dist/web/webmcp-ui-context.js +10 -0
- package/dist/web/webmcp-ui.d.ts.map +1 -1
- package/dist/web/webmcp-ui.js +16 -3
- package/package.json +17 -17
package/AGENTS.md
CHANGED
|
@@ -20,6 +20,9 @@ subpath you are editing. This file keeps what holds in every module.
|
|
|
20
20
|
| `src/web/webmcp-provider.ts` | Provider config for generated data/model WebMCP tools: definitions, effect policy, namespace, budget, legacy/canonical filters, and fetcher seams (#2520) | — |
|
|
21
21
|
| `src/web/webmcp-ui.ts` | Fixed, low-cardinality WebMCP adapter over the Provider's mounted form-control and data-surface registries (#2521) | — |
|
|
22
22
|
| `src/web/webmcp.svelte.ts` (`useWebMcpTool`) | Component-owned bespoke WebMCP tool. Routes through `@happyvertical/smrt-web`'s `registerWebMcpBespokeTool`, so it shares the fail-closed effect classification and `effects` exposure policy of generated tools — undeclared `annotations` classify destructive and are excluded by default. `namespace` and `maxTools` never apply to a bespoke tool (#2586) | — |
|
|
23
|
+
| `src/web/{list,link,step}-surface.svelte.ts` (`useListSurface`, `useLinkSurface`, `useStepSurface`) | Lifecycle hooks mounting a rendered list (projected `state.rows` + a `find` text lookup), a menu/tab row (`open` → `navigate`, hrefs never published), and a wizard (`next`/`back`/`go-to`; a writing step is revealed via `showNext`, never pressed) on the Provider's data-surface registry. The Svelte-free halves live in `@happyvertical/smrt-ui/data` | — |
|
|
24
|
+
| `src/web/webmcp-ui-context.ts` (`useWebMcpUi`, `tryUseWebMcpUi`) | Provider mounted-UI registries; `tryUseWebMcpUi` returns `null` instead of throwing when there is no Provider or its WebMCP UI is off (#2915) | — |
|
|
25
|
+
| `src/components/forms/Form.svelte` + `FormScope.svelte` | Rich Form spreads native attributes/attachments, takes `enhance`, and its `*_stage_changes` tool covers smrt-ui controls via `controlProposalProperties`/`stageControlProposals` (smrt-ui); `FormScope` is the same without a `<form>` element | — |
|
|
23
26
|
|
|
24
27
|
The composed WebMCP fixture in
|
|
25
28
|
`src/web/__tests__/webmcp-composed.integration.svelte.test.ts` mounts a real
|
package/README.md
CHANGED
|
@@ -182,6 +182,25 @@ default) — except a canonical table-control id (`set-filters`, `reset`,
|
|
|
182
182
|
reaches `onControl`, even under a custom label. See
|
|
183
183
|
`docs/data-surface-conformance.md` for the full contract.
|
|
184
184
|
|
|
185
|
+
Three lifecycle hooks cover the common page shapes without any registration
|
|
186
|
+
code; each resolves the nearest Provider's registry (or an explicit one),
|
|
187
|
+
re-mounts on an identity change, and unmounts with the component:
|
|
188
|
+
|
|
189
|
+
- `useListSurface(() => ({ surfaceId, label, description, columns, rows }))`
|
|
190
|
+
publishes the page's rendered rows, projected to the declared columns and
|
|
191
|
+
capped (`maxRows`, default 50), as `state.rows`. Columns marked
|
|
192
|
+
`searchable` enable a `find` control: `{ text }` publishes the rows whose
|
|
193
|
+
searchable columns contain the text (any case, accents ignored) across all
|
|
194
|
+
rows, with `state.find = { text, matches }` — so an agent can look an
|
|
195
|
+
article up by title on the page. It narrows only what the agent reads;
|
|
196
|
+
table commands stay refused and the page's own controls stay in charge.
|
|
197
|
+
- `useLinkSurface(() => ({ surfaceId, label, description, links, navigate }))`
|
|
198
|
+
exposes a menu or tab row as an `open` control (see `registerLinkSurface`
|
|
199
|
+
in smrt-ui).
|
|
200
|
+
- `useStepSurface(() => ({ surfaceId, …, steps, current, next, back,
|
|
201
|
+
nextWrites, showNext }))` exposes a wizard's `next`/`back`/`go-to`; a step
|
|
202
|
+
whose forward button writes is revealed for the person, never pressed.
|
|
203
|
+
|
|
185
204
|
The default prefix is `smrt_ui_`. Configure `ui.prefix` when multiple Providers
|
|
186
205
|
must coexist in one document; the same prefix cannot be registered twice. The
|
|
187
206
|
six derived names are reserved through the document-global tool-name lock, so
|
|
@@ -201,6 +220,47 @@ hidden data-surface columns are not serialized. Read responses are marked as
|
|
|
201
220
|
untrusted content. Bespoke `useWebMcpTool` and `<Form webmcp>` tools retain their
|
|
202
221
|
existing lifecycle and submit behavior.
|
|
203
222
|
|
|
223
|
+
#### `<Form webmcp>` with SvelteKit `enhance` and smrt-ui fields
|
|
224
|
+
|
|
225
|
+
The rich `Form` renders its own `<form>`, so it forwards every native form
|
|
226
|
+
attribute (`method`, `action`, `enctype`, `novalidate`, `autocomplete`,
|
|
227
|
+
`aria-*`, `data-*`, …) and attachments (`{@attach …}`) to that element. A
|
|
228
|
+
component cannot take `use:`, so pass SvelteKit's `enhance` as a prop — it is
|
|
229
|
+
applied to the rendered `<form>` and torn down with it. Without `onsubmit`
|
|
230
|
+
the Form never prevents submission, so `enhance` (or a native POST) runs as
|
|
231
|
+
usual and an error summary above the fields keeps working:
|
|
232
|
+
|
|
233
|
+
```svelte
|
|
234
|
+
<script lang="ts">
|
|
235
|
+
import { enhance } from '$app/forms';
|
|
236
|
+
import { Form } from '@happyvertical/smrt-svelte/forms';
|
|
237
|
+
import { ErrorSummary, FormGroup, Input } from '@happyvertical/smrt-ui/forms';
|
|
238
|
+
</script>
|
|
239
|
+
|
|
240
|
+
<Form formId="setup-network" webmcp method="POST" action="?/create" novalidate
|
|
241
|
+
enhance={(form) => enhance(form, submit)}>
|
|
242
|
+
<ErrorSummary errors={errors} />
|
|
243
|
+
<FormGroup label="Network name" id="name"><Input name="name" bind:value={name} /></FormGroup>
|
|
244
|
+
</Form>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Its `<formId>_stage_changes` tool describes rich fields (`TextInput`,
|
|
248
|
+
`MoneyInput`, …) and every other control the registry holds for the form:
|
|
249
|
+
smrt-ui primitives (`FormGroup` + `Input`/`Select`/`Textarea`/`Combobox`/…)
|
|
250
|
+
and composites registered with `useControlRegistration`. Proposals for either
|
|
251
|
+
kind are staged for the person to review and apply; nothing is written or
|
|
252
|
+
submitted by the tool.
|
|
253
|
+
|
|
254
|
+
`FormScope` (`@happyvertical/smrt-svelte/forms`) is the same tool and review
|
|
255
|
+
surface without a `<form>` element — for a fetch-driven wizard, an editor
|
|
256
|
+
saved in parts, or a page that must keep its own `<form>` markup. It joins
|
|
257
|
+
the nearest Provider's control registry (or a local one) and registers no
|
|
258
|
+
tool until at least one proposable control is mounted.
|
|
259
|
+
|
|
260
|
+
`tryUseWebMcpUi()` returns the Provider's mounted-UI registries, or `null`
|
|
261
|
+
when there is no Provider or its WebMCP UI is off — use it to fall back to a
|
|
262
|
+
local registry instead of catching `useWebMcpUi()`'s throw (#2915).
|
|
263
|
+
|
|
204
264
|
Custom rich fields may continue to call `registerField(field)` and later
|
|
205
265
|
`unregisterField(name)`. New code should retain and invoke the disposer returned
|
|
206
266
|
by `registerField`: it is bound to that exact registration, so cleanup cannot
|
|
@@ -319,6 +379,112 @@ fetch) and mount `AdminShell` in `+layout.svelte`. The `template-sveltekit`
|
|
|
319
379
|
scaffold adopts AdminShell as its default chrome exactly this way; copy its
|
|
320
380
|
`src/routes/+layout.server.ts` / `+layout.svelte` / `settings/+page.svelte`.
|
|
321
381
|
|
|
382
|
+
#### Responsive chrome and resizable edges
|
|
383
|
+
|
|
384
|
+
AdminShell carries the phone/tablet/desktop chrome itself, so hosts do not
|
|
385
|
+
wrap it in a frame. Everything below is opt-in; without these props and config
|
|
386
|
+
keys the shell renders exactly as before.
|
|
387
|
+
|
|
388
|
+
```svelte
|
|
389
|
+
<script lang="ts">
|
|
390
|
+
import {
|
|
391
|
+
AdminShell,
|
|
392
|
+
createShellState,
|
|
393
|
+
PhoneBottomBar,
|
|
394
|
+
PhoneTopBar,
|
|
395
|
+
ShellNavToggle,
|
|
396
|
+
ShellTitle,
|
|
397
|
+
phoneTopBarFor,
|
|
398
|
+
} from '@happyvertical/smrt-svelte/workspace';
|
|
399
|
+
|
|
400
|
+
const shell = createShellState({
|
|
401
|
+
config: {
|
|
402
|
+
top: false,
|
|
403
|
+
bottom: false,
|
|
404
|
+
left: {
|
|
405
|
+
label: 'Navigation',
|
|
406
|
+
// Drawer on phones, icons on tablets, open on desktop.
|
|
407
|
+
viewportDefaults: { phone: 'collapsed', tablet: 'collapsed', desktop: 'expanded' },
|
|
408
|
+
},
|
|
409
|
+
right: {
|
|
410
|
+
label: 'Assistant',
|
|
411
|
+
expandedSize: '28rem',
|
|
412
|
+
resizable: { min: 320, max: 720 }, // drag, arrows, Home/End, double-click resets
|
|
413
|
+
phone: 'sheet', // or 'hidden' when the host shows it elsewhere on phones
|
|
414
|
+
keepMounted: true, // closing the sheet keeps the chat (and its draft) alive
|
|
415
|
+
overlayMedia: '(max-width: 99.9375rem)', // slide over the page below 1600px
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
storageKey: 'my-app-shell',
|
|
419
|
+
});
|
|
420
|
+
</script>
|
|
421
|
+
|
|
422
|
+
<AdminShell state={shell} path={page.url.pathname} phone={{ scrim: true, swipeToClose: true }}>
|
|
423
|
+
{#snippet header()}
|
|
424
|
+
<ShellNavToggle expanded={shell.panels.left === 'expanded'} onclick={() => shell.togglePanel('left')} />
|
|
425
|
+
<ShellTitle title="Acme" href="/" />
|
|
426
|
+
{/snippet}
|
|
427
|
+
{#snippet phoneTopBar()}
|
|
428
|
+
<PhoneTopBar model={phoneTopBarFor({ path, homeHref: '/', homeTitle: 'Acme', navItems })} homeHref="/" />
|
|
429
|
+
{/snippet}
|
|
430
|
+
{#snippet phoneBottomBar()}
|
|
431
|
+
<PhoneBottomBar {items} />
|
|
432
|
+
{/snippet}
|
|
433
|
+
{#snippet overlays({ viewport })}<!-- PhoneSheet, WorkingStrip, … -->{/snippet}
|
|
434
|
+
…
|
|
435
|
+
</AdminShell>
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
- **`header`** is a full-width row above every edge (`#smrt-admin-shell-header`,
|
|
439
|
+
`--smrt-admin-shell-header-size`, default 3.5rem). On phones it is replaced
|
|
440
|
+
by `phoneTopBar` when one is given.
|
|
441
|
+
- **Viewport classes**: `shell.viewport` is `phone` (≤ 48rem), `tablet`
|
|
442
|
+
(≤ 64rem) or `desktop`; `ADMIN_SHELL_PHONE_QUERY` / `ADMIN_SHELL_DESKTOP_QUERY`.
|
|
443
|
+
`viewportDefaults` apply on first render and whenever the class changes; such
|
|
444
|
+
an edge never persists its open/closed state.
|
|
445
|
+
- **Phone chrome**: `phoneTopBar` overlays the top of the main region and hides
|
|
446
|
+
on scroll (`phone.hideOnScroll`, pinned by `pinChrome` or an open drawer);
|
|
447
|
+
`phoneBottomBar` is a bottom row that a form's `[data-form-action-bar]` (see
|
|
448
|
+
smrt-ui `FormActionBar`) replaces and the on-screen keyboard hides
|
|
449
|
+
(`:root[data-keyboard-open]`). `phone.scrim` dims the page behind a drawer or
|
|
450
|
+
sheet, `phone.swipeToClose` closes drawers by swiping. A `path` change on a
|
|
451
|
+
phone closes open drawers. `overlays` is a layer above the edges (and above
|
|
452
|
+
the bottom bar's row) for `PhoneSheet`s and status strips.
|
|
453
|
+
- **Page contracts** on phones: `data-shell-breadcrumbs` is hidden,
|
|
454
|
+
`data-shell-page-title` is visually hidden while the phone top bar shows
|
|
455
|
+
(the bar carries the title), `data-shell-tabs` sticks under the top bar and
|
|
456
|
+
slides away with it. smrt-ui's `PageHeader` renders the first two.
|
|
457
|
+
- **Page trail**: `shellPageTrailFor({ path, homeHref, homeTitle, navItems,
|
|
458
|
+
parents })` is the one source for a page's ancestors: section homes (home
|
|
459
|
+
and top-level nav pages) have none; other pages get the home, the nav items
|
|
460
|
+
above them, then page-given `parents`, never the page itself. Pass the
|
|
461
|
+
`crumbs` to `PageHeader`; `phoneTopBarFor` (same input plus `pageTitle`)
|
|
462
|
+
sends the phone back arrow to the last crumb.
|
|
463
|
+
- **Resizable edges**: `resizable` on a `push` side edge adds a
|
|
464
|
+
`role="separator"` (with `aria-valuenow/min/max` in px) on its inner border.
|
|
465
|
+
Sizes are stored as `ShellSettingsDelta.sizes` through the settings adapter;
|
|
466
|
+
`persist: false` / `{ state?: false, size?: false }` opts an edge out of
|
|
467
|
+
storage.
|
|
468
|
+
- **Overlay edges**: `overlayMedia` on a side edge is a media query under
|
|
469
|
+
which the expanded edge slides over the page (above a scrim, content keeps
|
|
470
|
+
its width) instead of pushing it, on tablet and desktop; above it the edge
|
|
471
|
+
docks. `shell.presentationFor(edge)` reports the live presentation. While
|
|
472
|
+
an overlay is open the page and the other edge are `inert`, the panel takes
|
|
473
|
+
focus (unless something inside already has it) and returns it on close;
|
|
474
|
+
Escape or a scrim click closes it, sliding it back out as the scrim fades
|
|
475
|
+
(no motion under `prefers-reduced-motion`). Resizing applies only while docked.
|
|
476
|
+
- **Kept panels**: `keepMounted: true` keeps a collapsed edge's panel content
|
|
477
|
+
(`appPanel`, `tenantPanel`, the focus panel, `systemPanel`) mounted with the
|
|
478
|
+
`hidden` attribute instead of unmounting it, so component state (a chat
|
|
479
|
+
draft, scroll position) survives closing. The rail renders as usual; the
|
|
480
|
+
left edge keeps only a `tenantPanel`.
|
|
481
|
+
- **Nav attention**: a `ShellNavItem` with `attention: true` (or a string
|
|
482
|
+
label) shows a dot in `TenantNav`, over the icon when collapsed, and
|
|
483
|
+
announces the label ("Needs attention" by default) inside the link.
|
|
484
|
+
- **Public region ids** (`ADMIN_SHELL_REGION_IDS`): `smrt-admin-shell-header`,
|
|
485
|
+
`smrt-admin-shell-{top,left,right,bottom}-panel`, and
|
|
486
|
+
`smrt-admin-shell-main`, which is the page scroller.
|
|
487
|
+
|
|
322
488
|
- **Migration guide** (first-generation `WorkspaceShell`/`RoleShell` →
|
|
323
489
|
`AdminShell`; adoption is additive and non-breaking):
|
|
324
490
|
[`src/components/workspace/MIGRATION.md`](./src/components/workspace/MIGRATION.md)
|
|
@@ -357,7 +523,7 @@ resolve against `smrt-ui`:
|
|
|
357
523
|
| `@happyvertical/smrt-ui` | `PermissionCheck`, `permission` / `hasPermission` / `hasAnyPermission` / `hasAllPermissions` |
|
|
358
524
|
| `@happyvertical/smrt-ui/ui` | UI primitives (Button, Card, Badge, Pagination) |
|
|
359
525
|
| `@happyvertical/smrt-ui/layout` | Layout (Container, Grid, Header, Footer, Masthead, etc.) |
|
|
360
|
-
| `@happyvertical/smrt-ui/calendar` | Calendar
|
|
526
|
+
| `@happyvertical/smrt-ui/calendar` | CalendarView (deprecated: Calendar, DayView) |
|
|
361
527
|
| `@happyvertical/smrt-ui/data` | DataTable, CollectionList/ContentList, CollectionToolbar |
|
|
362
528
|
| `@happyvertical/smrt-ui/feedback` | Modal, ConfirmDialog, LoadingOverlay, ProgressBar |
|
|
363
529
|
| `@happyvertical/smrt-ui/chat` | Message bubble, reaction picker, typing indicator |
|
|
@@ -447,3 +613,50 @@ public registry interactions. It does not require `document.modelContext` or
|
|
|
447
613
|
export mounted closures to remote MCP. Agent proposals retain trusted human
|
|
448
614
|
staged review. See the [bridge contract](../mcp-apps/README.md) for origin
|
|
449
615
|
configuration, capabilities, fallback and synthetic-browser evidence limits.
|
|
616
|
+
|
|
617
|
+
AdminShell's optional `homeHref` turns default branding into a named home link.
|
|
618
|
+
Use `logoSrc`/`logoAlt` for a logo or a `brand` snippet receiving `{ compact }`
|
|
619
|
+
for custom marks. A collapsed tenant rail keeps a compact linked logo or initial;
|
|
620
|
+
the default app bar keeps the full brand visible at narrow widths. A custom
|
|
621
|
+
`appBar` continues to own its branding. Without these props, branding stays text.
|
|
622
|
+
|
|
623
|
+
### Voice mode migration (#3261)
|
|
624
|
+
|
|
625
|
+
**Changed default:** Provider and standalone `createAppState()` now keep voice
|
|
626
|
+
mode off unless explicitly requested. Omitted `autoEnableSmrt` defaults to
|
|
627
|
+
`false`; speech-capable browsers stay in `default` mode. Apps that relied on
|
|
628
|
+
automatic voice activation must opt in with `autoEnableSmrt={true}`. Use
|
|
629
|
+
`mode="smrt"` for an explicit voice-mode choice. An explicit `mode="default"`
|
|
630
|
+
wins over auto-detection from the start of initialization, including when
|
|
631
|
+
`autoEnableSmrt` is true; no transient voice-mode activation occurs.
|
|
632
|
+
Standalone state callers can opt in through initial session preferences
|
|
633
|
+
(`autoEnableSmrt: true`); `initialMode` takes precedence. Runtime mode prop
|
|
634
|
+
updates retain their existing behavior.
|
|
635
|
+
|
|
636
|
+
### Runtime panel controls (#3246)
|
|
637
|
+
|
|
638
|
+
Use `shell.setPanelState(edge, 'hidden' | 'collapsed' | 'expanded')` to persist
|
|
639
|
+
runtime panel preferences; app-configured hidden edges remain unavailable.
|
|
640
|
+
AdminShell keeps a discoverable tenant collapse control with supplied navigation
|
|
641
|
+
(`showTenantToggle={false}` opts out), a Menu opener in narrow layouts, and the
|
|
642
|
+
system toggle alongside a custom `systemBar`. Closed narrow drawers are inert;
|
|
643
|
+
opening focuses the first control, and closing or Escape restores the opener.
|
|
644
|
+
TenantNav accepts `density="touch"` for canonical 48px link targets, including
|
|
645
|
+
collapsed rail links; omit density to inherit the theme's control density.
|
|
646
|
+
|
|
647
|
+
`AdminShell` keeps a supplied `tenantFooter` in the app bar while the left pane
|
|
648
|
+
is collapsed, hidden, or a closed narrow drawer; it moves back to the expanded
|
|
649
|
+
pane without duplicate account controls. Use the optional `account` snippet for
|
|
650
|
+
an account permanently in the app bar (including with a custom `appBar`).
|
|
651
|
+
`WorkspaceAccountMenu density="touch"` sizes its trigger and menu actions to the
|
|
652
|
+
canonical 48px target; omit density to inherit the theme, or choose
|
|
653
|
+
`"comfortable"`. Account menus in the shell app bar open below the bar so the
|
|
654
|
+
existing footer's default placement stays reachable on narrow screens.
|
|
655
|
+
|
|
656
|
+
`TenantNav` accepts optional `groups: ShellNavGroup[]` (`{ heading, items }`)
|
|
657
|
+
next to its existing flat `items`. Each group has a labelled `role="group"` and
|
|
658
|
+
native disclosure summary; keyboard and touch users can collapse it even in the
|
|
659
|
+
icon rail. Groups start open, preserve nested links and active-route markers,
|
|
660
|
+
and inherit the navigation density. Supply `aria-label="Shop navigation"` for
|
|
661
|
+
an instance-specific landmark name; omission uses the localized default.
|
|
662
|
+
`ShellNavGroup` is exported from `@happyvertical/smrt-svelte/workspace`.
|
package/agents/components.md
CHANGED
|
@@ -34,7 +34,14 @@ props, a golden test, and a playground page (`playground/.../primitives`).
|
|
|
34
34
|
migrate domain re-rolls *onto* them rather than build new primitives:
|
|
35
35
|
|
|
36
36
|
- **`FileUpload`** (`./forms`) — the canonical upload input; replace ad-hoc
|
|
37
|
-
drop zones.
|
|
37
|
+
drop zones. With `name` it posts in a native `multipart/form-data` submit:
|
|
38
|
+
the accepted list (picks, drops, removals, bound resets) is mirrored onto the
|
|
39
|
+
visually-hidden input via `DataTransfer` — never `hidden`, which would break
|
|
40
|
+
`required` validation. Without that constructor the input keeps only a
|
|
41
|
+
native selection that matches the list, else clears (#3260). A reset of the
|
|
42
|
+
owning form (`form.reset()`, a reset button, `enhance`'s `update()`) empties
|
|
43
|
+
the list and reports `onchange([])`, so the list never shows files that no
|
|
44
|
+
longer post.
|
|
38
45
|
- **`Modal` + forms** (`./feedback` + `./forms`) — compose for dialogs; no
|
|
39
46
|
bespoke modal shells.
|
|
40
47
|
- **`ConfirmDialog`** (`./feedback`) — the standard confirm/destructive-action
|
package/agents/workspace.md
CHANGED
|
@@ -45,6 +45,30 @@ App settings, tenant names and environment badges wrap within narrow drawers.
|
|
|
45
45
|
system bars never clip wrapped chips. Custom bars must allow this flex child
|
|
46
46
|
to shrink; use safe alignment if aligning its contents to the end.
|
|
47
47
|
|
|
48
|
+
### Responsive chrome, viewport defaults, resizable edges
|
|
49
|
+
|
|
50
|
+
The root grid has five rows: `header` · top edge · body · bottom edge · phone
|
|
51
|
+
bottom bar. The header and phone-bar tracks are `0rem` unless their snippets
|
|
52
|
+
render, so hosts that pass none of the new props get the original layout; keep
|
|
53
|
+
that invariant (`AdminShell.responsive.test.ts` "backward compatibility").
|
|
54
|
+
Phone drawers and the bottom drawer offset themselves by the header and phone
|
|
55
|
+
bar tracks. `shell.viewport` comes from `ADMIN_SHELL_PHONE_QUERY` (48rem, the
|
|
56
|
+
same boundary as the CSS media query — never make one configurable without the
|
|
57
|
+
other) and `ADMIN_SHELL_DESKTOP_QUERY` (64rem); `installShellViewport` runs on
|
|
58
|
+
mount and the `ShellState` constructor detects the initial class so the first
|
|
59
|
+
paint is right.
|
|
60
|
+
|
|
61
|
+
Edge config: `viewportDefaults` (applied per class change; such an edge never
|
|
62
|
+
stores its state), `phone: 'drawer' | 'sheet' | 'hidden'` (`sheet` is right
|
|
63
|
+
edge only), `resizable` (side edges, `push` only, not on phones) and `persist`.
|
|
64
|
+
`stripUnpersistedSettings` runs on both `hydrate()` and adapter writes;
|
|
65
|
+
runtime `applySettings` is never filtered. Resized widths live in
|
|
66
|
+
`ShellSettingsDelta.sizes` (px; `null` = configured size, pruned on write).
|
|
67
|
+
Drags update with `persist: false` and persist once on pointer up.
|
|
68
|
+
|
|
69
|
+
The resize separator is a focusable `role="separator"`: its svelte-ignore of
|
|
70
|
+
the non-interactive a11y warnings is deliberate (WAI-ARIA window splitter).
|
|
71
|
+
|
|
48
72
|
Run `pnpm --filter @happyvertical/smrt-svelte test:e2e` after building the
|
|
49
73
|
package dependencies. The package-local fixture exercises real Chromium bounds
|
|
50
74
|
and touch input across mobile widths and desktop panel-state combinations.
|
package/dist/Provider.svelte
CHANGED
|
@@ -26,12 +26,12 @@ import { setWebMcpUiContext } from './web/webmcp-ui-context.js';
|
|
|
26
26
|
interface Props {
|
|
27
27
|
/**
|
|
28
28
|
* Initial mode: 'default' | 'smrt'
|
|
29
|
-
*
|
|
29
|
+
* Defaults to standard mode; auto-detection requires autoEnableSmrt=true
|
|
30
30
|
*/
|
|
31
31
|
mode?: AppMode;
|
|
32
32
|
/**
|
|
33
33
|
* Whether to auto-enable smrt mode when capabilities are available
|
|
34
|
-
* @default
|
|
34
|
+
* @default false
|
|
35
35
|
*/
|
|
36
36
|
autoEnableSmrt?: boolean;
|
|
37
37
|
/**
|
|
@@ -98,7 +98,7 @@ interface Props {
|
|
|
98
98
|
|
|
99
99
|
const {
|
|
100
100
|
mode,
|
|
101
|
-
autoEnableSmrt =
|
|
101
|
+
autoEnableSmrt = false,
|
|
102
102
|
user = null,
|
|
103
103
|
permissions = [],
|
|
104
104
|
socket,
|
|
@@ -114,18 +114,22 @@ const {
|
|
|
114
114
|
// Determine if we should show the loading overlay
|
|
115
115
|
const showLoadingOverlay = $derived(ai?.showLoadingOverlay ?? true);
|
|
116
116
|
|
|
117
|
-
//
|
|
118
|
-
const appState =
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
117
|
+
// Capture initial props once; the effects below own subsequent prop updates.
|
|
118
|
+
const appState = untrack(() =>
|
|
119
|
+
createAppState({
|
|
120
|
+
initialMode: mode,
|
|
121
|
+
session: { preferences: { autoEnableSmrt } },
|
|
122
|
+
onCapabilitiesDetected: () => {
|
|
123
|
+
onReady?.();
|
|
124
|
+
},
|
|
125
|
+
onModeChange: (newMode) => {
|
|
126
|
+
onModeChange?.(newMode);
|
|
127
|
+
},
|
|
128
|
+
onAILoadingChange: (state) => {
|
|
129
|
+
onAILoadingChange?.(state);
|
|
130
|
+
},
|
|
131
|
+
}),
|
|
132
|
+
);
|
|
129
133
|
|
|
130
134
|
const localControlRegistry = createControlInteractionRegistry();
|
|
131
135
|
const localDataSurfaceRegistry = createDataSurfaceRegistry();
|
|
@@ -5,12 +5,12 @@ import type { WebMcpProviderConfig } from './web/webmcp-provider.js';
|
|
|
5
5
|
interface Props {
|
|
6
6
|
/**
|
|
7
7
|
* Initial mode: 'default' | 'smrt'
|
|
8
|
-
*
|
|
8
|
+
* Defaults to standard mode; auto-detection requires autoEnableSmrt=true
|
|
9
9
|
*/
|
|
10
10
|
mode?: AppMode;
|
|
11
11
|
/**
|
|
12
12
|
* Whether to auto-enable smrt mode when capabilities are available
|
|
13
|
-
* @default
|
|
13
|
+
* @default false
|
|
14
14
|
*/
|
|
15
15
|
autoEnableSmrt?: boolean;
|
|
16
16
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Provider.svelte.d.ts","sourceRoot":"","sources":["../src/Provider.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,YAAY,EAElB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGtC,OAAO,KAAK,EACV,QAAQ,EACR,cAAc,EACd,OAAO,EACP,YAAY,EACZ,IAAI,EACL,MAAM,sBAAsB,CAAC;AAI9B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAKrE,UAAU,KAAK;IACb;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB;;;OAGG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;;;;;;;;;;;;;OAeG;IACH,EAAE,CAAC,EAAE,QAAQ,CAAC;IACd;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC;;OAEG;IACH,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IACpD;;;;;OAKG;IACH,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,oBAAoB,CAAC;IACxC;;OAEG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;
|
|
1
|
+
{"version":3,"file":"Provider.svelte.d.ts","sourceRoot":"","sources":["../src/Provider.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,EAEL,KAAK,YAAY,EAElB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGtC,OAAO,KAAK,EACV,QAAQ,EACR,cAAc,EACd,OAAO,EACP,YAAY,EACZ,IAAI,EACL,MAAM,sBAAsB,CAAC;AAI9B,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAKrE,UAAU,KAAK;IACb;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB;;;OAGG;IACH,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB;;;;;;;;;;;;;;;OAeG;IACH,EAAE,CAAC,EAAE,QAAQ,CAAC;IACd;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC;;OAEG;IACH,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IACpD;;;;;OAKG;IACH,IAAI,CAAC,EAAE,YAAY,CAAC;IACpB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,GAAG,oBAAoB,CAAC;IACxC;;OAEG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;AAgQD,QAAA,MAAM,QAAQ,2CAAwC,CAAC;AACvD,KAAK,QAAQ,GAAG,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC;AAC5C,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Provider from '../Provider.svelte';
|
|
3
|
+
import type { AppMode } from '../state/app-state.js';
|
|
4
|
+
import type { SmrtAppStateManager } from '../state/app-state.svelte.js';
|
|
5
|
+
import Probe from './provider-mode-probe.svelte';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
mode?: AppMode;
|
|
9
|
+
autoEnableSmrt?: boolean;
|
|
10
|
+
capture: (state: SmrtAppStateManager) => void;
|
|
11
|
+
}
|
|
12
|
+
const { mode, autoEnableSmrt, capture }: Props = $props();
|
|
13
|
+
</script>
|
|
14
|
+
<Provider {mode} {autoEnableSmrt}><Probe {capture} /></Provider>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AppMode } from '../state/app-state.js';
|
|
2
|
+
import type { SmrtAppStateManager } from '../state/app-state.svelte.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
mode?: AppMode;
|
|
5
|
+
autoEnableSmrt?: boolean;
|
|
6
|
+
capture: (state: SmrtAppStateManager) => void;
|
|
7
|
+
}
|
|
8
|
+
declare const ProviderModeHarness: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type ProviderModeHarness = ReturnType<typeof ProviderModeHarness>;
|
|
10
|
+
export default ProviderModeHarness;
|
|
11
|
+
//# sourceMappingURL=provider-mode-harness.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-mode-harness.svelte.d.ts","sourceRoot":"","sources":["../../src/__tests__/provider-mode-harness.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAIxE,UAAU,KAAK;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC/C;AAaD,QAAA,MAAM,mBAAmB,2CAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { untrack } from 'svelte';
|
|
3
|
+
import type { SmrtAppStateManager } from '../state/app-state.svelte.js';
|
|
4
|
+
import { getAppStateContext } from '../state/context.js';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
capture: (state: SmrtAppStateManager) => void;
|
|
8
|
+
}
|
|
9
|
+
const { capture }: Props = $props();
|
|
10
|
+
const appState = getAppStateContext();
|
|
11
|
+
untrack(() => capture(appState));
|
|
12
|
+
</script>
|
|
13
|
+
<output aria-label="App mode">{appState.state.mode}</output>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SmrtAppStateManager } from '../state/app-state.svelte.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
capture: (state: SmrtAppStateManager) => void;
|
|
4
|
+
}
|
|
5
|
+
declare const ProviderModeProbe: import("svelte").Component<Props, {}, "">;
|
|
6
|
+
type ProviderModeProbe = ReturnType<typeof ProviderModeProbe>;
|
|
7
|
+
export default ProviderModeProbe;
|
|
8
|
+
//# sourceMappingURL=provider-mode-probe.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-mode-probe.svelte.d.ts","sourceRoot":"","sources":["../../src/__tests__/provider-mode-probe.svelte.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAIxE,UAAU,KAAK;IACb,OAAO,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC/C;AAcD,QAAA,MAAM,iBAAiB,2CAAwC,CAAC;AAChE,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC9D,eAAe,iBAAiB,CAAC"}
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import type { InitState, OnProgress } from '../../core/types.js';
|
|
2
2
|
import type { BrowserSpeechSTTOptions, STTAdapter, STTCapabilities, STTOptions, STTResult } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* An error from the browser's speech recognition. `speechError` is the Web
|
|
5
|
+
* Speech error code (`network`, `not-allowed`, `service-not-allowed`,
|
|
6
|
+
* `no-speech`, `audio-capture`, `aborted`, `language-not-supported`, …).
|
|
7
|
+
*/
|
|
8
|
+
export type BrowserSpeechError = Error & {
|
|
9
|
+
speechError?: string;
|
|
10
|
+
speechMessage?: string;
|
|
11
|
+
};
|
|
3
12
|
/**
|
|
4
13
|
* Browser Speech Recognition adapter
|
|
5
14
|
*/
|
|
@@ -9,6 +18,8 @@ export declare class BrowserSpeechSTTAdapter implements STTAdapter {
|
|
|
9
18
|
private recognition;
|
|
10
19
|
private options;
|
|
11
20
|
private _isListening;
|
|
21
|
+
/** Set while our own `abort()` runs, so its `aborted` error is not reported. */
|
|
22
|
+
private aborting;
|
|
12
23
|
private resultListeners;
|
|
13
24
|
private errorListeners;
|
|
14
25
|
private startListeners;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-speech.d.ts","sourceRoot":"","sources":["../../../../src/browser-ai/adapters/stt/browser-speech.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EACV,uBAAuB,EACvB,UAAU,EACV,eAAe,EACf,UAAU,EACV,SAAS,EACV,MAAM,YAAY,CAAC;AAWpB;;GAEG;AACH,qBAAa,uBAAwB,YAAW,UAAU;IACxD,QAAQ,CAAC,IAAI,EAAG,gBAAgB,CAAU;IAE1C,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,YAAY,CAAS;
|
|
1
|
+
{"version":3,"file":"browser-speech.d.ts","sourceRoot":"","sources":["../../../../src/browser-ai/adapters/stt/browser-speech.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,KAAK,EACV,uBAAuB,EACvB,UAAU,EACV,eAAe,EACf,UAAU,EACV,SAAS,EACV,MAAM,YAAY,CAAC;AAWpB;;;;GAIG;AACH,MAAM,MAAM,kBAAkB,GAAG,KAAK,GAAG;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;GAEG;AACH,qBAAa,uBAAwB,YAAW,UAAU;IACxD,QAAQ,CAAC,IAAI,EAAG,gBAAgB,CAAU;IAE1C,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,YAAY,CAAS;IAC7B,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAS;IAGzB,OAAO,CAAC,eAAe,CAA0C;IACjE,OAAO,CAAC,cAAc,CAAqC;IAC3D,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,YAAY,CAAyB;gBAEjC,OAAO,GAAE,uBAA4B;IAQjD,IAAI,SAAS,IAAI,SAAS,CAEzB;IAEK,iBAAiB,CAAC,WAAW,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAqChE,OAAO,CAAC,wBAAwB;IAqFhC,eAAe,IAAI,eAAe;IA2B5B,KAAK,CAAC,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAM3B,KAAK,IAAI,IAAI;IAOb,WAAW,IAAI,OAAO;IAItB,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI;IAK3D,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,MAAM,IAAI;IAKrD,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAKzC,KAAK,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAKjC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAS/B"}
|
|
@@ -23,6 +23,8 @@ export class BrowserSpeechSTTAdapter {
|
|
|
23
23
|
recognition = null;
|
|
24
24
|
options;
|
|
25
25
|
_isListening = false;
|
|
26
|
+
/** Set while our own `abort()` runs, so its `aborted` error is not reported. */
|
|
27
|
+
aborting = false;
|
|
26
28
|
// Event listeners
|
|
27
29
|
resultListeners = new Set();
|
|
28
30
|
errorListeners = new Set();
|
|
@@ -94,8 +96,13 @@ export class BrowserSpeechSTTAdapter {
|
|
|
94
96
|
}
|
|
95
97
|
};
|
|
96
98
|
this.recognition.onerror = (event) => {
|
|
99
|
+
const code = event.error;
|
|
100
|
+
if (code === 'aborted' && this.aborting) {
|
|
101
|
+
// We called abort() ourselves: not an error.
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
97
104
|
let error;
|
|
98
|
-
switch (
|
|
105
|
+
switch (code) {
|
|
99
106
|
case 'not-allowed':
|
|
100
107
|
case 'service-not-allowed':
|
|
101
108
|
error = new PermissionDeniedError('microphone', 'browser-speech');
|
|
@@ -110,11 +117,18 @@ export class BrowserSpeechSTTAdapter {
|
|
|
110
117
|
error = new Error('Network error during speech recognition');
|
|
111
118
|
break;
|
|
112
119
|
case 'aborted':
|
|
113
|
-
//
|
|
114
|
-
|
|
120
|
+
// Not our abort(): the browser (or another tab) cut it off.
|
|
121
|
+
error = new Error('Speech recognition was aborted');
|
|
122
|
+
break;
|
|
115
123
|
default:
|
|
116
|
-
error = new Error(`Speech recognition error: ${
|
|
124
|
+
error = new Error(`Speech recognition error: ${code}`);
|
|
117
125
|
}
|
|
126
|
+
// The Web Speech error code, so callers can tell `service-not-allowed`
|
|
127
|
+
// (the browser has no speech service, e.g. Brave) from `not-allowed`
|
|
128
|
+
// (the microphone is blocked), and `network` from everything else.
|
|
129
|
+
error.speechError = code;
|
|
130
|
+
if (event.message)
|
|
131
|
+
error.speechMessage = event.message;
|
|
118
132
|
for (const cb of this.errorListeners) {
|
|
119
133
|
cb(error);
|
|
120
134
|
}
|
|
@@ -127,6 +141,7 @@ export class BrowserSpeechSTTAdapter {
|
|
|
127
141
|
};
|
|
128
142
|
this.recognition.onend = () => {
|
|
129
143
|
this._isListening = false;
|
|
144
|
+
this.aborting = false;
|
|
130
145
|
for (const cb of this.endListeners) {
|
|
131
146
|
cb();
|
|
132
147
|
}
|
|
@@ -181,6 +196,7 @@ export class BrowserSpeechSTTAdapter {
|
|
|
181
196
|
}
|
|
182
197
|
abort() {
|
|
183
198
|
if (this.recognition && this._isListening) {
|
|
199
|
+
this.aborting = true;
|
|
184
200
|
this.recognition.abort();
|
|
185
201
|
}
|
|
186
202
|
}
|