@happyvertical/smrt-svelte 0.55.6 → 0.55.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/AGENTS.md +17 -2
  2. package/README.md +83 -4
  3. package/agents/workspace.md +202 -5
  4. package/dist/browser-ai/adapters/stt/dictation-source.d.ts.map +1 -1
  5. package/dist/browser-ai/adapters/stt/dictation-source.js +8 -2
  6. package/dist/browser-ai/adapters/stt/factory.d.ts +6 -3
  7. package/dist/browser-ai/adapters/stt/factory.d.ts.map +1 -1
  8. package/dist/browser-ai/adapters/stt/factory.js +19 -9
  9. package/dist/browser-ai/adapters/stt/index.d.ts +3 -1
  10. package/dist/browser-ai/adapters/stt/index.d.ts.map +1 -1
  11. package/dist/browser-ai/adapters/stt/index.js +3 -1
  12. package/dist/browser-ai/adapters/stt/local-speech-model.d.ts +111 -0
  13. package/dist/browser-ai/adapters/stt/local-speech-model.d.ts.map +1 -0
  14. package/dist/browser-ai/adapters/stt/local-speech-model.js +377 -0
  15. package/dist/browser-ai/adapters/stt/local-speech.d.ts +82 -0
  16. package/dist/browser-ai/adapters/stt/local-speech.d.ts.map +1 -0
  17. package/dist/browser-ai/adapters/stt/local-speech.js +317 -0
  18. package/dist/browser-ai/adapters/stt/local-speech.worker.d.ts +2 -0
  19. package/dist/browser-ai/adapters/stt/local-speech.worker.d.ts.map +1 -0
  20. package/dist/browser-ai/adapters/stt/local-speech.worker.js +32 -0
  21. package/dist/browser-ai/adapters/stt/transformers-env.d.ts +8 -0
  22. package/dist/browser-ai/adapters/stt/transformers-env.d.ts.map +1 -0
  23. package/dist/browser-ai/adapters/stt/transformers-env.js +17 -0
  24. package/dist/browser-ai/adapters/stt/types.d.ts +54 -9
  25. package/dist/browser-ai/adapters/stt/types.d.ts.map +1 -1
  26. package/dist/browser-ai/capabilities/speech-probe.d.ts +29 -0
  27. package/dist/browser-ai/capabilities/speech-probe.d.ts.map +1 -0
  28. package/dist/browser-ai/capabilities/speech-probe.js +19 -0
  29. package/dist/browser-ai/core/errors.d.ts +9 -0
  30. package/dist/browser-ai/core/errors.d.ts.map +1 -1
  31. package/dist/browser-ai/core/errors.js +12 -0
  32. package/dist/browser-ai/index.d.ts +1 -0
  33. package/dist/browser-ai/index.d.ts.map +1 -1
  34. package/dist/browser-ai/index.js +1 -0
  35. package/dist/components/app/AppShell.svelte +587 -33
  36. package/dist/components/app/AppShell.svelte.d.ts +75 -4
  37. package/dist/components/app/AppShell.svelte.d.ts.map +1 -1
  38. package/dist/components/app/DockToggles.svelte +0 -1
  39. package/dist/components/app/dock-toggle.d.ts +7 -1
  40. package/dist/components/app/dock-toggle.d.ts.map +1 -1
  41. package/dist/components/app/index.d.ts +2 -1
  42. package/dist/components/app/index.d.ts.map +1 -1
  43. package/dist/components/app/slot-item.d.ts +21 -0
  44. package/dist/components/app/slot-item.d.ts.map +1 -0
  45. package/dist/components/app/slot-item.js +1 -0
  46. package/dist/components/forms/AddressInput.svelte +2 -15
  47. package/dist/components/forms/AddressInput.svelte.d.ts.map +1 -1
  48. package/dist/components/forms/DateRangeInput.svelte +2 -15
  49. package/dist/components/forms/DateRangeInput.svelte.d.ts.map +1 -1
  50. package/dist/components/forms/DateTimeInput.svelte +8 -15
  51. package/dist/components/forms/DateTimeInput.svelte.d.ts.map +1 -1
  52. package/dist/components/forms/MeasurementInput.svelte +2 -15
  53. package/dist/components/forms/MeasurementInput.svelte.d.ts.map +1 -1
  54. package/dist/components/forms/MoneyInput.svelte +8 -25
  55. package/dist/components/forms/MoneyInput.svelte.d.ts.map +1 -1
  56. package/dist/components/forms/NumberInput.svelte +7 -15
  57. package/dist/components/forms/NumberInput.svelte.d.ts.map +1 -1
  58. package/dist/components/forms/PhoneInput.svelte +7 -15
  59. package/dist/components/forms/PhoneInput.svelte.d.ts.map +1 -1
  60. package/dist/components/forms/SelectInput.svelte +34 -40
  61. package/dist/components/forms/SelectInput.svelte.d.ts.map +1 -1
  62. package/dist/components/forms/TextInput.svelte +20 -37
  63. package/dist/components/forms/TextInput.svelte.d.ts.map +1 -1
  64. package/dist/components/forms/TextareaInput.svelte +19 -35
  65. package/dist/components/forms/TextareaInput.svelte.d.ts.map +1 -1
  66. package/dist/components/sortable/Sortable.svelte +6 -33
  67. package/dist/components/sortable/Sortable.svelte.d.ts.map +1 -1
  68. package/dist/components/sortable/announce.d.ts +5 -0
  69. package/dist/components/sortable/announce.d.ts.map +1 -0
  70. package/dist/components/sortable/announce.js +33 -0
  71. package/dist/components/workspace/admin-shell/AdminShell.svelte +424 -58
  72. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts +27 -2
  73. package/dist/components/workspace/admin-shell/AdminShell.svelte.d.ts.map +1 -1
  74. package/dist/components/workspace/admin-shell/ShellBrand.svelte +86 -0
  75. package/dist/components/workspace/admin-shell/ShellBrand.svelte.d.ts +18 -0
  76. package/dist/components/workspace/admin-shell/ShellBrand.svelte.d.ts.map +1 -0
  77. package/dist/components/workspace/admin-shell/ShellIconButton.svelte +77 -0
  78. package/dist/components/workspace/admin-shell/ShellIconButton.svelte.d.ts +26 -0
  79. package/dist/components/workspace/admin-shell/ShellIconButton.svelte.d.ts.map +1 -0
  80. package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte +88 -16
  81. package/dist/components/workspace/admin-shell/ShellLayoutEditor.svelte.d.ts.map +1 -1
  82. package/dist/components/workspace/admin-shell/ShellNavEditor.svelte +531 -0
  83. package/dist/components/workspace/admin-shell/ShellNavEditor.svelte.d.ts +26 -0
  84. package/dist/components/workspace/admin-shell/ShellNavEditor.svelte.d.ts.map +1 -0
  85. package/dist/components/workspace/admin-shell/ShellSectionIcon.svelte +57 -0
  86. package/dist/components/workspace/admin-shell/ShellSectionIcon.svelte.d.ts +22 -0
  87. package/dist/components/workspace/admin-shell/ShellSectionIcon.svelte.d.ts.map +1 -0
  88. package/dist/components/workspace/admin-shell/ShellSectionMenu.svelte +290 -0
  89. package/dist/components/workspace/admin-shell/ShellSectionMenu.svelte.d.ts +32 -0
  90. package/dist/components/workspace/admin-shell/ShellSectionMenu.svelte.d.ts.map +1 -0
  91. package/dist/components/workspace/admin-shell/TenantNav.svelte +67 -2
  92. package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts +8 -0
  93. package/dist/components/workspace/admin-shell/TenantNav.svelte.d.ts.map +1 -1
  94. package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts +67 -0
  95. package/dist/components/workspace/admin-shell/layout-controller.svelte.d.ts.map +1 -1
  96. package/dist/components/workspace/admin-shell/layout-controller.svelte.js +129 -1
  97. package/dist/components/workspace/admin-shell/layout.d.ts +122 -0
  98. package/dist/components/workspace/admin-shell/layout.d.ts.map +1 -1
  99. package/dist/components/workspace/admin-shell/layout.js +410 -10
  100. package/dist/components/workspace/admin-shell/settings.js +4 -4
  101. package/dist/components/workspace/admin-shell/shell-icons.d.ts +45 -0
  102. package/dist/components/workspace/admin-shell/shell-icons.d.ts.map +1 -0
  103. package/dist/components/workspace/admin-shell/shell-icons.js +66 -0
  104. package/dist/components/workspace/admin-shell/slot-labels.d.ts +33 -0
  105. package/dist/components/workspace/admin-shell/slot-labels.d.ts.map +1 -0
  106. package/dist/components/workspace/admin-shell/slot-labels.js +33 -0
  107. package/dist/components/workspace/admin-shell/slots.d.ts +50 -0
  108. package/dist/components/workspace/admin-shell/slots.d.ts.map +1 -0
  109. package/dist/components/workspace/admin-shell/slots.js +77 -0
  110. package/dist/components/workspace/admin-shell/state.svelte.d.ts +35 -0
  111. package/dist/components/workspace/admin-shell/state.svelte.d.ts.map +1 -1
  112. package/dist/components/workspace/admin-shell/state.svelte.js +111 -0
  113. package/dist/components/workspace/admin-shell/types.d.ts +101 -1
  114. package/dist/components/workspace/admin-shell/types.d.ts.map +1 -1
  115. package/dist/components/workspace/index.d.ts +9 -2
  116. package/dist/components/workspace/index.d.ts.map +1 -1
  117. package/dist/components/workspace/index.js +6 -0
  118. package/dist/i18n/strings.workspace.d.ts +49 -0
  119. package/dist/i18n/strings.workspace.d.ts.map +1 -1
  120. package/dist/i18n/strings.workspace.js +49 -0
  121. package/package.json +30 -19
  122. package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts +0 -51
  123. package/dist/browser-ai/adapters/stt/whisper-wasm.d.ts.map +0 -1
  124. package/dist/browser-ai/adapters/stt/whisper-wasm.js +0 -380
@@ -1,380 +0,0 @@
1
- /**
2
- * Whisper adapter using @xenova/transformers (v2)
3
- *
4
- * Uses Whisper models running in the browser via WASM.
5
- * V2 uses a simpler, more stable pipeline API.
6
- * Requires downloading model files (~40MB-150MB depending on model size).
7
- */
8
- import { importOptional } from '@happyvertical/smrt-ui/utils/import-optional.js';
9
- import { CapabilityNotAvailableError, InitializationError, } from '../../core/errors.js';
10
- // Model IDs - using Xenova's quantized models for v2
11
- const MODEL_IDS = {
12
- tiny: 'Xenova/whisper-tiny.en',
13
- base: 'Xenova/whisper-base.en',
14
- small: 'Xenova/whisper-small.en',
15
- };
16
- // Approximate download sizes
17
- const MODEL_SIZES = {
18
- tiny: 40 * 1024 * 1024, // ~40MB
19
- base: 75 * 1024 * 1024, // ~75MB
20
- small: 150 * 1024 * 1024, // ~150MB
21
- };
22
- /**
23
- * Whisper adapter for high-accuracy speech recognition
24
- * Uses transformers.js v2 pipeline API
25
- */
26
- export class WhisperWasmSTTAdapter {
27
- type = 'whisper-wasm';
28
- _initState = 'uninitialized';
29
- options;
30
- _isListening = false;
31
- // Audio recording
32
- mediaRecorder = null;
33
- audioChunks = [];
34
- stream = null;
35
- // Event listeners
36
- resultListeners = new Set();
37
- errorListeners = new Set();
38
- startListeners = new Set();
39
- endListeners = new Set();
40
- // Transformers.js v2 pipeline
41
- transcriber = null;
42
- // Promise that resolves when transcription is complete
43
- processingPromise = null;
44
- processingResolve = null;
45
- constructor(options = {}) {
46
- this.options = {
47
- type: 'whisper-wasm',
48
- modelSize: 'tiny',
49
- defaultLanguage: 'en',
50
- ...options,
51
- };
52
- }
53
- get initState() {
54
- return this._initState;
55
- }
56
- async ensureInitialized(onProgress) {
57
- if (this._initState === 'ready')
58
- return;
59
- if (this._initState === 'initializing') {
60
- return new Promise((resolve, reject) => {
61
- const check = () => {
62
- if (this._initState === 'ready')
63
- resolve();
64
- else if (this._initState === 'error')
65
- reject(new Error('Initialization failed'));
66
- else
67
- setTimeout(check, 50);
68
- };
69
- check();
70
- });
71
- }
72
- this._initState = 'initializing';
73
- try {
74
- // Check WASM support
75
- if (typeof WebAssembly === 'undefined') {
76
- throw new CapabilityNotAvailableError('WebAssembly', 'whisper-wasm');
77
- }
78
- const modelSize = this.options.modelSize || 'tiny';
79
- const modelId = MODEL_IDS[modelSize] || MODEL_IDS.tiny;
80
- const totalSize = MODEL_SIZES[modelSize] || MODEL_SIZES.tiny;
81
- onProgress?.({
82
- state: 'downloading',
83
- bytesLoaded: 0,
84
- bytesTotal: totalSize,
85
- percent: 0,
86
- currentFile: `${modelId}`,
87
- });
88
- // Dynamically import transformers.js v2
89
- const transformers = await this.importTransformers();
90
- const { pipeline } = transformers;
91
- console.log('[Whisper v2] Loading pipeline...');
92
- // Create ASR pipeline - v2 uses simpler API
93
- this.transcriber = await pipeline('automatic-speech-recognition', modelId, {
94
- progress_callback: (progress) => {
95
- if (progress.status === 'progress' &&
96
- progress.progress !== undefined) {
97
- const percent = Math.round(progress.progress);
98
- onProgress?.({
99
- state: 'downloading',
100
- bytesLoaded: Math.round((totalSize * percent) / 100),
101
- bytesTotal: totalSize,
102
- percent,
103
- currentFile: progress.file || modelId,
104
- });
105
- }
106
- },
107
- });
108
- console.log('[Whisper v2] Pipeline loaded successfully');
109
- onProgress?.({
110
- state: 'complete',
111
- bytesLoaded: totalSize,
112
- bytesTotal: totalSize,
113
- percent: 100,
114
- });
115
- this._initState = 'ready';
116
- }
117
- catch (error) {
118
- this._initState = 'error';
119
- console.error('[Whisper v2] Initialization error:', error);
120
- throw error instanceof Error
121
- ? error
122
- : new InitializationError('whisper-wasm', String(error));
123
- }
124
- }
125
- // Store the transformers module for read_audio utility
126
- transformersModule = null;
127
- async importTransformers() {
128
- try {
129
- // Try v2 (@xenova/transformers) first
130
- this.transformersModule = await importOptional('@xenova/transformers');
131
- this.configureTransformersEnv(this.transformersModule);
132
- return this.transformersModule;
133
- }
134
- catch {
135
- try {
136
- // Fall back to v3 (@huggingface/transformers)
137
- this.transformersModule = await importOptional('@huggingface/transformers');
138
- this.configureTransformersEnv(this.transformersModule);
139
- return this.transformersModule;
140
- }
141
- catch {
142
- throw new InitializationError('whisper-wasm', 'Neither @xenova/transformers nor @huggingface/transformers installed.');
143
- }
144
- }
145
- }
146
- /**
147
- * Configure transformers.js environment settings
148
- */
149
- configureTransformersEnv(transformers) {
150
- if (transformers.env) {
151
- // Default to remote models unless explicitly allowing local
152
- const allowLocal = this.options.allowLocalModels ?? false;
153
- transformers.env.allowLocalModels = allowLocal;
154
- // Always use browser cache for better performance
155
- transformers.env.useBrowserCache = true;
156
- console.log('[Whisper v2] Configured env:', {
157
- allowLocalModels: allowLocal,
158
- useBrowserCache: true,
159
- });
160
- }
161
- }
162
- getCapabilities() {
163
- const modelSize = this.options.modelSize || 'tiny';
164
- return {
165
- continuous: false, // Whisper processes chunks, not continuous
166
- interimResults: false, // No interim results in standard Whisper
167
- languages: [
168
- 'en',
169
- 'es',
170
- 'fr',
171
- 'de',
172
- 'it',
173
- 'pt',
174
- 'nl',
175
- 'pl',
176
- 'ru',
177
- 'zh',
178
- 'ja',
179
- 'ko',
180
- 'ar',
181
- 'hi',
182
- 'tr',
183
- 'vi',
184
- 'th',
185
- ],
186
- accuracy: modelSize === 'tiny' ? 'medium' : 'high',
187
- requiresDownload: true,
188
- downloadSize: MODEL_SIZES[modelSize] || MODEL_SIZES.tiny,
189
- };
190
- }
191
- async start(_options = {}) {
192
- await this.ensureInitialized();
193
- if (this._isListening)
194
- return;
195
- try {
196
- // Get microphone access
197
- this.stream = await navigator.mediaDevices.getUserMedia({
198
- audio: {
199
- channelCount: 1,
200
- sampleRate: 16000, // Whisper expects 16kHz
201
- },
202
- });
203
- this.audioChunks = [];
204
- // Create MediaRecorder
205
- this.mediaRecorder = new MediaRecorder(this.stream, {
206
- mimeType: this.getSupportedMimeType(),
207
- });
208
- this.mediaRecorder.ondataavailable = (event) => {
209
- if (event.data.size > 0) {
210
- this.audioChunks.push(event.data);
211
- }
212
- };
213
- this.mediaRecorder.onstop = async () => {
214
- await this.processRecording();
215
- this.processingResolve?.();
216
- };
217
- // Create promise that resolves when transcription is done
218
- this.processingPromise = new Promise((resolve) => {
219
- this.processingResolve = resolve;
220
- });
221
- // Start with timeslice to capture audio in chunks (more reliable)
222
- this.mediaRecorder.start(500);
223
- this._isListening = true;
224
- for (const cb of this.startListeners) {
225
- cb();
226
- }
227
- }
228
- catch (error) {
229
- const err = error instanceof Error ? error : new Error(String(error));
230
- for (const cb of this.errorListeners) {
231
- cb(err);
232
- }
233
- throw err;
234
- }
235
- }
236
- getSupportedMimeType() {
237
- const types = [
238
- 'audio/webm;codecs=opus',
239
- 'audio/webm',
240
- 'audio/ogg;codecs=opus',
241
- 'audio/mp4',
242
- ];
243
- for (const type of types) {
244
- if (MediaRecorder.isTypeSupported(type)) {
245
- return type;
246
- }
247
- }
248
- return 'audio/webm'; // Fallback
249
- }
250
- async processRecording() {
251
- console.log('[Whisper v2] processRecording called, chunks:', this.audioChunks.length);
252
- if (this.audioChunks.length === 0) {
253
- console.log('[Whisper v2] No audio chunks captured');
254
- return;
255
- }
256
- // Use the actual MIME type from the recorder
257
- const mimeType = this.mediaRecorder?.mimeType || 'audio/webm';
258
- const audioBlob = new Blob(this.audioChunks, { type: mimeType });
259
- console.log('[Whisper v2] Audio blob size:', audioBlob.size, 'type:', mimeType);
260
- try {
261
- if (!this.transcriber || !this.transformersModule) {
262
- throw new Error('Whisper not initialized');
263
- }
264
- // Decode audio using Web Audio API for better format support
265
- console.log('[Whisper v2] Decoding audio with Web Audio API...');
266
- const arrayBuffer = await audioBlob.arrayBuffer();
267
- const audioContext = new AudioContext({ sampleRate: 16000 });
268
- const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
269
- // Get mono audio data
270
- const audioData = audioBuffer.getChannelData(0);
271
- console.log('[Whisper v2] Audio decoded - length:', audioData.length, 'samples, duration:', audioBuffer.duration.toFixed(2), 's');
272
- // Debug: check audio levels
273
- let maxLevel = 0;
274
- let sumSquares = 0;
275
- for (let i = 0; i < audioData.length; i++) {
276
- const abs = Math.abs(audioData[i]);
277
- if (abs > maxLevel)
278
- maxLevel = abs;
279
- sumSquares += audioData[i] * audioData[i];
280
- }
281
- const rms = Math.sqrt(sumSquares / audioData.length);
282
- console.log('[Whisper v2] Audio levels - max:', maxLevel.toFixed(4), 'RMS:', rms.toFixed(4));
283
- if (maxLevel < 0.01) {
284
- console.warn('[Whisper v2] Audio appears to be silent or very quiet!');
285
- }
286
- // Close audio context
287
- await audioContext.close();
288
- console.log('[Whisper v2] Starting transcription...');
289
- const startTime = performance.now();
290
- // Pass the Float32Array audio data
291
- const result = await this.transcriber(audioData);
292
- const elapsedTime = performance.now() - startTime;
293
- console.log('[Whisper v2] Transcription completed in', Math.round(elapsedTime), 'ms');
294
- console.log('[Whisper v2] Result:', result);
295
- const text = result.text?.trim() || '';
296
- console.log('[Whisper v2] Transcription result:', text);
297
- const sttResult = {
298
- text,
299
- confidence: 0.95,
300
- isFinal: true,
301
- timestamp: Date.now(),
302
- };
303
- for (const cb of this.resultListeners) {
304
- cb(sttResult);
305
- }
306
- }
307
- catch (error) {
308
- console.error('[Whisper v2] Processing error:', error);
309
- const err = error instanceof Error ? error : new Error(String(error));
310
- for (const cb of this.errorListeners) {
311
- cb(err);
312
- }
313
- }
314
- }
315
- async stop() {
316
- if (this.mediaRecorder && this._isListening) {
317
- this.mediaRecorder.stop();
318
- if (this.stream) {
319
- for (const track of this.stream.getTracks()) {
320
- track.stop();
321
- }
322
- }
323
- this.stream = null;
324
- if (this.processingPromise) {
325
- await this.processingPromise;
326
- this.processingPromise = null;
327
- this.processingResolve = null;
328
- }
329
- this._isListening = false;
330
- for (const cb of this.endListeners) {
331
- cb();
332
- }
333
- }
334
- }
335
- abort() {
336
- if (this.mediaRecorder && this._isListening) {
337
- this.mediaRecorder.stop();
338
- this._isListening = false;
339
- this.audioChunks = [];
340
- if (this.stream) {
341
- for (const track of this.stream.getTracks()) {
342
- track.stop();
343
- }
344
- }
345
- this.stream = null;
346
- for (const cb of this.endListeners) {
347
- cb();
348
- }
349
- }
350
- }
351
- isListening() {
352
- return this._isListening;
353
- }
354
- onResult(callback) {
355
- this.resultListeners.add(callback);
356
- return () => this.resultListeners.delete(callback);
357
- }
358
- onError(callback) {
359
- this.errorListeners.add(callback);
360
- return () => this.errorListeners.delete(callback);
361
- }
362
- onStart(callback) {
363
- this.startListeners.add(callback);
364
- return () => this.startListeners.delete(callback);
365
- }
366
- onEnd(callback) {
367
- this.endListeners.add(callback);
368
- return () => this.endListeners.delete(callback);
369
- }
370
- async dispose() {
371
- this.abort();
372
- this.transcriber = null;
373
- this.transformersModule = null;
374
- this.resultListeners.clear();
375
- this.errorListeners.clear();
376
- this.startListeners.clear();
377
- this.endListeners.clear();
378
- this._initState = 'uninitialized';
379
- }
380
- }