@wcstack/speech 1.14.0 → 1.16.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/dist/index.d.ts CHANGED
@@ -1,3 +1,24 @@
1
+ interface IWcBindableProperty {
2
+ readonly name: string;
3
+ readonly event: string;
4
+ readonly getter?: (event: Event) => any;
5
+ }
6
+ interface IWcBindableInput {
7
+ readonly name: string;
8
+ readonly attribute?: string;
9
+ }
10
+ interface IWcBindableCommand {
11
+ readonly name: string;
12
+ readonly async?: boolean;
13
+ }
14
+ interface IWcBindable {
15
+ readonly protocol: "wc-bindable";
16
+ readonly version: 1;
17
+ readonly properties: readonly IWcBindableProperty[];
18
+ readonly inputs?: readonly IWcBindableInput[];
19
+ readonly commands?: readonly IWcBindableCommand[];
20
+ }
21
+
1
22
  interface ITagNames {
2
23
  readonly speak: string;
3
24
  readonly listen: string;
@@ -20,26 +41,7 @@ interface IWritableConfig {
20
41
  listenTriggerAttribute?: string;
21
42
  tagNames?: IWritableTagNames;
22
43
  }
23
- interface IWcBindableProperty {
24
- readonly name: string;
25
- readonly event: string;
26
- readonly getter?: (event: Event) => any;
27
- }
28
- interface IWcBindableInput {
29
- readonly name: string;
30
- readonly attribute?: string;
31
- }
32
- interface IWcBindableCommand {
33
- readonly name: string;
34
- readonly async?: boolean;
35
- }
36
- interface IWcBindable {
37
- readonly protocol: "wc-bindable";
38
- readonly version: number;
39
- readonly properties: IWcBindableProperty[];
40
- readonly inputs?: IWcBindableInput[];
41
- readonly commands?: IWcBindableCommand[];
42
- }
44
+
43
45
  /**
44
46
  * Structured-clone-friendly snapshot of a `SpeechSynthesisVoice`. The live voice
45
47
  * objects are not serializable and cannot flow through data binding, so the Core
@@ -76,7 +78,7 @@ interface SpeakOptions {
76
78
  }
77
79
  /**
78
80
  * Value types for SpeakCore (headless) — the observable state properties. Use
79
- * with `bind()` from `@wc-bindable/core` for compile-time type checking.
81
+ * with `bind()` from `a wc-bindable binding core` for compile-time type checking.
80
82
  */
81
83
  interface WcsSpeakCoreValues {
82
84
  voices: SpeechVoiceInfo[];
@@ -260,6 +262,7 @@ declare class SpeakCore extends EventTarget {
260
262
  private _started;
261
263
  private _gen;
262
264
  private _voicesSubscribed;
265
+ private _ready;
263
266
  constructor(target?: EventTarget);
264
267
  get voices(): SpeechVoiceInfo[];
265
268
  get speaking(): boolean;
@@ -269,6 +272,8 @@ declare class SpeakCore extends EventTarget {
269
272
  get spokenWord(): string | null;
270
273
  get error(): WcsSpeakErrorDetail | null;
271
274
  get unsupported(): boolean;
275
+ /** Resolves once the first probe settles (immediate — see `_ready`). */
276
+ get ready(): Promise<void>;
272
277
  private _setVoices;
273
278
  private _voicesEqual;
274
279
  private _setSpeaking;
@@ -299,6 +304,15 @@ declare class SpeakCore extends EventTarget {
299
304
  * not double-subscribe.
300
305
  */
301
306
  reinitVoices(): void;
307
+ /**
308
+ * Establish monitoring (§3.5). Synthesis is command-driven (speak/cancel), so
309
+ * observe() only (re-)establishes the live `voiceschanged` subscription —
310
+ * idempotent via reinitVoices()'s `_voicesSubscribed` guard, so the first
311
+ * connect after construction does not double-subscribe while a reconnect after
312
+ * dispose() does. Returns the `ready` promise for SSR. Call from the Shell's
313
+ * connectedCallback.
314
+ */
315
+ observe(): Promise<void>;
302
316
  /**
303
317
  * Detach the live voiceschanged listener and neutralize any in-flight
304
318
  * utterance callbacks. Call from the Shell's `disconnectedCallback`.
@@ -327,10 +341,13 @@ declare class SpeakCore extends EventTarget {
327
341
  * charIndex / spokenWord / error / unsupported) via delegated getters.
328
342
  */
329
343
  declare class WcsSpeak extends HTMLElement {
344
+ static hasConnectedCallbackPromise: boolean;
330
345
  static wcBindable: IWcBindable;
331
346
  private _core;
332
347
  private _say;
348
+ private _connectedCallbackPromise;
333
349
  constructor();
350
+ get connectedCallbackPromise(): Promise<void>;
334
351
  get rate(): number;
335
352
  set rate(value: number);
336
353
  get pitch(): number;
@@ -404,6 +421,7 @@ declare class ListenCore extends EventTarget {
404
421
  private _permissionStatus;
405
422
  private _permissionSubscribed;
406
423
  private _permGen;
424
+ private _ready;
407
425
  constructor(target?: EventTarget);
408
426
  get interimTranscript(): string;
409
427
  get finalTranscript(): string;
@@ -412,6 +430,8 @@ declare class ListenCore extends EventTarget {
412
430
  get permission(): ListenPermissionState;
413
431
  get error(): WcsListenErrorDetail | null;
414
432
  get unsupported(): boolean;
433
+ /** Resolves once the first probe settles (immediate — see `_ready`). */
434
+ get ready(): Promise<void>;
415
435
  private _setInterim;
416
436
  private _setFinal;
417
437
  private _setResult;
@@ -432,6 +452,15 @@ declare class ListenCore extends EventTarget {
432
452
  * Re-establish the permission `change` subscription after a dispose().
433
453
  */
434
454
  reinitPermission(): void;
455
+ /**
456
+ * Establish monitoring (§3.5). Recognition is command-driven (start/stop), so
457
+ * observe() only (re-)establishes the live permission subscription — idempotent
458
+ * via reinitPermission()'s `_permissionSubscribed` guard, so the first connect
459
+ * after construction does not double-subscribe while a reconnect after dispose()
460
+ * does. Returns the `ready` promise for SSR. Call from the Shell's
461
+ * connectedCallback.
462
+ */
463
+ observe(): Promise<void>;
435
464
  /**
436
465
  * Stop recognition and detach the live permission listener. Call from the
437
466
  * Shell's `disconnectedCallback`.
@@ -458,10 +487,13 @@ declare class ListenCore extends EventTarget {
458
487
  * `continuous` attribute selects the auto-restarting session phase.
459
488
  */
460
489
  declare class WcsListen extends HTMLElement {
490
+ static hasConnectedCallbackPromise: boolean;
461
491
  static wcBindable: IWcBindable;
462
492
  private _core;
463
493
  private _trigger;
494
+ private _connectedCallbackPromise;
464
495
  constructor();
496
+ get connectedCallbackPromise(): Promise<void>;
465
497
  get lang(): string;
466
498
  set lang(value: string | null);
467
499
  get continuous(): boolean;
package/dist/index.esm.js CHANGED
@@ -125,6 +125,12 @@ class SpeakCore extends EventTarget {
125
125
  // construction does not double-subscribe, while a reconnect after dispose()
126
126
  // does re-subscribe.
127
127
  _voicesSubscribed = false;
128
+ // SSR: feature detection (`_setUnsupported`) and the initial `getVoices()` read
129
+ // are synchronous, and the `voiceschanged` subscription is established eagerly
130
+ // in the constructor, so there is no asynchronous probe to await before
131
+ // snapshotting — readiness is immediate. The Shell exposes this as
132
+ // connectedCallbackPromise.
133
+ _ready = Promise.resolve();
128
134
  constructor(target) {
129
135
  super();
130
136
  this._target = target ?? this;
@@ -162,6 +168,10 @@ class SpeakCore extends EventTarget {
162
168
  get unsupported() {
163
169
  return this._unsupported;
164
170
  }
171
+ /** Resolves once the first probe settles (immediate — see `_ready`). */
172
+ get ready() {
173
+ return this._ready;
174
+ }
165
175
  // --- State setters with event dispatch ---
166
176
  _setVoices(voices) {
167
177
  // Same-value guard, like the other setters. `voiceschanged` can fire several
@@ -388,6 +398,18 @@ class SpeakCore extends EventTarget {
388
398
  this._initVoices();
389
399
  }
390
400
  }
401
+ /**
402
+ * Establish monitoring (§3.5). Synthesis is command-driven (speak/cancel), so
403
+ * observe() only (re-)establishes the live `voiceschanged` subscription —
404
+ * idempotent via reinitVoices()'s `_voicesSubscribed` guard, so the first
405
+ * connect after construction does not double-subscribe while a reconnect after
406
+ * dispose() does. Returns the `ready` promise for SSR. Call from the Shell's
407
+ * connectedCallback.
408
+ */
409
+ observe() {
410
+ this.reinitVoices();
411
+ return this._ready;
412
+ }
391
413
  /**
392
414
  * Detach the live voiceschanged listener and neutralize any in-flight
393
415
  * utterance callbacks. Call from the Shell's `disconnectedCallback`.
@@ -527,6 +549,7 @@ function registerAutoTrigger() {
527
549
  * charIndex / spokenWord / error / unsupported) via delegated getters.
528
550
  */
529
551
  class WcsSpeak extends HTMLElement {
552
+ static hasConnectedCallbackPromise = true;
530
553
  static wcBindable = {
531
554
  ...SpeakCore.wcBindable,
532
555
  // Shell-level settable surface. `say` is a momentary reactive command-property
@@ -546,10 +569,14 @@ class WcsSpeak extends HTMLElement {
546
569
  };
547
570
  _core;
548
571
  _say = "";
572
+ _connectedCallbackPromise = Promise.resolve();
549
573
  constructor() {
550
574
  super();
551
575
  this._core = new SpeakCore(this);
552
576
  }
577
+ get connectedCallbackPromise() {
578
+ return this._connectedCallbackPromise;
579
+ }
553
580
  // --- Attribute accessors ---
554
581
  get rate() {
555
582
  return this._numberAttr("rate", 1);
@@ -694,9 +721,10 @@ class WcsSpeak extends HTMLElement {
694
721
  if (config.autoTrigger) {
695
722
  registerAutoTrigger();
696
723
  }
697
- // Revive the voiceschanged subscription after a reconnect (reparenting).
698
- // No-op on the first connect since the constructor already subscribed.
699
- this._core.reinitVoices();
724
+ // observe() revives the voiceschanged subscription after a reconnect
725
+ // (reparenting) and returns the readiness promise for SSR; it wraps
726
+ // reinitVoices() (no-op on the first connect — the constructor subscribed).
727
+ this._connectedCallbackPromise = this._core.observe();
700
728
  }
701
729
  disconnectedCallback() {
702
730
  // Detach event subscriptions and neutralize in-flight utterance callbacks.
@@ -769,6 +797,11 @@ class ListenCore extends EventTarget {
769
797
  _permissionStatus = null;
770
798
  _permissionSubscribed = false;
771
799
  _permGen = 0;
800
+ // SSR: feature detection (`_setUnsupported`) is synchronous and the permission
801
+ // `change` subscription is established eagerly in the constructor, so there is
802
+ // no asynchronous probe to await before snapshotting — readiness is immediate.
803
+ // The Shell exposes this as connectedCallbackPromise.
804
+ _ready = Promise.resolve();
772
805
  constructor(target) {
773
806
  super();
774
807
  this._target = target ?? this;
@@ -804,6 +837,10 @@ class ListenCore extends EventTarget {
804
837
  get unsupported() {
805
838
  return this._unsupported;
806
839
  }
840
+ /** Resolves once the first probe settles (immediate — see `_ready`). */
841
+ get ready() {
842
+ return this._ready;
843
+ }
807
844
  // --- State setters with event dispatch ---
808
845
  _setInterim(value) {
809
846
  if (this._interimTranscript === value)
@@ -901,6 +938,18 @@ class ListenCore extends EventTarget {
901
938
  this._initPermission();
902
939
  }
903
940
  }
941
+ /**
942
+ * Establish monitoring (§3.5). Recognition is command-driven (start/stop), so
943
+ * observe() only (re-)establishes the live permission subscription — idempotent
944
+ * via reinitPermission()'s `_permissionSubscribed` guard, so the first connect
945
+ * after construction does not double-subscribe while a reconnect after dispose()
946
+ * does. Returns the `ready` promise for SSR. Call from the Shell's
947
+ * connectedCallback.
948
+ */
949
+ observe() {
950
+ this.reinitPermission();
951
+ return this._ready;
952
+ }
904
953
  /**
905
954
  * Stop recognition and detach the live permission listener. Call from the
906
955
  * Shell's `disconnectedCallback`.
@@ -1133,6 +1182,7 @@ function registerListenAutoTrigger() {
1133
1182
  * `continuous` attribute selects the auto-restarting session phase.
1134
1183
  */
1135
1184
  class WcsListen extends HTMLElement {
1185
+ static hasConnectedCallbackPromise = true;
1136
1186
  static wcBindable = {
1137
1187
  ...ListenCore.wcBindable,
1138
1188
  properties: [
@@ -1151,10 +1201,14 @@ class WcsListen extends HTMLElement {
1151
1201
  };
1152
1202
  _core;
1153
1203
  _trigger = false;
1204
+ _connectedCallbackPromise = Promise.resolve();
1154
1205
  constructor() {
1155
1206
  super();
1156
1207
  this._core = new ListenCore(this);
1157
1208
  }
1209
+ get connectedCallbackPromise() {
1210
+ return this._connectedCallbackPromise;
1211
+ }
1158
1212
  // --- Attribute accessors ---
1159
1213
  get lang() {
1160
1214
  return this.getAttribute("lang") ?? "";
@@ -1277,7 +1331,9 @@ class WcsListen extends HTMLElement {
1277
1331
  if (config.autoTrigger) {
1278
1332
  registerListenAutoTrigger();
1279
1333
  }
1280
- this._core.reinitPermission();
1334
+ // observe() (re-)establishes the permission subscription and returns the
1335
+ // readiness promise for SSR; it wraps reinitPermission() (idempotent).
1336
+ this._connectedCallbackPromise = this._core.observe();
1281
1337
  if (!this.manual) {
1282
1338
  // Non-blocking auto-start, mirroring <wcs-geo>: start() is fired
1283
1339
  // unconditionally without first awaiting/inspecting the (async) permission
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/SpeakCore.ts","../src/autoTrigger.ts","../src/components/Speak.ts","../src/core/ListenCore.ts","../src/listenAutoTrigger.ts","../src/components/Listen.ts","../src/registerComponents.ts","../src/bootstrapSpeech.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n listenTriggerAttribute: string;\n tagNames: {\n speak: string;\n listen: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-speaktarget\",\n listenTriggerAttribute: \"data-listentarget\",\n tagNames: {\n speak: \"wcs-speak\",\n listen: \"wcs-listen\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Internal, mutable live config used by the components/autoTriggers (they read it\n// at call time so setConfig() takes effect without re-import). Typed as the\n// readonly IConfig at the export boundary — the `as IConfig` is a compile-time\n// view only and does NOT freeze the object, so this export must stay\n// package-internal (it is not re-exported from exports.ts). Public consumers get\n// the deep-frozen clone from getConfig() instead, which is the only safe\n// read-only handle.\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (typeof partialConfig.listenTriggerAttribute === \"string\") {\n _config.listenTriggerAttribute = partialConfig.listenTriggerAttribute;\n }\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import {\n IWcBindable, SpeakOptions, SpeechVoiceInfo, WcsSpeakErrorDetail,\n} from \"../types.js\";\n\n/**\n * Headless text-to-speech primitive. A thin, framework-agnostic wrapper around\n * the SpeechSynthesis API exposed through the wc-bindable protocol.\n *\n * It is the \"command\" half of the speech package (the recognition half is\n * ListenCore): state drives the element, never the reverse, except for the\n * observable progress/status it publishes back.\n *\n * - **speak(text, options)** queues an utterance. Like the native API, multiple\n * calls queue; `cancel()` clears the queue and stops the current utterance.\n * - **pause() / resume()** suspend and resume the queue.\n * - The observable surface mirrors the live SpeechSynthesis flags\n * (`speaking` / `paused` / `pending`) and exposes voice-list loading\n * (`voices`, which the API populates asynchronously via `voiceschanged`) plus\n * word-boundary progress (`charIndex` / `spokenWord`) for karaoke-style\n * highlighting.\n *\n * Unlike geolocation/clipboard there is no permission gate — synthesis needs no\n * user grant. Failures never throw: they surface through the `error` property so\n * they flow into the declarative state.\n */\nexport class SpeakCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"voices\", event: \"wcs-speak:voices-changed\" },\n { name: \"speaking\", event: \"wcs-speak:speaking-changed\" },\n { name: \"paused\", event: \"wcs-speak:paused-changed\" },\n { name: \"pending\", event: \"wcs-speak:pending-changed\" },\n { name: \"charIndex\", event: \"wcs-speak:boundary\", getter: (e: Event) => (e as CustomEvent).detail?.charIndex ?? null },\n { name: \"spokenWord\", event: \"wcs-speak:boundary\", getter: (e: Event) => (e as CustomEvent).detail?.word ?? null },\n { name: \"error\", event: \"wcs-speak:error\" },\n { name: \"unsupported\", event: \"wcs-speak:unsupported-changed\" },\n ],\n commands: [\n { name: \"speak\" },\n { name: \"cancel\" },\n { name: \"pause\" },\n { name: \"resume\" },\n ],\n };\n\n private _target: EventTarget;\n\n private _voices: SpeechVoiceInfo[] = [];\n private _rawVoices: SpeechSynthesisVoice[] = [];\n private _speaking: boolean = false;\n private _paused: boolean = false;\n private _pending: boolean = false;\n private _charIndex: number | null = null;\n private _spokenWord: string | null = null;\n private _error: WcsSpeakErrorDetail | null = null;\n private _unsupported: boolean = false;\n\n // Count of utterances submitted via speak() but not yet started, and of\n // utterances started but not yet ended/errored. `pending`/`speaking` are\n // derived from these so the queue model is reflected accurately even when\n // several utterances are in flight.\n private _queued: number = 0;\n private _started: number = 0;\n\n // Monotonic id of the current synthesis lifecycle. Bumped by cancel() and\n // dispose(). Each speak() captures it; every utterance event handler bails if\n // it is stale, so a queued/canceled utterance's late callback (notably the\n // \"canceled\" error the browser fires from cancel()) never mutates state or\n // dispatches on a torn-down element.\n private _gen: number = 0;\n\n // True once the voiceschanged subscription has been (or is being) established;\n // reset by dispose(). Guards reinitVoices() so the first connect after\n // construction does not double-subscribe, while a reconnect after dispose()\n // does re-subscribe.\n private _voicesSubscribed: boolean = false;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n // Probe support up front so observers see the real flag before the first read.\n // Routed through the setter (not a direct assignment) so the field starts at\n // its `false` default and the unsupported case actually transitions\n // false→true; the supported case is same-value guarded and dispatches nothing.\n this._setUnsupported(!this._hasApi());\n this._initVoices();\n }\n\n get voices(): SpeechVoiceInfo[] {\n return this._voices;\n }\n\n get speaking(): boolean {\n return this._speaking;\n }\n\n get paused(): boolean {\n return this._paused;\n }\n\n get pending(): boolean {\n return this._pending;\n }\n\n get charIndex(): number | null {\n return this._charIndex;\n }\n\n get spokenWord(): string | null {\n return this._spokenWord;\n }\n\n get error(): WcsSpeakErrorDetail | null {\n return this._error;\n }\n\n // Resolved once in the constructor (`_setUnsupported(!_hasApi())`) and never\n // re-evaluated: the speechSynthesis API's presence is immutable for the\n // lifetime of a document, so there's nothing to re-check.\n get unsupported(): boolean {\n return this._unsupported;\n }\n\n // --- State setters with event dispatch ---\n\n private _setVoices(voices: SpeechVoiceInfo[]): void {\n // Same-value guard, like the other setters. `voiceschanged` can fire several\n // times with an identical list (engines re-announce after warm-up); compare\n // the normalized snapshot content so a redundant re-announcement does not\n // re-dispatch voices-changed. A genuine list change (length or any field)\n // still fires.\n if (this._voicesEqual(this._voices, voices)) return;\n this._voices = voices;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:voices-changed\", {\n detail: voices,\n bubbles: true,\n }));\n }\n\n private _voicesEqual(a: SpeechVoiceInfo[], b: SpeechVoiceInfo[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n const x = a[i];\n const y = b[i];\n if (x.name !== y.name || x.lang !== y.lang || x.default !== y.default\n || x.localService !== y.localService || x.voiceURI !== y.voiceURI) {\n return false;\n }\n }\n return true;\n }\n\n private _setSpeaking(speaking: boolean): void {\n if (this._speaking === speaking) return;\n this._speaking = speaking;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:speaking-changed\", {\n detail: speaking,\n bubbles: true,\n }));\n }\n\n private _setPaused(paused: boolean): void {\n if (this._paused === paused) return;\n this._paused = paused;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:paused-changed\", {\n detail: paused,\n bubbles: true,\n }));\n }\n\n private _setPending(pending: boolean): void {\n if (this._pending === pending) return;\n this._pending = pending;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:pending-changed\", {\n detail: pending,\n bubbles: true,\n }));\n }\n\n private _setBoundary(charIndex: number | null, word: string | null): void {\n // Boundary events stream rapidly with changing offsets; dispatch each. The\n // guard only suppresses redundant resets (e.g. an end after an already-null\n // boundary) so a cleared highlight does not re-fire.\n if (this._charIndex === charIndex && this._spokenWord === word) return;\n this._charIndex = charIndex;\n this._spokenWord = word;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:boundary\", {\n detail: { charIndex, word },\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsSpeakErrorDetail | null): void {\n if (this._error === error) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n private _setUnsupported(unsupported: boolean): void {\n if (this._unsupported === unsupported) return;\n this._unsupported = unsupported;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:unsupported-changed\", {\n detail: unsupported,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n /**\n * Queue an utterance for `text` with optional per-utterance parameters. Never\n * throws: when the API is unavailable it surfaces an `error` and returns. An\n * empty/whitespace-only `text` is a no-op (the browser would not fire start).\n */\n speak(text: string, options: SpeakOptions = {}): void {\n if (!this._hasApi()) {\n this._setError(this._unsupportedError());\n return;\n }\n if (typeof text !== \"string\" || text.trim() === \"\") {\n return;\n }\n\n const synth = window.speechSynthesis;\n const utterance = new window.SpeechSynthesisUtterance(text);\n if (typeof options.rate === \"number\") utterance.rate = options.rate;\n if (typeof options.pitch === \"number\") utterance.pitch = options.pitch;\n if (typeof options.volume === \"number\") utterance.volume = options.volume;\n if (typeof options.lang === \"string\" && options.lang !== \"\") utterance.lang = options.lang;\n if (typeof options.voice === \"string\" && options.voice !== \"\") {\n const match = this._rawVoices.find((v) => v.name === options.voice);\n if (match) utterance.voice = match;\n }\n\n const gen = this._gen;\n // Per-utterance \"has started\" flag. The browser can fire onerror/onend\n // *before* onstart (e.g. a `synthesis-unavailable` / `audio-busy` failure on\n // a still-queued utterance). In that case the utterance only ever counted\n // toward `_queued`, so the terminal handler must decrement `_queued` — not\n // `_started` — otherwise `pending` (derived from `_queued > 0`) sticks true\n // forever. onstart sets this flag so the terminal handler knows which counter\n // to release.\n let started = false;\n utterance.onstart = (): void => {\n if (gen !== this._gen) return;\n started = true;\n this._queued = Math.max(0, this._queued - 1);\n this._started++;\n this._setSpeaking(true);\n this._setPending(this._queued > 0);\n this._setBoundary(null, null);\n };\n utterance.onboundary = (event: SpeechSynthesisEvent): void => {\n if (gen !== this._gen) return;\n try {\n const charIndex = event.charIndex;\n const length = (event as unknown as { charLength?: number }).charLength;\n // Prefer the engine-provided word length. Some engines omit `charLength`\n // on word boundaries; fall back to the run of non-whitespace at charIndex\n // so `spokenWord` (the karaoke highlight) still works there.\n const word = (typeof length === \"number\" && length > 0)\n ? text.substring(charIndex, charIndex + length)\n : (text.slice(charIndex).match(/^\\S+/)?.[0] ?? \"\");\n this._setBoundary(charIndex, word);\n } catch {\n // A malformed boundary event must not escape the browser callback.\n }\n };\n utterance.onpause = (): void => {\n if (gen !== this._gen) return;\n this._setPaused(true);\n };\n utterance.onresume = (): void => {\n if (gen !== this._gen) return;\n this._setPaused(false);\n };\n utterance.onend = (): void => {\n if (gen !== this._gen) return;\n this._finishUtterance(started);\n };\n utterance.onerror = (event: SpeechSynthesisErrorEvent): void => {\n if (gen !== this._gen) return;\n this._setError(this._normalizeError(event));\n this._finishUtterance(started);\n };\n\n this._setError(null);\n this._queued++;\n this._setPending(true);\n synth.speak(utterance);\n }\n\n /**\n * Clear the queue and stop the current utterance immediately. Resets all\n * progress state synchronously and invalidates in-flight utterance callbacks\n * (the browser fires a \"canceled\" error per utterance) so they do not surface\n * as real errors.\n */\n cancel(): void {\n if (!this._hasApi()) return;\n // Neutralize every in-flight utterance's pending callbacks before triggering\n // the native cancel (which fires \"canceled\" onerror/onend on each).\n this._gen++;\n // Chrome quirk: cancelling while the engine is paused can leave the synth in\n // a state where the *next* speak() produces no audio. Resume first so cancel\n // happens from a running state. resume() on an idle/non-paused engine is a\n // harmless no-op, so guarding on the tracked `_paused` flag is sufficient.\n if (this._paused) {\n window.speechSynthesis.resume();\n }\n window.speechSynthesis.cancel();\n this._queued = 0;\n this._started = 0;\n this._setSpeaking(false);\n this._setPending(false);\n this._setPaused(false);\n this._setBoundary(null, null);\n }\n\n pause(): void {\n if (!this._hasApi()) return;\n window.speechSynthesis.pause();\n }\n\n resume(): void {\n if (!this._hasApi()) return;\n window.speechSynthesis.resume();\n }\n\n /**\n * Re-establish the voiceschanged subscription after a dispose() — e.g. the\n * Shell element was disconnected and then reconnected (reparented). No-op while\n * a subscription is already live, so the first connect after construction does\n * not double-subscribe.\n */\n reinitVoices(): void {\n if (!this._voicesSubscribed) {\n this._initVoices();\n }\n }\n\n /**\n * Detach the live voiceschanged listener and neutralize any in-flight\n * utterance callbacks. Call from the Shell's `disconnectedCallback`.\n */\n dispose(): void {\n this._voicesSubscribed = false;\n this._gen++;\n // Reset the queue bookkeeping silently (no dispatch on a disposed element);\n // a reconnect starts fresh. The observable snapshot (error / charIndex /\n // spokenWord) is intentionally *kept* so a reparented element preserves its\n // last state, mirroring GeolocationCore.dispose(). The next speak() resets\n // error / boundary for its own lifecycle.\n this._queued = 0;\n this._started = 0;\n this._speaking = false;\n this._paused = false;\n this._pending = false;\n if (this._hasApi()) {\n window.speechSynthesis.removeEventListener(\"voiceschanged\", this._onVoicesChanged);\n }\n }\n\n // --- Internal ---\n\n // `started` is the per-utterance flag set by its onstart. An utterance that\n // ended/errored after starting releases a `_started` slot; one that never\n // started (terminal event before onstart) releases its `_queued` slot instead,\n // so `pending` correctly returns to false.\n private _finishUtterance(started: boolean): void {\n if (started) {\n this._started = Math.max(0, this._started - 1);\n } else {\n this._queued = Math.max(0, this._queued - 1);\n }\n this._setSpeaking(this._started > 0);\n this._setPending(this._queued > 0);\n if (this._started === 0 && this._queued === 0) {\n this._setPaused(false);\n this._setBoundary(null, null);\n }\n }\n\n private _hasApi(): boolean {\n return typeof window !== \"undefined\"\n && !!window.speechSynthesis\n && typeof (window as unknown as { SpeechSynthesisUtterance?: unknown }).SpeechSynthesisUtterance === \"function\";\n }\n\n private _initVoices(): void {\n if (!this._hasApi()) return;\n this._voicesSubscribed = true;\n this._loadVoices();\n window.speechSynthesis.addEventListener(\"voiceschanged\", this._onVoicesChanged);\n }\n\n private _onVoicesChanged = (): void => {\n this._loadVoices();\n };\n\n private _loadVoices(): void {\n const raw = window.speechSynthesis.getVoices() ?? [];\n this._rawVoices = raw;\n this._setVoices(raw.map((v) => this._normalizeVoice(v)));\n }\n\n private _normalizeVoice(voice: SpeechSynthesisVoice): SpeechVoiceInfo {\n return {\n name: voice.name,\n lang: voice.lang,\n default: voice.default,\n localService: voice.localService,\n voiceURI: voice.voiceURI,\n };\n }\n\n private _normalizeError(event: SpeechSynthesisErrorEvent): WcsSpeakErrorDetail {\n const error = event.error ?? \"synthesis-failed\";\n return { error, message: `Speech synthesis failed: ${error}.` };\n }\n\n private _unsupportedError(): WcsSpeakErrorDetail {\n return { error: \"unsupported\", message: \"SpeechSynthesis API is not available in this environment.\" };\n }\n}\n","import { config } from \"./config.js\";\nimport type { WcsSpeak } from \"./components/Speak.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n // A misconfigured triggerAttribute (e.g. one with a space) makes the attribute\n // selector invalid and closest() throw SyntaxError; guard so a bad config\n // disables only this shortcut rather than killing every document click handler.\n let triggerElement: Element | null;\n try {\n triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n } catch {\n return;\n }\n if (!triggerElement) return;\n\n const speakId = triggerElement.getAttribute(config.triggerAttribute);\n if (!speakId) return;\n\n // Resolve the registered constructor at call time instead of importing Speak as\n // a value, avoiding a components/Speak.ts ⇄ autoTrigger.ts cycle\n // (Speak.connectedCallback() calls registerAutoTrigger()). instanceof against\n // the customElements registry keeps the same identity guarantee.\n const SpeakCtor = customElements.get(config.tagNames.speak);\n const speakElement = document.getElementById(speakId);\n if (!SpeakCtor || !(speakElement instanceof SpeakCtor)) return;\n\n // The text to speak comes from the trigger element: an explicit `data-speaktext`\n // attribute wins, otherwise the element's text content. This keeps the\n // click-driven shortcut declarative without inventing a payload channel.\n const explicit = triggerElement.getAttribute(\"data-speaktext\");\n // textContent is always a string for an Element; the cast avoids an\n // unreachable null-coalesce branch. speak() tolerates a non-string anyway.\n // The textContent fallback is trimmed (HTML indentation otherwise leaks leading\n // / trailing whitespace into the utterance); an explicit data-speaktext is kept\n // verbatim so an author can deliberately include surrounding spaces.\n const text = explicit !== null ? explicit : (triggerElement.textContent as string).trim();\n\n event.preventDefault();\n (speakElement as WcsSpeak).speak(text);\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { IWcBindable, SpeakOptions, SpeechVoiceInfo, WcsSpeakErrorDetail } from \"../types.js\";\nimport { SpeakCore } from \"../core/SpeakCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\n/**\n * `<wcs-speak>` — declarative text-to-speech. Wraps SpeakCore and exposes:\n *\n * - **`say`** (reactive input): writing a value speaks it, suppressing same-value\n * writes so it fires only when the bound source actually changes. The\n * imperative `speak` command instead speaks on demand (even the same text\n * again). See `docs/speech-tag-design.md` § 5.\n * - per-utterance parameters (`rate` / `pitch` / `volume` / `voice` / `lang`) as\n * mirrored attributes.\n * - the Core's observable surface (voices / speaking / paused / pending /\n * charIndex / spokenWord / error / unsupported) via delegated getters.\n */\nexport class WcsSpeak extends HTMLElement {\n static wcBindable: IWcBindable = {\n ...SpeakCore.wcBindable,\n // Shell-level settable surface. `say` is a momentary reactive command-property\n // with no mirrored attribute (it carries dynamic text, not declarative config),\n // mirroring how <wcs-geo>'s `trigger` has no attribute. The rest mirror their\n // HTML attributes idempotently.\n inputs: [\n { name: \"say\" },\n { name: \"rate\", attribute: \"rate\" },\n { name: \"pitch\", attribute: \"pitch\" },\n { name: \"volume\", attribute: \"volume\" },\n { name: \"voice\", attribute: \"voice\" },\n { name: \"lang\", attribute: \"lang\" },\n { name: \"manual\", attribute: \"manual\" },\n ],\n commands: SpeakCore.wcBindable.commands,\n };\n\n private _core: SpeakCore;\n private _say: string = \"\";\n\n constructor() {\n super();\n this._core = new SpeakCore(this);\n }\n\n // --- Attribute accessors ---\n\n get rate(): number {\n return this._numberAttr(\"rate\", 1);\n }\n\n set rate(value: number) {\n this.setAttribute(\"rate\", String(value));\n }\n\n get pitch(): number {\n return this._numberAttr(\"pitch\", 1);\n }\n\n set pitch(value: number) {\n this.setAttribute(\"pitch\", String(value));\n }\n\n get volume(): number {\n return this._numberAttr(\"volume\", 1);\n }\n\n set volume(value: number) {\n this.setAttribute(\"volume\", String(value));\n }\n\n get voice(): string {\n return this.getAttribute(\"voice\") ?? \"\";\n }\n\n set voice(value: string | null) {\n if (value == null) {\n this.removeAttribute(\"voice\");\n } else {\n this.setAttribute(\"voice\", String(value));\n }\n }\n\n get lang(): string {\n return this.getAttribute(\"lang\") ?? \"\";\n }\n\n set lang(value: string | null) {\n if (value == null) {\n this.removeAttribute(\"lang\");\n } else {\n this.setAttribute(\"lang\", String(value));\n }\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Reactive command-property ---\n\n get say(): string {\n return this._say;\n }\n\n set say(value: string | null) {\n // Reactive: writing a new value speaks it. `manual` mutes the path entirely\n // (the imperative `speak` command still works) — both an opt-out and the hook\n // used to avoid a recognition echo loop while listening. A conforming binder\n // never delivers `undefined` (it skips the write), but a direct assignment\n // can, so normalize null/undefined to a no-op.\n //\n // ECHO-LOOP WARNING: when wiring <wcs-listen> → state → `say`, the synthesized\n // audio will be re-recognized unless speech is muted while listening. There is\n // no code-level interlock here (the two tags are decoupled): the consumer MUST\n // wire it — bind `manual` to the listening flag (or gate the bound source).\n // See README \"Echo loop\" and the speech-echo example.\n if (value == null) return;\n if (this.manual) return;\n const v = String(value);\n // Same-value guard: only speak when the bound source actually changes. For\n // \"speak the same text again on demand\", use the `speak` command instead.\n if (v === this._say) return;\n this._say = v;\n this.speak(v);\n }\n\n // --- Core delegated getters ---\n\n get voices(): SpeechVoiceInfo[] {\n return this._core.voices;\n }\n\n get speaking(): boolean {\n return this._core.speaking;\n }\n\n get paused(): boolean {\n return this._core.paused;\n }\n\n get pending(): boolean {\n return this._core.pending;\n }\n\n get charIndex(): number | null {\n return this._core.charIndex;\n }\n\n get spokenWord(): string | null {\n return this._core.spokenWord;\n }\n\n get error(): WcsSpeakErrorDetail | null {\n return this._core.error;\n }\n\n get unsupported(): boolean {\n return this._core.unsupported;\n }\n\n // --- Commands ---\n\n speak(text: string): void {\n this._core.speak(text, this._options());\n }\n\n cancel(): void {\n this._core.cancel();\n }\n\n pause(): void {\n this._core.pause();\n }\n\n resume(): void {\n this._core.resume();\n }\n\n // --- Internal ---\n\n private _numberAttr(name: string, fallback: number): number {\n const attr = this.getAttribute(name);\n if (attr === null || attr.trim() === \"\") return fallback;\n // Strict parse via Number() (unlike parseInt, \"1px\" -> NaN, not 1). Fall back\n // to the API default for any non-finite value, matching the geolocation\n // \"invalid values fall back to default\" convention.\n const parsed = Number(attr);\n return Number.isFinite(parsed) ? parsed : fallback;\n }\n\n private _options(): SpeakOptions {\n return {\n rate: this.rate,\n pitch: this.pitch,\n volume: this.volume,\n voice: this.voice,\n lang: this.lang,\n };\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n // Revive the voiceschanged subscription after a reconnect (reparenting).\n // No-op on the first connect since the constructor already subscribed.\n this._core.reinitVoices();\n }\n\n disconnectedCallback(): void {\n // Detach event subscriptions and neutralize in-flight utterance callbacks.\n // Any utterance already speaking finishes naturally (SpeechSynthesis is a\n // global singleton; cancelling here would stop other <wcs-speak> elements\n // too). Call `cancel()` explicitly to stop audio.\n this._core.dispose();\n }\n}\n","import {\n IWcBindable, ListenOptions, ListenPermissionState,\n WcsListenResultDetail, WcsListenAlternative, WcsListenErrorDetail,\n} from \"../types.js\";\n\n// The vendor-prefixed constructor is not in the DOM lib types; declare a minimal\n// shape so we can feature-detect and construct it.\n// Minimal structural shapes for the recognition result/error events. The DOM lib\n// does not ship the prefixed API's types, so we declare just the fields read here\n// instead of using `any`, keeping the handlers type-checked and consistent with\n// the typed state fields. All fields are optional/loose because real engines vary\n// (resultIndex / charLength omitted, malformed events) and the handlers already\n// defend against that at runtime.\ninterface RecognitionAlternativeLike {\n transcript?: string;\n confidence?: number;\n}\ninterface RecognitionResultLike {\n readonly length: number;\n isFinal?: boolean;\n [index: number]: RecognitionAlternativeLike;\n}\ninterface RecognitionResultListLike {\n readonly length: number;\n [index: number]: RecognitionResultLike;\n}\ninterface RecognitionResultEventLike {\n results: RecognitionResultListLike;\n resultIndex?: number;\n}\ninterface RecognitionErrorEventLike {\n error?: string;\n}\n\ninterface SpeechRecognitionLike extends EventTarget {\n lang: string;\n continuous: boolean;\n interimResults: boolean;\n maxAlternatives: number;\n start(): void;\n stop(): void;\n abort(): void;\n onstart: ((event: Event) => void) | null;\n onend: ((event: Event) => void) | null;\n onresult: ((event: RecognitionResultEventLike) => void) | null;\n onerror: ((event: RecognitionErrorEventLike) => void) | null;\n}\n\ntype SpeechRecognitionCtor = new () => SpeechRecognitionLike;\n\n/**\n * Headless speech-to-text primitive. A thin, framework-agnostic wrapper around\n * the SpeechRecognition API (vendor-prefixed `webkitSpeechRecognition` in\n * Chrome) exposed through the wc-bindable protocol.\n *\n * It is the \"event\" half of the speech package (the synthesis half is\n * SpeakCore): recognition results flow element → state.\n *\n * Two phases mirror geolocation:\n * - **one-shot** (`continuous = false`) — recognize until the first `end`.\n * - **continuous** (`continuous = true`) — keep a single session open across\n * phrases. The browser still ends a session on silence; auto-restart bridges\n * that gap **but is opt-in via `maxRestarts`**: with the default `maxRestarts\n * = 0` a continuous session is *not* restarted on `end` (the safe default —\n * unbounded restart is the infinite-loop risk we guard against). Set\n * `maxRestarts > 0` to bridge N silences. The cap also stops a persistent\n * failure (e.g. `not-allowed`) from spinning forever or exhausting quota; a\n * real result resets the budget so only consecutive empty restarts count.\n *\n * A microphone permission gate (like geolocation's) reflects\n * `navigator.permissions.query({ name: \"microphone\" })`. Failures never throw —\n * they surface through the `error` property.\n */\nexport class ListenCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"interimTranscript\", event: \"wcs-listen:interim-changed\" },\n { name: \"finalTranscript\", event: \"wcs-listen:final-changed\" },\n { name: \"result\", event: \"wcs-listen:result\" },\n { name: \"listening\", event: \"wcs-listen:listening-changed\" },\n { name: \"permission\", event: \"wcs-listen:permission-changed\" },\n { name: \"error\", event: \"wcs-listen:error\" },\n { name: \"unsupported\", event: \"wcs-listen:unsupported-changed\" },\n ],\n commands: [\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"abort\" },\n ],\n };\n\n private _target: EventTarget;\n private _recognition: SpeechRecognitionLike | null = null;\n\n private _interimTranscript: string = \"\";\n private _finalTranscript: string = \"\";\n private _result: WcsListenResultDetail | null = null;\n private _listening: boolean = false;\n private _permission: ListenPermissionState = \"prompt\";\n private _error: WcsListenErrorDetail | null = null;\n private _unsupported: boolean = false;\n\n // Intent flag: true between start() and stop()/abort()/terminal-error. Gates\n // the auto-restart loop so a session that ended because the user stopped it\n // does not restart.\n private _active: boolean = false;\n private _continuous: boolean = false;\n private _maxRestarts: number = 0;\n private _restartCount: number = 0;\n\n // Permission tracking — same machinery as GeolocationCore.\n private _permissionStatus: PermissionStatus | null = null;\n private _permissionSubscribed: boolean = false;\n private _permGen: number = 0;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n const Ctor = this._getCtor();\n this._setUnsupported(!Ctor);\n if (Ctor) {\n this._recognition = new Ctor();\n this._attachHandlers(this._recognition);\n }\n this._initPermission();\n }\n\n get interimTranscript(): string {\n return this._interimTranscript;\n }\n\n get finalTranscript(): string {\n return this._finalTranscript;\n }\n\n get result(): WcsListenResultDetail | null {\n return this._result;\n }\n\n get listening(): boolean {\n return this._listening;\n }\n\n get permission(): ListenPermissionState {\n return this._permission;\n }\n\n get error(): WcsListenErrorDetail | null {\n return this._error;\n }\n\n // Resolved once in the constructor (`_setUnsupported(!Ctor)`) and never\n // re-evaluated: the SpeechRecognition API's presence is immutable for the\n // lifetime of a document, so there's nothing to re-check.\n get unsupported(): boolean {\n return this._unsupported;\n }\n\n // --- State setters with event dispatch ---\n\n private _setInterim(value: string): void {\n if (this._interimTranscript === value) return;\n this._interimTranscript = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:interim-changed\", { detail: value, bubbles: true }));\n }\n\n private _setFinal(value: string): void {\n if (this._finalTranscript === value) return;\n this._finalTranscript = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:final-changed\", { detail: value, bubbles: true }));\n }\n\n private _setResult(value: WcsListenResultDetail): void {\n this._result = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:result\", { detail: value, bubbles: true }));\n }\n\n private _setListening(value: boolean): void {\n if (this._listening === value) return;\n this._listening = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:listening-changed\", { detail: value, bubbles: true }));\n }\n\n private _setPermission(value: ListenPermissionState): void {\n if (this._permission === value) return;\n this._permission = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:permission-changed\", { detail: value, bubbles: true }));\n }\n\n private _setError(value: WcsListenErrorDetail | null): void {\n if (this._error === value) return;\n this._error = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:error\", { detail: value, bubbles: true }));\n }\n\n private _setUnsupported(value: boolean): void {\n if (this._unsupported === value) return;\n this._unsupported = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:unsupported-changed\", { detail: value, bubbles: true }));\n }\n\n // --- Public API ---\n\n /**\n * Begin a recognition session. Resets the transcripts (a fresh, user-initiated\n * listen), applies options, and starts. Idempotent while already listening: a\n * redundant start() is ignored so the browser does not throw \"recognition has\n * already started\".\n */\n start(options: ListenOptions = {}): void {\n if (!this._recognition) {\n this._setError(this._unsupportedError());\n return;\n }\n if (this._active) return;\n\n this._continuous = options.continuous ?? false;\n // `maxRestarts` is a restart *count*, so floor any fractional input to an\n // integer (e.g. 2.5 → 2). `_restartCount` increments by 1, so a fractional\n // cap would otherwise compare inconsistently. Non-finite/negative → 0.\n this._maxRestarts = typeof options.maxRestarts === \"number\" && options.maxRestarts >= 0\n ? Math.floor(options.maxRestarts)\n : 0;\n this._restartCount = 0;\n this._recognition.lang = options.lang ?? \"\";\n this._recognition.continuous = this._continuous;\n this._recognition.interimResults = options.interimResults ?? false;\n if (typeof options.maxAlternatives === \"number\") {\n this._recognition.maxAlternatives = options.maxAlternatives;\n }\n\n // Fresh session: clear prior transcripts and error.\n this._setInterim(\"\");\n this._setFinal(\"\");\n this._setError(null);\n this._active = true;\n this._safeStart();\n }\n\n stop(): void {\n if (!this._recognition) return;\n // Clear intent first so the end handler does not auto-restart.\n this._active = false;\n this._recognition.stop();\n }\n\n abort(): void {\n if (!this._recognition) return;\n this._active = false;\n this._recognition.abort();\n }\n\n /**\n * Re-establish the permission `change` subscription after a dispose().\n */\n reinitPermission(): void {\n if (!this._permissionSubscribed) {\n this._initPermission();\n }\n }\n\n /**\n * Stop recognition and detach the live permission listener. Call from the\n * Shell's `disconnectedCallback`.\n */\n dispose(): void {\n // Only the live subscriptions and the listening shadow are reset here. The\n // observable snapshot (transcripts / result / error) is intentionally *kept*\n // so a reparented element preserves its last state, mirroring how\n // GeolocationCore.dispose() leaves `position` / `error` intact. The next\n // start() clears the transcripts and error for its fresh session anyway.\n this._active = false;\n this._permissionSubscribed = false;\n this._permGen++;\n if (this._recognition) {\n // abort() is the immediate teardown; guard against environments where it\n // throws on an idle recognizer.\n try {\n this._recognition.abort();\n } catch {\n // ignore — teardown is best-effort.\n }\n }\n // Reset the listening shadow silently (no dispatch on a disposed element),\n // mirroring GeolocationCore's `_loading` reset. The abort() above neutralizes\n // the recognizer but its `end` (which would clear listening via the setter)\n // may not have fired yet; forcing false here means a reconnect+start's\n // `onstart` still transitions false→true through the same-value guard, so the\n // state never desyncs to a stale `true`.\n this._listening = false;\n if (this._permissionStatus) {\n this._permissionStatus.removeEventListener(\"change\", this._onPermissionChange);\n this._permissionStatus = null;\n }\n }\n\n // --- Internal: recognition lifecycle ---\n\n private _attachHandlers(recognition: SpeechRecognitionLike): void {\n recognition.onstart = (): void => {\n this._setListening(true);\n };\n recognition.onresult = (event: RecognitionResultEventLike): void => {\n try {\n this._handleResult(event);\n } catch {\n // A malformed result event must not escape the browser callback.\n }\n };\n recognition.onerror = (event: RecognitionErrorEventLike): void => {\n this._setError(this._normalizeError(event));\n // Terminal errors must not be retried — they would spin the restart loop.\n // The set is deliberately limited to the permission-class errors that can\n // never self-recover within a session. Transient failures\n // (`network` / `audio-capture` / `no-speech`) are intentionally *not*\n // terminal: they are recoverable, so a continuous session restarts through\n // them, bounded by `maxRestarts` (the cap is the guard against a persistent\n // transient failure spinning forever).\n if (event && (event.error === \"not-allowed\" || event.error === \"service-not-allowed\")) {\n this._active = false;\n }\n };\n recognition.onend = (): void => {\n if (this._active && this._continuous && this._restartCount < this._maxRestarts) {\n // Auto-restart bridges a silence-induced `end`. Keep `listening` true\n // across the gap rather than flickering true→false→true: from the\n // consumer's perspective the continuous session never stopped. The\n // immediately-following start()'s `onstart` re-sets true (same-value\n // guarded → no-op), so the flag stays steady. A genuine stop (no\n // restart) still drops to false below.\n this._restartCount++;\n this._safeStart();\n // _safeStart() clears _active if the restart threw; in that case the\n // session is over, so reflect listening=false rather than leaving it\n // stuck true.\n if (!this._active) this._setListening(false);\n return;\n }\n // No restart: the session is fully over.\n this._setListening(false);\n this._active = false;\n };\n }\n\n private _handleResult(event: RecognitionResultEventLike): void {\n const results = event.results;\n let interim = \"\";\n let finalChunk = \"\";\n // Per the Web Speech spec, `resultIndex` is the lowest index in `results`\n // that changed in this event, so we only fold in `[resultIndex, length)` and\n // accumulate finals (`this._finalTranscript + finalChunk`). This assumes the\n // engine advances `resultIndex` past already-finalized results. A nonconforming\n // engine that omits `resultIndex` (`?? 0`) or re-reports finalized results at\n // index 0 on every event could double-accumulate the same final chunk; standard\n // browser engines don't, so this is not hardened against here.\n for (let i = event.resultIndex ?? 0; i < results.length; i++) {\n const res = results[i];\n const transcript = res?.[0]?.transcript ?? \"\";\n if (res?.isFinal) {\n finalChunk += transcript;\n } else {\n interim += transcript;\n }\n }\n if (finalChunk !== \"\") {\n this._setFinal(this._finalTranscript + finalChunk);\n }\n this._setInterim(interim);\n // Any result is progress — reset the restart budget so only *consecutive*\n // empty restarts count toward the cap.\n this._restartCount = 0;\n\n const last = results[results.length - 1];\n if (last) {\n this._setResult(this._normalizeResult(last));\n }\n }\n\n private _normalizeResult(result: RecognitionResultLike): WcsListenResultDetail {\n const alternatives: WcsListenAlternative[] = [];\n for (let i = 0; i < result.length; i++) {\n alternatives.push({\n transcript: result[i]?.transcript ?? \"\",\n confidence: result[i]?.confidence ?? 0,\n });\n }\n const top = alternatives[0] ?? { transcript: \"\", confidence: 0 };\n return {\n transcript: top.transcript,\n confidence: top.confidence,\n isFinal: !!result.isFinal,\n alternatives,\n };\n }\n\n private _safeStart(): void {\n try {\n this._recognition!.start();\n } catch {\n // start() throws if already started; surface nothing — the live session\n // continues. Reset intent so state stays consistent.\n this._active = false;\n }\n }\n\n // --- Internal: feature detection & permission (mirrors GeolocationCore) ---\n\n private _getCtor(): SpeechRecognitionCtor | null {\n // Guard window access without a separate (in-browser unreachable) early\n // return, mirroring SpeakCore's `_hasApi` style.\n const w = (typeof window === \"undefined\" ? undefined : window) as unknown as {\n SpeechRecognition?: SpeechRecognitionCtor;\n webkitSpeechRecognition?: SpeechRecognitionCtor;\n } | undefined;\n return w?.SpeechRecognition ?? w?.webkitSpeechRecognition ?? null;\n }\n\n private _initPermission(): void {\n if (typeof navigator === \"undefined\" || !navigator.permissions || typeof navigator.permissions.query !== \"function\") {\n this._setPermission(\"unsupported\");\n return;\n }\n this._permissionSubscribed = true;\n const gen = ++this._permGen;\n navigator.permissions.query({ name: \"microphone\" as PermissionName }).then(\n (status) => {\n if (gen !== this._permGen) return;\n this._permissionStatus = status;\n this._setPermission(status.state as ListenPermissionState);\n status.addEventListener(\"change\", this._onPermissionChange);\n },\n () => {\n if (gen !== this._permGen) return;\n this._setPermission(\"unsupported\");\n },\n );\n }\n\n private _onPermissionChange = (event: Event): void => {\n const status = event.target as PermissionStatus;\n this._setPermission(status.state as ListenPermissionState);\n };\n\n private _normalizeError(event: RecognitionErrorEventLike): WcsListenErrorDetail {\n const error = (event && event.error) ? event.error : \"aborted\";\n return { error, message: `Speech recognition failed: ${error}.` };\n }\n\n private _unsupportedError(): WcsListenErrorDetail {\n return { error: \"unsupported\", message: \"SpeechRecognition API is not available in this environment.\" };\n }\n}\n","import { config } from \"./config.js\";\nimport type { WcsListen } from \"./components/Listen.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n // A misconfigured listenTriggerAttribute (e.g. one with a space) makes the\n // attribute selector invalid and closest() throw SyntaxError; guard so a bad\n // config disables only this shortcut rather than killing every click handler.\n let triggerElement: Element | null;\n try {\n triggerElement = target.closest<Element>(`[${config.listenTriggerAttribute}]`);\n } catch {\n return;\n }\n if (!triggerElement) return;\n\n const listenId = triggerElement.getAttribute(config.listenTriggerAttribute);\n if (!listenId) return;\n\n const ListenCtor = customElements.get(config.tagNames.listen);\n const listenElement = document.getElementById(listenId);\n if (!ListenCtor || !(listenElement instanceof ListenCtor)) return;\n\n event.preventDefault();\n // Toggle: clicking starts a session, clicking again while listening stops it.\n const el = listenElement as WcsListen;\n if (el.listening) {\n el.stop();\n } else {\n el.start();\n }\n}\n\nexport function registerListenAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterListenAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport {\n IWcBindable, ListenOptions, ListenPermissionState, WcsListenResultDetail, WcsListenErrorDetail,\n} from \"../types.js\";\nimport { ListenCore } from \"../core/ListenCore.js\";\nimport { registerListenAutoTrigger } from \"../listenAutoTrigger.js\";\n\n/**\n * `<wcs-listen>` — declarative speech-to-text. Wraps ListenCore and exposes the\n * recognition surface (interim/final transcripts, structured result, listening\n * flag, microphone permission, error) plus the two-phase start/stop/abort\n * commands and a momentary `trigger` for DOM-driven starts.\n *\n * Mirrors `<wcs-geo>`: `manual` suppresses the connect-time auto-start, and the\n * `continuous` attribute selects the auto-restarting session phase.\n */\nexport class WcsListen extends HTMLElement {\n static wcBindable: IWcBindable = {\n ...ListenCore.wcBindable,\n properties: [\n ...ListenCore.wcBindable.properties,\n { name: \"trigger\", event: \"wcs-listen:trigger-changed\" },\n ],\n inputs: [\n { name: \"lang\", attribute: \"lang\" },\n { name: \"continuous\", attribute: \"continuous\" },\n { name: \"interim\", attribute: \"interim\" },\n { name: \"maxRestarts\", attribute: \"max-restarts\" },\n { name: \"manual\", attribute: \"manual\" },\n { name: \"trigger\" },\n ],\n commands: ListenCore.wcBindable.commands,\n };\n\n private _core: ListenCore;\n private _trigger: boolean = false;\n\n constructor() {\n super();\n this._core = new ListenCore(this);\n }\n\n // --- Attribute accessors ---\n\n get lang(): string {\n return this.getAttribute(\"lang\") ?? \"\";\n }\n\n set lang(value: string | null) {\n if (value == null) {\n this.removeAttribute(\"lang\");\n } else {\n this.setAttribute(\"lang\", String(value));\n }\n }\n\n get continuous(): boolean {\n return this.hasAttribute(\"continuous\");\n }\n\n set continuous(value: boolean) {\n if (value) {\n this.setAttribute(\"continuous\", \"\");\n } else {\n this.removeAttribute(\"continuous\");\n }\n }\n\n get interim(): boolean {\n return this.hasAttribute(\"interim\");\n }\n\n set interim(value: boolean) {\n if (value) {\n this.setAttribute(\"interim\", \"\");\n } else {\n this.removeAttribute(\"interim\");\n }\n }\n\n get maxRestarts(): number {\n const attr = this.getAttribute(\"max-restarts\");\n if (attr === null || attr.trim() === \"\") return 0;\n const parsed = Number(attr);\n // A restart *count* is an integer, so floor fractional input (e.g. 1.9 → 1)\n // here too, keeping the getter's value identical to the effective cap the\n // Core applies (ListenCore.start floors it as well). Non-finite/negative → 0.\n return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : 0;\n }\n\n set maxRestarts(value: number) {\n this.setAttribute(\"max-restarts\", String(value));\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get interimTranscript(): string {\n return this._core.interimTranscript;\n }\n\n get finalTranscript(): string {\n return this._core.finalTranscript;\n }\n\n get result(): WcsListenResultDetail | null {\n return this._core.result;\n }\n\n get listening(): boolean {\n return this._core.listening;\n }\n\n get permission(): ListenPermissionState {\n return this._core.permission;\n }\n\n get error(): WcsListenErrorDetail | null {\n return this._core.error;\n }\n\n get unsupported(): boolean {\n return this._core.unsupported;\n }\n\n // --- Command property ---\n\n get trigger(): boolean {\n return this._trigger;\n }\n\n set trigger(value: boolean) {\n // Momentary command-property: a false→true write starts a session. Mirrors\n // <wcs-geo>'s trigger. Prefer the command-token protocol (`command.start:\n // $command.listen`) for state-driven starts; this exists for DOM triggers and\n // simple boolean bindings.\n const v = !!value;\n if (v) {\n this._trigger = true;\n this.start();\n this._trigger = false;\n this.dispatchEvent(new CustomEvent(\"wcs-listen:trigger-changed\", { detail: false, bubbles: true }));\n }\n }\n\n // --- Commands ---\n\n start(): void {\n this._core.start(this._options());\n }\n\n stop(): void {\n this._core.stop();\n }\n\n abort(): void {\n this._core.abort();\n }\n\n // --- Internal ---\n\n private _options(): ListenOptions {\n return {\n lang: this.lang,\n continuous: this.continuous,\n interimResults: this.interim,\n maxRestarts: this.maxRestarts,\n };\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerListenAutoTrigger();\n }\n this._core.reinitPermission();\n if (!this.manual) {\n // Non-blocking auto-start, mirroring <wcs-geo>: start() is fired\n // unconditionally without first awaiting/inspecting the (async) permission\n // state. A `denied` mic surfaces as a `not-allowed` error via the `error`\n // property (and stops auto-restart), rather than the connect path silently\n // suppressing the start. This keeps the permission model declarative and\n // consistent with geolocation. Use `manual` to require an explicit start.\n this.start();\n }\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { WcsSpeak } from \"./components/Speak.js\";\nimport { WcsListen } from \"./components/Listen.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.speak)) {\n customElements.define(config.tagNames.speak, WcsSpeak);\n }\n if (!customElements.get(config.tagNames.listen)) {\n customElements.define(config.tagNames.listen, WcsListen);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapSpeech(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":["registered","handleClick"],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.js","sources":["../src/config.ts","../src/core/SpeakCore.ts","../src/autoTrigger.ts","../src/components/Speak.ts","../src/core/ListenCore.ts","../src/listenAutoTrigger.ts","../src/components/Listen.ts","../src/registerComponents.ts","../src/bootstrapSpeech.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n listenTriggerAttribute: string;\n tagNames: {\n speak: string;\n listen: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-speaktarget\",\n listenTriggerAttribute: \"data-listentarget\",\n tagNames: {\n speak: \"wcs-speak\",\n listen: \"wcs-listen\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Internal, mutable live config used by the components/autoTriggers (they read it\n// at call time so setConfig() takes effect without re-import). Typed as the\n// readonly IConfig at the export boundary — the `as IConfig` is a compile-time\n// view only and does NOT freeze the object, so this export must stay\n// package-internal (it is not re-exported from exports.ts). Public consumers get\n// the deep-frozen clone from getConfig() instead, which is the only safe\n// read-only handle.\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (typeof partialConfig.listenTriggerAttribute === \"string\") {\n _config.listenTriggerAttribute = partialConfig.listenTriggerAttribute;\n }\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import {\n IWcBindable, SpeakOptions, SpeechVoiceInfo, WcsSpeakErrorDetail,\n} from \"../types.js\";\n\n/**\n * Headless text-to-speech primitive. A thin, framework-agnostic wrapper around\n * the SpeechSynthesis API exposed through the wc-bindable protocol.\n *\n * It is the \"command\" half of the speech package (the recognition half is\n * ListenCore): state drives the element, never the reverse, except for the\n * observable progress/status it publishes back.\n *\n * - **speak(text, options)** queues an utterance. Like the native API, multiple\n * calls queue; `cancel()` clears the queue and stops the current utterance.\n * - **pause() / resume()** suspend and resume the queue.\n * - The observable surface mirrors the live SpeechSynthesis flags\n * (`speaking` / `paused` / `pending`) and exposes voice-list loading\n * (`voices`, which the API populates asynchronously via `voiceschanged`) plus\n * word-boundary progress (`charIndex` / `spokenWord`) for karaoke-style\n * highlighting.\n *\n * Unlike geolocation/clipboard there is no permission gate — synthesis needs no\n * user grant. Failures never throw: they surface through the `error` property so\n * they flow into the declarative state.\n */\nexport class SpeakCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"voices\", event: \"wcs-speak:voices-changed\" },\n { name: \"speaking\", event: \"wcs-speak:speaking-changed\" },\n { name: \"paused\", event: \"wcs-speak:paused-changed\" },\n { name: \"pending\", event: \"wcs-speak:pending-changed\" },\n { name: \"charIndex\", event: \"wcs-speak:boundary\", getter: (e: Event) => (e as CustomEvent).detail?.charIndex ?? null },\n { name: \"spokenWord\", event: \"wcs-speak:boundary\", getter: (e: Event) => (e as CustomEvent).detail?.word ?? null },\n { name: \"error\", event: \"wcs-speak:error\" },\n { name: \"unsupported\", event: \"wcs-speak:unsupported-changed\" },\n ],\n commands: [\n { name: \"speak\" },\n { name: \"cancel\" },\n { name: \"pause\" },\n { name: \"resume\" },\n ],\n };\n\n private _target: EventTarget;\n\n private _voices: SpeechVoiceInfo[] = [];\n private _rawVoices: SpeechSynthesisVoice[] = [];\n private _speaking: boolean = false;\n private _paused: boolean = false;\n private _pending: boolean = false;\n private _charIndex: number | null = null;\n private _spokenWord: string | null = null;\n private _error: WcsSpeakErrorDetail | null = null;\n private _unsupported: boolean = false;\n\n // Count of utterances submitted via speak() but not yet started, and of\n // utterances started but not yet ended/errored. `pending`/`speaking` are\n // derived from these so the queue model is reflected accurately even when\n // several utterances are in flight.\n private _queued: number = 0;\n private _started: number = 0;\n\n // Monotonic id of the current synthesis lifecycle. Bumped by cancel() and\n // dispose(). Each speak() captures it; every utterance event handler bails if\n // it is stale, so a queued/canceled utterance's late callback (notably the\n // \"canceled\" error the browser fires from cancel()) never mutates state or\n // dispatches on a torn-down element.\n private _gen: number = 0;\n\n // True once the voiceschanged subscription has been (or is being) established;\n // reset by dispose(). Guards reinitVoices() so the first connect after\n // construction does not double-subscribe, while a reconnect after dispose()\n // does re-subscribe.\n private _voicesSubscribed: boolean = false;\n\n // SSR: feature detection (`_setUnsupported`) and the initial `getVoices()` read\n // are synchronous, and the `voiceschanged` subscription is established eagerly\n // in the constructor, so there is no asynchronous probe to await before\n // snapshotting — readiness is immediate. The Shell exposes this as\n // connectedCallbackPromise.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n // Probe support up front so observers see the real flag before the first read.\n // Routed through the setter (not a direct assignment) so the field starts at\n // its `false` default and the unsupported case actually transitions\n // false→true; the supported case is same-value guarded and dispatches nothing.\n this._setUnsupported(!this._hasApi());\n this._initVoices();\n }\n\n get voices(): SpeechVoiceInfo[] {\n return this._voices;\n }\n\n get speaking(): boolean {\n return this._speaking;\n }\n\n get paused(): boolean {\n return this._paused;\n }\n\n get pending(): boolean {\n return this._pending;\n }\n\n get charIndex(): number | null {\n return this._charIndex;\n }\n\n get spokenWord(): string | null {\n return this._spokenWord;\n }\n\n get error(): WcsSpeakErrorDetail | null {\n return this._error;\n }\n\n // Resolved once in the constructor (`_setUnsupported(!_hasApi())`) and never\n // re-evaluated: the speechSynthesis API's presence is immutable for the\n // lifetime of a document, so there's nothing to re-check.\n get unsupported(): boolean {\n return this._unsupported;\n }\n\n /** Resolves once the first probe settles (immediate — see `_ready`). */\n get ready(): Promise<void> {\n return this._ready;\n }\n\n // --- State setters with event dispatch ---\n\n private _setVoices(voices: SpeechVoiceInfo[]): void {\n // Same-value guard, like the other setters. `voiceschanged` can fire several\n // times with an identical list (engines re-announce after warm-up); compare\n // the normalized snapshot content so a redundant re-announcement does not\n // re-dispatch voices-changed. A genuine list change (length or any field)\n // still fires.\n if (this._voicesEqual(this._voices, voices)) return;\n this._voices = voices;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:voices-changed\", {\n detail: voices,\n bubbles: true,\n }));\n }\n\n private _voicesEqual(a: SpeechVoiceInfo[], b: SpeechVoiceInfo[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n const x = a[i];\n const y = b[i];\n if (x.name !== y.name || x.lang !== y.lang || x.default !== y.default\n || x.localService !== y.localService || x.voiceURI !== y.voiceURI) {\n return false;\n }\n }\n return true;\n }\n\n private _setSpeaking(speaking: boolean): void {\n if (this._speaking === speaking) return;\n this._speaking = speaking;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:speaking-changed\", {\n detail: speaking,\n bubbles: true,\n }));\n }\n\n private _setPaused(paused: boolean): void {\n if (this._paused === paused) return;\n this._paused = paused;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:paused-changed\", {\n detail: paused,\n bubbles: true,\n }));\n }\n\n private _setPending(pending: boolean): void {\n if (this._pending === pending) return;\n this._pending = pending;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:pending-changed\", {\n detail: pending,\n bubbles: true,\n }));\n }\n\n private _setBoundary(charIndex: number | null, word: string | null): void {\n // Boundary events stream rapidly with changing offsets; dispatch each. The\n // guard only suppresses redundant resets (e.g. an end after an already-null\n // boundary) so a cleared highlight does not re-fire.\n if (this._charIndex === charIndex && this._spokenWord === word) return;\n this._charIndex = charIndex;\n this._spokenWord = word;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:boundary\", {\n detail: { charIndex, word },\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsSpeakErrorDetail | null): void {\n if (this._error === error) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n private _setUnsupported(unsupported: boolean): void {\n if (this._unsupported === unsupported) return;\n this._unsupported = unsupported;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:unsupported-changed\", {\n detail: unsupported,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n /**\n * Queue an utterance for `text` with optional per-utterance parameters. Never\n * throws: when the API is unavailable it surfaces an `error` and returns. An\n * empty/whitespace-only `text` is a no-op (the browser would not fire start).\n */\n speak(text: string, options: SpeakOptions = {}): void {\n if (!this._hasApi()) {\n this._setError(this._unsupportedError());\n return;\n }\n if (typeof text !== \"string\" || text.trim() === \"\") {\n return;\n }\n\n const synth = window.speechSynthesis;\n const utterance = new window.SpeechSynthesisUtterance(text);\n if (typeof options.rate === \"number\") utterance.rate = options.rate;\n if (typeof options.pitch === \"number\") utterance.pitch = options.pitch;\n if (typeof options.volume === \"number\") utterance.volume = options.volume;\n if (typeof options.lang === \"string\" && options.lang !== \"\") utterance.lang = options.lang;\n if (typeof options.voice === \"string\" && options.voice !== \"\") {\n const match = this._rawVoices.find((v) => v.name === options.voice);\n if (match) utterance.voice = match;\n }\n\n const gen = this._gen;\n // Per-utterance \"has started\" flag. The browser can fire onerror/onend\n // *before* onstart (e.g. a `synthesis-unavailable` / `audio-busy` failure on\n // a still-queued utterance). In that case the utterance only ever counted\n // toward `_queued`, so the terminal handler must decrement `_queued` — not\n // `_started` — otherwise `pending` (derived from `_queued > 0`) sticks true\n // forever. onstart sets this flag so the terminal handler knows which counter\n // to release.\n let started = false;\n utterance.onstart = (): void => {\n if (gen !== this._gen) return;\n started = true;\n this._queued = Math.max(0, this._queued - 1);\n this._started++;\n this._setSpeaking(true);\n this._setPending(this._queued > 0);\n this._setBoundary(null, null);\n };\n utterance.onboundary = (event: SpeechSynthesisEvent): void => {\n if (gen !== this._gen) return;\n try {\n const charIndex = event.charIndex;\n const length = (event as unknown as { charLength?: number }).charLength;\n // Prefer the engine-provided word length. Some engines omit `charLength`\n // on word boundaries; fall back to the run of non-whitespace at charIndex\n // so `spokenWord` (the karaoke highlight) still works there.\n const word = (typeof length === \"number\" && length > 0)\n ? text.substring(charIndex, charIndex + length)\n : (text.slice(charIndex).match(/^\\S+/)?.[0] ?? \"\");\n this._setBoundary(charIndex, word);\n } catch {\n // A malformed boundary event must not escape the browser callback.\n }\n };\n utterance.onpause = (): void => {\n if (gen !== this._gen) return;\n this._setPaused(true);\n };\n utterance.onresume = (): void => {\n if (gen !== this._gen) return;\n this._setPaused(false);\n };\n utterance.onend = (): void => {\n if (gen !== this._gen) return;\n this._finishUtterance(started);\n };\n utterance.onerror = (event: SpeechSynthesisErrorEvent): void => {\n if (gen !== this._gen) return;\n this._setError(this._normalizeError(event));\n this._finishUtterance(started);\n };\n\n this._setError(null);\n this._queued++;\n this._setPending(true);\n synth.speak(utterance);\n }\n\n /**\n * Clear the queue and stop the current utterance immediately. Resets all\n * progress state synchronously and invalidates in-flight utterance callbacks\n * (the browser fires a \"canceled\" error per utterance) so they do not surface\n * as real errors.\n */\n cancel(): void {\n if (!this._hasApi()) return;\n // Neutralize every in-flight utterance's pending callbacks before triggering\n // the native cancel (which fires \"canceled\" onerror/onend on each).\n this._gen++;\n // Chrome quirk: cancelling while the engine is paused can leave the synth in\n // a state where the *next* speak() produces no audio. Resume first so cancel\n // happens from a running state. resume() on an idle/non-paused engine is a\n // harmless no-op, so guarding on the tracked `_paused` flag is sufficient.\n if (this._paused) {\n window.speechSynthesis.resume();\n }\n window.speechSynthesis.cancel();\n this._queued = 0;\n this._started = 0;\n this._setSpeaking(false);\n this._setPending(false);\n this._setPaused(false);\n this._setBoundary(null, null);\n }\n\n pause(): void {\n if (!this._hasApi()) return;\n window.speechSynthesis.pause();\n }\n\n resume(): void {\n if (!this._hasApi()) return;\n window.speechSynthesis.resume();\n }\n\n /**\n * Re-establish the voiceschanged subscription after a dispose() — e.g. the\n * Shell element was disconnected and then reconnected (reparented). No-op while\n * a subscription is already live, so the first connect after construction does\n * not double-subscribe.\n */\n reinitVoices(): void {\n if (!this._voicesSubscribed) {\n this._initVoices();\n }\n }\n\n /**\n * Establish monitoring (§3.5). Synthesis is command-driven (speak/cancel), so\n * observe() only (re-)establishes the live `voiceschanged` subscription —\n * idempotent via reinitVoices()'s `_voicesSubscribed` guard, so the first\n * connect after construction does not double-subscribe while a reconnect after\n * dispose() does. Returns the `ready` promise for SSR. Call from the Shell's\n * connectedCallback.\n */\n observe(): Promise<void> {\n this.reinitVoices();\n return this._ready;\n }\n\n /**\n * Detach the live voiceschanged listener and neutralize any in-flight\n * utterance callbacks. Call from the Shell's `disconnectedCallback`.\n */\n dispose(): void {\n this._voicesSubscribed = false;\n this._gen++;\n // Reset the queue bookkeeping silently (no dispatch on a disposed element);\n // a reconnect starts fresh. The observable snapshot (error / charIndex /\n // spokenWord) is intentionally *kept* so a reparented element preserves its\n // last state, mirroring GeolocationCore.dispose(). The next speak() resets\n // error / boundary for its own lifecycle.\n this._queued = 0;\n this._started = 0;\n this._speaking = false;\n this._paused = false;\n this._pending = false;\n if (this._hasApi()) {\n window.speechSynthesis.removeEventListener(\"voiceschanged\", this._onVoicesChanged);\n }\n }\n\n // --- Internal ---\n\n // `started` is the per-utterance flag set by its onstart. An utterance that\n // ended/errored after starting releases a `_started` slot; one that never\n // started (terminal event before onstart) releases its `_queued` slot instead,\n // so `pending` correctly returns to false.\n private _finishUtterance(started: boolean): void {\n if (started) {\n this._started = Math.max(0, this._started - 1);\n } else {\n this._queued = Math.max(0, this._queued - 1);\n }\n this._setSpeaking(this._started > 0);\n this._setPending(this._queued > 0);\n if (this._started === 0 && this._queued === 0) {\n this._setPaused(false);\n this._setBoundary(null, null);\n }\n }\n\n private _hasApi(): boolean {\n return typeof window !== \"undefined\"\n && !!window.speechSynthesis\n && typeof (window as unknown as { SpeechSynthesisUtterance?: unknown }).SpeechSynthesisUtterance === \"function\";\n }\n\n private _initVoices(): void {\n if (!this._hasApi()) return;\n this._voicesSubscribed = true;\n this._loadVoices();\n window.speechSynthesis.addEventListener(\"voiceschanged\", this._onVoicesChanged);\n }\n\n private _onVoicesChanged = (): void => {\n this._loadVoices();\n };\n\n private _loadVoices(): void {\n const raw = window.speechSynthesis.getVoices() ?? [];\n this._rawVoices = raw;\n this._setVoices(raw.map((v) => this._normalizeVoice(v)));\n }\n\n private _normalizeVoice(voice: SpeechSynthesisVoice): SpeechVoiceInfo {\n return {\n name: voice.name,\n lang: voice.lang,\n default: voice.default,\n localService: voice.localService,\n voiceURI: voice.voiceURI,\n };\n }\n\n private _normalizeError(event: SpeechSynthesisErrorEvent): WcsSpeakErrorDetail {\n const error = event.error ?? \"synthesis-failed\";\n return { error, message: `Speech synthesis failed: ${error}.` };\n }\n\n private _unsupportedError(): WcsSpeakErrorDetail {\n return { error: \"unsupported\", message: \"SpeechSynthesis API is not available in this environment.\" };\n }\n}\n","import { config } from \"./config.js\";\nimport type { WcsSpeak } from \"./components/Speak.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n // A misconfigured triggerAttribute (e.g. one with a space) makes the attribute\n // selector invalid and closest() throw SyntaxError; guard so a bad config\n // disables only this shortcut rather than killing every document click handler.\n let triggerElement: Element | null;\n try {\n triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n } catch {\n return;\n }\n if (!triggerElement) return;\n\n const speakId = triggerElement.getAttribute(config.triggerAttribute);\n if (!speakId) return;\n\n // Resolve the registered constructor at call time instead of importing Speak as\n // a value, avoiding a components/Speak.ts ⇄ autoTrigger.ts cycle\n // (Speak.connectedCallback() calls registerAutoTrigger()). instanceof against\n // the customElements registry keeps the same identity guarantee.\n const SpeakCtor = customElements.get(config.tagNames.speak);\n const speakElement = document.getElementById(speakId);\n if (!SpeakCtor || !(speakElement instanceof SpeakCtor)) return;\n\n // The text to speak comes from the trigger element: an explicit `data-speaktext`\n // attribute wins, otherwise the element's text content. This keeps the\n // click-driven shortcut declarative without inventing a payload channel.\n const explicit = triggerElement.getAttribute(\"data-speaktext\");\n // textContent is always a string for an Element; the cast avoids an\n // unreachable null-coalesce branch. speak() tolerates a non-string anyway.\n // The textContent fallback is trimmed (HTML indentation otherwise leaks leading\n // / trailing whitespace into the utterance); an explicit data-speaktext is kept\n // verbatim so an author can deliberately include surrounding spaces.\n const text = explicit !== null ? explicit : (triggerElement.textContent as string).trim();\n\n event.preventDefault();\n (speakElement as WcsSpeak).speak(text);\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { IWcBindable, SpeakOptions, SpeechVoiceInfo, WcsSpeakErrorDetail } from \"../types.js\";\nimport { SpeakCore } from \"../core/SpeakCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\n/**\n * `<wcs-speak>` — declarative text-to-speech. Wraps SpeakCore and exposes:\n *\n * - **`say`** (reactive input): writing a value speaks it, suppressing same-value\n * writes so it fires only when the bound source actually changes. The\n * imperative `speak` command instead speaks on demand (even the same text\n * again). See `docs/speech-tag-design.md` § 5.\n * - per-utterance parameters (`rate` / `pitch` / `volume` / `voice` / `lang`) as\n * mirrored attributes.\n * - the Core's observable surface (voices / speaking / paused / pending /\n * charIndex / spokenWord / error / unsupported) via delegated getters.\n */\nexport class WcsSpeak extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...SpeakCore.wcBindable,\n // Shell-level settable surface. `say` is a momentary reactive command-property\n // with no mirrored attribute (it carries dynamic text, not declarative config),\n // mirroring how <wcs-geo>'s `trigger` has no attribute. The rest mirror their\n // HTML attributes idempotently.\n inputs: [\n { name: \"say\" },\n { name: \"rate\", attribute: \"rate\" },\n { name: \"pitch\", attribute: \"pitch\" },\n { name: \"volume\", attribute: \"volume\" },\n { name: \"voice\", attribute: \"voice\" },\n { name: \"lang\", attribute: \"lang\" },\n { name: \"manual\", attribute: \"manual\" },\n ],\n commands: SpeakCore.wcBindable.commands,\n };\n\n private _core: SpeakCore;\n private _say: string = \"\";\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new SpeakCore(this);\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Attribute accessors ---\n\n get rate(): number {\n return this._numberAttr(\"rate\", 1);\n }\n\n set rate(value: number) {\n this.setAttribute(\"rate\", String(value));\n }\n\n get pitch(): number {\n return this._numberAttr(\"pitch\", 1);\n }\n\n set pitch(value: number) {\n this.setAttribute(\"pitch\", String(value));\n }\n\n get volume(): number {\n return this._numberAttr(\"volume\", 1);\n }\n\n set volume(value: number) {\n this.setAttribute(\"volume\", String(value));\n }\n\n get voice(): string {\n return this.getAttribute(\"voice\") ?? \"\";\n }\n\n set voice(value: string | null) {\n if (value == null) {\n this.removeAttribute(\"voice\");\n } else {\n this.setAttribute(\"voice\", String(value));\n }\n }\n\n get lang(): string {\n return this.getAttribute(\"lang\") ?? \"\";\n }\n\n set lang(value: string | null) {\n if (value == null) {\n this.removeAttribute(\"lang\");\n } else {\n this.setAttribute(\"lang\", String(value));\n }\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Reactive command-property ---\n\n get say(): string {\n return this._say;\n }\n\n set say(value: string | null) {\n // Reactive: writing a new value speaks it. `manual` mutes the path entirely\n // (the imperative `speak` command still works) — both an opt-out and the hook\n // used to avoid a recognition echo loop while listening. A conforming binder\n // never delivers `undefined` (it skips the write), but a direct assignment\n // can, so normalize null/undefined to a no-op.\n //\n // ECHO-LOOP WARNING: when wiring <wcs-listen> → state → `say`, the synthesized\n // audio will be re-recognized unless speech is muted while listening. There is\n // no code-level interlock here (the two tags are decoupled): the consumer MUST\n // wire it — bind `manual` to the listening flag (or gate the bound source).\n // See README \"Echo loop\" and the speech-echo example.\n if (value == null) return;\n if (this.manual) return;\n const v = String(value);\n // Same-value guard: only speak when the bound source actually changes. For\n // \"speak the same text again on demand\", use the `speak` command instead.\n if (v === this._say) return;\n this._say = v;\n this.speak(v);\n }\n\n // --- Core delegated getters ---\n\n get voices(): SpeechVoiceInfo[] {\n return this._core.voices;\n }\n\n get speaking(): boolean {\n return this._core.speaking;\n }\n\n get paused(): boolean {\n return this._core.paused;\n }\n\n get pending(): boolean {\n return this._core.pending;\n }\n\n get charIndex(): number | null {\n return this._core.charIndex;\n }\n\n get spokenWord(): string | null {\n return this._core.spokenWord;\n }\n\n get error(): WcsSpeakErrorDetail | null {\n return this._core.error;\n }\n\n get unsupported(): boolean {\n return this._core.unsupported;\n }\n\n // --- Commands ---\n\n speak(text: string): void {\n this._core.speak(text, this._options());\n }\n\n cancel(): void {\n this._core.cancel();\n }\n\n pause(): void {\n this._core.pause();\n }\n\n resume(): void {\n this._core.resume();\n }\n\n // --- Internal ---\n\n private _numberAttr(name: string, fallback: number): number {\n const attr = this.getAttribute(name);\n if (attr === null || attr.trim() === \"\") return fallback;\n // Strict parse via Number() (unlike parseInt, \"1px\" -> NaN, not 1). Fall back\n // to the API default for any non-finite value, matching the geolocation\n // \"invalid values fall back to default\" convention.\n const parsed = Number(attr);\n return Number.isFinite(parsed) ? parsed : fallback;\n }\n\n private _options(): SpeakOptions {\n return {\n rate: this.rate,\n pitch: this.pitch,\n volume: this.volume,\n voice: this.voice,\n lang: this.lang,\n };\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n // observe() revives the voiceschanged subscription after a reconnect\n // (reparenting) and returns the readiness promise for SSR; it wraps\n // reinitVoices() (no-op on the first connect — the constructor subscribed).\n this._connectedCallbackPromise = this._core.observe();\n }\n\n disconnectedCallback(): void {\n // Detach event subscriptions and neutralize in-flight utterance callbacks.\n // Any utterance already speaking finishes naturally (SpeechSynthesis is a\n // global singleton; cancelling here would stop other <wcs-speak> elements\n // too). Call `cancel()` explicitly to stop audio.\n this._core.dispose();\n }\n}\n","import {\n IWcBindable, ListenOptions, ListenPermissionState,\n WcsListenResultDetail, WcsListenAlternative, WcsListenErrorDetail,\n} from \"../types.js\";\n\n// The vendor-prefixed constructor is not in the DOM lib types; declare a minimal\n// shape so we can feature-detect and construct it.\n// Minimal structural shapes for the recognition result/error events. The DOM lib\n// does not ship the prefixed API's types, so we declare just the fields read here\n// instead of using `any`, keeping the handlers type-checked and consistent with\n// the typed state fields. All fields are optional/loose because real engines vary\n// (resultIndex / charLength omitted, malformed events) and the handlers already\n// defend against that at runtime.\ninterface RecognitionAlternativeLike {\n transcript?: string;\n confidence?: number;\n}\ninterface RecognitionResultLike {\n readonly length: number;\n isFinal?: boolean;\n [index: number]: RecognitionAlternativeLike;\n}\ninterface RecognitionResultListLike {\n readonly length: number;\n [index: number]: RecognitionResultLike;\n}\ninterface RecognitionResultEventLike {\n results: RecognitionResultListLike;\n resultIndex?: number;\n}\ninterface RecognitionErrorEventLike {\n error?: string;\n}\n\ninterface SpeechRecognitionLike extends EventTarget {\n lang: string;\n continuous: boolean;\n interimResults: boolean;\n maxAlternatives: number;\n start(): void;\n stop(): void;\n abort(): void;\n onstart: ((event: Event) => void) | null;\n onend: ((event: Event) => void) | null;\n onresult: ((event: RecognitionResultEventLike) => void) | null;\n onerror: ((event: RecognitionErrorEventLike) => void) | null;\n}\n\ntype SpeechRecognitionCtor = new () => SpeechRecognitionLike;\n\n/**\n * Headless speech-to-text primitive. A thin, framework-agnostic wrapper around\n * the SpeechRecognition API (vendor-prefixed `webkitSpeechRecognition` in\n * Chrome) exposed through the wc-bindable protocol.\n *\n * It is the \"event\" half of the speech package (the synthesis half is\n * SpeakCore): recognition results flow element → state.\n *\n * Two phases mirror geolocation:\n * - **one-shot** (`continuous = false`) — recognize until the first `end`.\n * - **continuous** (`continuous = true`) — keep a single session open across\n * phrases. The browser still ends a session on silence; auto-restart bridges\n * that gap **but is opt-in via `maxRestarts`**: with the default `maxRestarts\n * = 0` a continuous session is *not* restarted on `end` (the safe default —\n * unbounded restart is the infinite-loop risk we guard against). Set\n * `maxRestarts > 0` to bridge N silences. The cap also stops a persistent\n * failure (e.g. `not-allowed`) from spinning forever or exhausting quota; a\n * real result resets the budget so only consecutive empty restarts count.\n *\n * A microphone permission gate (like geolocation's) reflects\n * `navigator.permissions.query({ name: \"microphone\" })`. Failures never throw —\n * they surface through the `error` property.\n */\nexport class ListenCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"interimTranscript\", event: \"wcs-listen:interim-changed\" },\n { name: \"finalTranscript\", event: \"wcs-listen:final-changed\" },\n { name: \"result\", event: \"wcs-listen:result\" },\n { name: \"listening\", event: \"wcs-listen:listening-changed\" },\n { name: \"permission\", event: \"wcs-listen:permission-changed\" },\n { name: \"error\", event: \"wcs-listen:error\" },\n { name: \"unsupported\", event: \"wcs-listen:unsupported-changed\" },\n ],\n commands: [\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"abort\" },\n ],\n };\n\n private _target: EventTarget;\n private _recognition: SpeechRecognitionLike | null = null;\n\n private _interimTranscript: string = \"\";\n private _finalTranscript: string = \"\";\n private _result: WcsListenResultDetail | null = null;\n private _listening: boolean = false;\n private _permission: ListenPermissionState = \"prompt\";\n private _error: WcsListenErrorDetail | null = null;\n private _unsupported: boolean = false;\n\n // Intent flag: true between start() and stop()/abort()/terminal-error. Gates\n // the auto-restart loop so a session that ended because the user stopped it\n // does not restart.\n private _active: boolean = false;\n private _continuous: boolean = false;\n private _maxRestarts: number = 0;\n private _restartCount: number = 0;\n\n // Permission tracking — same machinery as GeolocationCore.\n private _permissionStatus: PermissionStatus | null = null;\n private _permissionSubscribed: boolean = false;\n private _permGen: number = 0;\n\n // SSR: feature detection (`_setUnsupported`) is synchronous and the permission\n // `change` subscription is established eagerly in the constructor, so there is\n // no asynchronous probe to await before snapshotting — readiness is immediate.\n // The Shell exposes this as connectedCallbackPromise.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n const Ctor = this._getCtor();\n this._setUnsupported(!Ctor);\n if (Ctor) {\n this._recognition = new Ctor();\n this._attachHandlers(this._recognition);\n }\n this._initPermission();\n }\n\n get interimTranscript(): string {\n return this._interimTranscript;\n }\n\n get finalTranscript(): string {\n return this._finalTranscript;\n }\n\n get result(): WcsListenResultDetail | null {\n return this._result;\n }\n\n get listening(): boolean {\n return this._listening;\n }\n\n get permission(): ListenPermissionState {\n return this._permission;\n }\n\n get error(): WcsListenErrorDetail | null {\n return this._error;\n }\n\n // Resolved once in the constructor (`_setUnsupported(!Ctor)`) and never\n // re-evaluated: the SpeechRecognition API's presence is immutable for the\n // lifetime of a document, so there's nothing to re-check.\n get unsupported(): boolean {\n return this._unsupported;\n }\n\n /** Resolves once the first probe settles (immediate — see `_ready`). */\n get ready(): Promise<void> {\n return this._ready;\n }\n\n // --- State setters with event dispatch ---\n\n private _setInterim(value: string): void {\n if (this._interimTranscript === value) return;\n this._interimTranscript = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:interim-changed\", { detail: value, bubbles: true }));\n }\n\n private _setFinal(value: string): void {\n if (this._finalTranscript === value) return;\n this._finalTranscript = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:final-changed\", { detail: value, bubbles: true }));\n }\n\n private _setResult(value: WcsListenResultDetail): void {\n this._result = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:result\", { detail: value, bubbles: true }));\n }\n\n private _setListening(value: boolean): void {\n if (this._listening === value) return;\n this._listening = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:listening-changed\", { detail: value, bubbles: true }));\n }\n\n private _setPermission(value: ListenPermissionState): void {\n if (this._permission === value) return;\n this._permission = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:permission-changed\", { detail: value, bubbles: true }));\n }\n\n private _setError(value: WcsListenErrorDetail | null): void {\n if (this._error === value) return;\n this._error = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:error\", { detail: value, bubbles: true }));\n }\n\n private _setUnsupported(value: boolean): void {\n if (this._unsupported === value) return;\n this._unsupported = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:unsupported-changed\", { detail: value, bubbles: true }));\n }\n\n // --- Public API ---\n\n /**\n * Begin a recognition session. Resets the transcripts (a fresh, user-initiated\n * listen), applies options, and starts. Idempotent while already listening: a\n * redundant start() is ignored so the browser does not throw \"recognition has\n * already started\".\n */\n start(options: ListenOptions = {}): void {\n if (!this._recognition) {\n this._setError(this._unsupportedError());\n return;\n }\n if (this._active) return;\n\n this._continuous = options.continuous ?? false;\n // `maxRestarts` is a restart *count*, so floor any fractional input to an\n // integer (e.g. 2.5 → 2). `_restartCount` increments by 1, so a fractional\n // cap would otherwise compare inconsistently. Non-finite/negative → 0.\n this._maxRestarts = typeof options.maxRestarts === \"number\" && options.maxRestarts >= 0\n ? Math.floor(options.maxRestarts)\n : 0;\n this._restartCount = 0;\n this._recognition.lang = options.lang ?? \"\";\n this._recognition.continuous = this._continuous;\n this._recognition.interimResults = options.interimResults ?? false;\n if (typeof options.maxAlternatives === \"number\") {\n this._recognition.maxAlternatives = options.maxAlternatives;\n }\n\n // Fresh session: clear prior transcripts and error.\n this._setInterim(\"\");\n this._setFinal(\"\");\n this._setError(null);\n this._active = true;\n this._safeStart();\n }\n\n stop(): void {\n if (!this._recognition) return;\n // Clear intent first so the end handler does not auto-restart.\n this._active = false;\n this._recognition.stop();\n }\n\n abort(): void {\n if (!this._recognition) return;\n this._active = false;\n this._recognition.abort();\n }\n\n /**\n * Re-establish the permission `change` subscription after a dispose().\n */\n reinitPermission(): void {\n if (!this._permissionSubscribed) {\n this._initPermission();\n }\n }\n\n /**\n * Establish monitoring (§3.5). Recognition is command-driven (start/stop), so\n * observe() only (re-)establishes the live permission subscription — idempotent\n * via reinitPermission()'s `_permissionSubscribed` guard, so the first connect\n * after construction does not double-subscribe while a reconnect after dispose()\n * does. Returns the `ready` promise for SSR. Call from the Shell's\n * connectedCallback.\n */\n observe(): Promise<void> {\n this.reinitPermission();\n return this._ready;\n }\n\n /**\n * Stop recognition and detach the live permission listener. Call from the\n * Shell's `disconnectedCallback`.\n */\n dispose(): void {\n // Only the live subscriptions and the listening shadow are reset here. The\n // observable snapshot (transcripts / result / error) is intentionally *kept*\n // so a reparented element preserves its last state, mirroring how\n // GeolocationCore.dispose() leaves `position` / `error` intact. The next\n // start() clears the transcripts and error for its fresh session anyway.\n this._active = false;\n this._permissionSubscribed = false;\n this._permGen++;\n if (this._recognition) {\n // abort() is the immediate teardown; guard against environments where it\n // throws on an idle recognizer.\n try {\n this._recognition.abort();\n } catch {\n // ignore — teardown is best-effort.\n }\n }\n // Reset the listening shadow silently (no dispatch on a disposed element),\n // mirroring GeolocationCore's `_loading` reset. The abort() above neutralizes\n // the recognizer but its `end` (which would clear listening via the setter)\n // may not have fired yet; forcing false here means a reconnect+start's\n // `onstart` still transitions false→true through the same-value guard, so the\n // state never desyncs to a stale `true`.\n this._listening = false;\n if (this._permissionStatus) {\n this._permissionStatus.removeEventListener(\"change\", this._onPermissionChange);\n this._permissionStatus = null;\n }\n }\n\n // --- Internal: recognition lifecycle ---\n\n private _attachHandlers(recognition: SpeechRecognitionLike): void {\n recognition.onstart = (): void => {\n this._setListening(true);\n };\n recognition.onresult = (event: RecognitionResultEventLike): void => {\n try {\n this._handleResult(event);\n } catch {\n // A malformed result event must not escape the browser callback.\n }\n };\n recognition.onerror = (event: RecognitionErrorEventLike): void => {\n this._setError(this._normalizeError(event));\n // Terminal errors must not be retried — they would spin the restart loop.\n // The set is deliberately limited to the permission-class errors that can\n // never self-recover within a session. Transient failures\n // (`network` / `audio-capture` / `no-speech`) are intentionally *not*\n // terminal: they are recoverable, so a continuous session restarts through\n // them, bounded by `maxRestarts` (the cap is the guard against a persistent\n // transient failure spinning forever).\n if (event && (event.error === \"not-allowed\" || event.error === \"service-not-allowed\")) {\n this._active = false;\n }\n };\n recognition.onend = (): void => {\n if (this._active && this._continuous && this._restartCount < this._maxRestarts) {\n // Auto-restart bridges a silence-induced `end`. Keep `listening` true\n // across the gap rather than flickering true→false→true: from the\n // consumer's perspective the continuous session never stopped. The\n // immediately-following start()'s `onstart` re-sets true (same-value\n // guarded → no-op), so the flag stays steady. A genuine stop (no\n // restart) still drops to false below.\n this._restartCount++;\n this._safeStart();\n // _safeStart() clears _active if the restart threw; in that case the\n // session is over, so reflect listening=false rather than leaving it\n // stuck true.\n if (!this._active) this._setListening(false);\n return;\n }\n // No restart: the session is fully over.\n this._setListening(false);\n this._active = false;\n };\n }\n\n private _handleResult(event: RecognitionResultEventLike): void {\n const results = event.results;\n let interim = \"\";\n let finalChunk = \"\";\n // Per the Web Speech spec, `resultIndex` is the lowest index in `results`\n // that changed in this event, so we only fold in `[resultIndex, length)` and\n // accumulate finals (`this._finalTranscript + finalChunk`). This assumes the\n // engine advances `resultIndex` past already-finalized results. A nonconforming\n // engine that omits `resultIndex` (`?? 0`) or re-reports finalized results at\n // index 0 on every event could double-accumulate the same final chunk; standard\n // browser engines don't, so this is not hardened against here.\n for (let i = event.resultIndex ?? 0; i < results.length; i++) {\n const res = results[i];\n const transcript = res?.[0]?.transcript ?? \"\";\n if (res?.isFinal) {\n finalChunk += transcript;\n } else {\n interim += transcript;\n }\n }\n if (finalChunk !== \"\") {\n this._setFinal(this._finalTranscript + finalChunk);\n }\n this._setInterim(interim);\n // Any result is progress — reset the restart budget so only *consecutive*\n // empty restarts count toward the cap.\n this._restartCount = 0;\n\n const last = results[results.length - 1];\n if (last) {\n this._setResult(this._normalizeResult(last));\n }\n }\n\n private _normalizeResult(result: RecognitionResultLike): WcsListenResultDetail {\n const alternatives: WcsListenAlternative[] = [];\n for (let i = 0; i < result.length; i++) {\n alternatives.push({\n transcript: result[i]?.transcript ?? \"\",\n confidence: result[i]?.confidence ?? 0,\n });\n }\n const top = alternatives[0] ?? { transcript: \"\", confidence: 0 };\n return {\n transcript: top.transcript,\n confidence: top.confidence,\n isFinal: !!result.isFinal,\n alternatives,\n };\n }\n\n private _safeStart(): void {\n try {\n this._recognition!.start();\n } catch {\n // start() throws if already started; surface nothing — the live session\n // continues. Reset intent so state stays consistent.\n this._active = false;\n }\n }\n\n // --- Internal: feature detection & permission (mirrors GeolocationCore) ---\n\n private _getCtor(): SpeechRecognitionCtor | null {\n // Guard window access without a separate (in-browser unreachable) early\n // return, mirroring SpeakCore's `_hasApi` style.\n const w = (typeof window === \"undefined\" ? undefined : window) as unknown as {\n SpeechRecognition?: SpeechRecognitionCtor;\n webkitSpeechRecognition?: SpeechRecognitionCtor;\n } | undefined;\n return w?.SpeechRecognition ?? w?.webkitSpeechRecognition ?? null;\n }\n\n private _initPermission(): void {\n if (typeof navigator === \"undefined\" || !navigator.permissions || typeof navigator.permissions.query !== \"function\") {\n this._setPermission(\"unsupported\");\n return;\n }\n this._permissionSubscribed = true;\n const gen = ++this._permGen;\n navigator.permissions.query({ name: \"microphone\" as PermissionName }).then(\n (status) => {\n if (gen !== this._permGen) return;\n this._permissionStatus = status;\n this._setPermission(status.state as ListenPermissionState);\n status.addEventListener(\"change\", this._onPermissionChange);\n },\n () => {\n if (gen !== this._permGen) return;\n this._setPermission(\"unsupported\");\n },\n );\n }\n\n private _onPermissionChange = (event: Event): void => {\n const status = event.target as PermissionStatus;\n this._setPermission(status.state as ListenPermissionState);\n };\n\n private _normalizeError(event: RecognitionErrorEventLike): WcsListenErrorDetail {\n const error = (event && event.error) ? event.error : \"aborted\";\n return { error, message: `Speech recognition failed: ${error}.` };\n }\n\n private _unsupportedError(): WcsListenErrorDetail {\n return { error: \"unsupported\", message: \"SpeechRecognition API is not available in this environment.\" };\n }\n}\n","import { config } from \"./config.js\";\nimport type { WcsListen } from \"./components/Listen.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n // A misconfigured listenTriggerAttribute (e.g. one with a space) makes the\n // attribute selector invalid and closest() throw SyntaxError; guard so a bad\n // config disables only this shortcut rather than killing every click handler.\n let triggerElement: Element | null;\n try {\n triggerElement = target.closest<Element>(`[${config.listenTriggerAttribute}]`);\n } catch {\n return;\n }\n if (!triggerElement) return;\n\n const listenId = triggerElement.getAttribute(config.listenTriggerAttribute);\n if (!listenId) return;\n\n const ListenCtor = customElements.get(config.tagNames.listen);\n const listenElement = document.getElementById(listenId);\n if (!ListenCtor || !(listenElement instanceof ListenCtor)) return;\n\n event.preventDefault();\n // Toggle: clicking starts a session, clicking again while listening stops it.\n const el = listenElement as WcsListen;\n if (el.listening) {\n el.stop();\n } else {\n el.start();\n }\n}\n\nexport function registerListenAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterListenAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport {\n IWcBindable, ListenOptions, ListenPermissionState, WcsListenResultDetail, WcsListenErrorDetail,\n} from \"../types.js\";\nimport { ListenCore } from \"../core/ListenCore.js\";\nimport { registerListenAutoTrigger } from \"../listenAutoTrigger.js\";\n\n/**\n * `<wcs-listen>` — declarative speech-to-text. Wraps ListenCore and exposes the\n * recognition surface (interim/final transcripts, structured result, listening\n * flag, microphone permission, error) plus the two-phase start/stop/abort\n * commands and a momentary `trigger` for DOM-driven starts.\n *\n * Mirrors `<wcs-geo>`: `manual` suppresses the connect-time auto-start, and the\n * `continuous` attribute selects the auto-restarting session phase.\n */\nexport class WcsListen extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...ListenCore.wcBindable,\n properties: [\n ...ListenCore.wcBindable.properties,\n { name: \"trigger\", event: \"wcs-listen:trigger-changed\" },\n ],\n inputs: [\n { name: \"lang\", attribute: \"lang\" },\n { name: \"continuous\", attribute: \"continuous\" },\n { name: \"interim\", attribute: \"interim\" },\n { name: \"maxRestarts\", attribute: \"max-restarts\" },\n { name: \"manual\", attribute: \"manual\" },\n { name: \"trigger\" },\n ],\n commands: ListenCore.wcBindable.commands,\n };\n\n private _core: ListenCore;\n private _trigger: boolean = false;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new ListenCore(this);\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Attribute accessors ---\n\n get lang(): string {\n return this.getAttribute(\"lang\") ?? \"\";\n }\n\n set lang(value: string | null) {\n if (value == null) {\n this.removeAttribute(\"lang\");\n } else {\n this.setAttribute(\"lang\", String(value));\n }\n }\n\n get continuous(): boolean {\n return this.hasAttribute(\"continuous\");\n }\n\n set continuous(value: boolean) {\n if (value) {\n this.setAttribute(\"continuous\", \"\");\n } else {\n this.removeAttribute(\"continuous\");\n }\n }\n\n get interim(): boolean {\n return this.hasAttribute(\"interim\");\n }\n\n set interim(value: boolean) {\n if (value) {\n this.setAttribute(\"interim\", \"\");\n } else {\n this.removeAttribute(\"interim\");\n }\n }\n\n get maxRestarts(): number {\n const attr = this.getAttribute(\"max-restarts\");\n if (attr === null || attr.trim() === \"\") return 0;\n const parsed = Number(attr);\n // A restart *count* is an integer, so floor fractional input (e.g. 1.9 → 1)\n // here too, keeping the getter's value identical to the effective cap the\n // Core applies (ListenCore.start floors it as well). Non-finite/negative → 0.\n return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : 0;\n }\n\n set maxRestarts(value: number) {\n this.setAttribute(\"max-restarts\", String(value));\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get interimTranscript(): string {\n return this._core.interimTranscript;\n }\n\n get finalTranscript(): string {\n return this._core.finalTranscript;\n }\n\n get result(): WcsListenResultDetail | null {\n return this._core.result;\n }\n\n get listening(): boolean {\n return this._core.listening;\n }\n\n get permission(): ListenPermissionState {\n return this._core.permission;\n }\n\n get error(): WcsListenErrorDetail | null {\n return this._core.error;\n }\n\n get unsupported(): boolean {\n return this._core.unsupported;\n }\n\n // --- Command property ---\n\n get trigger(): boolean {\n return this._trigger;\n }\n\n set trigger(value: boolean) {\n // Momentary command-property: a false→true write starts a session. Mirrors\n // <wcs-geo>'s trigger. Prefer the command-token protocol (`command.start:\n // $command.listen`) for state-driven starts; this exists for DOM triggers and\n // simple boolean bindings.\n const v = !!value;\n if (v) {\n this._trigger = true;\n this.start();\n this._trigger = false;\n this.dispatchEvent(new CustomEvent(\"wcs-listen:trigger-changed\", { detail: false, bubbles: true }));\n }\n }\n\n // --- Commands ---\n\n start(): void {\n this._core.start(this._options());\n }\n\n stop(): void {\n this._core.stop();\n }\n\n abort(): void {\n this._core.abort();\n }\n\n // --- Internal ---\n\n private _options(): ListenOptions {\n return {\n lang: this.lang,\n continuous: this.continuous,\n interimResults: this.interim,\n maxRestarts: this.maxRestarts,\n };\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerListenAutoTrigger();\n }\n // observe() (re-)establishes the permission subscription and returns the\n // readiness promise for SSR; it wraps reinitPermission() (idempotent).\n this._connectedCallbackPromise = this._core.observe();\n if (!this.manual) {\n // Non-blocking auto-start, mirroring <wcs-geo>: start() is fired\n // unconditionally without first awaiting/inspecting the (async) permission\n // state. A `denied` mic surfaces as a `not-allowed` error via the `error`\n // property (and stops auto-restart), rather than the connect path silently\n // suppressing the start. This keeps the permission model declarative and\n // consistent with geolocation. Use `manual` to require an explicit start.\n this.start();\n }\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { WcsSpeak } from \"./components/Speak.js\";\nimport { WcsListen } from \"./components/Listen.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.speak)) {\n customElements.define(config.tagNames.speak, WcsSpeak);\n }\n if (!customElements.get(config.tagNames.listen)) {\n customElements.define(config.tagNames.listen, WcsListen);\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapSpeech(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n"],"names":["registered","handleClick"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- const t={autoTrigger:!0,triggerAttribute:"data-speaktarget",listenTriggerAttribute:"data-listentarget",tagNames:{speak:"wcs-speak",listen:"wcs-listen"}};function e(t){if(null===t||"object"!=typeof t)return t;Object.freeze(t);for(const s of Object.keys(t))e(t[s]);return t}function s(t){if(null===t||"object"!=typeof t)return t;const e={};for(const i of Object.keys(t))e[i]=s(t[i]);return e}let i=null;const n=t;function r(){return i||(i=e(s(t))),i}class a extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"voices",event:"wcs-speak:voices-changed"},{name:"speaking",event:"wcs-speak:speaking-changed"},{name:"paused",event:"wcs-speak:paused-changed"},{name:"pending",event:"wcs-speak:pending-changed"},{name:"charIndex",event:"wcs-speak:boundary",getter:t=>t.detail?.charIndex??null},{name:"spokenWord",event:"wcs-speak:boundary",getter:t=>t.detail?.word??null},{name:"error",event:"wcs-speak:error"},{name:"unsupported",event:"wcs-speak:unsupported-changed"}],commands:[{name:"speak"},{name:"cancel"},{name:"pause"},{name:"resume"}]};_target;_voices=[];_rawVoices=[];_speaking=!1;_paused=!1;_pending=!1;_charIndex=null;_spokenWord=null;_error=null;_unsupported=!1;_queued=0;_started=0;_gen=0;_voicesSubscribed=!1;constructor(t){super(),this._target=t??this,this._setUnsupported(!this._hasApi()),this._initVoices()}get voices(){return this._voices}get speaking(){return this._speaking}get paused(){return this._paused}get pending(){return this._pending}get charIndex(){return this._charIndex}get spokenWord(){return this._spokenWord}get error(){return this._error}get unsupported(){return this._unsupported}_setVoices(t){this._voicesEqual(this._voices,t)||(this._voices=t,this._target.dispatchEvent(new CustomEvent("wcs-speak:voices-changed",{detail:t,bubbles:!0})))}_voicesEqual(t,e){if(t.length!==e.length)return!1;for(let s=0;s<t.length;s++){const i=t[s],n=e[s];if(i.name!==n.name||i.lang!==n.lang||i.default!==n.default||i.localService!==n.localService||i.voiceURI!==n.voiceURI)return!1}return!0}_setSpeaking(t){this._speaking!==t&&(this._speaking=t,this._target.dispatchEvent(new CustomEvent("wcs-speak:speaking-changed",{detail:t,bubbles:!0})))}_setPaused(t){this._paused!==t&&(this._paused=t,this._target.dispatchEvent(new CustomEvent("wcs-speak:paused-changed",{detail:t,bubbles:!0})))}_setPending(t){this._pending!==t&&(this._pending=t,this._target.dispatchEvent(new CustomEvent("wcs-speak:pending-changed",{detail:t,bubbles:!0})))}_setBoundary(t,e){this._charIndex===t&&this._spokenWord===e||(this._charIndex=t,this._spokenWord=e,this._target.dispatchEvent(new CustomEvent("wcs-speak:boundary",{detail:{charIndex:t,word:e},bubbles:!0})))}_setError(t){this._error!==t&&(this._error=t,this._target.dispatchEvent(new CustomEvent("wcs-speak:error",{detail:t,bubbles:!0})))}_setUnsupported(t){this._unsupported!==t&&(this._unsupported=t,this._target.dispatchEvent(new CustomEvent("wcs-speak:unsupported-changed",{detail:t,bubbles:!0})))}speak(t,e={}){if(!this._hasApi())return void this._setError(this._unsupportedError());if("string"!=typeof t||""===t.trim())return;const s=window.speechSynthesis,i=new window.SpeechSynthesisUtterance(t);if("number"==typeof e.rate&&(i.rate=e.rate),"number"==typeof e.pitch&&(i.pitch=e.pitch),"number"==typeof e.volume&&(i.volume=e.volume),"string"==typeof e.lang&&""!==e.lang&&(i.lang=e.lang),"string"==typeof e.voice&&""!==e.voice){const t=this._rawVoices.find(t=>t.name===e.voice);t&&(i.voice=t)}const n=this._gen;let r=!1;i.onstart=()=>{n===this._gen&&(r=!0,this._queued=Math.max(0,this._queued-1),this._started++,this._setSpeaking(!0),this._setPending(this._queued>0),this._setBoundary(null,null))},i.onboundary=e=>{if(n===this._gen)try{const s=e.charIndex,i=e.charLength,n="number"==typeof i&&i>0?t.substring(s,s+i):t.slice(s).match(/^\S+/)?.[0]??"";this._setBoundary(s,n)}catch{}},i.onpause=()=>{n===this._gen&&this._setPaused(!0)},i.onresume=()=>{n===this._gen&&this._setPaused(!1)},i.onend=()=>{n===this._gen&&this._finishUtterance(r)},i.onerror=t=>{n===this._gen&&(this._setError(this._normalizeError(t)),this._finishUtterance(r))},this._setError(null),this._queued++,this._setPending(!0),s.speak(i)}cancel(){this._hasApi()&&(this._gen++,this._paused&&window.speechSynthesis.resume(),window.speechSynthesis.cancel(),this._queued=0,this._started=0,this._setSpeaking(!1),this._setPending(!1),this._setPaused(!1),this._setBoundary(null,null))}pause(){this._hasApi()&&window.speechSynthesis.pause()}resume(){this._hasApi()&&window.speechSynthesis.resume()}reinitVoices(){this._voicesSubscribed||this._initVoices()}dispose(){this._voicesSubscribed=!1,this._gen++,this._queued=0,this._started=0,this._speaking=!1,this._paused=!1,this._pending=!1,this._hasApi()&&window.speechSynthesis.removeEventListener("voiceschanged",this._onVoicesChanged)}_finishUtterance(t){t?this._started=Math.max(0,this._started-1):this._queued=Math.max(0,this._queued-1),this._setSpeaking(this._started>0),this._setPending(this._queued>0),0===this._started&&0===this._queued&&(this._setPaused(!1),this._setBoundary(null,null))}_hasApi(){return"undefined"!=typeof window&&!!window.speechSynthesis&&"function"==typeof window.SpeechSynthesisUtterance}_initVoices(){this._hasApi()&&(this._voicesSubscribed=!0,this._loadVoices(),window.speechSynthesis.addEventListener("voiceschanged",this._onVoicesChanged))}_onVoicesChanged=()=>{this._loadVoices()};_loadVoices(){const t=window.speechSynthesis.getVoices()??[];this._rawVoices=t,this._setVoices(t.map(t=>this._normalizeVoice(t)))}_normalizeVoice(t){return{name:t.name,lang:t.lang,default:t.default,localService:t.localService,voiceURI:t.voiceURI}}_normalizeError(t){const e=t.error??"synthesis-failed";return{error:e,message:`Speech synthesis failed: ${e}.`}}_unsupportedError(){return{error:"unsupported",message:"SpeechSynthesis API is not available in this environment."}}}let o=!1;function u(t){const e=t.target;if(!(e instanceof Element))return;let s;try{s=e.closest(`[${n.triggerAttribute}]`)}catch{return}if(!s)return;const i=s.getAttribute(n.triggerAttribute);if(!i)return;const r=customElements.get(n.tagNames.speak),a=document.getElementById(i);if(!(r&&a instanceof r))return;const o=s.getAttribute("data-speaktext"),u=null!==o?o:s.textContent.trim();t.preventDefault(),a.speak(u)}class c extends HTMLElement{static wcBindable={...a.wcBindable,inputs:[{name:"say"},{name:"rate",attribute:"rate"},{name:"pitch",attribute:"pitch"},{name:"volume",attribute:"volume"},{name:"voice",attribute:"voice"},{name:"lang",attribute:"lang"},{name:"manual",attribute:"manual"}],commands:a.wcBindable.commands};_core;_say="";constructor(){super(),this._core=new a(this)}get rate(){return this._numberAttr("rate",1)}set rate(t){this.setAttribute("rate",String(t))}get pitch(){return this._numberAttr("pitch",1)}set pitch(t){this.setAttribute("pitch",String(t))}get volume(){return this._numberAttr("volume",1)}set volume(t){this.setAttribute("volume",String(t))}get voice(){return this.getAttribute("voice")??""}set voice(t){null==t?this.removeAttribute("voice"):this.setAttribute("voice",String(t))}get lang(){return this.getAttribute("lang")??""}set lang(t){null==t?this.removeAttribute("lang"):this.setAttribute("lang",String(t))}get manual(){return this.hasAttribute("manual")}set manual(t){t?this.setAttribute("manual",""):this.removeAttribute("manual")}get say(){return this._say}set say(t){if(null==t)return;if(this.manual)return;const e=String(t);e!==this._say&&(this._say=e,this.speak(e))}get voices(){return this._core.voices}get speaking(){return this._core.speaking}get paused(){return this._core.paused}get pending(){return this._core.pending}get charIndex(){return this._core.charIndex}get spokenWord(){return this._core.spokenWord}get error(){return this._core.error}get unsupported(){return this._core.unsupported}speak(t){this._core.speak(t,this._options())}cancel(){this._core.cancel()}pause(){this._core.pause()}resume(){this._core.resume()}_numberAttr(t,e){const s=this.getAttribute(t);if(null===s||""===s.trim())return e;const i=Number(s);return Number.isFinite(i)?i:e}_options(){return{rate:this.rate,pitch:this.pitch,volume:this.volume,voice:this.voice,lang:this.lang}}connectedCallback(){this.style.display="none",n.autoTrigger&&(o||(o=!0,document.addEventListener("click",u))),this._core.reinitVoices()}disconnectedCallback(){this._core.dispose()}}class h extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"interimTranscript",event:"wcs-listen:interim-changed"},{name:"finalTranscript",event:"wcs-listen:final-changed"},{name:"result",event:"wcs-listen:result"},{name:"listening",event:"wcs-listen:listening-changed"},{name:"permission",event:"wcs-listen:permission-changed"},{name:"error",event:"wcs-listen:error"},{name:"unsupported",event:"wcs-listen:unsupported-changed"}],commands:[{name:"start"},{name:"stop"},{name:"abort"}]};_target;_recognition=null;_interimTranscript="";_finalTranscript="";_result=null;_listening=!1;_permission="prompt";_error=null;_unsupported=!1;_active=!1;_continuous=!1;_maxRestarts=0;_restartCount=0;_permissionStatus=null;_permissionSubscribed=!1;_permGen=0;constructor(t){super(),this._target=t??this;const e=this._getCtor();this._setUnsupported(!e),e&&(this._recognition=new e,this._attachHandlers(this._recognition)),this._initPermission()}get interimTranscript(){return this._interimTranscript}get finalTranscript(){return this._finalTranscript}get result(){return this._result}get listening(){return this._listening}get permission(){return this._permission}get error(){return this._error}get unsupported(){return this._unsupported}_setInterim(t){this._interimTranscript!==t&&(this._interimTranscript=t,this._target.dispatchEvent(new CustomEvent("wcs-listen:interim-changed",{detail:t,bubbles:!0})))}_setFinal(t){this._finalTranscript!==t&&(this._finalTranscript=t,this._target.dispatchEvent(new CustomEvent("wcs-listen:final-changed",{detail:t,bubbles:!0})))}_setResult(t){this._result=t,this._target.dispatchEvent(new CustomEvent("wcs-listen:result",{detail:t,bubbles:!0}))}_setListening(t){this._listening!==t&&(this._listening=t,this._target.dispatchEvent(new CustomEvent("wcs-listen:listening-changed",{detail:t,bubbles:!0})))}_setPermission(t){this._permission!==t&&(this._permission=t,this._target.dispatchEvent(new CustomEvent("wcs-listen:permission-changed",{detail:t,bubbles:!0})))}_setError(t){this._error!==t&&(this._error=t,this._target.dispatchEvent(new CustomEvent("wcs-listen:error",{detail:t,bubbles:!0})))}_setUnsupported(t){this._unsupported!==t&&(this._unsupported=t,this._target.dispatchEvent(new CustomEvent("wcs-listen:unsupported-changed",{detail:t,bubbles:!0})))}start(t={}){this._recognition?this._active||(this._continuous=t.continuous??!1,this._maxRestarts="number"==typeof t.maxRestarts&&t.maxRestarts>=0?Math.floor(t.maxRestarts):0,this._restartCount=0,this._recognition.lang=t.lang??"",this._recognition.continuous=this._continuous,this._recognition.interimResults=t.interimResults??!1,"number"==typeof t.maxAlternatives&&(this._recognition.maxAlternatives=t.maxAlternatives),this._setInterim(""),this._setFinal(""),this._setError(null),this._active=!0,this._safeStart()):this._setError(this._unsupportedError())}stop(){this._recognition&&(this._active=!1,this._recognition.stop())}abort(){this._recognition&&(this._active=!1,this._recognition.abort())}reinitPermission(){this._permissionSubscribed||this._initPermission()}dispose(){if(this._active=!1,this._permissionSubscribed=!1,this._permGen++,this._recognition)try{this._recognition.abort()}catch{}this._listening=!1,this._permissionStatus&&(this._permissionStatus.removeEventListener("change",this._onPermissionChange),this._permissionStatus=null)}_attachHandlers(t){t.onstart=()=>{this._setListening(!0)},t.onresult=t=>{try{this._handleResult(t)}catch{}},t.onerror=t=>{this._setError(this._normalizeError(t)),!t||"not-allowed"!==t.error&&"service-not-allowed"!==t.error||(this._active=!1)},t.onend=()=>{if(this._active&&this._continuous&&this._restartCount<this._maxRestarts)return this._restartCount++,this._safeStart(),void(this._active||this._setListening(!1));this._setListening(!1),this._active=!1}}_handleResult(t){const e=t.results;let s="",i="";for(let n=t.resultIndex??0;n<e.length;n++){const t=e[n],r=t?.[0]?.transcript??"";t?.isFinal?i+=r:s+=r}""!==i&&this._setFinal(this._finalTranscript+i),this._setInterim(s),this._restartCount=0;const n=e[e.length-1];n&&this._setResult(this._normalizeResult(n))}_normalizeResult(t){const e=[];for(let s=0;s<t.length;s++)e.push({transcript:t[s]?.transcript??"",confidence:t[s]?.confidence??0});const s=e[0]??{transcript:"",confidence:0};return{transcript:s.transcript,confidence:s.confidence,isFinal:!!t.isFinal,alternatives:e}}_safeStart(){try{this._recognition.start()}catch{this._active=!1}}_getCtor(){const t="undefined"==typeof window?void 0:window;return t?.SpeechRecognition??t?.webkitSpeechRecognition??null}_initPermission(){if("undefined"==typeof navigator||!navigator.permissions||"function"!=typeof navigator.permissions.query)return void this._setPermission("unsupported");this._permissionSubscribed=!0;const t=++this._permGen;navigator.permissions.query({name:"microphone"}).then(e=>{t===this._permGen&&(this._permissionStatus=e,this._setPermission(e.state),e.addEventListener("change",this._onPermissionChange))},()=>{t===this._permGen&&this._setPermission("unsupported")})}_onPermissionChange=t=>{const e=t.target;this._setPermission(e.state)};_normalizeError(t){const e=t&&t.error?t.error:"aborted";return{error:e,message:`Speech recognition failed: ${e}.`}}_unsupportedError(){return{error:"unsupported",message:"SpeechRecognition API is not available in this environment."}}}let l=!1;function g(t){const e=t.target;if(!(e instanceof Element))return;let s;try{s=e.closest(`[${n.listenTriggerAttribute}]`)}catch{return}if(!s)return;const i=s.getAttribute(n.listenTriggerAttribute);if(!i)return;const r=customElements.get(n.tagNames.listen),a=document.getElementById(i);if(!(r&&a instanceof r))return;t.preventDefault();const o=a;o.listening?o.stop():o.start()}class _ extends HTMLElement{static wcBindable={...h.wcBindable,properties:[...h.wcBindable.properties,{name:"trigger",event:"wcs-listen:trigger-changed"}],inputs:[{name:"lang",attribute:"lang"},{name:"continuous",attribute:"continuous"},{name:"interim",attribute:"interim"},{name:"maxRestarts",attribute:"max-restarts"},{name:"manual",attribute:"manual"},{name:"trigger"}],commands:h.wcBindable.commands};_core;_trigger=!1;constructor(){super(),this._core=new h(this)}get lang(){return this.getAttribute("lang")??""}set lang(t){null==t?this.removeAttribute("lang"):this.setAttribute("lang",String(t))}get continuous(){return this.hasAttribute("continuous")}set continuous(t){t?this.setAttribute("continuous",""):this.removeAttribute("continuous")}get interim(){return this.hasAttribute("interim")}set interim(t){t?this.setAttribute("interim",""):this.removeAttribute("interim")}get maxRestarts(){const t=this.getAttribute("max-restarts");if(null===t||""===t.trim())return 0;const e=Number(t);return Number.isFinite(e)&&e>=0?Math.floor(e):0}set maxRestarts(t){this.setAttribute("max-restarts",String(t))}get manual(){return this.hasAttribute("manual")}set manual(t){t?this.setAttribute("manual",""):this.removeAttribute("manual")}get interimTranscript(){return this._core.interimTranscript}get finalTranscript(){return this._core.finalTranscript}get result(){return this._core.result}get listening(){return this._core.listening}get permission(){return this._core.permission}get error(){return this._core.error}get unsupported(){return this._core.unsupported}get trigger(){return this._trigger}set trigger(t){!!t&&(this._trigger=!0,this.start(),this._trigger=!1,this.dispatchEvent(new CustomEvent("wcs-listen:trigger-changed",{detail:!1,bubbles:!0})))}start(){this._core.start(this._options())}stop(){this._core.stop()}abort(){this._core.abort()}_options(){return{lang:this.lang,continuous:this.continuous,interimResults:this.interim,maxRestarts:this.maxRestarts}}connectedCallback(){this.style.display="none",n.autoTrigger&&(l||(l=!0,document.addEventListener("click",g))),this._core.reinitPermission(),this.manual||this.start()}disconnectedCallback(){this._core.dispose()}}function p(e){var s;e&&("boolean"==typeof(s=e).autoTrigger&&(t.autoTrigger=s.autoTrigger),"string"==typeof s.triggerAttribute&&(t.triggerAttribute=s.triggerAttribute),"string"==typeof s.listenTriggerAttribute&&(t.listenTriggerAttribute=s.listenTriggerAttribute),s.tagNames&&Object.assign(t.tagNames,s.tagNames),i=null),customElements.get(n.tagNames.speak)||customElements.define(n.tagNames.speak,c),customElements.get(n.tagNames.listen)||customElements.define(n.tagNames.listen,_)}export{h as ListenCore,a as SpeakCore,_ as WcsListen,c as WcsSpeak,p as bootstrapSpeech,r as getConfig};
1
+ const t={autoTrigger:!0,triggerAttribute:"data-speaktarget",listenTriggerAttribute:"data-listentarget",tagNames:{speak:"wcs-speak",listen:"wcs-listen"}};function e(t){if(null===t||"object"!=typeof t)return t;Object.freeze(t);for(const s of Object.keys(t))e(t[s]);return t}function s(t){if(null===t||"object"!=typeof t)return t;const e={};for(const i of Object.keys(t))e[i]=s(t[i]);return e}let i=null;const n=t;function r(){return i||(i=e(s(t))),i}class a extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"voices",event:"wcs-speak:voices-changed"},{name:"speaking",event:"wcs-speak:speaking-changed"},{name:"paused",event:"wcs-speak:paused-changed"},{name:"pending",event:"wcs-speak:pending-changed"},{name:"charIndex",event:"wcs-speak:boundary",getter:t=>t.detail?.charIndex??null},{name:"spokenWord",event:"wcs-speak:boundary",getter:t=>t.detail?.word??null},{name:"error",event:"wcs-speak:error"},{name:"unsupported",event:"wcs-speak:unsupported-changed"}],commands:[{name:"speak"},{name:"cancel"},{name:"pause"},{name:"resume"}]};_target;_voices=[];_rawVoices=[];_speaking=!1;_paused=!1;_pending=!1;_charIndex=null;_spokenWord=null;_error=null;_unsupported=!1;_queued=0;_started=0;_gen=0;_voicesSubscribed=!1;_ready=Promise.resolve();constructor(t){super(),this._target=t??this,this._setUnsupported(!this._hasApi()),this._initVoices()}get voices(){return this._voices}get speaking(){return this._speaking}get paused(){return this._paused}get pending(){return this._pending}get charIndex(){return this._charIndex}get spokenWord(){return this._spokenWord}get error(){return this._error}get unsupported(){return this._unsupported}get ready(){return this._ready}_setVoices(t){this._voicesEqual(this._voices,t)||(this._voices=t,this._target.dispatchEvent(new CustomEvent("wcs-speak:voices-changed",{detail:t,bubbles:!0})))}_voicesEqual(t,e){if(t.length!==e.length)return!1;for(let s=0;s<t.length;s++){const i=t[s],n=e[s];if(i.name!==n.name||i.lang!==n.lang||i.default!==n.default||i.localService!==n.localService||i.voiceURI!==n.voiceURI)return!1}return!0}_setSpeaking(t){this._speaking!==t&&(this._speaking=t,this._target.dispatchEvent(new CustomEvent("wcs-speak:speaking-changed",{detail:t,bubbles:!0})))}_setPaused(t){this._paused!==t&&(this._paused=t,this._target.dispatchEvent(new CustomEvent("wcs-speak:paused-changed",{detail:t,bubbles:!0})))}_setPending(t){this._pending!==t&&(this._pending=t,this._target.dispatchEvent(new CustomEvent("wcs-speak:pending-changed",{detail:t,bubbles:!0})))}_setBoundary(t,e){this._charIndex===t&&this._spokenWord===e||(this._charIndex=t,this._spokenWord=e,this._target.dispatchEvent(new CustomEvent("wcs-speak:boundary",{detail:{charIndex:t,word:e},bubbles:!0})))}_setError(t){this._error!==t&&(this._error=t,this._target.dispatchEvent(new CustomEvent("wcs-speak:error",{detail:t,bubbles:!0})))}_setUnsupported(t){this._unsupported!==t&&(this._unsupported=t,this._target.dispatchEvent(new CustomEvent("wcs-speak:unsupported-changed",{detail:t,bubbles:!0})))}speak(t,e={}){if(!this._hasApi())return void this._setError(this._unsupportedError());if("string"!=typeof t||""===t.trim())return;const s=window.speechSynthesis,i=new window.SpeechSynthesisUtterance(t);if("number"==typeof e.rate&&(i.rate=e.rate),"number"==typeof e.pitch&&(i.pitch=e.pitch),"number"==typeof e.volume&&(i.volume=e.volume),"string"==typeof e.lang&&""!==e.lang&&(i.lang=e.lang),"string"==typeof e.voice&&""!==e.voice){const t=this._rawVoices.find(t=>t.name===e.voice);t&&(i.voice=t)}const n=this._gen;let r=!1;i.onstart=()=>{n===this._gen&&(r=!0,this._queued=Math.max(0,this._queued-1),this._started++,this._setSpeaking(!0),this._setPending(this._queued>0),this._setBoundary(null,null))},i.onboundary=e=>{if(n===this._gen)try{const s=e.charIndex,i=e.charLength,n="number"==typeof i&&i>0?t.substring(s,s+i):t.slice(s).match(/^\S+/)?.[0]??"";this._setBoundary(s,n)}catch{}},i.onpause=()=>{n===this._gen&&this._setPaused(!0)},i.onresume=()=>{n===this._gen&&this._setPaused(!1)},i.onend=()=>{n===this._gen&&this._finishUtterance(r)},i.onerror=t=>{n===this._gen&&(this._setError(this._normalizeError(t)),this._finishUtterance(r))},this._setError(null),this._queued++,this._setPending(!0),s.speak(i)}cancel(){this._hasApi()&&(this._gen++,this._paused&&window.speechSynthesis.resume(),window.speechSynthesis.cancel(),this._queued=0,this._started=0,this._setSpeaking(!1),this._setPending(!1),this._setPaused(!1),this._setBoundary(null,null))}pause(){this._hasApi()&&window.speechSynthesis.pause()}resume(){this._hasApi()&&window.speechSynthesis.resume()}reinitVoices(){this._voicesSubscribed||this._initVoices()}observe(){return this.reinitVoices(),this._ready}dispose(){this._voicesSubscribed=!1,this._gen++,this._queued=0,this._started=0,this._speaking=!1,this._paused=!1,this._pending=!1,this._hasApi()&&window.speechSynthesis.removeEventListener("voiceschanged",this._onVoicesChanged)}_finishUtterance(t){t?this._started=Math.max(0,this._started-1):this._queued=Math.max(0,this._queued-1),this._setSpeaking(this._started>0),this._setPending(this._queued>0),0===this._started&&0===this._queued&&(this._setPaused(!1),this._setBoundary(null,null))}_hasApi(){return"undefined"!=typeof window&&!!window.speechSynthesis&&"function"==typeof window.SpeechSynthesisUtterance}_initVoices(){this._hasApi()&&(this._voicesSubscribed=!0,this._loadVoices(),window.speechSynthesis.addEventListener("voiceschanged",this._onVoicesChanged))}_onVoicesChanged=()=>{this._loadVoices()};_loadVoices(){const t=window.speechSynthesis.getVoices()??[];this._rawVoices=t,this._setVoices(t.map(t=>this._normalizeVoice(t)))}_normalizeVoice(t){return{name:t.name,lang:t.lang,default:t.default,localService:t.localService,voiceURI:t.voiceURI}}_normalizeError(t){const e=t.error??"synthesis-failed";return{error:e,message:`Speech synthesis failed: ${e}.`}}_unsupportedError(){return{error:"unsupported",message:"SpeechSynthesis API is not available in this environment."}}}let o=!1;function u(t){const e=t.target;if(!(e instanceof Element))return;let s;try{s=e.closest(`[${n.triggerAttribute}]`)}catch{return}if(!s)return;const i=s.getAttribute(n.triggerAttribute);if(!i)return;const r=customElements.get(n.tagNames.speak),a=document.getElementById(i);if(!(r&&a instanceof r))return;const o=s.getAttribute("data-speaktext"),u=null!==o?o:s.textContent.trim();t.preventDefault(),a.speak(u)}class c extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...a.wcBindable,inputs:[{name:"say"},{name:"rate",attribute:"rate"},{name:"pitch",attribute:"pitch"},{name:"volume",attribute:"volume"},{name:"voice",attribute:"voice"},{name:"lang",attribute:"lang"},{name:"manual",attribute:"manual"}],commands:a.wcBindable.commands};_core;_say="";_connectedCallbackPromise=Promise.resolve();constructor(){super(),this._core=new a(this)}get connectedCallbackPromise(){return this._connectedCallbackPromise}get rate(){return this._numberAttr("rate",1)}set rate(t){this.setAttribute("rate",String(t))}get pitch(){return this._numberAttr("pitch",1)}set pitch(t){this.setAttribute("pitch",String(t))}get volume(){return this._numberAttr("volume",1)}set volume(t){this.setAttribute("volume",String(t))}get voice(){return this.getAttribute("voice")??""}set voice(t){null==t?this.removeAttribute("voice"):this.setAttribute("voice",String(t))}get lang(){return this.getAttribute("lang")??""}set lang(t){null==t?this.removeAttribute("lang"):this.setAttribute("lang",String(t))}get manual(){return this.hasAttribute("manual")}set manual(t){t?this.setAttribute("manual",""):this.removeAttribute("manual")}get say(){return this._say}set say(t){if(null==t)return;if(this.manual)return;const e=String(t);e!==this._say&&(this._say=e,this.speak(e))}get voices(){return this._core.voices}get speaking(){return this._core.speaking}get paused(){return this._core.paused}get pending(){return this._core.pending}get charIndex(){return this._core.charIndex}get spokenWord(){return this._core.spokenWord}get error(){return this._core.error}get unsupported(){return this._core.unsupported}speak(t){this._core.speak(t,this._options())}cancel(){this._core.cancel()}pause(){this._core.pause()}resume(){this._core.resume()}_numberAttr(t,e){const s=this.getAttribute(t);if(null===s||""===s.trim())return e;const i=Number(s);return Number.isFinite(i)?i:e}_options(){return{rate:this.rate,pitch:this.pitch,volume:this.volume,voice:this.voice,lang:this.lang}}connectedCallback(){this.style.display="none",n.autoTrigger&&(o||(o=!0,document.addEventListener("click",u))),this._connectedCallbackPromise=this._core.observe()}disconnectedCallback(){this._core.dispose()}}class h extends EventTarget{static wcBindable={protocol:"wc-bindable",version:1,properties:[{name:"interimTranscript",event:"wcs-listen:interim-changed"},{name:"finalTranscript",event:"wcs-listen:final-changed"},{name:"result",event:"wcs-listen:result"},{name:"listening",event:"wcs-listen:listening-changed"},{name:"permission",event:"wcs-listen:permission-changed"},{name:"error",event:"wcs-listen:error"},{name:"unsupported",event:"wcs-listen:unsupported-changed"}],commands:[{name:"start"},{name:"stop"},{name:"abort"}]};_target;_recognition=null;_interimTranscript="";_finalTranscript="";_result=null;_listening=!1;_permission="prompt";_error=null;_unsupported=!1;_active=!1;_continuous=!1;_maxRestarts=0;_restartCount=0;_permissionStatus=null;_permissionSubscribed=!1;_permGen=0;_ready=Promise.resolve();constructor(t){super(),this._target=t??this;const e=this._getCtor();this._setUnsupported(!e),e&&(this._recognition=new e,this._attachHandlers(this._recognition)),this._initPermission()}get interimTranscript(){return this._interimTranscript}get finalTranscript(){return this._finalTranscript}get result(){return this._result}get listening(){return this._listening}get permission(){return this._permission}get error(){return this._error}get unsupported(){return this._unsupported}get ready(){return this._ready}_setInterim(t){this._interimTranscript!==t&&(this._interimTranscript=t,this._target.dispatchEvent(new CustomEvent("wcs-listen:interim-changed",{detail:t,bubbles:!0})))}_setFinal(t){this._finalTranscript!==t&&(this._finalTranscript=t,this._target.dispatchEvent(new CustomEvent("wcs-listen:final-changed",{detail:t,bubbles:!0})))}_setResult(t){this._result=t,this._target.dispatchEvent(new CustomEvent("wcs-listen:result",{detail:t,bubbles:!0}))}_setListening(t){this._listening!==t&&(this._listening=t,this._target.dispatchEvent(new CustomEvent("wcs-listen:listening-changed",{detail:t,bubbles:!0})))}_setPermission(t){this._permission!==t&&(this._permission=t,this._target.dispatchEvent(new CustomEvent("wcs-listen:permission-changed",{detail:t,bubbles:!0})))}_setError(t){this._error!==t&&(this._error=t,this._target.dispatchEvent(new CustomEvent("wcs-listen:error",{detail:t,bubbles:!0})))}_setUnsupported(t){this._unsupported!==t&&(this._unsupported=t,this._target.dispatchEvent(new CustomEvent("wcs-listen:unsupported-changed",{detail:t,bubbles:!0})))}start(t={}){this._recognition?this._active||(this._continuous=t.continuous??!1,this._maxRestarts="number"==typeof t.maxRestarts&&t.maxRestarts>=0?Math.floor(t.maxRestarts):0,this._restartCount=0,this._recognition.lang=t.lang??"",this._recognition.continuous=this._continuous,this._recognition.interimResults=t.interimResults??!1,"number"==typeof t.maxAlternatives&&(this._recognition.maxAlternatives=t.maxAlternatives),this._setInterim(""),this._setFinal(""),this._setError(null),this._active=!0,this._safeStart()):this._setError(this._unsupportedError())}stop(){this._recognition&&(this._active=!1,this._recognition.stop())}abort(){this._recognition&&(this._active=!1,this._recognition.abort())}reinitPermission(){this._permissionSubscribed||this._initPermission()}observe(){return this.reinitPermission(),this._ready}dispose(){if(this._active=!1,this._permissionSubscribed=!1,this._permGen++,this._recognition)try{this._recognition.abort()}catch{}this._listening=!1,this._permissionStatus&&(this._permissionStatus.removeEventListener("change",this._onPermissionChange),this._permissionStatus=null)}_attachHandlers(t){t.onstart=()=>{this._setListening(!0)},t.onresult=t=>{try{this._handleResult(t)}catch{}},t.onerror=t=>{this._setError(this._normalizeError(t)),!t||"not-allowed"!==t.error&&"service-not-allowed"!==t.error||(this._active=!1)},t.onend=()=>{if(this._active&&this._continuous&&this._restartCount<this._maxRestarts)return this._restartCount++,this._safeStart(),void(this._active||this._setListening(!1));this._setListening(!1),this._active=!1}}_handleResult(t){const e=t.results;let s="",i="";for(let n=t.resultIndex??0;n<e.length;n++){const t=e[n],r=t?.[0]?.transcript??"";t?.isFinal?i+=r:s+=r}""!==i&&this._setFinal(this._finalTranscript+i),this._setInterim(s),this._restartCount=0;const n=e[e.length-1];n&&this._setResult(this._normalizeResult(n))}_normalizeResult(t){const e=[];for(let s=0;s<t.length;s++)e.push({transcript:t[s]?.transcript??"",confidence:t[s]?.confidence??0});const s=e[0]??{transcript:"",confidence:0};return{transcript:s.transcript,confidence:s.confidence,isFinal:!!t.isFinal,alternatives:e}}_safeStart(){try{this._recognition.start()}catch{this._active=!1}}_getCtor(){const t="undefined"==typeof window?void 0:window;return t?.SpeechRecognition??t?.webkitSpeechRecognition??null}_initPermission(){if("undefined"==typeof navigator||!navigator.permissions||"function"!=typeof navigator.permissions.query)return void this._setPermission("unsupported");this._permissionSubscribed=!0;const t=++this._permGen;navigator.permissions.query({name:"microphone"}).then(e=>{t===this._permGen&&(this._permissionStatus=e,this._setPermission(e.state),e.addEventListener("change",this._onPermissionChange))},()=>{t===this._permGen&&this._setPermission("unsupported")})}_onPermissionChange=t=>{const e=t.target;this._setPermission(e.state)};_normalizeError(t){const e=t&&t.error?t.error:"aborted";return{error:e,message:`Speech recognition failed: ${e}.`}}_unsupportedError(){return{error:"unsupported",message:"SpeechRecognition API is not available in this environment."}}}let l=!1;function _(t){const e=t.target;if(!(e instanceof Element))return;let s;try{s=e.closest(`[${n.listenTriggerAttribute}]`)}catch{return}if(!s)return;const i=s.getAttribute(n.listenTriggerAttribute);if(!i)return;const r=customElements.get(n.tagNames.listen),a=document.getElementById(i);if(!(r&&a instanceof r))return;t.preventDefault();const o=a;o.listening?o.stop():o.start()}class g extends HTMLElement{static hasConnectedCallbackPromise=!0;static wcBindable={...h.wcBindable,properties:[...h.wcBindable.properties,{name:"trigger",event:"wcs-listen:trigger-changed"}],inputs:[{name:"lang",attribute:"lang"},{name:"continuous",attribute:"continuous"},{name:"interim",attribute:"interim"},{name:"maxRestarts",attribute:"max-restarts"},{name:"manual",attribute:"manual"},{name:"trigger"}],commands:h.wcBindable.commands};_core;_trigger=!1;_connectedCallbackPromise=Promise.resolve();constructor(){super(),this._core=new h(this)}get connectedCallbackPromise(){return this._connectedCallbackPromise}get lang(){return this.getAttribute("lang")??""}set lang(t){null==t?this.removeAttribute("lang"):this.setAttribute("lang",String(t))}get continuous(){return this.hasAttribute("continuous")}set continuous(t){t?this.setAttribute("continuous",""):this.removeAttribute("continuous")}get interim(){return this.hasAttribute("interim")}set interim(t){t?this.setAttribute("interim",""):this.removeAttribute("interim")}get maxRestarts(){const t=this.getAttribute("max-restarts");if(null===t||""===t.trim())return 0;const e=Number(t);return Number.isFinite(e)&&e>=0?Math.floor(e):0}set maxRestarts(t){this.setAttribute("max-restarts",String(t))}get manual(){return this.hasAttribute("manual")}set manual(t){t?this.setAttribute("manual",""):this.removeAttribute("manual")}get interimTranscript(){return this._core.interimTranscript}get finalTranscript(){return this._core.finalTranscript}get result(){return this._core.result}get listening(){return this._core.listening}get permission(){return this._core.permission}get error(){return this._core.error}get unsupported(){return this._core.unsupported}get trigger(){return this._trigger}set trigger(t){!!t&&(this._trigger=!0,this.start(),this._trigger=!1,this.dispatchEvent(new CustomEvent("wcs-listen:trigger-changed",{detail:!1,bubbles:!0})))}start(){this._core.start(this._options())}stop(){this._core.stop()}abort(){this._core.abort()}_options(){return{lang:this.lang,continuous:this.continuous,interimResults:this.interim,maxRestarts:this.maxRestarts}}connectedCallback(){this.style.display="none",n.autoTrigger&&(l||(l=!0,document.addEventListener("click",_))),this._connectedCallbackPromise=this._core.observe(),this.manual||this.start()}disconnectedCallback(){this._core.dispose()}}function p(e){var s;e&&("boolean"==typeof(s=e).autoTrigger&&(t.autoTrigger=s.autoTrigger),"string"==typeof s.triggerAttribute&&(t.triggerAttribute=s.triggerAttribute),"string"==typeof s.listenTriggerAttribute&&(t.listenTriggerAttribute=s.listenTriggerAttribute),s.tagNames&&Object.assign(t.tagNames,s.tagNames),i=null),customElements.get(n.tagNames.speak)||customElements.define(n.tagNames.speak,c),customElements.get(n.tagNames.listen)||customElements.define(n.tagNames.listen,g)}export{h as ListenCore,a as SpeakCore,g as WcsListen,c as WcsSpeak,p as bootstrapSpeech,r as getConfig};
2
2
  //# sourceMappingURL=index.esm.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/core/SpeakCore.ts","../src/autoTrigger.ts","../src/components/Speak.ts","../src/core/ListenCore.ts","../src/listenAutoTrigger.ts","../src/components/Listen.ts","../src/bootstrapSpeech.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n listenTriggerAttribute: string;\n tagNames: {\n speak: string;\n listen: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-speaktarget\",\n listenTriggerAttribute: \"data-listentarget\",\n tagNames: {\n speak: \"wcs-speak\",\n listen: \"wcs-listen\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Internal, mutable live config used by the components/autoTriggers (they read it\n// at call time so setConfig() takes effect without re-import). Typed as the\n// readonly IConfig at the export boundary — the `as IConfig` is a compile-time\n// view only and does NOT freeze the object, so this export must stay\n// package-internal (it is not re-exported from exports.ts). Public consumers get\n// the deep-frozen clone from getConfig() instead, which is the only safe\n// read-only handle.\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (typeof partialConfig.listenTriggerAttribute === \"string\") {\n _config.listenTriggerAttribute = partialConfig.listenTriggerAttribute;\n }\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import {\n IWcBindable, SpeakOptions, SpeechVoiceInfo, WcsSpeakErrorDetail,\n} from \"../types.js\";\n\n/**\n * Headless text-to-speech primitive. A thin, framework-agnostic wrapper around\n * the SpeechSynthesis API exposed through the wc-bindable protocol.\n *\n * It is the \"command\" half of the speech package (the recognition half is\n * ListenCore): state drives the element, never the reverse, except for the\n * observable progress/status it publishes back.\n *\n * - **speak(text, options)** queues an utterance. Like the native API, multiple\n * calls queue; `cancel()` clears the queue and stops the current utterance.\n * - **pause() / resume()** suspend and resume the queue.\n * - The observable surface mirrors the live SpeechSynthesis flags\n * (`speaking` / `paused` / `pending`) and exposes voice-list loading\n * (`voices`, which the API populates asynchronously via `voiceschanged`) plus\n * word-boundary progress (`charIndex` / `spokenWord`) for karaoke-style\n * highlighting.\n *\n * Unlike geolocation/clipboard there is no permission gate — synthesis needs no\n * user grant. Failures never throw: they surface through the `error` property so\n * they flow into the declarative state.\n */\nexport class SpeakCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"voices\", event: \"wcs-speak:voices-changed\" },\n { name: \"speaking\", event: \"wcs-speak:speaking-changed\" },\n { name: \"paused\", event: \"wcs-speak:paused-changed\" },\n { name: \"pending\", event: \"wcs-speak:pending-changed\" },\n { name: \"charIndex\", event: \"wcs-speak:boundary\", getter: (e: Event) => (e as CustomEvent).detail?.charIndex ?? null },\n { name: \"spokenWord\", event: \"wcs-speak:boundary\", getter: (e: Event) => (e as CustomEvent).detail?.word ?? null },\n { name: \"error\", event: \"wcs-speak:error\" },\n { name: \"unsupported\", event: \"wcs-speak:unsupported-changed\" },\n ],\n commands: [\n { name: \"speak\" },\n { name: \"cancel\" },\n { name: \"pause\" },\n { name: \"resume\" },\n ],\n };\n\n private _target: EventTarget;\n\n private _voices: SpeechVoiceInfo[] = [];\n private _rawVoices: SpeechSynthesisVoice[] = [];\n private _speaking: boolean = false;\n private _paused: boolean = false;\n private _pending: boolean = false;\n private _charIndex: number | null = null;\n private _spokenWord: string | null = null;\n private _error: WcsSpeakErrorDetail | null = null;\n private _unsupported: boolean = false;\n\n // Count of utterances submitted via speak() but not yet started, and of\n // utterances started but not yet ended/errored. `pending`/`speaking` are\n // derived from these so the queue model is reflected accurately even when\n // several utterances are in flight.\n private _queued: number = 0;\n private _started: number = 0;\n\n // Monotonic id of the current synthesis lifecycle. Bumped by cancel() and\n // dispose(). Each speak() captures it; every utterance event handler bails if\n // it is stale, so a queued/canceled utterance's late callback (notably the\n // \"canceled\" error the browser fires from cancel()) never mutates state or\n // dispatches on a torn-down element.\n private _gen: number = 0;\n\n // True once the voiceschanged subscription has been (or is being) established;\n // reset by dispose(). Guards reinitVoices() so the first connect after\n // construction does not double-subscribe, while a reconnect after dispose()\n // does re-subscribe.\n private _voicesSubscribed: boolean = false;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n // Probe support up front so observers see the real flag before the first read.\n // Routed through the setter (not a direct assignment) so the field starts at\n // its `false` default and the unsupported case actually transitions\n // false→true; the supported case is same-value guarded and dispatches nothing.\n this._setUnsupported(!this._hasApi());\n this._initVoices();\n }\n\n get voices(): SpeechVoiceInfo[] {\n return this._voices;\n }\n\n get speaking(): boolean {\n return this._speaking;\n }\n\n get paused(): boolean {\n return this._paused;\n }\n\n get pending(): boolean {\n return this._pending;\n }\n\n get charIndex(): number | null {\n return this._charIndex;\n }\n\n get spokenWord(): string | null {\n return this._spokenWord;\n }\n\n get error(): WcsSpeakErrorDetail | null {\n return this._error;\n }\n\n // Resolved once in the constructor (`_setUnsupported(!_hasApi())`) and never\n // re-evaluated: the speechSynthesis API's presence is immutable for the\n // lifetime of a document, so there's nothing to re-check.\n get unsupported(): boolean {\n return this._unsupported;\n }\n\n // --- State setters with event dispatch ---\n\n private _setVoices(voices: SpeechVoiceInfo[]): void {\n // Same-value guard, like the other setters. `voiceschanged` can fire several\n // times with an identical list (engines re-announce after warm-up); compare\n // the normalized snapshot content so a redundant re-announcement does not\n // re-dispatch voices-changed. A genuine list change (length or any field)\n // still fires.\n if (this._voicesEqual(this._voices, voices)) return;\n this._voices = voices;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:voices-changed\", {\n detail: voices,\n bubbles: true,\n }));\n }\n\n private _voicesEqual(a: SpeechVoiceInfo[], b: SpeechVoiceInfo[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n const x = a[i];\n const y = b[i];\n if (x.name !== y.name || x.lang !== y.lang || x.default !== y.default\n || x.localService !== y.localService || x.voiceURI !== y.voiceURI) {\n return false;\n }\n }\n return true;\n }\n\n private _setSpeaking(speaking: boolean): void {\n if (this._speaking === speaking) return;\n this._speaking = speaking;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:speaking-changed\", {\n detail: speaking,\n bubbles: true,\n }));\n }\n\n private _setPaused(paused: boolean): void {\n if (this._paused === paused) return;\n this._paused = paused;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:paused-changed\", {\n detail: paused,\n bubbles: true,\n }));\n }\n\n private _setPending(pending: boolean): void {\n if (this._pending === pending) return;\n this._pending = pending;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:pending-changed\", {\n detail: pending,\n bubbles: true,\n }));\n }\n\n private _setBoundary(charIndex: number | null, word: string | null): void {\n // Boundary events stream rapidly with changing offsets; dispatch each. The\n // guard only suppresses redundant resets (e.g. an end after an already-null\n // boundary) so a cleared highlight does not re-fire.\n if (this._charIndex === charIndex && this._spokenWord === word) return;\n this._charIndex = charIndex;\n this._spokenWord = word;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:boundary\", {\n detail: { charIndex, word },\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsSpeakErrorDetail | null): void {\n if (this._error === error) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n private _setUnsupported(unsupported: boolean): void {\n if (this._unsupported === unsupported) return;\n this._unsupported = unsupported;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:unsupported-changed\", {\n detail: unsupported,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n /**\n * Queue an utterance for `text` with optional per-utterance parameters. Never\n * throws: when the API is unavailable it surfaces an `error` and returns. An\n * empty/whitespace-only `text` is a no-op (the browser would not fire start).\n */\n speak(text: string, options: SpeakOptions = {}): void {\n if (!this._hasApi()) {\n this._setError(this._unsupportedError());\n return;\n }\n if (typeof text !== \"string\" || text.trim() === \"\") {\n return;\n }\n\n const synth = window.speechSynthesis;\n const utterance = new window.SpeechSynthesisUtterance(text);\n if (typeof options.rate === \"number\") utterance.rate = options.rate;\n if (typeof options.pitch === \"number\") utterance.pitch = options.pitch;\n if (typeof options.volume === \"number\") utterance.volume = options.volume;\n if (typeof options.lang === \"string\" && options.lang !== \"\") utterance.lang = options.lang;\n if (typeof options.voice === \"string\" && options.voice !== \"\") {\n const match = this._rawVoices.find((v) => v.name === options.voice);\n if (match) utterance.voice = match;\n }\n\n const gen = this._gen;\n // Per-utterance \"has started\" flag. The browser can fire onerror/onend\n // *before* onstart (e.g. a `synthesis-unavailable` / `audio-busy` failure on\n // a still-queued utterance). In that case the utterance only ever counted\n // toward `_queued`, so the terminal handler must decrement `_queued` — not\n // `_started` — otherwise `pending` (derived from `_queued > 0`) sticks true\n // forever. onstart sets this flag so the terminal handler knows which counter\n // to release.\n let started = false;\n utterance.onstart = (): void => {\n if (gen !== this._gen) return;\n started = true;\n this._queued = Math.max(0, this._queued - 1);\n this._started++;\n this._setSpeaking(true);\n this._setPending(this._queued > 0);\n this._setBoundary(null, null);\n };\n utterance.onboundary = (event: SpeechSynthesisEvent): void => {\n if (gen !== this._gen) return;\n try {\n const charIndex = event.charIndex;\n const length = (event as unknown as { charLength?: number }).charLength;\n // Prefer the engine-provided word length. Some engines omit `charLength`\n // on word boundaries; fall back to the run of non-whitespace at charIndex\n // so `spokenWord` (the karaoke highlight) still works there.\n const word = (typeof length === \"number\" && length > 0)\n ? text.substring(charIndex, charIndex + length)\n : (text.slice(charIndex).match(/^\\S+/)?.[0] ?? \"\");\n this._setBoundary(charIndex, word);\n } catch {\n // A malformed boundary event must not escape the browser callback.\n }\n };\n utterance.onpause = (): void => {\n if (gen !== this._gen) return;\n this._setPaused(true);\n };\n utterance.onresume = (): void => {\n if (gen !== this._gen) return;\n this._setPaused(false);\n };\n utterance.onend = (): void => {\n if (gen !== this._gen) return;\n this._finishUtterance(started);\n };\n utterance.onerror = (event: SpeechSynthesisErrorEvent): void => {\n if (gen !== this._gen) return;\n this._setError(this._normalizeError(event));\n this._finishUtterance(started);\n };\n\n this._setError(null);\n this._queued++;\n this._setPending(true);\n synth.speak(utterance);\n }\n\n /**\n * Clear the queue and stop the current utterance immediately. Resets all\n * progress state synchronously and invalidates in-flight utterance callbacks\n * (the browser fires a \"canceled\" error per utterance) so they do not surface\n * as real errors.\n */\n cancel(): void {\n if (!this._hasApi()) return;\n // Neutralize every in-flight utterance's pending callbacks before triggering\n // the native cancel (which fires \"canceled\" onerror/onend on each).\n this._gen++;\n // Chrome quirk: cancelling while the engine is paused can leave the synth in\n // a state where the *next* speak() produces no audio. Resume first so cancel\n // happens from a running state. resume() on an idle/non-paused engine is a\n // harmless no-op, so guarding on the tracked `_paused` flag is sufficient.\n if (this._paused) {\n window.speechSynthesis.resume();\n }\n window.speechSynthesis.cancel();\n this._queued = 0;\n this._started = 0;\n this._setSpeaking(false);\n this._setPending(false);\n this._setPaused(false);\n this._setBoundary(null, null);\n }\n\n pause(): void {\n if (!this._hasApi()) return;\n window.speechSynthesis.pause();\n }\n\n resume(): void {\n if (!this._hasApi()) return;\n window.speechSynthesis.resume();\n }\n\n /**\n * Re-establish the voiceschanged subscription after a dispose() — e.g. the\n * Shell element was disconnected and then reconnected (reparented). No-op while\n * a subscription is already live, so the first connect after construction does\n * not double-subscribe.\n */\n reinitVoices(): void {\n if (!this._voicesSubscribed) {\n this._initVoices();\n }\n }\n\n /**\n * Detach the live voiceschanged listener and neutralize any in-flight\n * utterance callbacks. Call from the Shell's `disconnectedCallback`.\n */\n dispose(): void {\n this._voicesSubscribed = false;\n this._gen++;\n // Reset the queue bookkeeping silently (no dispatch on a disposed element);\n // a reconnect starts fresh. The observable snapshot (error / charIndex /\n // spokenWord) is intentionally *kept* so a reparented element preserves its\n // last state, mirroring GeolocationCore.dispose(). The next speak() resets\n // error / boundary for its own lifecycle.\n this._queued = 0;\n this._started = 0;\n this._speaking = false;\n this._paused = false;\n this._pending = false;\n if (this._hasApi()) {\n window.speechSynthesis.removeEventListener(\"voiceschanged\", this._onVoicesChanged);\n }\n }\n\n // --- Internal ---\n\n // `started` is the per-utterance flag set by its onstart. An utterance that\n // ended/errored after starting releases a `_started` slot; one that never\n // started (terminal event before onstart) releases its `_queued` slot instead,\n // so `pending` correctly returns to false.\n private _finishUtterance(started: boolean): void {\n if (started) {\n this._started = Math.max(0, this._started - 1);\n } else {\n this._queued = Math.max(0, this._queued - 1);\n }\n this._setSpeaking(this._started > 0);\n this._setPending(this._queued > 0);\n if (this._started === 0 && this._queued === 0) {\n this._setPaused(false);\n this._setBoundary(null, null);\n }\n }\n\n private _hasApi(): boolean {\n return typeof window !== \"undefined\"\n && !!window.speechSynthesis\n && typeof (window as unknown as { SpeechSynthesisUtterance?: unknown }).SpeechSynthesisUtterance === \"function\";\n }\n\n private _initVoices(): void {\n if (!this._hasApi()) return;\n this._voicesSubscribed = true;\n this._loadVoices();\n window.speechSynthesis.addEventListener(\"voiceschanged\", this._onVoicesChanged);\n }\n\n private _onVoicesChanged = (): void => {\n this._loadVoices();\n };\n\n private _loadVoices(): void {\n const raw = window.speechSynthesis.getVoices() ?? [];\n this._rawVoices = raw;\n this._setVoices(raw.map((v) => this._normalizeVoice(v)));\n }\n\n private _normalizeVoice(voice: SpeechSynthesisVoice): SpeechVoiceInfo {\n return {\n name: voice.name,\n lang: voice.lang,\n default: voice.default,\n localService: voice.localService,\n voiceURI: voice.voiceURI,\n };\n }\n\n private _normalizeError(event: SpeechSynthesisErrorEvent): WcsSpeakErrorDetail {\n const error = event.error ?? \"synthesis-failed\";\n return { error, message: `Speech synthesis failed: ${error}.` };\n }\n\n private _unsupportedError(): WcsSpeakErrorDetail {\n return { error: \"unsupported\", message: \"SpeechSynthesis API is not available in this environment.\" };\n }\n}\n","import { config } from \"./config.js\";\nimport type { WcsSpeak } from \"./components/Speak.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n // A misconfigured triggerAttribute (e.g. one with a space) makes the attribute\n // selector invalid and closest() throw SyntaxError; guard so a bad config\n // disables only this shortcut rather than killing every document click handler.\n let triggerElement: Element | null;\n try {\n triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n } catch {\n return;\n }\n if (!triggerElement) return;\n\n const speakId = triggerElement.getAttribute(config.triggerAttribute);\n if (!speakId) return;\n\n // Resolve the registered constructor at call time instead of importing Speak as\n // a value, avoiding a components/Speak.ts ⇄ autoTrigger.ts cycle\n // (Speak.connectedCallback() calls registerAutoTrigger()). instanceof against\n // the customElements registry keeps the same identity guarantee.\n const SpeakCtor = customElements.get(config.tagNames.speak);\n const speakElement = document.getElementById(speakId);\n if (!SpeakCtor || !(speakElement instanceof SpeakCtor)) return;\n\n // The text to speak comes from the trigger element: an explicit `data-speaktext`\n // attribute wins, otherwise the element's text content. This keeps the\n // click-driven shortcut declarative without inventing a payload channel.\n const explicit = triggerElement.getAttribute(\"data-speaktext\");\n // textContent is always a string for an Element; the cast avoids an\n // unreachable null-coalesce branch. speak() tolerates a non-string anyway.\n // The textContent fallback is trimmed (HTML indentation otherwise leaks leading\n // / trailing whitespace into the utterance); an explicit data-speaktext is kept\n // verbatim so an author can deliberately include surrounding spaces.\n const text = explicit !== null ? explicit : (triggerElement.textContent as string).trim();\n\n event.preventDefault();\n (speakElement as WcsSpeak).speak(text);\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { IWcBindable, SpeakOptions, SpeechVoiceInfo, WcsSpeakErrorDetail } from \"../types.js\";\nimport { SpeakCore } from \"../core/SpeakCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\n/**\n * `<wcs-speak>` — declarative text-to-speech. Wraps SpeakCore and exposes:\n *\n * - **`say`** (reactive input): writing a value speaks it, suppressing same-value\n * writes so it fires only when the bound source actually changes. The\n * imperative `speak` command instead speaks on demand (even the same text\n * again). See `docs/speech-tag-design.md` § 5.\n * - per-utterance parameters (`rate` / `pitch` / `volume` / `voice` / `lang`) as\n * mirrored attributes.\n * - the Core's observable surface (voices / speaking / paused / pending /\n * charIndex / spokenWord / error / unsupported) via delegated getters.\n */\nexport class WcsSpeak extends HTMLElement {\n static wcBindable: IWcBindable = {\n ...SpeakCore.wcBindable,\n // Shell-level settable surface. `say` is a momentary reactive command-property\n // with no mirrored attribute (it carries dynamic text, not declarative config),\n // mirroring how <wcs-geo>'s `trigger` has no attribute. The rest mirror their\n // HTML attributes idempotently.\n inputs: [\n { name: \"say\" },\n { name: \"rate\", attribute: \"rate\" },\n { name: \"pitch\", attribute: \"pitch\" },\n { name: \"volume\", attribute: \"volume\" },\n { name: \"voice\", attribute: \"voice\" },\n { name: \"lang\", attribute: \"lang\" },\n { name: \"manual\", attribute: \"manual\" },\n ],\n commands: SpeakCore.wcBindable.commands,\n };\n\n private _core: SpeakCore;\n private _say: string = \"\";\n\n constructor() {\n super();\n this._core = new SpeakCore(this);\n }\n\n // --- Attribute accessors ---\n\n get rate(): number {\n return this._numberAttr(\"rate\", 1);\n }\n\n set rate(value: number) {\n this.setAttribute(\"rate\", String(value));\n }\n\n get pitch(): number {\n return this._numberAttr(\"pitch\", 1);\n }\n\n set pitch(value: number) {\n this.setAttribute(\"pitch\", String(value));\n }\n\n get volume(): number {\n return this._numberAttr(\"volume\", 1);\n }\n\n set volume(value: number) {\n this.setAttribute(\"volume\", String(value));\n }\n\n get voice(): string {\n return this.getAttribute(\"voice\") ?? \"\";\n }\n\n set voice(value: string | null) {\n if (value == null) {\n this.removeAttribute(\"voice\");\n } else {\n this.setAttribute(\"voice\", String(value));\n }\n }\n\n get lang(): string {\n return this.getAttribute(\"lang\") ?? \"\";\n }\n\n set lang(value: string | null) {\n if (value == null) {\n this.removeAttribute(\"lang\");\n } else {\n this.setAttribute(\"lang\", String(value));\n }\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Reactive command-property ---\n\n get say(): string {\n return this._say;\n }\n\n set say(value: string | null) {\n // Reactive: writing a new value speaks it. `manual` mutes the path entirely\n // (the imperative `speak` command still works) — both an opt-out and the hook\n // used to avoid a recognition echo loop while listening. A conforming binder\n // never delivers `undefined` (it skips the write), but a direct assignment\n // can, so normalize null/undefined to a no-op.\n //\n // ECHO-LOOP WARNING: when wiring <wcs-listen> → state → `say`, the synthesized\n // audio will be re-recognized unless speech is muted while listening. There is\n // no code-level interlock here (the two tags are decoupled): the consumer MUST\n // wire it — bind `manual` to the listening flag (or gate the bound source).\n // See README \"Echo loop\" and the speech-echo example.\n if (value == null) return;\n if (this.manual) return;\n const v = String(value);\n // Same-value guard: only speak when the bound source actually changes. For\n // \"speak the same text again on demand\", use the `speak` command instead.\n if (v === this._say) return;\n this._say = v;\n this.speak(v);\n }\n\n // --- Core delegated getters ---\n\n get voices(): SpeechVoiceInfo[] {\n return this._core.voices;\n }\n\n get speaking(): boolean {\n return this._core.speaking;\n }\n\n get paused(): boolean {\n return this._core.paused;\n }\n\n get pending(): boolean {\n return this._core.pending;\n }\n\n get charIndex(): number | null {\n return this._core.charIndex;\n }\n\n get spokenWord(): string | null {\n return this._core.spokenWord;\n }\n\n get error(): WcsSpeakErrorDetail | null {\n return this._core.error;\n }\n\n get unsupported(): boolean {\n return this._core.unsupported;\n }\n\n // --- Commands ---\n\n speak(text: string): void {\n this._core.speak(text, this._options());\n }\n\n cancel(): void {\n this._core.cancel();\n }\n\n pause(): void {\n this._core.pause();\n }\n\n resume(): void {\n this._core.resume();\n }\n\n // --- Internal ---\n\n private _numberAttr(name: string, fallback: number): number {\n const attr = this.getAttribute(name);\n if (attr === null || attr.trim() === \"\") return fallback;\n // Strict parse via Number() (unlike parseInt, \"1px\" -> NaN, not 1). Fall back\n // to the API default for any non-finite value, matching the geolocation\n // \"invalid values fall back to default\" convention.\n const parsed = Number(attr);\n return Number.isFinite(parsed) ? parsed : fallback;\n }\n\n private _options(): SpeakOptions {\n return {\n rate: this.rate,\n pitch: this.pitch,\n volume: this.volume,\n voice: this.voice,\n lang: this.lang,\n };\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n // Revive the voiceschanged subscription after a reconnect (reparenting).\n // No-op on the first connect since the constructor already subscribed.\n this._core.reinitVoices();\n }\n\n disconnectedCallback(): void {\n // Detach event subscriptions and neutralize in-flight utterance callbacks.\n // Any utterance already speaking finishes naturally (SpeechSynthesis is a\n // global singleton; cancelling here would stop other <wcs-speak> elements\n // too). Call `cancel()` explicitly to stop audio.\n this._core.dispose();\n }\n}\n","import {\n IWcBindable, ListenOptions, ListenPermissionState,\n WcsListenResultDetail, WcsListenAlternative, WcsListenErrorDetail,\n} from \"../types.js\";\n\n// The vendor-prefixed constructor is not in the DOM lib types; declare a minimal\n// shape so we can feature-detect and construct it.\n// Minimal structural shapes for the recognition result/error events. The DOM lib\n// does not ship the prefixed API's types, so we declare just the fields read here\n// instead of using `any`, keeping the handlers type-checked and consistent with\n// the typed state fields. All fields are optional/loose because real engines vary\n// (resultIndex / charLength omitted, malformed events) and the handlers already\n// defend against that at runtime.\ninterface RecognitionAlternativeLike {\n transcript?: string;\n confidence?: number;\n}\ninterface RecognitionResultLike {\n readonly length: number;\n isFinal?: boolean;\n [index: number]: RecognitionAlternativeLike;\n}\ninterface RecognitionResultListLike {\n readonly length: number;\n [index: number]: RecognitionResultLike;\n}\ninterface RecognitionResultEventLike {\n results: RecognitionResultListLike;\n resultIndex?: number;\n}\ninterface RecognitionErrorEventLike {\n error?: string;\n}\n\ninterface SpeechRecognitionLike extends EventTarget {\n lang: string;\n continuous: boolean;\n interimResults: boolean;\n maxAlternatives: number;\n start(): void;\n stop(): void;\n abort(): void;\n onstart: ((event: Event) => void) | null;\n onend: ((event: Event) => void) | null;\n onresult: ((event: RecognitionResultEventLike) => void) | null;\n onerror: ((event: RecognitionErrorEventLike) => void) | null;\n}\n\ntype SpeechRecognitionCtor = new () => SpeechRecognitionLike;\n\n/**\n * Headless speech-to-text primitive. A thin, framework-agnostic wrapper around\n * the SpeechRecognition API (vendor-prefixed `webkitSpeechRecognition` in\n * Chrome) exposed through the wc-bindable protocol.\n *\n * It is the \"event\" half of the speech package (the synthesis half is\n * SpeakCore): recognition results flow element → state.\n *\n * Two phases mirror geolocation:\n * - **one-shot** (`continuous = false`) — recognize until the first `end`.\n * - **continuous** (`continuous = true`) — keep a single session open across\n * phrases. The browser still ends a session on silence; auto-restart bridges\n * that gap **but is opt-in via `maxRestarts`**: with the default `maxRestarts\n * = 0` a continuous session is *not* restarted on `end` (the safe default —\n * unbounded restart is the infinite-loop risk we guard against). Set\n * `maxRestarts > 0` to bridge N silences. The cap also stops a persistent\n * failure (e.g. `not-allowed`) from spinning forever or exhausting quota; a\n * real result resets the budget so only consecutive empty restarts count.\n *\n * A microphone permission gate (like geolocation's) reflects\n * `navigator.permissions.query({ name: \"microphone\" })`. Failures never throw —\n * they surface through the `error` property.\n */\nexport class ListenCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"interimTranscript\", event: \"wcs-listen:interim-changed\" },\n { name: \"finalTranscript\", event: \"wcs-listen:final-changed\" },\n { name: \"result\", event: \"wcs-listen:result\" },\n { name: \"listening\", event: \"wcs-listen:listening-changed\" },\n { name: \"permission\", event: \"wcs-listen:permission-changed\" },\n { name: \"error\", event: \"wcs-listen:error\" },\n { name: \"unsupported\", event: \"wcs-listen:unsupported-changed\" },\n ],\n commands: [\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"abort\" },\n ],\n };\n\n private _target: EventTarget;\n private _recognition: SpeechRecognitionLike | null = null;\n\n private _interimTranscript: string = \"\";\n private _finalTranscript: string = \"\";\n private _result: WcsListenResultDetail | null = null;\n private _listening: boolean = false;\n private _permission: ListenPermissionState = \"prompt\";\n private _error: WcsListenErrorDetail | null = null;\n private _unsupported: boolean = false;\n\n // Intent flag: true between start() and stop()/abort()/terminal-error. Gates\n // the auto-restart loop so a session that ended because the user stopped it\n // does not restart.\n private _active: boolean = false;\n private _continuous: boolean = false;\n private _maxRestarts: number = 0;\n private _restartCount: number = 0;\n\n // Permission tracking — same machinery as GeolocationCore.\n private _permissionStatus: PermissionStatus | null = null;\n private _permissionSubscribed: boolean = false;\n private _permGen: number = 0;\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n const Ctor = this._getCtor();\n this._setUnsupported(!Ctor);\n if (Ctor) {\n this._recognition = new Ctor();\n this._attachHandlers(this._recognition);\n }\n this._initPermission();\n }\n\n get interimTranscript(): string {\n return this._interimTranscript;\n }\n\n get finalTranscript(): string {\n return this._finalTranscript;\n }\n\n get result(): WcsListenResultDetail | null {\n return this._result;\n }\n\n get listening(): boolean {\n return this._listening;\n }\n\n get permission(): ListenPermissionState {\n return this._permission;\n }\n\n get error(): WcsListenErrorDetail | null {\n return this._error;\n }\n\n // Resolved once in the constructor (`_setUnsupported(!Ctor)`) and never\n // re-evaluated: the SpeechRecognition API's presence is immutable for the\n // lifetime of a document, so there's nothing to re-check.\n get unsupported(): boolean {\n return this._unsupported;\n }\n\n // --- State setters with event dispatch ---\n\n private _setInterim(value: string): void {\n if (this._interimTranscript === value) return;\n this._interimTranscript = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:interim-changed\", { detail: value, bubbles: true }));\n }\n\n private _setFinal(value: string): void {\n if (this._finalTranscript === value) return;\n this._finalTranscript = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:final-changed\", { detail: value, bubbles: true }));\n }\n\n private _setResult(value: WcsListenResultDetail): void {\n this._result = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:result\", { detail: value, bubbles: true }));\n }\n\n private _setListening(value: boolean): void {\n if (this._listening === value) return;\n this._listening = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:listening-changed\", { detail: value, bubbles: true }));\n }\n\n private _setPermission(value: ListenPermissionState): void {\n if (this._permission === value) return;\n this._permission = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:permission-changed\", { detail: value, bubbles: true }));\n }\n\n private _setError(value: WcsListenErrorDetail | null): void {\n if (this._error === value) return;\n this._error = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:error\", { detail: value, bubbles: true }));\n }\n\n private _setUnsupported(value: boolean): void {\n if (this._unsupported === value) return;\n this._unsupported = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:unsupported-changed\", { detail: value, bubbles: true }));\n }\n\n // --- Public API ---\n\n /**\n * Begin a recognition session. Resets the transcripts (a fresh, user-initiated\n * listen), applies options, and starts. Idempotent while already listening: a\n * redundant start() is ignored so the browser does not throw \"recognition has\n * already started\".\n */\n start(options: ListenOptions = {}): void {\n if (!this._recognition) {\n this._setError(this._unsupportedError());\n return;\n }\n if (this._active) return;\n\n this._continuous = options.continuous ?? false;\n // `maxRestarts` is a restart *count*, so floor any fractional input to an\n // integer (e.g. 2.5 → 2). `_restartCount` increments by 1, so a fractional\n // cap would otherwise compare inconsistently. Non-finite/negative → 0.\n this._maxRestarts = typeof options.maxRestarts === \"number\" && options.maxRestarts >= 0\n ? Math.floor(options.maxRestarts)\n : 0;\n this._restartCount = 0;\n this._recognition.lang = options.lang ?? \"\";\n this._recognition.continuous = this._continuous;\n this._recognition.interimResults = options.interimResults ?? false;\n if (typeof options.maxAlternatives === \"number\") {\n this._recognition.maxAlternatives = options.maxAlternatives;\n }\n\n // Fresh session: clear prior transcripts and error.\n this._setInterim(\"\");\n this._setFinal(\"\");\n this._setError(null);\n this._active = true;\n this._safeStart();\n }\n\n stop(): void {\n if (!this._recognition) return;\n // Clear intent first so the end handler does not auto-restart.\n this._active = false;\n this._recognition.stop();\n }\n\n abort(): void {\n if (!this._recognition) return;\n this._active = false;\n this._recognition.abort();\n }\n\n /**\n * Re-establish the permission `change` subscription after a dispose().\n */\n reinitPermission(): void {\n if (!this._permissionSubscribed) {\n this._initPermission();\n }\n }\n\n /**\n * Stop recognition and detach the live permission listener. Call from the\n * Shell's `disconnectedCallback`.\n */\n dispose(): void {\n // Only the live subscriptions and the listening shadow are reset here. The\n // observable snapshot (transcripts / result / error) is intentionally *kept*\n // so a reparented element preserves its last state, mirroring how\n // GeolocationCore.dispose() leaves `position` / `error` intact. The next\n // start() clears the transcripts and error for its fresh session anyway.\n this._active = false;\n this._permissionSubscribed = false;\n this._permGen++;\n if (this._recognition) {\n // abort() is the immediate teardown; guard against environments where it\n // throws on an idle recognizer.\n try {\n this._recognition.abort();\n } catch {\n // ignore — teardown is best-effort.\n }\n }\n // Reset the listening shadow silently (no dispatch on a disposed element),\n // mirroring GeolocationCore's `_loading` reset. The abort() above neutralizes\n // the recognizer but its `end` (which would clear listening via the setter)\n // may not have fired yet; forcing false here means a reconnect+start's\n // `onstart` still transitions false→true through the same-value guard, so the\n // state never desyncs to a stale `true`.\n this._listening = false;\n if (this._permissionStatus) {\n this._permissionStatus.removeEventListener(\"change\", this._onPermissionChange);\n this._permissionStatus = null;\n }\n }\n\n // --- Internal: recognition lifecycle ---\n\n private _attachHandlers(recognition: SpeechRecognitionLike): void {\n recognition.onstart = (): void => {\n this._setListening(true);\n };\n recognition.onresult = (event: RecognitionResultEventLike): void => {\n try {\n this._handleResult(event);\n } catch {\n // A malformed result event must not escape the browser callback.\n }\n };\n recognition.onerror = (event: RecognitionErrorEventLike): void => {\n this._setError(this._normalizeError(event));\n // Terminal errors must not be retried — they would spin the restart loop.\n // The set is deliberately limited to the permission-class errors that can\n // never self-recover within a session. Transient failures\n // (`network` / `audio-capture` / `no-speech`) are intentionally *not*\n // terminal: they are recoverable, so a continuous session restarts through\n // them, bounded by `maxRestarts` (the cap is the guard against a persistent\n // transient failure spinning forever).\n if (event && (event.error === \"not-allowed\" || event.error === \"service-not-allowed\")) {\n this._active = false;\n }\n };\n recognition.onend = (): void => {\n if (this._active && this._continuous && this._restartCount < this._maxRestarts) {\n // Auto-restart bridges a silence-induced `end`. Keep `listening` true\n // across the gap rather than flickering true→false→true: from the\n // consumer's perspective the continuous session never stopped. The\n // immediately-following start()'s `onstart` re-sets true (same-value\n // guarded → no-op), so the flag stays steady. A genuine stop (no\n // restart) still drops to false below.\n this._restartCount++;\n this._safeStart();\n // _safeStart() clears _active if the restart threw; in that case the\n // session is over, so reflect listening=false rather than leaving it\n // stuck true.\n if (!this._active) this._setListening(false);\n return;\n }\n // No restart: the session is fully over.\n this._setListening(false);\n this._active = false;\n };\n }\n\n private _handleResult(event: RecognitionResultEventLike): void {\n const results = event.results;\n let interim = \"\";\n let finalChunk = \"\";\n // Per the Web Speech spec, `resultIndex` is the lowest index in `results`\n // that changed in this event, so we only fold in `[resultIndex, length)` and\n // accumulate finals (`this._finalTranscript + finalChunk`). This assumes the\n // engine advances `resultIndex` past already-finalized results. A nonconforming\n // engine that omits `resultIndex` (`?? 0`) or re-reports finalized results at\n // index 0 on every event could double-accumulate the same final chunk; standard\n // browser engines don't, so this is not hardened against here.\n for (let i = event.resultIndex ?? 0; i < results.length; i++) {\n const res = results[i];\n const transcript = res?.[0]?.transcript ?? \"\";\n if (res?.isFinal) {\n finalChunk += transcript;\n } else {\n interim += transcript;\n }\n }\n if (finalChunk !== \"\") {\n this._setFinal(this._finalTranscript + finalChunk);\n }\n this._setInterim(interim);\n // Any result is progress — reset the restart budget so only *consecutive*\n // empty restarts count toward the cap.\n this._restartCount = 0;\n\n const last = results[results.length - 1];\n if (last) {\n this._setResult(this._normalizeResult(last));\n }\n }\n\n private _normalizeResult(result: RecognitionResultLike): WcsListenResultDetail {\n const alternatives: WcsListenAlternative[] = [];\n for (let i = 0; i < result.length; i++) {\n alternatives.push({\n transcript: result[i]?.transcript ?? \"\",\n confidence: result[i]?.confidence ?? 0,\n });\n }\n const top = alternatives[0] ?? { transcript: \"\", confidence: 0 };\n return {\n transcript: top.transcript,\n confidence: top.confidence,\n isFinal: !!result.isFinal,\n alternatives,\n };\n }\n\n private _safeStart(): void {\n try {\n this._recognition!.start();\n } catch {\n // start() throws if already started; surface nothing — the live session\n // continues. Reset intent so state stays consistent.\n this._active = false;\n }\n }\n\n // --- Internal: feature detection & permission (mirrors GeolocationCore) ---\n\n private _getCtor(): SpeechRecognitionCtor | null {\n // Guard window access without a separate (in-browser unreachable) early\n // return, mirroring SpeakCore's `_hasApi` style.\n const w = (typeof window === \"undefined\" ? undefined : window) as unknown as {\n SpeechRecognition?: SpeechRecognitionCtor;\n webkitSpeechRecognition?: SpeechRecognitionCtor;\n } | undefined;\n return w?.SpeechRecognition ?? w?.webkitSpeechRecognition ?? null;\n }\n\n private _initPermission(): void {\n if (typeof navigator === \"undefined\" || !navigator.permissions || typeof navigator.permissions.query !== \"function\") {\n this._setPermission(\"unsupported\");\n return;\n }\n this._permissionSubscribed = true;\n const gen = ++this._permGen;\n navigator.permissions.query({ name: \"microphone\" as PermissionName }).then(\n (status) => {\n if (gen !== this._permGen) return;\n this._permissionStatus = status;\n this._setPermission(status.state as ListenPermissionState);\n status.addEventListener(\"change\", this._onPermissionChange);\n },\n () => {\n if (gen !== this._permGen) return;\n this._setPermission(\"unsupported\");\n },\n );\n }\n\n private _onPermissionChange = (event: Event): void => {\n const status = event.target as PermissionStatus;\n this._setPermission(status.state as ListenPermissionState);\n };\n\n private _normalizeError(event: RecognitionErrorEventLike): WcsListenErrorDetail {\n const error = (event && event.error) ? event.error : \"aborted\";\n return { error, message: `Speech recognition failed: ${error}.` };\n }\n\n private _unsupportedError(): WcsListenErrorDetail {\n return { error: \"unsupported\", message: \"SpeechRecognition API is not available in this environment.\" };\n }\n}\n","import { config } from \"./config.js\";\nimport type { WcsListen } from \"./components/Listen.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n // A misconfigured listenTriggerAttribute (e.g. one with a space) makes the\n // attribute selector invalid and closest() throw SyntaxError; guard so a bad\n // config disables only this shortcut rather than killing every click handler.\n let triggerElement: Element | null;\n try {\n triggerElement = target.closest<Element>(`[${config.listenTriggerAttribute}]`);\n } catch {\n return;\n }\n if (!triggerElement) return;\n\n const listenId = triggerElement.getAttribute(config.listenTriggerAttribute);\n if (!listenId) return;\n\n const ListenCtor = customElements.get(config.tagNames.listen);\n const listenElement = document.getElementById(listenId);\n if (!ListenCtor || !(listenElement instanceof ListenCtor)) return;\n\n event.preventDefault();\n // Toggle: clicking starts a session, clicking again while listening stops it.\n const el = listenElement as WcsListen;\n if (el.listening) {\n el.stop();\n } else {\n el.start();\n }\n}\n\nexport function registerListenAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterListenAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport {\n IWcBindable, ListenOptions, ListenPermissionState, WcsListenResultDetail, WcsListenErrorDetail,\n} from \"../types.js\";\nimport { ListenCore } from \"../core/ListenCore.js\";\nimport { registerListenAutoTrigger } from \"../listenAutoTrigger.js\";\n\n/**\n * `<wcs-listen>` — declarative speech-to-text. Wraps ListenCore and exposes the\n * recognition surface (interim/final transcripts, structured result, listening\n * flag, microphone permission, error) plus the two-phase start/stop/abort\n * commands and a momentary `trigger` for DOM-driven starts.\n *\n * Mirrors `<wcs-geo>`: `manual` suppresses the connect-time auto-start, and the\n * `continuous` attribute selects the auto-restarting session phase.\n */\nexport class WcsListen extends HTMLElement {\n static wcBindable: IWcBindable = {\n ...ListenCore.wcBindable,\n properties: [\n ...ListenCore.wcBindable.properties,\n { name: \"trigger\", event: \"wcs-listen:trigger-changed\" },\n ],\n inputs: [\n { name: \"lang\", attribute: \"lang\" },\n { name: \"continuous\", attribute: \"continuous\" },\n { name: \"interim\", attribute: \"interim\" },\n { name: \"maxRestarts\", attribute: \"max-restarts\" },\n { name: \"manual\", attribute: \"manual\" },\n { name: \"trigger\" },\n ],\n commands: ListenCore.wcBindable.commands,\n };\n\n private _core: ListenCore;\n private _trigger: boolean = false;\n\n constructor() {\n super();\n this._core = new ListenCore(this);\n }\n\n // --- Attribute accessors ---\n\n get lang(): string {\n return this.getAttribute(\"lang\") ?? \"\";\n }\n\n set lang(value: string | null) {\n if (value == null) {\n this.removeAttribute(\"lang\");\n } else {\n this.setAttribute(\"lang\", String(value));\n }\n }\n\n get continuous(): boolean {\n return this.hasAttribute(\"continuous\");\n }\n\n set continuous(value: boolean) {\n if (value) {\n this.setAttribute(\"continuous\", \"\");\n } else {\n this.removeAttribute(\"continuous\");\n }\n }\n\n get interim(): boolean {\n return this.hasAttribute(\"interim\");\n }\n\n set interim(value: boolean) {\n if (value) {\n this.setAttribute(\"interim\", \"\");\n } else {\n this.removeAttribute(\"interim\");\n }\n }\n\n get maxRestarts(): number {\n const attr = this.getAttribute(\"max-restarts\");\n if (attr === null || attr.trim() === \"\") return 0;\n const parsed = Number(attr);\n // A restart *count* is an integer, so floor fractional input (e.g. 1.9 → 1)\n // here too, keeping the getter's value identical to the effective cap the\n // Core applies (ListenCore.start floors it as well). Non-finite/negative → 0.\n return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : 0;\n }\n\n set maxRestarts(value: number) {\n this.setAttribute(\"max-restarts\", String(value));\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get interimTranscript(): string {\n return this._core.interimTranscript;\n }\n\n get finalTranscript(): string {\n return this._core.finalTranscript;\n }\n\n get result(): WcsListenResultDetail | null {\n return this._core.result;\n }\n\n get listening(): boolean {\n return this._core.listening;\n }\n\n get permission(): ListenPermissionState {\n return this._core.permission;\n }\n\n get error(): WcsListenErrorDetail | null {\n return this._core.error;\n }\n\n get unsupported(): boolean {\n return this._core.unsupported;\n }\n\n // --- Command property ---\n\n get trigger(): boolean {\n return this._trigger;\n }\n\n set trigger(value: boolean) {\n // Momentary command-property: a false→true write starts a session. Mirrors\n // <wcs-geo>'s trigger. Prefer the command-token protocol (`command.start:\n // $command.listen`) for state-driven starts; this exists for DOM triggers and\n // simple boolean bindings.\n const v = !!value;\n if (v) {\n this._trigger = true;\n this.start();\n this._trigger = false;\n this.dispatchEvent(new CustomEvent(\"wcs-listen:trigger-changed\", { detail: false, bubbles: true }));\n }\n }\n\n // --- Commands ---\n\n start(): void {\n this._core.start(this._options());\n }\n\n stop(): void {\n this._core.stop();\n }\n\n abort(): void {\n this._core.abort();\n }\n\n // --- Internal ---\n\n private _options(): ListenOptions {\n return {\n lang: this.lang,\n continuous: this.continuous,\n interimResults: this.interim,\n maxRestarts: this.maxRestarts,\n };\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerListenAutoTrigger();\n }\n this._core.reinitPermission();\n if (!this.manual) {\n // Non-blocking auto-start, mirroring <wcs-geo>: start() is fired\n // unconditionally without first awaiting/inspecting the (async) permission\n // state. A `denied` mic surfaces as a `not-allowed` error via the `error`\n // property (and stops auto-restart), rather than the connect path silently\n // suppressing the start. This keeps the permission model declarative and\n // consistent with geolocation. Use `manual` to require an explicit start.\n this.start();\n }\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapSpeech(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsSpeak } from \"./components/Speak.js\";\nimport { WcsListen } from \"./components/Listen.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.speak)) {\n customElements.define(config.tagNames.speak, WcsSpeak);\n }\n if (!customElements.get(config.tagNames.listen)) {\n customElements.define(config.tagNames.listen, WcsListen);\n }\n}\n"],"names":["_config","autoTrigger","triggerAttribute","listenTriggerAttribute","tagNames","speak","listen","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","SpeakCore","EventTarget","static","protocol","version","properties","name","event","getter","e","detail","charIndex","word","commands","_target","_voices","_rawVoices","_speaking","_paused","_pending","_charIndex","_spokenWord","_error","_unsupported","_queued","_started","_gen","_voicesSubscribed","constructor","target","super","this","_setUnsupported","_hasApi","_initVoices","voices","speaking","paused","pending","spokenWord","error","unsupported","_setVoices","_voicesEqual","dispatchEvent","CustomEvent","bubbles","a","b","length","i","x","y","lang","default","localService","voiceURI","_setSpeaking","_setPaused","_setPending","_setBoundary","_setError","text","options","_unsupportedError","trim","synth","window","speechSynthesis","utterance","SpeechSynthesisUtterance","rate","pitch","volume","voice","match","find","v","gen","started","onstart","Math","max","onboundary","charLength","substring","slice","onpause","onresume","onend","_finishUtterance","onerror","_normalizeError","cancel","resume","pause","reinitVoices","dispose","removeEventListener","_onVoicesChanged","_loadVoices","addEventListener","raw","getVoices","map","_normalizeVoice","message","registered","handleClick","Element","triggerElement","closest","speakId","getAttribute","SpeakCtor","customElements","get","speakElement","document","getElementById","explicit","textContent","preventDefault","WcsSpeak","HTMLElement","wcBindable","inputs","attribute","_core","_say","_numberAttr","value","setAttribute","String","removeAttribute","manual","hasAttribute","say","_options","fallback","attr","parsed","Number","isFinite","connectedCallback","style","display","disconnectedCallback","ListenCore","_recognition","_interimTranscript","_finalTranscript","_result","_listening","_permission","_active","_continuous","_maxRestarts","_restartCount","_permissionStatus","_permissionSubscribed","_permGen","Ctor","_getCtor","_attachHandlers","_initPermission","interimTranscript","finalTranscript","result","listening","permission","_setInterim","_setFinal","_setResult","_setListening","_setPermission","start","continuous","maxRestarts","floor","interimResults","maxAlternatives","_safeStart","stop","abort","reinitPermission","_onPermissionChange","recognition","onresult","_handleResult","results","interim","finalChunk","resultIndex","res","transcript","isFinal","last","_normalizeResult","alternatives","push","confidence","top","w","undefined","SpeechRecognition","webkitSpeechRecognition","navigator","permissions","query","then","status","state","listenId","ListenCtor","listenElement","el","WcsListen","_trigger","trigger","bootstrapSpeech","userConfig","partialConfig","assign","define"],"mappings":"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"}
1
+ {"version":3,"file":"index.esm.min.js","sources":["../src/config.ts","../src/core/SpeakCore.ts","../src/autoTrigger.ts","../src/components/Speak.ts","../src/core/ListenCore.ts","../src/listenAutoTrigger.ts","../src/components/Listen.ts","../src/bootstrapSpeech.ts","../src/registerComponents.ts"],"sourcesContent":["import { IConfig, IWritableConfig } from \"./types.js\";\n\ninterface IInternalConfig extends IConfig {\n autoTrigger: boolean;\n triggerAttribute: string;\n listenTriggerAttribute: string;\n tagNames: {\n speak: string;\n listen: string;\n };\n}\n\nconst _config: IInternalConfig = {\n autoTrigger: true,\n triggerAttribute: \"data-speaktarget\",\n listenTriggerAttribute: \"data-listentarget\",\n tagNames: {\n speak: \"wcs-speak\",\n listen: \"wcs-listen\",\n },\n};\n\nfunction deepFreeze<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n Object.freeze(obj);\n for (const key of Object.keys(obj)) {\n deepFreeze((obj as Record<string, unknown>)[key]);\n }\n return obj;\n}\n\nfunction deepClone<T>(obj: T): T {\n if (obj === null || typeof obj !== \"object\") return obj;\n const clone: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n clone[key] = deepClone((obj as Record<string, unknown>)[key]);\n }\n return clone as T;\n}\n\nlet frozenConfig: IConfig | null = null;\n\n// Internal, mutable live config used by the components/autoTriggers (they read it\n// at call time so setConfig() takes effect without re-import). Typed as the\n// readonly IConfig at the export boundary — the `as IConfig` is a compile-time\n// view only and does NOT freeze the object, so this export must stay\n// package-internal (it is not re-exported from exports.ts). Public consumers get\n// the deep-frozen clone from getConfig() instead, which is the only safe\n// read-only handle.\nexport const config: IConfig = _config as IConfig;\n\nexport function getConfig(): IConfig {\n if (!frozenConfig) {\n frozenConfig = deepFreeze(deepClone(_config));\n }\n return frozenConfig;\n}\n\nexport function setConfig(partialConfig: IWritableConfig): void {\n if (typeof partialConfig.autoTrigger === \"boolean\") {\n _config.autoTrigger = partialConfig.autoTrigger;\n }\n if (typeof partialConfig.triggerAttribute === \"string\") {\n _config.triggerAttribute = partialConfig.triggerAttribute;\n }\n if (typeof partialConfig.listenTriggerAttribute === \"string\") {\n _config.listenTriggerAttribute = partialConfig.listenTriggerAttribute;\n }\n if (partialConfig.tagNames) {\n Object.assign(_config.tagNames, partialConfig.tagNames);\n }\n frozenConfig = null;\n}\n","import {\n IWcBindable, SpeakOptions, SpeechVoiceInfo, WcsSpeakErrorDetail,\n} from \"../types.js\";\n\n/**\n * Headless text-to-speech primitive. A thin, framework-agnostic wrapper around\n * the SpeechSynthesis API exposed through the wc-bindable protocol.\n *\n * It is the \"command\" half of the speech package (the recognition half is\n * ListenCore): state drives the element, never the reverse, except for the\n * observable progress/status it publishes back.\n *\n * - **speak(text, options)** queues an utterance. Like the native API, multiple\n * calls queue; `cancel()` clears the queue and stops the current utterance.\n * - **pause() / resume()** suspend and resume the queue.\n * - The observable surface mirrors the live SpeechSynthesis flags\n * (`speaking` / `paused` / `pending`) and exposes voice-list loading\n * (`voices`, which the API populates asynchronously via `voiceschanged`) plus\n * word-boundary progress (`charIndex` / `spokenWord`) for karaoke-style\n * highlighting.\n *\n * Unlike geolocation/clipboard there is no permission gate — synthesis needs no\n * user grant. Failures never throw: they surface through the `error` property so\n * they flow into the declarative state.\n */\nexport class SpeakCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"voices\", event: \"wcs-speak:voices-changed\" },\n { name: \"speaking\", event: \"wcs-speak:speaking-changed\" },\n { name: \"paused\", event: \"wcs-speak:paused-changed\" },\n { name: \"pending\", event: \"wcs-speak:pending-changed\" },\n { name: \"charIndex\", event: \"wcs-speak:boundary\", getter: (e: Event) => (e as CustomEvent).detail?.charIndex ?? null },\n { name: \"spokenWord\", event: \"wcs-speak:boundary\", getter: (e: Event) => (e as CustomEvent).detail?.word ?? null },\n { name: \"error\", event: \"wcs-speak:error\" },\n { name: \"unsupported\", event: \"wcs-speak:unsupported-changed\" },\n ],\n commands: [\n { name: \"speak\" },\n { name: \"cancel\" },\n { name: \"pause\" },\n { name: \"resume\" },\n ],\n };\n\n private _target: EventTarget;\n\n private _voices: SpeechVoiceInfo[] = [];\n private _rawVoices: SpeechSynthesisVoice[] = [];\n private _speaking: boolean = false;\n private _paused: boolean = false;\n private _pending: boolean = false;\n private _charIndex: number | null = null;\n private _spokenWord: string | null = null;\n private _error: WcsSpeakErrorDetail | null = null;\n private _unsupported: boolean = false;\n\n // Count of utterances submitted via speak() but not yet started, and of\n // utterances started but not yet ended/errored. `pending`/`speaking` are\n // derived from these so the queue model is reflected accurately even when\n // several utterances are in flight.\n private _queued: number = 0;\n private _started: number = 0;\n\n // Monotonic id of the current synthesis lifecycle. Bumped by cancel() and\n // dispose(). Each speak() captures it; every utterance event handler bails if\n // it is stale, so a queued/canceled utterance's late callback (notably the\n // \"canceled\" error the browser fires from cancel()) never mutates state or\n // dispatches on a torn-down element.\n private _gen: number = 0;\n\n // True once the voiceschanged subscription has been (or is being) established;\n // reset by dispose(). Guards reinitVoices() so the first connect after\n // construction does not double-subscribe, while a reconnect after dispose()\n // does re-subscribe.\n private _voicesSubscribed: boolean = false;\n\n // SSR: feature detection (`_setUnsupported`) and the initial `getVoices()` read\n // are synchronous, and the `voiceschanged` subscription is established eagerly\n // in the constructor, so there is no asynchronous probe to await before\n // snapshotting — readiness is immediate. The Shell exposes this as\n // connectedCallbackPromise.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n // Probe support up front so observers see the real flag before the first read.\n // Routed through the setter (not a direct assignment) so the field starts at\n // its `false` default and the unsupported case actually transitions\n // false→true; the supported case is same-value guarded and dispatches nothing.\n this._setUnsupported(!this._hasApi());\n this._initVoices();\n }\n\n get voices(): SpeechVoiceInfo[] {\n return this._voices;\n }\n\n get speaking(): boolean {\n return this._speaking;\n }\n\n get paused(): boolean {\n return this._paused;\n }\n\n get pending(): boolean {\n return this._pending;\n }\n\n get charIndex(): number | null {\n return this._charIndex;\n }\n\n get spokenWord(): string | null {\n return this._spokenWord;\n }\n\n get error(): WcsSpeakErrorDetail | null {\n return this._error;\n }\n\n // Resolved once in the constructor (`_setUnsupported(!_hasApi())`) and never\n // re-evaluated: the speechSynthesis API's presence is immutable for the\n // lifetime of a document, so there's nothing to re-check.\n get unsupported(): boolean {\n return this._unsupported;\n }\n\n /** Resolves once the first probe settles (immediate — see `_ready`). */\n get ready(): Promise<void> {\n return this._ready;\n }\n\n // --- State setters with event dispatch ---\n\n private _setVoices(voices: SpeechVoiceInfo[]): void {\n // Same-value guard, like the other setters. `voiceschanged` can fire several\n // times with an identical list (engines re-announce after warm-up); compare\n // the normalized snapshot content so a redundant re-announcement does not\n // re-dispatch voices-changed. A genuine list change (length or any field)\n // still fires.\n if (this._voicesEqual(this._voices, voices)) return;\n this._voices = voices;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:voices-changed\", {\n detail: voices,\n bubbles: true,\n }));\n }\n\n private _voicesEqual(a: SpeechVoiceInfo[], b: SpeechVoiceInfo[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n const x = a[i];\n const y = b[i];\n if (x.name !== y.name || x.lang !== y.lang || x.default !== y.default\n || x.localService !== y.localService || x.voiceURI !== y.voiceURI) {\n return false;\n }\n }\n return true;\n }\n\n private _setSpeaking(speaking: boolean): void {\n if (this._speaking === speaking) return;\n this._speaking = speaking;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:speaking-changed\", {\n detail: speaking,\n bubbles: true,\n }));\n }\n\n private _setPaused(paused: boolean): void {\n if (this._paused === paused) return;\n this._paused = paused;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:paused-changed\", {\n detail: paused,\n bubbles: true,\n }));\n }\n\n private _setPending(pending: boolean): void {\n if (this._pending === pending) return;\n this._pending = pending;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:pending-changed\", {\n detail: pending,\n bubbles: true,\n }));\n }\n\n private _setBoundary(charIndex: number | null, word: string | null): void {\n // Boundary events stream rapidly with changing offsets; dispatch each. The\n // guard only suppresses redundant resets (e.g. an end after an already-null\n // boundary) so a cleared highlight does not re-fire.\n if (this._charIndex === charIndex && this._spokenWord === word) return;\n this._charIndex = charIndex;\n this._spokenWord = word;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:boundary\", {\n detail: { charIndex, word },\n bubbles: true,\n }));\n }\n\n private _setError(error: WcsSpeakErrorDetail | null): void {\n if (this._error === error) return;\n this._error = error;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:error\", {\n detail: error,\n bubbles: true,\n }));\n }\n\n private _setUnsupported(unsupported: boolean): void {\n if (this._unsupported === unsupported) return;\n this._unsupported = unsupported;\n this._target.dispatchEvent(new CustomEvent(\"wcs-speak:unsupported-changed\", {\n detail: unsupported,\n bubbles: true,\n }));\n }\n\n // --- Public API ---\n\n /**\n * Queue an utterance for `text` with optional per-utterance parameters. Never\n * throws: when the API is unavailable it surfaces an `error` and returns. An\n * empty/whitespace-only `text` is a no-op (the browser would not fire start).\n */\n speak(text: string, options: SpeakOptions = {}): void {\n if (!this._hasApi()) {\n this._setError(this._unsupportedError());\n return;\n }\n if (typeof text !== \"string\" || text.trim() === \"\") {\n return;\n }\n\n const synth = window.speechSynthesis;\n const utterance = new window.SpeechSynthesisUtterance(text);\n if (typeof options.rate === \"number\") utterance.rate = options.rate;\n if (typeof options.pitch === \"number\") utterance.pitch = options.pitch;\n if (typeof options.volume === \"number\") utterance.volume = options.volume;\n if (typeof options.lang === \"string\" && options.lang !== \"\") utterance.lang = options.lang;\n if (typeof options.voice === \"string\" && options.voice !== \"\") {\n const match = this._rawVoices.find((v) => v.name === options.voice);\n if (match) utterance.voice = match;\n }\n\n const gen = this._gen;\n // Per-utterance \"has started\" flag. The browser can fire onerror/onend\n // *before* onstart (e.g. a `synthesis-unavailable` / `audio-busy` failure on\n // a still-queued utterance). In that case the utterance only ever counted\n // toward `_queued`, so the terminal handler must decrement `_queued` — not\n // `_started` — otherwise `pending` (derived from `_queued > 0`) sticks true\n // forever. onstart sets this flag so the terminal handler knows which counter\n // to release.\n let started = false;\n utterance.onstart = (): void => {\n if (gen !== this._gen) return;\n started = true;\n this._queued = Math.max(0, this._queued - 1);\n this._started++;\n this._setSpeaking(true);\n this._setPending(this._queued > 0);\n this._setBoundary(null, null);\n };\n utterance.onboundary = (event: SpeechSynthesisEvent): void => {\n if (gen !== this._gen) return;\n try {\n const charIndex = event.charIndex;\n const length = (event as unknown as { charLength?: number }).charLength;\n // Prefer the engine-provided word length. Some engines omit `charLength`\n // on word boundaries; fall back to the run of non-whitespace at charIndex\n // so `spokenWord` (the karaoke highlight) still works there.\n const word = (typeof length === \"number\" && length > 0)\n ? text.substring(charIndex, charIndex + length)\n : (text.slice(charIndex).match(/^\\S+/)?.[0] ?? \"\");\n this._setBoundary(charIndex, word);\n } catch {\n // A malformed boundary event must not escape the browser callback.\n }\n };\n utterance.onpause = (): void => {\n if (gen !== this._gen) return;\n this._setPaused(true);\n };\n utterance.onresume = (): void => {\n if (gen !== this._gen) return;\n this._setPaused(false);\n };\n utterance.onend = (): void => {\n if (gen !== this._gen) return;\n this._finishUtterance(started);\n };\n utterance.onerror = (event: SpeechSynthesisErrorEvent): void => {\n if (gen !== this._gen) return;\n this._setError(this._normalizeError(event));\n this._finishUtterance(started);\n };\n\n this._setError(null);\n this._queued++;\n this._setPending(true);\n synth.speak(utterance);\n }\n\n /**\n * Clear the queue and stop the current utterance immediately. Resets all\n * progress state synchronously and invalidates in-flight utterance callbacks\n * (the browser fires a \"canceled\" error per utterance) so they do not surface\n * as real errors.\n */\n cancel(): void {\n if (!this._hasApi()) return;\n // Neutralize every in-flight utterance's pending callbacks before triggering\n // the native cancel (which fires \"canceled\" onerror/onend on each).\n this._gen++;\n // Chrome quirk: cancelling while the engine is paused can leave the synth in\n // a state where the *next* speak() produces no audio. Resume first so cancel\n // happens from a running state. resume() on an idle/non-paused engine is a\n // harmless no-op, so guarding on the tracked `_paused` flag is sufficient.\n if (this._paused) {\n window.speechSynthesis.resume();\n }\n window.speechSynthesis.cancel();\n this._queued = 0;\n this._started = 0;\n this._setSpeaking(false);\n this._setPending(false);\n this._setPaused(false);\n this._setBoundary(null, null);\n }\n\n pause(): void {\n if (!this._hasApi()) return;\n window.speechSynthesis.pause();\n }\n\n resume(): void {\n if (!this._hasApi()) return;\n window.speechSynthesis.resume();\n }\n\n /**\n * Re-establish the voiceschanged subscription after a dispose() — e.g. the\n * Shell element was disconnected and then reconnected (reparented). No-op while\n * a subscription is already live, so the first connect after construction does\n * not double-subscribe.\n */\n reinitVoices(): void {\n if (!this._voicesSubscribed) {\n this._initVoices();\n }\n }\n\n /**\n * Establish monitoring (§3.5). Synthesis is command-driven (speak/cancel), so\n * observe() only (re-)establishes the live `voiceschanged` subscription —\n * idempotent via reinitVoices()'s `_voicesSubscribed` guard, so the first\n * connect after construction does not double-subscribe while a reconnect after\n * dispose() does. Returns the `ready` promise for SSR. Call from the Shell's\n * connectedCallback.\n */\n observe(): Promise<void> {\n this.reinitVoices();\n return this._ready;\n }\n\n /**\n * Detach the live voiceschanged listener and neutralize any in-flight\n * utterance callbacks. Call from the Shell's `disconnectedCallback`.\n */\n dispose(): void {\n this._voicesSubscribed = false;\n this._gen++;\n // Reset the queue bookkeeping silently (no dispatch on a disposed element);\n // a reconnect starts fresh. The observable snapshot (error / charIndex /\n // spokenWord) is intentionally *kept* so a reparented element preserves its\n // last state, mirroring GeolocationCore.dispose(). The next speak() resets\n // error / boundary for its own lifecycle.\n this._queued = 0;\n this._started = 0;\n this._speaking = false;\n this._paused = false;\n this._pending = false;\n if (this._hasApi()) {\n window.speechSynthesis.removeEventListener(\"voiceschanged\", this._onVoicesChanged);\n }\n }\n\n // --- Internal ---\n\n // `started` is the per-utterance flag set by its onstart. An utterance that\n // ended/errored after starting releases a `_started` slot; one that never\n // started (terminal event before onstart) releases its `_queued` slot instead,\n // so `pending` correctly returns to false.\n private _finishUtterance(started: boolean): void {\n if (started) {\n this._started = Math.max(0, this._started - 1);\n } else {\n this._queued = Math.max(0, this._queued - 1);\n }\n this._setSpeaking(this._started > 0);\n this._setPending(this._queued > 0);\n if (this._started === 0 && this._queued === 0) {\n this._setPaused(false);\n this._setBoundary(null, null);\n }\n }\n\n private _hasApi(): boolean {\n return typeof window !== \"undefined\"\n && !!window.speechSynthesis\n && typeof (window as unknown as { SpeechSynthesisUtterance?: unknown }).SpeechSynthesisUtterance === \"function\";\n }\n\n private _initVoices(): void {\n if (!this._hasApi()) return;\n this._voicesSubscribed = true;\n this._loadVoices();\n window.speechSynthesis.addEventListener(\"voiceschanged\", this._onVoicesChanged);\n }\n\n private _onVoicesChanged = (): void => {\n this._loadVoices();\n };\n\n private _loadVoices(): void {\n const raw = window.speechSynthesis.getVoices() ?? [];\n this._rawVoices = raw;\n this._setVoices(raw.map((v) => this._normalizeVoice(v)));\n }\n\n private _normalizeVoice(voice: SpeechSynthesisVoice): SpeechVoiceInfo {\n return {\n name: voice.name,\n lang: voice.lang,\n default: voice.default,\n localService: voice.localService,\n voiceURI: voice.voiceURI,\n };\n }\n\n private _normalizeError(event: SpeechSynthesisErrorEvent): WcsSpeakErrorDetail {\n const error = event.error ?? \"synthesis-failed\";\n return { error, message: `Speech synthesis failed: ${error}.` };\n }\n\n private _unsupportedError(): WcsSpeakErrorDetail {\n return { error: \"unsupported\", message: \"SpeechSynthesis API is not available in this environment.\" };\n }\n}\n","import { config } from \"./config.js\";\nimport type { WcsSpeak } from \"./components/Speak.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n // A misconfigured triggerAttribute (e.g. one with a space) makes the attribute\n // selector invalid and closest() throw SyntaxError; guard so a bad config\n // disables only this shortcut rather than killing every document click handler.\n let triggerElement: Element | null;\n try {\n triggerElement = target.closest<Element>(`[${config.triggerAttribute}]`);\n } catch {\n return;\n }\n if (!triggerElement) return;\n\n const speakId = triggerElement.getAttribute(config.triggerAttribute);\n if (!speakId) return;\n\n // Resolve the registered constructor at call time instead of importing Speak as\n // a value, avoiding a components/Speak.ts ⇄ autoTrigger.ts cycle\n // (Speak.connectedCallback() calls registerAutoTrigger()). instanceof against\n // the customElements registry keeps the same identity guarantee.\n const SpeakCtor = customElements.get(config.tagNames.speak);\n const speakElement = document.getElementById(speakId);\n if (!SpeakCtor || !(speakElement instanceof SpeakCtor)) return;\n\n // The text to speak comes from the trigger element: an explicit `data-speaktext`\n // attribute wins, otherwise the element's text content. This keeps the\n // click-driven shortcut declarative without inventing a payload channel.\n const explicit = triggerElement.getAttribute(\"data-speaktext\");\n // textContent is always a string for an Element; the cast avoids an\n // unreachable null-coalesce branch. speak() tolerates a non-string anyway.\n // The textContent fallback is trimmed (HTML indentation otherwise leaks leading\n // / trailing whitespace into the utterance); an explicit data-speaktext is kept\n // verbatim so an author can deliberately include surrounding spaces.\n const text = explicit !== null ? explicit : (triggerElement.textContent as string).trim();\n\n event.preventDefault();\n (speakElement as WcsSpeak).speak(text);\n}\n\nexport function registerAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport { IWcBindable, SpeakOptions, SpeechVoiceInfo, WcsSpeakErrorDetail } from \"../types.js\";\nimport { SpeakCore } from \"../core/SpeakCore.js\";\nimport { registerAutoTrigger } from \"../autoTrigger.js\";\n\n/**\n * `<wcs-speak>` — declarative text-to-speech. Wraps SpeakCore and exposes:\n *\n * - **`say`** (reactive input): writing a value speaks it, suppressing same-value\n * writes so it fires only when the bound source actually changes. The\n * imperative `speak` command instead speaks on demand (even the same text\n * again). See `docs/speech-tag-design.md` § 5.\n * - per-utterance parameters (`rate` / `pitch` / `volume` / `voice` / `lang`) as\n * mirrored attributes.\n * - the Core's observable surface (voices / speaking / paused / pending /\n * charIndex / spokenWord / error / unsupported) via delegated getters.\n */\nexport class WcsSpeak extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...SpeakCore.wcBindable,\n // Shell-level settable surface. `say` is a momentary reactive command-property\n // with no mirrored attribute (it carries dynamic text, not declarative config),\n // mirroring how <wcs-geo>'s `trigger` has no attribute. The rest mirror their\n // HTML attributes idempotently.\n inputs: [\n { name: \"say\" },\n { name: \"rate\", attribute: \"rate\" },\n { name: \"pitch\", attribute: \"pitch\" },\n { name: \"volume\", attribute: \"volume\" },\n { name: \"voice\", attribute: \"voice\" },\n { name: \"lang\", attribute: \"lang\" },\n { name: \"manual\", attribute: \"manual\" },\n ],\n commands: SpeakCore.wcBindable.commands,\n };\n\n private _core: SpeakCore;\n private _say: string = \"\";\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new SpeakCore(this);\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Attribute accessors ---\n\n get rate(): number {\n return this._numberAttr(\"rate\", 1);\n }\n\n set rate(value: number) {\n this.setAttribute(\"rate\", String(value));\n }\n\n get pitch(): number {\n return this._numberAttr(\"pitch\", 1);\n }\n\n set pitch(value: number) {\n this.setAttribute(\"pitch\", String(value));\n }\n\n get volume(): number {\n return this._numberAttr(\"volume\", 1);\n }\n\n set volume(value: number) {\n this.setAttribute(\"volume\", String(value));\n }\n\n get voice(): string {\n return this.getAttribute(\"voice\") ?? \"\";\n }\n\n set voice(value: string | null) {\n if (value == null) {\n this.removeAttribute(\"voice\");\n } else {\n this.setAttribute(\"voice\", String(value));\n }\n }\n\n get lang(): string {\n return this.getAttribute(\"lang\") ?? \"\";\n }\n\n set lang(value: string | null) {\n if (value == null) {\n this.removeAttribute(\"lang\");\n } else {\n this.setAttribute(\"lang\", String(value));\n }\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Reactive command-property ---\n\n get say(): string {\n return this._say;\n }\n\n set say(value: string | null) {\n // Reactive: writing a new value speaks it. `manual` mutes the path entirely\n // (the imperative `speak` command still works) — both an opt-out and the hook\n // used to avoid a recognition echo loop while listening. A conforming binder\n // never delivers `undefined` (it skips the write), but a direct assignment\n // can, so normalize null/undefined to a no-op.\n //\n // ECHO-LOOP WARNING: when wiring <wcs-listen> → state → `say`, the synthesized\n // audio will be re-recognized unless speech is muted while listening. There is\n // no code-level interlock here (the two tags are decoupled): the consumer MUST\n // wire it — bind `manual` to the listening flag (or gate the bound source).\n // See README \"Echo loop\" and the speech-echo example.\n if (value == null) return;\n if (this.manual) return;\n const v = String(value);\n // Same-value guard: only speak when the bound source actually changes. For\n // \"speak the same text again on demand\", use the `speak` command instead.\n if (v === this._say) return;\n this._say = v;\n this.speak(v);\n }\n\n // --- Core delegated getters ---\n\n get voices(): SpeechVoiceInfo[] {\n return this._core.voices;\n }\n\n get speaking(): boolean {\n return this._core.speaking;\n }\n\n get paused(): boolean {\n return this._core.paused;\n }\n\n get pending(): boolean {\n return this._core.pending;\n }\n\n get charIndex(): number | null {\n return this._core.charIndex;\n }\n\n get spokenWord(): string | null {\n return this._core.spokenWord;\n }\n\n get error(): WcsSpeakErrorDetail | null {\n return this._core.error;\n }\n\n get unsupported(): boolean {\n return this._core.unsupported;\n }\n\n // --- Commands ---\n\n speak(text: string): void {\n this._core.speak(text, this._options());\n }\n\n cancel(): void {\n this._core.cancel();\n }\n\n pause(): void {\n this._core.pause();\n }\n\n resume(): void {\n this._core.resume();\n }\n\n // --- Internal ---\n\n private _numberAttr(name: string, fallback: number): number {\n const attr = this.getAttribute(name);\n if (attr === null || attr.trim() === \"\") return fallback;\n // Strict parse via Number() (unlike parseInt, \"1px\" -> NaN, not 1). Fall back\n // to the API default for any non-finite value, matching the geolocation\n // \"invalid values fall back to default\" convention.\n const parsed = Number(attr);\n return Number.isFinite(parsed) ? parsed : fallback;\n }\n\n private _options(): SpeakOptions {\n return {\n rate: this.rate,\n pitch: this.pitch,\n volume: this.volume,\n voice: this.voice,\n lang: this.lang,\n };\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerAutoTrigger();\n }\n // observe() revives the voiceschanged subscription after a reconnect\n // (reparenting) and returns the readiness promise for SSR; it wraps\n // reinitVoices() (no-op on the first connect — the constructor subscribed).\n this._connectedCallbackPromise = this._core.observe();\n }\n\n disconnectedCallback(): void {\n // Detach event subscriptions and neutralize in-flight utterance callbacks.\n // Any utterance already speaking finishes naturally (SpeechSynthesis is a\n // global singleton; cancelling here would stop other <wcs-speak> elements\n // too). Call `cancel()` explicitly to stop audio.\n this._core.dispose();\n }\n}\n","import {\n IWcBindable, ListenOptions, ListenPermissionState,\n WcsListenResultDetail, WcsListenAlternative, WcsListenErrorDetail,\n} from \"../types.js\";\n\n// The vendor-prefixed constructor is not in the DOM lib types; declare a minimal\n// shape so we can feature-detect and construct it.\n// Minimal structural shapes for the recognition result/error events. The DOM lib\n// does not ship the prefixed API's types, so we declare just the fields read here\n// instead of using `any`, keeping the handlers type-checked and consistent with\n// the typed state fields. All fields are optional/loose because real engines vary\n// (resultIndex / charLength omitted, malformed events) and the handlers already\n// defend against that at runtime.\ninterface RecognitionAlternativeLike {\n transcript?: string;\n confidence?: number;\n}\ninterface RecognitionResultLike {\n readonly length: number;\n isFinal?: boolean;\n [index: number]: RecognitionAlternativeLike;\n}\ninterface RecognitionResultListLike {\n readonly length: number;\n [index: number]: RecognitionResultLike;\n}\ninterface RecognitionResultEventLike {\n results: RecognitionResultListLike;\n resultIndex?: number;\n}\ninterface RecognitionErrorEventLike {\n error?: string;\n}\n\ninterface SpeechRecognitionLike extends EventTarget {\n lang: string;\n continuous: boolean;\n interimResults: boolean;\n maxAlternatives: number;\n start(): void;\n stop(): void;\n abort(): void;\n onstart: ((event: Event) => void) | null;\n onend: ((event: Event) => void) | null;\n onresult: ((event: RecognitionResultEventLike) => void) | null;\n onerror: ((event: RecognitionErrorEventLike) => void) | null;\n}\n\ntype SpeechRecognitionCtor = new () => SpeechRecognitionLike;\n\n/**\n * Headless speech-to-text primitive. A thin, framework-agnostic wrapper around\n * the SpeechRecognition API (vendor-prefixed `webkitSpeechRecognition` in\n * Chrome) exposed through the wc-bindable protocol.\n *\n * It is the \"event\" half of the speech package (the synthesis half is\n * SpeakCore): recognition results flow element → state.\n *\n * Two phases mirror geolocation:\n * - **one-shot** (`continuous = false`) — recognize until the first `end`.\n * - **continuous** (`continuous = true`) — keep a single session open across\n * phrases. The browser still ends a session on silence; auto-restart bridges\n * that gap **but is opt-in via `maxRestarts`**: with the default `maxRestarts\n * = 0` a continuous session is *not* restarted on `end` (the safe default —\n * unbounded restart is the infinite-loop risk we guard against). Set\n * `maxRestarts > 0` to bridge N silences. The cap also stops a persistent\n * failure (e.g. `not-allowed`) from spinning forever or exhausting quota; a\n * real result resets the budget so only consecutive empty restarts count.\n *\n * A microphone permission gate (like geolocation's) reflects\n * `navigator.permissions.query({ name: \"microphone\" })`. Failures never throw —\n * they surface through the `error` property.\n */\nexport class ListenCore extends EventTarget {\n static wcBindable: IWcBindable = {\n protocol: \"wc-bindable\",\n version: 1,\n properties: [\n { name: \"interimTranscript\", event: \"wcs-listen:interim-changed\" },\n { name: \"finalTranscript\", event: \"wcs-listen:final-changed\" },\n { name: \"result\", event: \"wcs-listen:result\" },\n { name: \"listening\", event: \"wcs-listen:listening-changed\" },\n { name: \"permission\", event: \"wcs-listen:permission-changed\" },\n { name: \"error\", event: \"wcs-listen:error\" },\n { name: \"unsupported\", event: \"wcs-listen:unsupported-changed\" },\n ],\n commands: [\n { name: \"start\" },\n { name: \"stop\" },\n { name: \"abort\" },\n ],\n };\n\n private _target: EventTarget;\n private _recognition: SpeechRecognitionLike | null = null;\n\n private _interimTranscript: string = \"\";\n private _finalTranscript: string = \"\";\n private _result: WcsListenResultDetail | null = null;\n private _listening: boolean = false;\n private _permission: ListenPermissionState = \"prompt\";\n private _error: WcsListenErrorDetail | null = null;\n private _unsupported: boolean = false;\n\n // Intent flag: true between start() and stop()/abort()/terminal-error. Gates\n // the auto-restart loop so a session that ended because the user stopped it\n // does not restart.\n private _active: boolean = false;\n private _continuous: boolean = false;\n private _maxRestarts: number = 0;\n private _restartCount: number = 0;\n\n // Permission tracking — same machinery as GeolocationCore.\n private _permissionStatus: PermissionStatus | null = null;\n private _permissionSubscribed: boolean = false;\n private _permGen: number = 0;\n\n // SSR: feature detection (`_setUnsupported`) is synchronous and the permission\n // `change` subscription is established eagerly in the constructor, so there is\n // no asynchronous probe to await before snapshotting — readiness is immediate.\n // The Shell exposes this as connectedCallbackPromise.\n private _ready: Promise<void> = Promise.resolve();\n\n constructor(target?: EventTarget) {\n super();\n this._target = target ?? this;\n const Ctor = this._getCtor();\n this._setUnsupported(!Ctor);\n if (Ctor) {\n this._recognition = new Ctor();\n this._attachHandlers(this._recognition);\n }\n this._initPermission();\n }\n\n get interimTranscript(): string {\n return this._interimTranscript;\n }\n\n get finalTranscript(): string {\n return this._finalTranscript;\n }\n\n get result(): WcsListenResultDetail | null {\n return this._result;\n }\n\n get listening(): boolean {\n return this._listening;\n }\n\n get permission(): ListenPermissionState {\n return this._permission;\n }\n\n get error(): WcsListenErrorDetail | null {\n return this._error;\n }\n\n // Resolved once in the constructor (`_setUnsupported(!Ctor)`) and never\n // re-evaluated: the SpeechRecognition API's presence is immutable for the\n // lifetime of a document, so there's nothing to re-check.\n get unsupported(): boolean {\n return this._unsupported;\n }\n\n /** Resolves once the first probe settles (immediate — see `_ready`). */\n get ready(): Promise<void> {\n return this._ready;\n }\n\n // --- State setters with event dispatch ---\n\n private _setInterim(value: string): void {\n if (this._interimTranscript === value) return;\n this._interimTranscript = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:interim-changed\", { detail: value, bubbles: true }));\n }\n\n private _setFinal(value: string): void {\n if (this._finalTranscript === value) return;\n this._finalTranscript = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:final-changed\", { detail: value, bubbles: true }));\n }\n\n private _setResult(value: WcsListenResultDetail): void {\n this._result = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:result\", { detail: value, bubbles: true }));\n }\n\n private _setListening(value: boolean): void {\n if (this._listening === value) return;\n this._listening = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:listening-changed\", { detail: value, bubbles: true }));\n }\n\n private _setPermission(value: ListenPermissionState): void {\n if (this._permission === value) return;\n this._permission = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:permission-changed\", { detail: value, bubbles: true }));\n }\n\n private _setError(value: WcsListenErrorDetail | null): void {\n if (this._error === value) return;\n this._error = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:error\", { detail: value, bubbles: true }));\n }\n\n private _setUnsupported(value: boolean): void {\n if (this._unsupported === value) return;\n this._unsupported = value;\n this._target.dispatchEvent(new CustomEvent(\"wcs-listen:unsupported-changed\", { detail: value, bubbles: true }));\n }\n\n // --- Public API ---\n\n /**\n * Begin a recognition session. Resets the transcripts (a fresh, user-initiated\n * listen), applies options, and starts. Idempotent while already listening: a\n * redundant start() is ignored so the browser does not throw \"recognition has\n * already started\".\n */\n start(options: ListenOptions = {}): void {\n if (!this._recognition) {\n this._setError(this._unsupportedError());\n return;\n }\n if (this._active) return;\n\n this._continuous = options.continuous ?? false;\n // `maxRestarts` is a restart *count*, so floor any fractional input to an\n // integer (e.g. 2.5 → 2). `_restartCount` increments by 1, so a fractional\n // cap would otherwise compare inconsistently. Non-finite/negative → 0.\n this._maxRestarts = typeof options.maxRestarts === \"number\" && options.maxRestarts >= 0\n ? Math.floor(options.maxRestarts)\n : 0;\n this._restartCount = 0;\n this._recognition.lang = options.lang ?? \"\";\n this._recognition.continuous = this._continuous;\n this._recognition.interimResults = options.interimResults ?? false;\n if (typeof options.maxAlternatives === \"number\") {\n this._recognition.maxAlternatives = options.maxAlternatives;\n }\n\n // Fresh session: clear prior transcripts and error.\n this._setInterim(\"\");\n this._setFinal(\"\");\n this._setError(null);\n this._active = true;\n this._safeStart();\n }\n\n stop(): void {\n if (!this._recognition) return;\n // Clear intent first so the end handler does not auto-restart.\n this._active = false;\n this._recognition.stop();\n }\n\n abort(): void {\n if (!this._recognition) return;\n this._active = false;\n this._recognition.abort();\n }\n\n /**\n * Re-establish the permission `change` subscription after a dispose().\n */\n reinitPermission(): void {\n if (!this._permissionSubscribed) {\n this._initPermission();\n }\n }\n\n /**\n * Establish monitoring (§3.5). Recognition is command-driven (start/stop), so\n * observe() only (re-)establishes the live permission subscription — idempotent\n * via reinitPermission()'s `_permissionSubscribed` guard, so the first connect\n * after construction does not double-subscribe while a reconnect after dispose()\n * does. Returns the `ready` promise for SSR. Call from the Shell's\n * connectedCallback.\n */\n observe(): Promise<void> {\n this.reinitPermission();\n return this._ready;\n }\n\n /**\n * Stop recognition and detach the live permission listener. Call from the\n * Shell's `disconnectedCallback`.\n */\n dispose(): void {\n // Only the live subscriptions and the listening shadow are reset here. The\n // observable snapshot (transcripts / result / error) is intentionally *kept*\n // so a reparented element preserves its last state, mirroring how\n // GeolocationCore.dispose() leaves `position` / `error` intact. The next\n // start() clears the transcripts and error for its fresh session anyway.\n this._active = false;\n this._permissionSubscribed = false;\n this._permGen++;\n if (this._recognition) {\n // abort() is the immediate teardown; guard against environments where it\n // throws on an idle recognizer.\n try {\n this._recognition.abort();\n } catch {\n // ignore — teardown is best-effort.\n }\n }\n // Reset the listening shadow silently (no dispatch on a disposed element),\n // mirroring GeolocationCore's `_loading` reset. The abort() above neutralizes\n // the recognizer but its `end` (which would clear listening via the setter)\n // may not have fired yet; forcing false here means a reconnect+start's\n // `onstart` still transitions false→true through the same-value guard, so the\n // state never desyncs to a stale `true`.\n this._listening = false;\n if (this._permissionStatus) {\n this._permissionStatus.removeEventListener(\"change\", this._onPermissionChange);\n this._permissionStatus = null;\n }\n }\n\n // --- Internal: recognition lifecycle ---\n\n private _attachHandlers(recognition: SpeechRecognitionLike): void {\n recognition.onstart = (): void => {\n this._setListening(true);\n };\n recognition.onresult = (event: RecognitionResultEventLike): void => {\n try {\n this._handleResult(event);\n } catch {\n // A malformed result event must not escape the browser callback.\n }\n };\n recognition.onerror = (event: RecognitionErrorEventLike): void => {\n this._setError(this._normalizeError(event));\n // Terminal errors must not be retried — they would spin the restart loop.\n // The set is deliberately limited to the permission-class errors that can\n // never self-recover within a session. Transient failures\n // (`network` / `audio-capture` / `no-speech`) are intentionally *not*\n // terminal: they are recoverable, so a continuous session restarts through\n // them, bounded by `maxRestarts` (the cap is the guard against a persistent\n // transient failure spinning forever).\n if (event && (event.error === \"not-allowed\" || event.error === \"service-not-allowed\")) {\n this._active = false;\n }\n };\n recognition.onend = (): void => {\n if (this._active && this._continuous && this._restartCount < this._maxRestarts) {\n // Auto-restart bridges a silence-induced `end`. Keep `listening` true\n // across the gap rather than flickering true→false→true: from the\n // consumer's perspective the continuous session never stopped. The\n // immediately-following start()'s `onstart` re-sets true (same-value\n // guarded → no-op), so the flag stays steady. A genuine stop (no\n // restart) still drops to false below.\n this._restartCount++;\n this._safeStart();\n // _safeStart() clears _active if the restart threw; in that case the\n // session is over, so reflect listening=false rather than leaving it\n // stuck true.\n if (!this._active) this._setListening(false);\n return;\n }\n // No restart: the session is fully over.\n this._setListening(false);\n this._active = false;\n };\n }\n\n private _handleResult(event: RecognitionResultEventLike): void {\n const results = event.results;\n let interim = \"\";\n let finalChunk = \"\";\n // Per the Web Speech spec, `resultIndex` is the lowest index in `results`\n // that changed in this event, so we only fold in `[resultIndex, length)` and\n // accumulate finals (`this._finalTranscript + finalChunk`). This assumes the\n // engine advances `resultIndex` past already-finalized results. A nonconforming\n // engine that omits `resultIndex` (`?? 0`) or re-reports finalized results at\n // index 0 on every event could double-accumulate the same final chunk; standard\n // browser engines don't, so this is not hardened against here.\n for (let i = event.resultIndex ?? 0; i < results.length; i++) {\n const res = results[i];\n const transcript = res?.[0]?.transcript ?? \"\";\n if (res?.isFinal) {\n finalChunk += transcript;\n } else {\n interim += transcript;\n }\n }\n if (finalChunk !== \"\") {\n this._setFinal(this._finalTranscript + finalChunk);\n }\n this._setInterim(interim);\n // Any result is progress — reset the restart budget so only *consecutive*\n // empty restarts count toward the cap.\n this._restartCount = 0;\n\n const last = results[results.length - 1];\n if (last) {\n this._setResult(this._normalizeResult(last));\n }\n }\n\n private _normalizeResult(result: RecognitionResultLike): WcsListenResultDetail {\n const alternatives: WcsListenAlternative[] = [];\n for (let i = 0; i < result.length; i++) {\n alternatives.push({\n transcript: result[i]?.transcript ?? \"\",\n confidence: result[i]?.confidence ?? 0,\n });\n }\n const top = alternatives[0] ?? { transcript: \"\", confidence: 0 };\n return {\n transcript: top.transcript,\n confidence: top.confidence,\n isFinal: !!result.isFinal,\n alternatives,\n };\n }\n\n private _safeStart(): void {\n try {\n this._recognition!.start();\n } catch {\n // start() throws if already started; surface nothing — the live session\n // continues. Reset intent so state stays consistent.\n this._active = false;\n }\n }\n\n // --- Internal: feature detection & permission (mirrors GeolocationCore) ---\n\n private _getCtor(): SpeechRecognitionCtor | null {\n // Guard window access without a separate (in-browser unreachable) early\n // return, mirroring SpeakCore's `_hasApi` style.\n const w = (typeof window === \"undefined\" ? undefined : window) as unknown as {\n SpeechRecognition?: SpeechRecognitionCtor;\n webkitSpeechRecognition?: SpeechRecognitionCtor;\n } | undefined;\n return w?.SpeechRecognition ?? w?.webkitSpeechRecognition ?? null;\n }\n\n private _initPermission(): void {\n if (typeof navigator === \"undefined\" || !navigator.permissions || typeof navigator.permissions.query !== \"function\") {\n this._setPermission(\"unsupported\");\n return;\n }\n this._permissionSubscribed = true;\n const gen = ++this._permGen;\n navigator.permissions.query({ name: \"microphone\" as PermissionName }).then(\n (status) => {\n if (gen !== this._permGen) return;\n this._permissionStatus = status;\n this._setPermission(status.state as ListenPermissionState);\n status.addEventListener(\"change\", this._onPermissionChange);\n },\n () => {\n if (gen !== this._permGen) return;\n this._setPermission(\"unsupported\");\n },\n );\n }\n\n private _onPermissionChange = (event: Event): void => {\n const status = event.target as PermissionStatus;\n this._setPermission(status.state as ListenPermissionState);\n };\n\n private _normalizeError(event: RecognitionErrorEventLike): WcsListenErrorDetail {\n const error = (event && event.error) ? event.error : \"aborted\";\n return { error, message: `Speech recognition failed: ${error}.` };\n }\n\n private _unsupportedError(): WcsListenErrorDetail {\n return { error: \"unsupported\", message: \"SpeechRecognition API is not available in this environment.\" };\n }\n}\n","import { config } from \"./config.js\";\nimport type { WcsListen } from \"./components/Listen.js\";\n\nlet registered = false;\n\nfunction handleClick(event: Event): void {\n const target = event.target;\n if (!(target instanceof Element)) return;\n\n // A misconfigured listenTriggerAttribute (e.g. one with a space) makes the\n // attribute selector invalid and closest() throw SyntaxError; guard so a bad\n // config disables only this shortcut rather than killing every click handler.\n let triggerElement: Element | null;\n try {\n triggerElement = target.closest<Element>(`[${config.listenTriggerAttribute}]`);\n } catch {\n return;\n }\n if (!triggerElement) return;\n\n const listenId = triggerElement.getAttribute(config.listenTriggerAttribute);\n if (!listenId) return;\n\n const ListenCtor = customElements.get(config.tagNames.listen);\n const listenElement = document.getElementById(listenId);\n if (!ListenCtor || !(listenElement instanceof ListenCtor)) return;\n\n event.preventDefault();\n // Toggle: clicking starts a session, clicking again while listening stops it.\n const el = listenElement as WcsListen;\n if (el.listening) {\n el.stop();\n } else {\n el.start();\n }\n}\n\nexport function registerListenAutoTrigger(): void {\n if (registered) return;\n registered = true;\n document.addEventListener(\"click\", handleClick);\n}\n\nexport function unregisterListenAutoTrigger(): void {\n if (!registered) return;\n registered = false;\n document.removeEventListener(\"click\", handleClick);\n}\n","import { config } from \"../config.js\";\nimport {\n IWcBindable, ListenOptions, ListenPermissionState, WcsListenResultDetail, WcsListenErrorDetail,\n} from \"../types.js\";\nimport { ListenCore } from \"../core/ListenCore.js\";\nimport { registerListenAutoTrigger } from \"../listenAutoTrigger.js\";\n\n/**\n * `<wcs-listen>` — declarative speech-to-text. Wraps ListenCore and exposes the\n * recognition surface (interim/final transcripts, structured result, listening\n * flag, microphone permission, error) plus the two-phase start/stop/abort\n * commands and a momentary `trigger` for DOM-driven starts.\n *\n * Mirrors `<wcs-geo>`: `manual` suppresses the connect-time auto-start, and the\n * `continuous` attribute selects the auto-restarting session phase.\n */\nexport class WcsListen extends HTMLElement {\n static hasConnectedCallbackPromise = true;\n static wcBindable: IWcBindable = {\n ...ListenCore.wcBindable,\n properties: [\n ...ListenCore.wcBindable.properties,\n { name: \"trigger\", event: \"wcs-listen:trigger-changed\" },\n ],\n inputs: [\n { name: \"lang\", attribute: \"lang\" },\n { name: \"continuous\", attribute: \"continuous\" },\n { name: \"interim\", attribute: \"interim\" },\n { name: \"maxRestarts\", attribute: \"max-restarts\" },\n { name: \"manual\", attribute: \"manual\" },\n { name: \"trigger\" },\n ],\n commands: ListenCore.wcBindable.commands,\n };\n\n private _core: ListenCore;\n private _trigger: boolean = false;\n private _connectedCallbackPromise: Promise<void> = Promise.resolve();\n\n constructor() {\n super();\n this._core = new ListenCore(this);\n }\n\n get connectedCallbackPromise(): Promise<void> {\n return this._connectedCallbackPromise;\n }\n\n // --- Attribute accessors ---\n\n get lang(): string {\n return this.getAttribute(\"lang\") ?? \"\";\n }\n\n set lang(value: string | null) {\n if (value == null) {\n this.removeAttribute(\"lang\");\n } else {\n this.setAttribute(\"lang\", String(value));\n }\n }\n\n get continuous(): boolean {\n return this.hasAttribute(\"continuous\");\n }\n\n set continuous(value: boolean) {\n if (value) {\n this.setAttribute(\"continuous\", \"\");\n } else {\n this.removeAttribute(\"continuous\");\n }\n }\n\n get interim(): boolean {\n return this.hasAttribute(\"interim\");\n }\n\n set interim(value: boolean) {\n if (value) {\n this.setAttribute(\"interim\", \"\");\n } else {\n this.removeAttribute(\"interim\");\n }\n }\n\n get maxRestarts(): number {\n const attr = this.getAttribute(\"max-restarts\");\n if (attr === null || attr.trim() === \"\") return 0;\n const parsed = Number(attr);\n // A restart *count* is an integer, so floor fractional input (e.g. 1.9 → 1)\n // here too, keeping the getter's value identical to the effective cap the\n // Core applies (ListenCore.start floors it as well). Non-finite/negative → 0.\n return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : 0;\n }\n\n set maxRestarts(value: number) {\n this.setAttribute(\"max-restarts\", String(value));\n }\n\n get manual(): boolean {\n return this.hasAttribute(\"manual\");\n }\n\n set manual(value: boolean) {\n if (value) {\n this.setAttribute(\"manual\", \"\");\n } else {\n this.removeAttribute(\"manual\");\n }\n }\n\n // --- Core delegated getters ---\n\n get interimTranscript(): string {\n return this._core.interimTranscript;\n }\n\n get finalTranscript(): string {\n return this._core.finalTranscript;\n }\n\n get result(): WcsListenResultDetail | null {\n return this._core.result;\n }\n\n get listening(): boolean {\n return this._core.listening;\n }\n\n get permission(): ListenPermissionState {\n return this._core.permission;\n }\n\n get error(): WcsListenErrorDetail | null {\n return this._core.error;\n }\n\n get unsupported(): boolean {\n return this._core.unsupported;\n }\n\n // --- Command property ---\n\n get trigger(): boolean {\n return this._trigger;\n }\n\n set trigger(value: boolean) {\n // Momentary command-property: a false→true write starts a session. Mirrors\n // <wcs-geo>'s trigger. Prefer the command-token protocol (`command.start:\n // $command.listen`) for state-driven starts; this exists for DOM triggers and\n // simple boolean bindings.\n const v = !!value;\n if (v) {\n this._trigger = true;\n this.start();\n this._trigger = false;\n this.dispatchEvent(new CustomEvent(\"wcs-listen:trigger-changed\", { detail: false, bubbles: true }));\n }\n }\n\n // --- Commands ---\n\n start(): void {\n this._core.start(this._options());\n }\n\n stop(): void {\n this._core.stop();\n }\n\n abort(): void {\n this._core.abort();\n }\n\n // --- Internal ---\n\n private _options(): ListenOptions {\n return {\n lang: this.lang,\n continuous: this.continuous,\n interimResults: this.interim,\n maxRestarts: this.maxRestarts,\n };\n }\n\n // --- Lifecycle ---\n\n connectedCallback(): void {\n this.style.display = \"none\";\n if (config.autoTrigger) {\n registerListenAutoTrigger();\n }\n // observe() (re-)establishes the permission subscription and returns the\n // readiness promise for SSR; it wraps reinitPermission() (idempotent).\n this._connectedCallbackPromise = this._core.observe();\n if (!this.manual) {\n // Non-blocking auto-start, mirroring <wcs-geo>: start() is fired\n // unconditionally without first awaiting/inspecting the (async) permission\n // state. A `denied` mic surfaces as a `not-allowed` error via the `error`\n // property (and stops auto-restart), rather than the connect path silently\n // suppressing the start. This keeps the permission model declarative and\n // consistent with geolocation. Use `manual` to require an explicit start.\n this.start();\n }\n }\n\n disconnectedCallback(): void {\n this._core.dispose();\n }\n}\n","import { setConfig } from \"./config.js\";\nimport { registerComponents } from \"./registerComponents.js\";\nimport { IWritableConfig } from \"./types.js\";\n\nexport function bootstrapSpeech(userConfig?: IWritableConfig): void {\n if (userConfig) {\n setConfig(userConfig);\n }\n registerComponents();\n}\n","import { WcsSpeak } from \"./components/Speak.js\";\nimport { WcsListen } from \"./components/Listen.js\";\nimport { config } from \"./config.js\";\n\nexport function registerComponents(): void {\n if (!customElements.get(config.tagNames.speak)) {\n customElements.define(config.tagNames.speak, WcsSpeak);\n }\n if (!customElements.get(config.tagNames.listen)) {\n customElements.define(config.tagNames.listen, WcsListen);\n }\n}\n"],"names":["_config","autoTrigger","triggerAttribute","listenTriggerAttribute","tagNames","speak","listen","deepFreeze","obj","Object","freeze","key","keys","deepClone","clone","frozenConfig","config","getConfig","SpeakCore","EventTarget","static","protocol","version","properties","name","event","getter","e","detail","charIndex","word","commands","_target","_voices","_rawVoices","_speaking","_paused","_pending","_charIndex","_spokenWord","_error","_unsupported","_queued","_started","_gen","_voicesSubscribed","_ready","Promise","resolve","constructor","target","super","this","_setUnsupported","_hasApi","_initVoices","voices","speaking","paused","pending","spokenWord","error","unsupported","ready","_setVoices","_voicesEqual","dispatchEvent","CustomEvent","bubbles","a","b","length","i","x","y","lang","default","localService","voiceURI","_setSpeaking","_setPaused","_setPending","_setBoundary","_setError","text","options","_unsupportedError","trim","synth","window","speechSynthesis","utterance","SpeechSynthesisUtterance","rate","pitch","volume","voice","match","find","v","gen","started","onstart","Math","max","onboundary","charLength","substring","slice","onpause","onresume","onend","_finishUtterance","onerror","_normalizeError","cancel","resume","pause","reinitVoices","observe","dispose","removeEventListener","_onVoicesChanged","_loadVoices","addEventListener","raw","getVoices","map","_normalizeVoice","message","registered","handleClick","Element","triggerElement","closest","speakId","getAttribute","SpeakCtor","customElements","get","speakElement","document","getElementById","explicit","textContent","preventDefault","WcsSpeak","HTMLElement","wcBindable","inputs","attribute","_core","_say","_connectedCallbackPromise","connectedCallbackPromise","_numberAttr","value","setAttribute","String","removeAttribute","manual","hasAttribute","say","_options","fallback","attr","parsed","Number","isFinite","connectedCallback","style","display","disconnectedCallback","ListenCore","_recognition","_interimTranscript","_finalTranscript","_result","_listening","_permission","_active","_continuous","_maxRestarts","_restartCount","_permissionStatus","_permissionSubscribed","_permGen","Ctor","_getCtor","_attachHandlers","_initPermission","interimTranscript","finalTranscript","result","listening","permission","_setInterim","_setFinal","_setResult","_setListening","_setPermission","start","continuous","maxRestarts","floor","interimResults","maxAlternatives","_safeStart","stop","abort","reinitPermission","_onPermissionChange","recognition","onresult","_handleResult","results","interim","finalChunk","resultIndex","res","transcript","isFinal","last","_normalizeResult","alternatives","push","confidence","top","w","undefined","SpeechRecognition","webkitSpeechRecognition","navigator","permissions","query","then","status","state","listenId","ListenCtor","listenElement","el","WcsListen","_trigger","trigger","bootstrapSpeech","userConfig","partialConfig","assign","define"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wcstack/speech",
3
- "version": "1.14.0",
3
+ "version": "1.16.0",
4
4
  "description": "Declarative Web Speech components for Web Components. Framework-agnostic SpeechSynthesis (TTS) and SpeechRecognition (STT) primitives via wc-bindable-protocol.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.esm.js",