@happyvertical/smrt-svelte 0.55.7 → 0.55.9
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 +7 -44
- package/README.md +112 -1
- package/agents/browser-ai.md +47 -0
- package/agents/command-palette.md +85 -0
- package/agents/import-export.md +62 -0
- package/agents/overview-surfaces.md +255 -0
- package/agents/screens.md +57 -0
- package/dist/browser-ai/adapters/tts/browser-synthesis.d.ts +5 -0
- package/dist/browser-ai/adapters/tts/browser-synthesis.d.ts.map +1 -1
- package/dist/browser-ai/adapters/tts/browser-synthesis.js +83 -55
- package/dist/components/command-palette/CommandPalette.svelte +75 -0
- package/dist/components/command-palette/CommandPalette.svelte.d.ts +22 -0
- package/dist/components/command-palette/CommandPalette.svelte.d.ts.map +1 -0
- package/dist/components/command-palette/CommandPaletteDialog.svelte +406 -0
- package/dist/components/command-palette/CommandPaletteDialog.svelte.d.ts +18 -0
- package/dist/components/command-palette/CommandPaletteDialog.svelte.d.ts.map +1 -0
- package/dist/components/command-palette/CommandPaletteTrigger.svelte +91 -0
- package/dist/components/command-palette/CommandPaletteTrigger.svelte.d.ts +17 -0
- package/dist/components/command-palette/CommandPaletteTrigger.svelte.d.ts.map +1 -0
- package/dist/components/command-palette/context.d.ts +18 -0
- package/dist/components/command-palette/context.d.ts.map +1 -0
- package/dist/components/command-palette/context.js +38 -0
- package/dist/components/command-palette/controller.svelte.d.ts +70 -0
- package/dist/components/command-palette/controller.svelte.d.ts.map +1 -0
- package/dist/components/command-palette/controller.svelte.js +379 -0
- package/dist/components/command-palette/hotkeys.d.ts +30 -0
- package/dist/components/command-palette/hotkeys.d.ts.map +1 -0
- package/dist/components/command-palette/hotkeys.js +161 -0
- package/dist/components/command-palette/index.d.ts +19 -0
- package/dist/components/command-palette/index.d.ts.map +1 -0
- package/dist/components/command-palette/index.js +17 -0
- package/dist/components/command-palette/match.d.ts +38 -0
- package/dist/components/command-palette/match.d.ts.map +1 -0
- package/dist/components/command-palette/match.js +172 -0
- package/dist/components/command-palette/providers.d.ts +159 -0
- package/dist/components/command-palette/providers.d.ts.map +1 -0
- package/dist/components/command-palette/providers.js +345 -0
- package/dist/components/command-palette/rank.d.ts +20 -0
- package/dist/components/command-palette/rank.d.ts.map +1 -0
- package/dist/components/command-palette/rank.js +74 -0
- package/dist/components/command-palette/types.d.ts +119 -0
- package/dist/components/command-palette/types.d.ts.map +1 -0
- package/dist/components/command-palette/types.js +10 -0
- package/dist/components/command-palette/use-provider.svelte.d.ts +14 -0
- package/dist/components/command-palette/use-provider.svelte.d.ts.map +1 -0
- package/dist/components/command-palette/use-provider.svelte.js +23 -0
- package/dist/components/import-export/ImportExport.svelte +678 -0
- package/dist/components/import-export/ImportExport.svelte.d.ts +5 -0
- package/dist/components/import-export/ImportExport.svelte.d.ts.map +1 -0
- package/dist/components/import-export/collection-adapter.d.ts +35 -0
- package/dist/components/import-export/collection-adapter.d.ts.map +1 -0
- package/dist/components/import-export/collection-adapter.js +42 -0
- package/dist/components/import-export/csv.d.ts +60 -0
- package/dist/components/import-export/csv.d.ts.map +1 -0
- package/dist/components/import-export/csv.js +192 -0
- package/dist/components/import-export/download.d.ts +4 -0
- package/dist/components/import-export/download.d.ts.map +1 -0
- package/dist/components/import-export/download.js +17 -0
- package/dist/components/import-export/export.d.ts +25 -0
- package/dist/components/import-export/export.d.ts.map +1 -0
- package/dist/components/import-export/export.js +68 -0
- package/dist/components/import-export/fields.d.ts +48 -0
- package/dist/components/import-export/fields.d.ts.map +1 -0
- package/dist/components/import-export/fields.js +103 -0
- package/dist/components/import-export/index.d.ts +12 -0
- package/dist/components/import-export/index.d.ts.map +1 -0
- package/dist/components/import-export/index.js +9 -0
- package/dist/components/import-export/mapping.d.ts +12 -0
- package/dist/components/import-export/mapping.d.ts.map +1 -0
- package/dist/components/import-export/mapping.js +38 -0
- package/dist/components/import-export/props.d.ts +34 -0
- package/dist/components/import-export/props.d.ts.map +1 -0
- package/dist/components/import-export/props.js +1 -0
- package/dist/components/import-export/report.d.ts +13 -0
- package/dist/components/import-export/report.d.ts.map +1 -0
- package/dist/components/import-export/report.js +50 -0
- package/dist/components/import-export/runner.d.ts +17 -0
- package/dist/components/import-export/runner.d.ts.map +1 -0
- package/dist/components/import-export/runner.js +39 -0
- package/dist/components/import-export/types.d.ts +100 -0
- package/dist/components/import-export/types.d.ts.map +1 -0
- package/dist/components/import-export/types.js +6 -0
- package/dist/components/import-export/validate.d.ts +39 -0
- package/dist/components/import-export/validate.d.ts.map +1 -0
- package/dist/components/import-export/validate.js +268 -0
- package/dist/components/overview/OverviewGrid.svelte +579 -0
- package/dist/components/overview/OverviewGrid.svelte.d.ts +5 -0
- package/dist/components/overview/OverviewGrid.svelte.d.ts.map +1 -0
- package/dist/components/overview/WidgetOptionsForm.svelte +193 -0
- package/dist/components/overview/WidgetOptionsForm.svelte.d.ts +5 -0
- package/dist/components/overview/WidgetOptionsForm.svelte.d.ts.map +1 -0
- package/dist/components/overview/controller.svelte.d.ts +92 -0
- package/dist/components/overview/controller.svelte.d.ts.map +1 -0
- package/dist/components/overview/controller.svelte.js +336 -0
- package/dist/components/overview/format.d.ts +13 -0
- package/dist/components/overview/format.d.ts.map +1 -0
- package/dist/components/overview/format.js +45 -0
- package/dist/components/overview/grid-types.d.ts +52 -0
- package/dist/components/overview/grid-types.d.ts.map +1 -0
- package/dist/components/overview/grid-types.js +1 -0
- package/dist/components/overview/index.d.ts +24 -0
- package/dist/components/overview/index.d.ts.map +1 -0
- package/dist/components/overview/index.js +22 -0
- package/dist/components/overview/load.d.ts +30 -0
- package/dist/components/overview/load.d.ts.map +1 -0
- package/dist/components/overview/load.js +92 -0
- package/dist/components/overview/markdown.d.ts +53 -0
- package/dist/components/overview/markdown.d.ts.map +1 -0
- package/dist/components/overview/markdown.js +203 -0
- package/dist/components/overview/model.d.ts +100 -0
- package/dist/components/overview/model.d.ts.map +1 -0
- package/dist/components/overview/model.js +460 -0
- package/dist/components/overview/recipe-widgets.d.ts +77 -0
- package/dist/components/overview/recipe-widgets.d.ts.map +1 -0
- package/dist/components/overview/recipe-widgets.js +151 -0
- package/dist/components/overview/registry.d.ts +45 -0
- package/dist/components/overview/registry.d.ts.map +1 -0
- package/dist/components/overview/registry.js +112 -0
- package/dist/components/overview/resize.d.ts +25 -0
- package/dist/components/overview/resize.d.ts.map +1 -0
- package/dist/components/overview/resize.js +41 -0
- package/dist/components/overview/schema.d.ts +34 -0
- package/dist/components/overview/schema.d.ts.map +1 -0
- package/dist/components/overview/schema.js +180 -0
- package/dist/components/overview/server.d.ts +14 -0
- package/dist/components/overview/server.d.ts.map +1 -0
- package/dist/components/overview/server.js +13 -0
- package/dist/components/overview/types.d.ts +235 -0
- package/dist/components/overview/types.d.ts.map +1 -0
- package/dist/components/overview/types.js +17 -0
- package/dist/components/overview/widgets/ChartWidget.svelte +111 -0
- package/dist/components/overview/widgets/ChartWidget.svelte.d.ts +6 -0
- package/dist/components/overview/widgets/ChartWidget.svelte.d.ts.map +1 -0
- package/dist/components/overview/widgets/MetricWidget.svelte +86 -0
- package/dist/components/overview/widgets/MetricWidget.svelte.d.ts +6 -0
- package/dist/components/overview/widgets/MetricWidget.svelte.d.ts.map +1 -0
- package/dist/components/overview/widgets/NoteWidget.svelte +61 -0
- package/dist/components/overview/widgets/NoteWidget.svelte.d.ts +5 -0
- package/dist/components/overview/widgets/NoteWidget.svelte.d.ts.map +1 -0
- package/dist/components/overview/widgets/RecordListWidget.svelte +91 -0
- package/dist/components/overview/widgets/RecordListWidget.svelte.d.ts +6 -0
- package/dist/components/overview/widgets/RecordListWidget.svelte.d.ts.map +1 -0
- package/dist/components/overview/widgets/ShortcutsWidget.svelte +80 -0
- package/dist/components/overview/widgets/ShortcutsWidget.svelte.d.ts +6 -0
- package/dist/components/overview/widgets/ShortcutsWidget.svelte.d.ts.map +1 -0
- package/dist/components/overview/widgets/core.d.ts +44 -0
- package/dist/components/overview/widgets/core.d.ts.map +1 -0
- package/dist/components/overview/widgets/core.js +222 -0
- package/dist/components/overview/widgets/data.d.ts +76 -0
- package/dist/components/overview/widgets/data.d.ts.map +1 -0
- package/dist/components/overview/widgets/data.js +40 -0
- package/dist/components/screens/DetailScreen.svelte +220 -0
- package/dist/components/screens/DetailScreen.svelte.d.ts +5 -0
- package/dist/components/screens/DetailScreen.svelte.d.ts.map +1 -0
- package/dist/components/screens/EditForm.svelte +313 -0
- package/dist/components/screens/EditForm.svelte.d.ts +5 -0
- package/dist/components/screens/EditForm.svelte.d.ts.map +1 -0
- package/dist/components/screens/ListScreen.svelte +191 -0
- package/dist/components/screens/ListScreen.svelte.d.ts +5 -0
- package/dist/components/screens/ListScreen.svelte.d.ts.map +1 -0
- package/dist/components/screens/RecipeScreens.svelte +237 -0
- package/dist/components/screens/RecipeScreens.svelte.d.ts +5 -0
- package/dist/components/screens/RecipeScreens.svelte.d.ts.map +1 -0
- package/dist/components/screens/fields.d.ts +54 -0
- package/dist/components/screens/fields.d.ts.map +1 -0
- package/dist/components/screens/fields.js +229 -0
- package/dist/components/screens/index.d.ts +8 -0
- package/dist/components/screens/index.d.ts.map +1 -0
- package/dist/components/screens/index.js +6 -0
- package/dist/components/screens/types.d.ts +215 -0
- package/dist/components/screens/types.d.ts.map +1 -0
- package/dist/components/screens/types.js +1 -0
- package/dist/components/screens/values.d.ts +62 -0
- package/dist/components/screens/values.d.ts.map +1 -0
- package/dist/components/screens/values.js +289 -0
- package/dist/components/sortable/controller.svelte.d.ts +10 -0
- package/dist/components/sortable/controller.svelte.d.ts.map +1 -1
- package/dist/components/sortable/controller.svelte.js +29 -7
- package/dist/components/workspace/manifest-nav.d.ts +20 -0
- package/dist/components/workspace/manifest-nav.d.ts.map +1 -1
- package/dist/components/workspace/manifest-nav.js +42 -13
- package/dist/i18n/server.d.ts +6 -0
- package/dist/i18n/server.d.ts.map +1 -1
- package/dist/i18n/server.js +6 -0
- package/dist/i18n/strings.import-export.d.ts +88 -0
- package/dist/i18n/strings.import-export.d.ts.map +1 -0
- package/dist/i18n/strings.import-export.js +88 -0
- package/dist/i18n/strings.overview.d.ts +100 -0
- package/dist/i18n/strings.overview.d.ts.map +1 -0
- package/dist/i18n/strings.overview.js +107 -0
- package/dist/i18n/strings.palette.d.ts +18 -0
- package/dist/i18n/strings.palette.d.ts.map +1 -0
- package/dist/i18n/strings.palette.js +18 -0
- package/dist/i18n/strings.screens.d.ts +31 -0
- package/dist/i18n/strings.screens.d.ts.map +1 -0
- package/dist/i18n/strings.screens.js +31 -0
- package/package.json +45 -16
package/AGENTS.md
CHANGED
|
@@ -13,11 +13,15 @@ subpath you are editing. This file keeps what holds in every module.
|
|
|
13
13
|
| `src/i18n/` (`./i18n` + `./i18n/server`) | `defineMessages` / `useI18n` / `<Trans>` / `buildI18nSnapshot`, the template-vs-render split, and hardcoded-string enforcement | [agents/i18n.md](agents/i18n.md) |
|
|
14
14
|
| `src/themes/` + `src/theme/` | which theme system is canonical and the full `--smrt-*` design-token vocabulary with its alias rules | [agents/themes.md](agents/themes.md) |
|
|
15
15
|
| `src/test-support/` + `__tests__/` | the golden-test harness and pattern for Svelte component tests | [agents/testing.md](agents/testing.md) |
|
|
16
|
+
| `src/components/import-export/` (`./import-export`) | `ImportExport` CSV/TSV import (column mapping, validation preview, error report) and export (column selection) driven by collection definitions + resolved field policy, over a pure Node-tested CSV layer (#3715) | [agents/import-export.md](agents/import-export.md) |
|
|
16
17
|
| `src/components/settings/` (`./settings`) | `SettingsCatalog`, `paginateSettingsCatalog`, and the summary-vs-detail scalability contract | [agents/settings.md](agents/settings.md) |
|
|
18
|
+
| `src/components/screens/` (`./screens`) | `RecipeScreens` / `ListScreen` / `DetailScreen` / `EditForm`: list, view, create and edit derived from a generated web definition plus resolved field policy; pure `deriveScreenFields` selection, minor-unit money handling, transport-neutral source (#3718) | [agents/screens.md](agents/screens.md) |
|
|
17
19
|
| `src/components/app/` (`./app`) | `AppShell` (Provider + ThemeProvider + theme CSS + AdminShell + nav/dock slots), `OwnerSetupForm` (props-driven first-run owner form), `ShellSettingsPage`, `RuntimeDiagnosticsWebMcp`. Browser-only; no `$app/*` or server imports | [agents/workspace.md](agents/workspace.md#app-shell-and-owner-setup-app) |
|
|
18
20
|
| `src/components/workspace/` (`./workspace` + `./web`) | the AdminShell family and its principles, the legacy ToolsDock surface, the `./web` activity-feed and `updateAvailable` adapters, and server-side dock gates | [agents/workspace.md](agents/workspace.md) |
|
|
21
|
+
| `src/components/command-palette/` (`./command-palette`) | `CommandPalette` trigger + dialog, `PaletteProvider` contract, nav and manifest providers (#3713) | [agents/command-palette.md](agents/command-palette.md) |
|
|
22
|
+
| `src/components/overview/` (`./overview` + `./overview/server`) | customizable overview surfaces: `OverviewGrid` flow grid (span 1-4, keyboard reorder/resize) joined to the shell's layout edit mode, host-owned `createOverview` controller, widget registry with versioned option schemas validated on save and load, the server `load(options, ctx)` contract (`loadOverview`), the metric/chart/records/shortcuts/note core widgets, and `registerRecipeWidgets` (recipe `widget` surfaces to registry registrations, injected module resolver) (#3727) | [agents/overview-surfaces.md](agents/overview-surfaces.md) |
|
|
19
23
|
| `src/web/remote-query.svelte.ts` | Svelte 5 binding for query-shaped remote pages: rows, page, totals, loading/refreshing/stale/error, retry, last-updated, and query-scoped live subscriptions (#2445) | — |
|
|
20
|
-
| `src/web/list-data-surface.svelte.ts` (`mountListDataSurface`) |
|
|
24
|
+
| `src/web/list-data-surface.svelte.ts` (`mountListDataSurface`) | Registers an existing non-`DataTable` list (mirroring a `DataTableController`) as a mounted `DataSurfaceDescriptor`; a same-behavior port of `registerContentListDataSurface`, not yet unified behind smrt-ui (#2917). The module doc comment is the authoritative list of divergences (#2906) | — |
|
|
21
25
|
| `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) | — |
|
|
22
26
|
| `src/web/webmcp-ui.ts` | Fixed, low-cardinality WebMCP adapter over the Provider's mounted form-control and data-surface registries (#2521) | — |
|
|
23
27
|
| `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) | — |
|
|
@@ -206,49 +210,8 @@ for a data-surface one — for exactly the component's lifetime.
|
|
|
206
210
|
- **Preload strategies**: `none`, `eager`, `idle` (recommended), `on-visible`
|
|
207
211
|
- **Warm client cache**: module-level Map survives navigation/remounts -- avoids re-downloading WASM/models
|
|
208
212
|
- **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.
|
|
224
213
|
- Cache API: `getCachedSTT()`, `getCachedTTS()`, `getCachedLLM()`, `getCacheStats()`, `clearAllCaches()`
|
|
225
|
-
- **
|
|
226
|
-
`kind: 'local'`, both refusing `tool`/`function` messages, non-string content, `tool_calls` and
|
|
227
|
-
`options.tools` with `InferencePathError` (`unsupported_message`) through the SHARED
|
|
228
|
-
`assertTextOnlyTurn`, so a per-call switch between backends never changes what a turn means.
|
|
229
|
-
- `createWebLlmInferenceBackend()` wraps an `LLMAdapter` — `status` derives from the adapter's
|
|
230
|
-
`initState` (plus an explicit in-flight flag, because the adapter only flips to `initializing`
|
|
231
|
-
after its own awaits), `progress` mirrors `DownloadProgressInfo`, and `load()`/`unload()` drive
|
|
232
|
-
`ensureInitialized`/`unloadModel`. It reports `unavailable` without WebGPU so an `auto` path skips
|
|
233
|
-
it. Pass `adapter` to share the app-state-managed warm-cached adapter; construct once, so two
|
|
234
|
-
callers cannot each start an unshared adapter and a second download.
|
|
235
|
-
Its `loadModule` option matters: the default `importOptional()` resolves a VARIABLE specifier,
|
|
236
|
-
which a browser cannot resolve (`TypeError: Failed to resolve module specifier`) — a host that has
|
|
237
|
-
`@mlc-ai/web-llm` must pass `() => import('@mlc-ai/web-llm')` so the bundler rewrites a static one.
|
|
238
|
-
It does NOT forward `signal` (the adapter has no cancellation seam).
|
|
239
|
-
- `createBitGpuInferenceBackend()` wraps `bitgpu` (MIT, optional peer), the engine behind the
|
|
240
|
-
Bonsai WebGPU demo PrismML's own docs list as their in-browser option. `loadBitGpu`/`loadChat`
|
|
241
|
-
are REQUIRED loaders (`() => import('bitgpu')`, `() => import('bitgpu/chat')`) — a static import
|
|
242
|
-
here would fail the build for every consumer without the optional peer, and a bare specifier is
|
|
243
|
-
not resolvable by a browser either. `bitgpu`'s `LoadProgress.phase` maps to `InferenceProgress`
|
|
244
|
-
(`manifest`/`weights` → `downloading`, `pipelines` → `extracting`). Unlike the WebLLM backend it
|
|
245
|
-
**forwards `signal`** — bitgpu aborts a live generation — and it does NOT lazily load: weights
|
|
246
|
-
run to GBs, so a turn before `load()` fails with a named reason. `model` in the per-call options
|
|
247
|
-
is ignored (one engine binds one model), and bitgpu's tools/JSON-schema/think support is
|
|
248
|
-
deliberately left unexposed for now so the text-only invariant holds across backends.
|
|
249
|
-
- **`ModelStatusControl.svelte`**: the user-facing model lifecycle surface (state, Load/Unload,
|
|
250
|
-
download progress) driven entirely by a backend's own `status`/`progress`/`subscribe`, so it works
|
|
251
|
-
without a `<Provider>` ancestor. `unavailable` is a first-class state that offers NO load action.
|
|
214
|
+
- **On-device speech and inference backends** (`whisper-local`/`moonshine` adapters, `createWebLlmInferenceBackend`, `createBitGpuInferenceBackend`, `ModelStatusControl`): see [agents/browser-ai.md](agents/browser-ai.md).
|
|
252
215
|
|
|
253
216
|
## Permission Action
|
|
254
217
|
|
|
@@ -272,5 +235,5 @@ for a data-surface one — for exactly the component's lifetime.
|
|
|
272
235
|
- `@happyvertical/smrt-types` (shared types) — includes the identity data contracts (`User`, `Role`, `Membership`, `Tenant`) the role/membership components type against, so no dependency on `smrt-users` / `smrt-profiles` is needed
|
|
273
236
|
- `@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.
|
|
274
237
|
- `@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.
|
|
275
|
-
- `@happyvertical/logger` (SDK) is a `dependency`
|
|
238
|
+
- `@happyvertical/logger` (SDK) is a `dependency` for voice/AI error reporting in the form components. Consume it **only** through `src/internal/logger.ts`, never `createLogger()` at module scope: it reads `process.env`, which throws `ReferenceError` in the browser under `vite dev` and kills hydration. The wrapper builds the logger lazily and falls back to a bare `ConsoleLogger`.
|
|
276
239
|
- 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
|
@@ -32,6 +32,50 @@ pipeline (`opportunity.stage`) without importing `@happyvertical/smrt-projects`.
|
|
|
32
32
|
|
|
33
33
|
Svelte 5 component library for the s-m-r-t framework. Provides UI components, browser AI integration (STT/TTS/LLM with warm cache), a theme system, permission-aware rendering, and module UI registry for agent admin panels.
|
|
34
34
|
|
|
35
|
+
## Import and export
|
|
36
|
+
|
|
37
|
+
`@happyvertical/smrt-svelte/import-export` provides `ImportExport`, a CSV and
|
|
38
|
+
TSV import/export panel for any collection. Spreadsheet users save as CSV;
|
|
39
|
+
there is no `.xlsx` reader. Import is a guided flow: pick a file, map each
|
|
40
|
+
column to a field (auto-detected from the field name, label, or alias),
|
|
41
|
+
check a validation preview, then import. The preview is the dry run and writes
|
|
42
|
+
nothing. Rows that fail validation are listed by file line with the offending
|
|
43
|
+
value, and are downloadable as a CSV error report. Valid rows are created one
|
|
44
|
+
request each, so a server rejection fails only that row. Export lets the user
|
|
45
|
+
choose columns, delimiter, and header style; the default `name` headers
|
|
46
|
+
re-import cleanly.
|
|
47
|
+
|
|
48
|
+
The columns come from the same sources as the generated forms:
|
|
49
|
+
`fieldsFromCollectionDefinition(definition, { policy })` reads the collection
|
|
50
|
+
definition (the manifest) and a resolved field policy. A policy-`hidden` field
|
|
51
|
+
is neither imported nor exported, a `locked` field is never overwritten, and a
|
|
52
|
+
resolved default fills an empty or unmapped cell. `createCollectionImportExport`
|
|
53
|
+
binds the panel to the generated CRUD fetchers, so import and export go through
|
|
54
|
+
the same authorized REST routes as every other client.
|
|
55
|
+
|
|
56
|
+
```svelte
|
|
57
|
+
<script lang="ts">
|
|
58
|
+
import {
|
|
59
|
+
ImportExport,
|
|
60
|
+
createCollectionImportExport,
|
|
61
|
+
} from '@happyvertical/smrt-svelte/import-export';
|
|
62
|
+
|
|
63
|
+
const io = createCollectionImportExport({ definition, fetchers, fieldOptions: { policy } });
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<ImportExport
|
|
67
|
+
fields={io.fields}
|
|
68
|
+
createRecord={io.createRecord}
|
|
69
|
+
loadRows={io.loadRows}
|
|
70
|
+
filename="products"
|
|
71
|
+
/>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The CSV layer (`parseTable`, `validateRows`, `buildExportFile`, `runImport`)
|
|
75
|
+
is pure and runs in Node. Exports neutralize spreadsheet formulas in text
|
|
76
|
+
cells. See [agents/import-export.md](./agents/import-export.md) for the
|
|
77
|
+
contract.
|
|
78
|
+
|
|
35
79
|
## Sortable and shell layout
|
|
36
80
|
|
|
37
81
|
`@happyvertical/smrt-svelte/sortable` provides `Sortable`, an accessible
|
|
@@ -590,10 +634,67 @@ keys the shell renders exactly as before.
|
|
|
590
634
|
- **Migration guide** (first-generation `WorkspaceShell`/`RoleShell` →
|
|
591
635
|
`AdminShell`; adoption is additive and non-breaking):
|
|
592
636
|
[`src/components/workspace/MIGRATION.md`](./src/components/workspace/MIGRATION.md)
|
|
593
|
-
- **Playground demos**: `playground/src/routes/
|
|
637
|
+
- **Playground demos**: `playground/src/routes/command-palette` shows the palette in the header slot;
|
|
638
|
+
`playground/src/routes/admin-shell` exercises all four
|
|
594
639
|
scopes, focus tools, and activities; `admin-shell-activity-feed` and
|
|
595
640
|
`admin-shell-system-feed` show live feeds.
|
|
596
641
|
|
|
642
|
+
### Command palette and global search
|
|
643
|
+
|
|
644
|
+
`@happyvertical/smrt-svelte/command-palette` is a keyboard-first "find anything"
|
|
645
|
+
dialog (Ctrl/Cmd+K) with a provider contract. Anything can register rows: the
|
|
646
|
+
shell navigation, "New invoice" commands and cross-model record search derived
|
|
647
|
+
from the manifest, or your own commands.
|
|
648
|
+
|
|
649
|
+
```ts
|
|
650
|
+
import {
|
|
651
|
+
CommandPalette,
|
|
652
|
+
createCommandPalette,
|
|
653
|
+
createModelProviders,
|
|
654
|
+
createNavigationProvider,
|
|
655
|
+
} from '@happyvertical/smrt-svelte/command-palette';
|
|
656
|
+
|
|
657
|
+
const palette = createCommandPalette({
|
|
658
|
+
navigate: goto,
|
|
659
|
+
providers: [
|
|
660
|
+
createNavigationProvider({ nav, groups }),
|
|
661
|
+
...createModelProviders({ manifest, pagesBasePath: '/app' }),
|
|
662
|
+
],
|
|
663
|
+
});
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
Render `<CommandPalette {palette} />` as an `AppShell` `slotItems` entry
|
|
667
|
+
(usually `header.center`). See
|
|
668
|
+
[`agents/command-palette.md`](./agents/command-palette.md) for the provider
|
|
669
|
+
contract, accessibility behavior, and the record-search limits.
|
|
670
|
+
|
|
671
|
+
### Customizable overviews
|
|
672
|
+
|
|
673
|
+
`@happyvertical/smrt-svelte/overview` turns an overview page into a grid of
|
|
674
|
+
registered widgets (metric, chart, record list, shortcuts, note) that admins
|
|
675
|
+
reorder, resize, configure, add and remove in the shell's layout edit mode,
|
|
676
|
+
inside an allowed set the page declares. A saved overview is declarative data:
|
|
677
|
+
widget options are validated against versioned schemas on save and on load, and
|
|
678
|
+
widget data comes from a server `load(options, ctx)` that runs inside the page's
|
|
679
|
+
load with the user's permissions, so the page renders fully on the server.
|
|
680
|
+
Persistence is host-owned (the same pattern as the shell layout).
|
|
681
|
+
|
|
682
|
+
```ts
|
|
683
|
+
import {
|
|
684
|
+
createOverview,
|
|
685
|
+
OverviewGrid,
|
|
686
|
+
registerCoreWidgets,
|
|
687
|
+
} from '@happyvertical/smrt-svelte/overview';
|
|
688
|
+
import {
|
|
689
|
+
defineOverview,
|
|
690
|
+
loadOverview,
|
|
691
|
+
resolveOverview,
|
|
692
|
+
} from '@happyvertical/smrt-svelte/overview/server';
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
See [`agents/overview-surfaces.md`](./agents/overview-surfaces.md) for the data
|
|
696
|
+
model, the validation rules, the server load contract and the extension points.
|
|
697
|
+
|
|
597
698
|
## Exports
|
|
598
699
|
|
|
599
700
|
### Entry Points
|
|
@@ -605,10 +706,15 @@ importable, even if it appears in `dist/`.
|
|
|
605
706
|
|-------------|----------|
|
|
606
707
|
| `@happyvertical/smrt-svelte` | `Provider`, hooks (`useAppState`, `useAuth`, `useLLM`, `useSocket`, `useSTT`, `useTheme`, `useTTS`), app state/context, `ModulePanel`, and the form components below |
|
|
607
708
|
| `@happyvertical/smrt-svelte/forms` | Form inputs (TextInput, Select, MoneyInput, DateTimeInput, Toggle, etc.) |
|
|
709
|
+
| `@happyvertical/smrt-svelte/import-export` | `ImportExport` (CSV/TSV import with column mapping, validation preview and error report; export with column selection), `createCollectionImportExport`, `fieldsFromCollectionDefinition`, and the pure CSV layer |
|
|
608
710
|
| `@happyvertical/smrt-svelte/settings` | Server-paged settings search, selection, and list/detail layout (`SettingsCatalog`, `paginateSettingsCatalog`) |
|
|
711
|
+
| `@happyvertical/smrt-svelte/screens` | List, view, create and edit screens derived from a generated web definition and resolved field policy (`RecipeScreens`, `ListScreen`, `DetailScreen`, `EditForm`) |
|
|
609
712
|
| `@happyvertical/smrt-svelte/workspace` | AdminShell, ShellState, tenant nav, focus tools, settings, activities, and system/app panels |
|
|
610
713
|
| `@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` |
|
|
611
714
|
| `@happyvertical/smrt-svelte/app/runtime-diagnostics` | Svelte-free diagnostics WebMCP registration and its tool name/endpoint constants, importable from server routes |
|
|
715
|
+
| `@happyvertical/smrt-svelte/command-palette` | Global search and command palette: `CommandPalette` (shell-slot trigger plus dialog), `createCommandPalette`, the provider contract, and the navigation and manifest-driven model providers |
|
|
716
|
+
| `@happyvertical/smrt-svelte/overview` | Customizable overview surfaces: `OverviewGrid` (flow grid with keyboard reorder and resize), `createOverview` (host-owned controller), the widget registry (`registerWidget`), `registerCoreWidgets` (metric, chart, record list, shortcuts, note) and `shortcutsFromNav` |
|
|
717
|
+
| `@happyvertical/smrt-svelte/overview/server` | Svelte-free half of the overview surface: the data model (`resolveOverview`, `checkOverviewOverride`), option-schema validation, `WidgetRegistry`, and `loadOverview` for a page's server load |
|
|
612
718
|
| `@happyvertical/smrt-svelte/workspace/legacy` | Opt-in ToolsDock compatibility surface for applications migrating to AdminShell |
|
|
613
719
|
| `@happyvertical/smrt-svelte/workspace/server` | Server-side workspace helpers (Node only) |
|
|
614
720
|
| `@happyvertical/smrt-svelte/workspace/live` | `systemFeed` — the AdminShell system scope (jobs/schedules/dispatch) polled from an app status endpoint; deliberately carries no `smrt-web` dependency |
|
|
@@ -704,6 +810,11 @@ all other currencies render their ISO code for deterministic SSR hydration.
|
|
|
704
810
|
|
|
705
811
|
`getCachedSTT`, `getCachedTTS`, `getCachedLLM`, `getCacheStats`, `clearAllCaches`
|
|
706
812
|
|
|
813
|
+
Browser speech synthesis honors `stop()`, replacement `speak()` calls, and
|
|
814
|
+
`dispose()` while voices are loading. Canceled `speak()` promises resolve even
|
|
815
|
+
when the browser emits no completion event; `onEnd` is reserved for natural
|
|
816
|
+
completion. Events from canceled utterances are ignored.
|
|
817
|
+
|
|
707
818
|
## Dependencies
|
|
708
819
|
|
|
709
820
|
- `@happyvertical/smrt-types` -- shared type definitions
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# smrt-svelte/browser-ai
|
|
2
|
+
|
|
3
|
+
Module semantics for `src/browser-ai/`. Package orientation and cross-module
|
|
4
|
+
invariants live in [../AGENTS.md](../AGENTS.md); read that first.
|
|
5
|
+
|
|
6
|
+
- **On-device speech** (`whisper-local` = alias `whisper-wasm`, and `moonshine`): thin adapters over
|
|
7
|
+
`@happyvertical/speech/local` (`LocalTranscriber`, or its worker host `serveLocalTranscriber`).
|
|
8
|
+
Push-to-talk (record on `start()`, transcribe on `stop()`, one final result) plus `transcribePcm()`
|
|
9
|
+
for smrt-ui's hands-free mode. Default `onnx-community/whisper-tiny.en` q8 (~45 MB, Cache
|
|
10
|
+
Storage); `model: 'moonshine-tiny'` (~32 MB) / `'moonshine-base'` (~67 MB) or any HF id
|
|
11
|
+
(`LOCAL_SPEECH_MODELS`); WebGPU else single-thread WASM (no COOP/COEP needed). The package never
|
|
12
|
+
imports the peers: a host passes `createWorker` (build it from
|
|
13
|
+
`@happyvertical/smrt-svelte/browser-ai/whisper-worker?worker`, alias `speech-worker`, the one module
|
|
14
|
+
that imports `@happyvertical/speech/local` and `@huggingface/transformers` statically) plus
|
|
15
|
+
`loadSpeech: () => import('@happyvertical/speech/local')`, or `loadModule` for the page thread.
|
|
16
|
+
`createLocalSpeechModel()` (alias `createWhisperLocalModel()`) / `adapter.model` is the consent API
|
|
17
|
+
(`estimateSize`, `isCached`, `load({ onProgress, signal })` with `downloading` -> `extracting` ->
|
|
18
|
+
`complete`); share it via `modelHandle`. English-only handling of `language`/`task` lives in the speech
|
|
19
|
+
package. `probeBrowserSpeech()` -> `works | missing | unreliable` (no network, no mic prompt)
|
|
20
|
+
decides whether to offer it. Sources set `stopTimeoutMs` so `Dictation` waits for the transcription.
|
|
21
|
+
- **Inference backends (browser-ai)**: two `@happyvertical/smrt-web/ai` `InferenceBackend`s, both
|
|
22
|
+
`kind: 'local'`, both refusing `tool`/`function` messages, non-string content, `tool_calls` and
|
|
23
|
+
`options.tools` with `InferencePathError` (`unsupported_message`) through the SHARED
|
|
24
|
+
`assertTextOnlyTurn`, so a per-call switch between backends never changes what a turn means.
|
|
25
|
+
- `createWebLlmInferenceBackend()` wraps an `LLMAdapter` — `status` derives from the adapter's
|
|
26
|
+
`initState` (plus an explicit in-flight flag, because the adapter only flips to `initializing`
|
|
27
|
+
after its own awaits), `progress` mirrors `DownloadProgressInfo`, and `load()`/`unload()` drive
|
|
28
|
+
`ensureInitialized`/`unloadModel`. It reports `unavailable` without WebGPU so an `auto` path skips
|
|
29
|
+
it. Pass `adapter` to share the app-state-managed warm-cached adapter; construct once, so two
|
|
30
|
+
callers cannot each start an unshared adapter and a second download.
|
|
31
|
+
Its `loadModule` option matters: the default `importOptional()` resolves a VARIABLE specifier,
|
|
32
|
+
which a browser cannot resolve (`TypeError: Failed to resolve module specifier`) — a host that has
|
|
33
|
+
`@mlc-ai/web-llm` must pass `() => import('@mlc-ai/web-llm')` so the bundler rewrites a static one.
|
|
34
|
+
It does NOT forward `signal` (the adapter has no cancellation seam).
|
|
35
|
+
- `createBitGpuInferenceBackend()` wraps `bitgpu` (MIT, optional peer), the engine behind the
|
|
36
|
+
Bonsai WebGPU demo PrismML's own docs list as their in-browser option. `loadBitGpu`/`loadChat`
|
|
37
|
+
are REQUIRED loaders (`() => import('bitgpu')`, `() => import('bitgpu/chat')`) — a static import
|
|
38
|
+
here would fail the build for every consumer without the optional peer, and a bare specifier is
|
|
39
|
+
not resolvable by a browser either. `bitgpu`'s `LoadProgress.phase` maps to `InferenceProgress`
|
|
40
|
+
(`manifest`/`weights` → `downloading`, `pipelines` → `extracting`). Unlike the WebLLM backend it
|
|
41
|
+
**forwards `signal`** — bitgpu aborts a live generation — and it does NOT lazily load: weights
|
|
42
|
+
run to GBs, so a turn before `load()` fails with a named reason. `model` in the per-call options
|
|
43
|
+
is ignored (one engine binds one model), and bitgpu's tools/JSON-schema/think support is
|
|
44
|
+
deliberately left unexposed for now so the text-only invariant holds across backends.
|
|
45
|
+
- **`ModelStatusControl.svelte`**: the user-facing model lifecycle surface (state, Load/Unload,
|
|
46
|
+
download progress) driven entirely by a backend's own `status`/`progress`/`subscribe`, so it works
|
|
47
|
+
without a `<Provider>` ancestor. `unavailable` is a first-class state that offers NO load action.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# smrt-svelte/command-palette
|
|
2
|
+
|
|
3
|
+
Module semantics for `src/components/command-palette/` (`./command-palette`).
|
|
4
|
+
Package orientation and cross-module invariants live in
|
|
5
|
+
[../AGENTS.md](../AGENTS.md); the shell it sits in is in
|
|
6
|
+
[workspace.md](workspace.md).
|
|
7
|
+
|
|
8
|
+
## Shape
|
|
9
|
+
|
|
10
|
+
- **Controller** (`controller.svelte.ts`): `createCommandPalette({ providers,
|
|
11
|
+
navigate, onError, searchDebounceMs, maxPerGroup })`. Owns open/query state,
|
|
12
|
+
the provider registry, local and remote rows, the highlight, and `activate`.
|
|
13
|
+
`setCommandPalette(controller)` puts it on context (call it above the
|
|
14
|
+
`AppShell`) so any descendant can `useCommandPaletteProvider(provider)`; that
|
|
15
|
+
hook is a no-op without a palette.
|
|
16
|
+
- **Components**: `CommandPalette` = `CommandPaletteTrigger` + `CommandPaletteDialog`.
|
|
17
|
+
Mount it **once**: the dialog owns the global shortcut (`hotkeys`, default
|
|
18
|
+
`['Mod+K']`; `false` for none). Place it with `AppShell` `slotItems`
|
|
19
|
+
(`header.center` is the usual slot); it is a movable item like any other.
|
|
20
|
+
Without a `palette` prop or context it builds one from `providers`/`navigate`.
|
|
21
|
+
- **Pure core**: `match.ts` (accent/case-insensitive, multi-term, word-start
|
|
22
|
+
then in-order fuzzy; returns title ranges), `rank.ts` (groups, limits,
|
|
23
|
+
boost), `hotkeys.ts` (`Mod` = Ctrl or Meta). Node-testable, no Svelte.
|
|
24
|
+
|
|
25
|
+
## Provider contract (`types.ts`)
|
|
26
|
+
|
|
27
|
+
A `PaletteProvider` is `{ id, label, order?, items?, search?, minQueryLength?,
|
|
28
|
+
limit? }`.
|
|
29
|
+
|
|
30
|
+
- `items(ctx)` rows are re-read **every time the palette opens** and matched
|
|
31
|
+
locally by the palette. Sync results land in the same tick; async ones fill
|
|
32
|
+
in (`busy`).
|
|
33
|
+
- `search(query, { signal, limit })` runs after the debounce with an abort
|
|
34
|
+
signal that fires on the next keystroke or close. Its rows are shown as
|
|
35
|
+
returned (not re-filtered); earlier rows stay until the new ones arrive. A
|
|
36
|
+
superseded result is dropped, never shown.
|
|
37
|
+
- Registering an id again replaces the provider; a stale disposer is a no-op.
|
|
38
|
+
The replaced provider's cached rows are cleared and its in-flight `items` or
|
|
39
|
+
`search` results (and failures) are dropped, never shown or reported.
|
|
40
|
+
- Authorization stays with the provider. A failing provider is reported through
|
|
41
|
+
`onError` (default: logger warning) and marked in `failedProviders`; the
|
|
42
|
+
palette keeps working. The palette is not a security boundary.
|
|
43
|
+
- A row with `run` runs; else a row with `href` goes through `navigate`
|
|
44
|
+
(default `window.location.assign`; SvelteKit apps pass `goto`). The palette
|
|
45
|
+
closes first, then runs.
|
|
46
|
+
|
|
47
|
+
## Shipped providers (`providers.ts`)
|
|
48
|
+
|
|
49
|
+
- `createNavigationProvider({ nav, groups })`: rows for the shell nav. Pass
|
|
50
|
+
getters over `useShellLayout().applied` to honor sections a user hid.
|
|
51
|
+
- `createModelProviders({ manifest, ... })` returns `[commands, records]`:
|
|
52
|
+
- eligibility is `navigableManifestEntries` (exported from
|
|
53
|
+
`workspace/manifest-nav.ts`), so it agrees with manifest-derived nav;
|
|
54
|
+
- commands: "New <noun>" for models whose REST `create` route exists;
|
|
55
|
+
destinations come from `hrefs` or the `<pagesBasePath>/<collection>[/new|/id]`
|
|
56
|
+
convention (the app owns its page routes);
|
|
57
|
+
- records: `GET <apiBasePath>/<collection>?<field>[like]=%q%` per model, one
|
|
58
|
+
text field each (first of `title`, `name`, `displayName`, ... that is a
|
|
59
|
+
non-sensitive, non-enum text field; override with `searchFields`), capped by
|
|
60
|
+
`maxModels` and `concurrency`, interleaved so one model cannot crowd out
|
|
61
|
+
the rest. The default fetch reads the generated list envelope
|
|
62
|
+
`{ items, count, limit, offset }` (a bare array also works; any other body
|
|
63
|
+
counts as no results). 401/403/404 mean "no results" for that model; the search throws
|
|
64
|
+
only when **every** request fails.
|
|
65
|
+
- Known limit: `LIKE` is case-insensitive on SQLite and case-sensitive on
|
|
66
|
+
PostgreSQL (sdk#1192). Supply `searchRows` to use a server search endpoint
|
|
67
|
+
instead.
|
|
68
|
+
|
|
69
|
+
## Accessibility and keyboard
|
|
70
|
+
|
|
71
|
+
WAI-ARIA combobox: focus stays in the search box; `aria-activedescendant`
|
|
72
|
+
names the highlighted option; options sit in labelled groups inside a
|
|
73
|
+
`listbox`; a polite status region announces counts and "no results". Up/Down
|
|
74
|
+
(wrapping, skipping disabled rows), PageUp/PageDown, Enter, Escape (the
|
|
75
|
+
`Modal` closes). Disabled rows stay visible with their reason as the tooltip.
|
|
76
|
+
A bare-key shortcut (`/`) never fires while typing in a field or during IME
|
|
77
|
+
composition; a modified one (`Mod+K`) does. Strings are in
|
|
78
|
+
`src/i18n/strings.palette.ts` (registered in `i18n/server.ts`); provider group
|
|
79
|
+
labels default to English and are options on the shipped providers.
|
|
80
|
+
|
|
81
|
+
## Not yet wired
|
|
82
|
+
|
|
83
|
+
Recipe surfaces (#3708): a recipe will declare the palette as a shell widget
|
|
84
|
+
and its own providers (for example an assistant provider) once non-model
|
|
85
|
+
surfaces exist. Until then hosts mount `CommandPalette` themselves.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# smrt-svelte/import-export
|
|
2
|
+
|
|
3
|
+
Module semantics for `src/components/import-export/` (`./import-export`).
|
|
4
|
+
Package orientation and cross-module invariants live in
|
|
5
|
+
[../AGENTS.md](../AGENTS.md); read that first.
|
|
6
|
+
|
|
7
|
+
## Shape
|
|
8
|
+
|
|
9
|
+
`ImportExport.svelte` is a thin shell over a pure layer. The pure files import
|
|
10
|
+
no Svelte or DOM API and are unit-tested in Node; keep logic there, not in the
|
|
11
|
+
component.
|
|
12
|
+
|
|
13
|
+
| File | Owns |
|
|
14
|
+
|------|------|
|
|
15
|
+
| `csv.ts` | RFC 4180 parse/format, delimiter detection, BOM, formula neutralization |
|
|
16
|
+
| `validate.ts` | `coerceCell`, `validateMapping`, `validateRows` (the dry run) |
|
|
17
|
+
| `mapping.ts` | `autoMapColumns`, `setColumnTarget` |
|
|
18
|
+
| `fields.ts` | `fieldsFromCollectionDefinition`: manifest + resolved field policy to `ImportExportField[]` |
|
|
19
|
+
| `collection-adapter.ts` | `createCollectionImportExport`: generated CRUD fetchers to `createRecord` / `loadRows` |
|
|
20
|
+
| `runner.ts` | `runImport`: bounded-concurrency create, per-row failures, abort |
|
|
21
|
+
| `export.ts` / `report.ts` | `buildExportFile`, `buildErrorReport` |
|
|
22
|
+
| `download.ts` | the only DOM touch: Blob download (overridable via `ondownload`) |
|
|
23
|
+
|
|
24
|
+
## Invariants
|
|
25
|
+
|
|
26
|
+
- **Field policy is a hard boundary.** A supplied resolved policy is an
|
|
27
|
+
allowlist: a field it omits is neither importable nor exportable (manifest
|
|
28
|
+
defaults apply only with no policy). `hidden` fields are neither importable
|
|
29
|
+
nor exportable; `locked` fields are not importable but keep their resolved
|
|
30
|
+
default; tenant fields (`tenantId`) are never imported (the server sets
|
|
31
|
+
them). `id` is export-only. Do not add a path that writes a non-importable
|
|
32
|
+
field: only importable and `locked` fields take a default; hidden and tenant
|
|
33
|
+
defaults never enter the payload.
|
|
34
|
+
- **Nothing is written during validation.** `validateRows` returns no records
|
|
35
|
+
while `mappingIssues` is non-empty (an unmapped required field would be
|
|
36
|
+
written without its value). `records` only ever holds fully valid rows;
|
|
37
|
+
there is no partial-row import.
|
|
38
|
+
- **Strict typing.** Integers must be JavaScript-safe integers (money is
|
|
39
|
+
integer minor units, never decimals); datetimes are strict ISO 8601 with a
|
|
40
|
+
missing offset meaning UTC; `3/5/2026` is rejected, not guessed. Booleans
|
|
41
|
+
accept `true/false/yes/no/y/n/1/0/on/off`. Enum matching is
|
|
42
|
+
case-insensitive and writes the canonical option.
|
|
43
|
+
- **Defaults fill empties.** An empty or unmapped cell takes the field's
|
|
44
|
+
resolved default; a required field with a default is satisfied.
|
|
45
|
+
- **Exports are spreadsheet-safe.** Text cells starting with `= + - @`, tab or
|
|
46
|
+
CR are prefixed with `'`; numeric and boolean columns are exempt so negative
|
|
47
|
+
numbers survive. Import strips that prefix back off text cells. The error
|
|
48
|
+
report is neutralized too, because it echoes user data.
|
|
49
|
+
- **Export never truncates silently.** `loadRows` pages with `limit`/`offset`
|
|
50
|
+
and throws `ExportLimitError` past `maxRows` (default 50,000).
|
|
51
|
+
- **Row failures are per row.** `runImport` continues past a rejected row and
|
|
52
|
+
reports it by file line; abort stops starting new rows and counts in-flight
|
|
53
|
+
ones.
|
|
54
|
+
- **Messages are i18n keys.** Issue text lives under `ui.importExport.issue.*`
|
|
55
|
+
in `src/i18n/strings.import-export.ts` (registered in `i18n/server.ts`);
|
|
56
|
+
`describeIssue` falls back to English defaults for the pure layer.
|
|
57
|
+
|
|
58
|
+
## Not here
|
|
59
|
+
|
|
60
|
+
Native `.xlsx` read/write (save as CSV first), server-side dry-run endpoints,
|
|
61
|
+
upsert-by-key, and recipe/nav wiring (offering the panel on list screens waits
|
|
62
|
+
on the recipe surface contract, #3708).
|