@sensiblestats/widget-react 0.19.0 → 0.20.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.js CHANGED
@@ -588,21 +588,42 @@ function reducer(state, action) {
588
588
  }
589
589
 
590
590
  // src/useChatStream.ts
591
+ var STREAM_FALLBACK_TIMEOUT_MS = 3e4;
591
592
  function useChatStream(conversation, starterTexts, greetingMessage) {
592
593
  const seed = useMemo(() => initialState(starterTexts.map(chipFromText)), []);
593
594
  const [state, dispatch] = useReducer(reducer, seed);
594
595
  const idRef = useRef(0);
595
596
  const handleRef = useRef(null);
597
+ const fallbackTimerRef = useRef(null);
596
598
  const stateRef = useRef(state);
597
599
  stateRef.current = state;
598
600
  const greetedRef = useRef(false);
599
601
  const nextId = () => `m${idRef.current++}`;
602
+ const clearFallbackTimer = useCallback(() => {
603
+ if (fallbackTimerRef.current != null) {
604
+ clearTimeout(fallbackTimerRef.current);
605
+ fallbackTimerRef.current = null;
606
+ }
607
+ }, []);
608
+ const armFallbackTimer = useCallback((onFire) => {
609
+ clearFallbackTimer();
610
+ fallbackTimerRef.current = setTimeout(() => {
611
+ fallbackTimerRef.current = null;
612
+ handleRef.current?.cancel();
613
+ handleRef.current = null;
614
+ onFire();
615
+ }, STREAM_FALLBACK_TIMEOUT_MS);
616
+ }, [clearFallbackTimer]);
600
617
  const consume = useCallback((handle, onError) => {
601
618
  ;
602
619
  (async () => {
603
620
  try {
604
- for await (const event of handle) dispatch({ kind: "EVENT", event });
621
+ for await (const event of handle) {
622
+ dispatch({ kind: "EVENT", event });
623
+ if (event.type === "turn_complete" || event.type === "error") clearFallbackTimer();
624
+ }
605
625
  } catch (err) {
626
+ if (handleRef.current === handle) clearFallbackTimer();
606
627
  if (err instanceof WidgetSdkError2) onError(err);
607
628
  else if (err?.name === "AbortError") {
608
629
  } else throw err;
@@ -610,18 +631,23 @@ function useChatStream(conversation, starterTexts, greetingMessage) {
610
631
  if (handleRef.current === handle) handleRef.current = null;
611
632
  }
612
633
  })();
613
- }, []);
634
+ }, [clearFallbackTimer]);
614
635
  const run = useCallback((input, displayText) => {
615
636
  handleRef.current?.cancel();
637
+ clearFallbackTimer();
616
638
  dispatch({ kind: "USER_SENT", input, userId: nextId(), assistantId: nextId(), displayText });
617
639
  const handle = conversation.send(input);
618
640
  handleRef.current = handle;
641
+ armFallbackTimer(() => {
642
+ dispatch({ kind: "STOP" });
643
+ dispatch({ kind: "STREAM_ERROR", error: new WidgetSdkError2("No response received in time", "timeout") });
644
+ });
619
645
  consume(handle, (err) => dispatch({ kind: "STREAM_ERROR", error: err }));
620
- }, [conversation, consume]);
646
+ }, [conversation, consume, clearFallbackTimer, armFallbackTimer]);
621
647
  const send = useCallback((text, opts) => {
622
648
  const trimmed = text.trim();
623
649
  if (!trimmed) return;
624
- run({ message: trimmed, actionId: opts?.actionId, grounding: opts?.grounding }, opts?.displayText);
650
+ run({ message: trimmed, actionId: opts?.actionId, actionRef: opts?.actionRef }, opts?.displayText);
625
651
  }, [run]);
626
652
  const retry = useCallback(() => {
627
653
  const last = stateRef.current.lastUserInput;
@@ -633,25 +659,29 @@ function useChatStream(conversation, starterTexts, greetingMessage) {
633
659
  greetedRef.current = true;
634
660
  try {
635
661
  handleRef.current?.cancel();
662
+ clearFallbackTimer();
636
663
  dispatch({ kind: "GREETING_SENT", assistantId: nextId() });
637
664
  const handle = conversation.send({ message });
638
665
  handleRef.current = handle;
666
+ armFallbackTimer(() => dispatch({ kind: "STOP" }));
639
667
  consume(handle, () => dispatch({ kind: "STOP" }));
640
668
  } catch {
641
669
  dispatch({ kind: "STOP" });
642
670
  }
643
- }, [greetingMessage, conversation, consume]);
671
+ }, [greetingMessage, conversation, consume, clearFallbackTimer, armFallbackTimer]);
644
672
  const open = useCallback(() => dispatch({ kind: "OPEN" }), []);
645
673
  const close = useCallback(() => {
646
674
  handleRef.current?.cancel();
647
675
  handleRef.current = null;
676
+ clearFallbackTimer();
648
677
  dispatch({ kind: "CLOSE" });
649
- }, []);
678
+ }, [clearFallbackTimer]);
650
679
  const stop = useCallback(() => {
651
680
  handleRef.current?.cancel();
652
681
  handleRef.current = null;
682
+ clearFallbackTimer();
653
683
  dispatch({ kind: "STOP" });
654
- }, []);
684
+ }, [clearFallbackTimer]);
655
685
  const toggle = useCallback(() => {
656
686
  if (stateRef.current.isOpen) close();
657
687
  else open();
@@ -1316,7 +1346,7 @@ function FixtureHeroBlock({
1316
1346
  block.venue ? /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-venue", children: block.venue }) : null,
1317
1347
  block.competition ? /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-comp", children: block.competition }) : null
1318
1348
  ] }),
1319
- /* @__PURE__ */ jsx18("button", { type: "button", className: "ss-w-cdash-cta", onClick: () => onAction(block.ctaActionValue, void 0, block.ctaLabel), children: block.ctaLabel })
1349
+ /* @__PURE__ */ jsx18("button", { type: "button", className: "ss-w-cdash-cta", onClick: () => onAction(block.ctaLabel, { actionRef: block.ctaActionRef }), children: block.ctaLabel })
1320
1350
  ] });
1321
1351
  }
1322
1352
 
@@ -1345,12 +1375,12 @@ function MatchPreviewBlock({
1345
1375
  return /* @__PURE__ */ jsxs16("div", { className: "ss-w-cdash-lines", children: [
1346
1376
  /* @__PURE__ */ jsx20("span", { className: "ss-w-cdash-lines-heading", children: heading }),
1347
1377
  block.facts.map(
1348
- (f, i) => f.entityRef ? /* @__PURE__ */ jsxs16(
1378
+ (f, i) => f.actionRef ? /* @__PURE__ */ jsxs16(
1349
1379
  "button",
1350
1380
  {
1351
1381
  type: "button",
1352
1382
  className: "ss-w-cdash-fact",
1353
- onClick: () => onAction(f.question ?? f.sentence, { ...f.entityRef, subject: f.sentence, personaHint: "club" }),
1383
+ onClick: () => onAction(f.label ?? f.sentence, { actionRef: f.actionRef }),
1354
1384
  children: [
1355
1385
  f.sentence,
1356
1386
  f.label ? /* @__PURE__ */ jsxs16("span", { className: "ss-w-cdash-fact-label", children: [
@@ -1377,12 +1407,12 @@ function TeamDnaBlock({
1377
1407
  return /* @__PURE__ */ jsxs17("div", { className: "ss-w-cdash-lines", children: [
1378
1408
  /* @__PURE__ */ jsx21("span", { className: "ss-w-cdash-lines-heading", children: heading }),
1379
1409
  block.facts.map(
1380
- (f, i) => f.entityRef ? /* @__PURE__ */ jsxs17(
1410
+ (f, i) => f.actionRef ? /* @__PURE__ */ jsxs17(
1381
1411
  "button",
1382
1412
  {
1383
1413
  type: "button",
1384
1414
  className: "ss-w-cdash-fact",
1385
- onClick: () => onAction(f.question ?? f.sentence, { ...f.entityRef, subject: f.sentence, personaHint: "club" }),
1415
+ onClick: () => onAction(f.label ?? f.sentence, { actionRef: f.actionRef }),
1386
1416
  children: [
1387
1417
  f.sentence,
1388
1418
  f.label ? /* @__PURE__ */ jsxs17("span", { className: "ss-w-cdash-fact-label", children: [
@@ -1528,7 +1558,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1528
1558
  send(tile.actionValue, { displayText: tile.title });
1529
1559
  if (m.role === "assistant" && m.greetingDashboard) onDashboardTap?.(tile, index, m.greetingDashboard.layout);
1530
1560
  },
1531
- onClubAction: (message, grounding, displayText) => send(message, { grounding, displayText: displayText ?? message }),
1561
+ onClubAction: (label, opts) => send(label, { actionRef: opts.actionRef }),
1532
1562
  ui,
1533
1563
  slip
1534
1564
  },