@smartspace/chat-ui 1.14.4-pr.453.43dda12 → 1.14.4-pr.453.d6dd83a

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
@@ -127,8 +127,8 @@ type UseDictationOptions = {
127
127
  onInterim?: (text: string) => void;
128
128
  /**
129
129
  * Hard stop, so a forgotten mic can't run for an hour. Keep it under the
130
- * token's ~10 minute life: the token is fetched once per session and never
131
- * refreshed, so a longer cap would end the session on an auth failure.
130
+ * token's ~10 minute life: the token is never refreshed once the session is
131
+ * running, so a longer cap would end the session on an auth failure.
132
132
  */
133
133
  maxDurationMs?: number;
134
134
  /** Stop after this long without any speech. */
@@ -136,7 +136,7 @@ type UseDictationOptions = {
136
136
  };
137
137
  /**
138
138
  * Streams microphone audio to Azure AI Speech and hands back recognised
139
- * phrases. Owns the whole session: mic permission + stream, lazy SDK load,
139
+ * phrases. Owns the whole session: mic permission + stream, SDK loading,
140
140
  * recogniser lifecycle, idle/max timers, and teardown on stop or unmount.
141
141
  * Interim (not yet final) text goes to `onInterim` for display only; only final
142
142
  * phrases reach `onPhrase`.
@@ -481,10 +481,12 @@ interface ChatService {
481
481
  * Speech-to-text (composer dictation). Both are optional so existing
482
482
  * service implementations keep compiling — the composer renders a
483
483
  * microphone only when both are provided AND the config says enabled.
484
- * `getSpeechConfig` is read once per session. `getSpeechToken` is called once
485
- * per dictation session, before the SDK connects — the token it returns is
486
- * handed over as-is and never refreshed, so a session is capped to stay
487
- * inside its life. No caching wanted: a stale token cannot be renewed.
484
+ * `getSpeechConfig` is read once per session. `getSpeechToken` is called
485
+ * when a dictation session starts (in parallel with the mic permission
486
+ * prompt — and again if that token aged out while the prompt sat open). The
487
+ * token is handed to the SDK as-is and never refreshed mid-session, so a
488
+ * session is capped to stay inside its life. No caching wanted: a stale
489
+ * token cannot be renewed.
488
490
  */
489
491
  getSpeechConfig?(): Promise<SpeechConfig>;
490
492
  getSpeechToken?(): Promise<SpeechToken>;
package/dist/index.js CHANGED
@@ -2557,6 +2557,17 @@ var MarkdownEditor = forwardRef((props, ref) => {
2557
2557
  });
2558
2558
  MarkdownEditor.displayName = "MarkdownEditor";
2559
2559
  var TOKEN_EXPIRY_MARGIN_MS = 15e3;
2560
+ var STALE_TOKEN_FLOOR_MS = 6e4;
2561
+ function kickOff(fn) {
2562
+ let promise;
2563
+ try {
2564
+ promise = Promise.resolve(fn());
2565
+ } catch (e) {
2566
+ promise = Promise.reject(e);
2567
+ }
2568
+ promise.catch(() => void 0);
2569
+ return promise;
2570
+ }
2560
2571
  var isSupported = () => typeof window !== "undefined" && window.isSecureContext && !!navigator.mediaDevices?.getUserMedia;
2561
2572
  function classifyTokenFailure(error) {
2562
2573
  const code3 = error?.code;
@@ -2588,6 +2599,18 @@ function useDictation({
2588
2599
  const clearInterim = useCallback(() => onInterimRef.current?.(""), []);
2589
2600
  const supported = isSupported();
2590
2601
  const available = supported && !!region && !!getToken;
2602
+ useEffect(() => {
2603
+ if (!available) return;
2604
+ const nav = navigator;
2605
+ if (nav.connection?.saveData) return;
2606
+ const warm = () => void kickOff(() => import('microsoft-cognitiveservices-speech-sdk'));
2607
+ if (typeof window.requestIdleCallback === "function") {
2608
+ const id2 = window.requestIdleCallback(warm, { timeout: 2e3 });
2609
+ return () => window.cancelIdleCallback(id2);
2610
+ }
2611
+ const id = window.setTimeout(warm, 1500);
2612
+ return () => window.clearTimeout(id);
2613
+ }, [available]);
2591
2614
  const stop = useCallback(() => {
2592
2615
  generationRef.current += 1;
2593
2616
  const session = sessionRef.current;
@@ -2609,15 +2632,22 @@ function useDictation({
2609
2632
  const superseded = () => generationRef.current !== generation;
2610
2633
  setError(null);
2611
2634
  setState("starting");
2612
- let stream;
2613
- try {
2614
- stream = await navigator.mediaDevices.getUserMedia({
2635
+ const streamPromise = kickOff(
2636
+ () => navigator.mediaDevices.getUserMedia({
2615
2637
  audio: {
2616
2638
  echoCancellation: true,
2617
2639
  noiseSuppression: true,
2618
2640
  autoGainControl: true
2619
2641
  }
2620
- });
2642
+ })
2643
+ );
2644
+ const tokenPromise = kickOff(getToken);
2645
+ const sdkPromise = kickOff(
2646
+ () => import('microsoft-cognitiveservices-speech-sdk')
2647
+ );
2648
+ let stream;
2649
+ try {
2650
+ stream = await streamPromise;
2621
2651
  } catch (e) {
2622
2652
  if (superseded()) return;
2623
2653
  const name = e?.name;
@@ -2633,7 +2663,10 @@ function useDictation({
2633
2663
  }
2634
2664
  let token;
2635
2665
  try {
2636
- token = await getToken();
2666
+ token = await tokenPromise;
2667
+ if (Date.parse(token.expiresOn) - Date.now() < STALE_TOKEN_FLOOR_MS) {
2668
+ token = await getToken();
2669
+ }
2637
2670
  } catch (e) {
2638
2671
  stream.getTracks().forEach((t) => t.stop());
2639
2672
  if (superseded()) return;
@@ -2648,7 +2681,7 @@ function useDictation({
2648
2681
  let recognizer = null;
2649
2682
  let published = null;
2650
2683
  try {
2651
- const sdk = await import('microsoft-cognitiveservices-speech-sdk');
2684
+ const sdk = await sdkPromise;
2652
2685
  if (superseded()) {
2653
2686
  stream.getTracks().forEach((t) => t.stop());
2654
2687
  return;