@sensiblestats/widget-react 0.25.0 → 0.26.1

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.cjs CHANGED
@@ -36,7 +36,7 @@ __export(index_exports, {
36
36
  module.exports = __toCommonJS(index_exports);
37
37
 
38
38
  // src/StatsWidget.tsx
39
- var import_react12 = require("react");
39
+ var import_react13 = require("react");
40
40
  var import_widget_sdk5 = require("@sensiblestats/widget-sdk");
41
41
 
42
42
  // src/kickoff.ts
@@ -262,7 +262,7 @@ function themeAttrs(ui) {
262
262
  }
263
263
 
264
264
  // src/useChatStream.ts
265
- var import_react = require("react");
265
+ var import_react2 = require("react");
266
266
  var import_widget_sdk3 = require("@sensiblestats/widget-sdk");
267
267
 
268
268
  // src/reducer.ts
@@ -604,28 +604,71 @@ function reducer(state, action) {
604
604
  return { ...state, error: action.error, isStreaming: false, status: null, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, done: true })) };
605
605
  case "STOP":
606
606
  return { ...state, isStreaming: false, status: null, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, done: true })) };
607
+ case "LIVE_REFRESH":
608
+ return {
609
+ ...state,
610
+ messages: state.messages.map(
611
+ (m) => m.role === "assistant" && m.id === action.messageId && m.clubFanDashboard ? { ...m, clubFanDashboard: { ...m.clubFanDashboard, cards: action.cards } } : m
612
+ )
613
+ };
614
+ }
615
+ }
616
+
617
+ // src/useLiveDashboardPoll.ts
618
+ var import_react = require("react");
619
+ var POLL_INTERVAL_MS = 6e4;
620
+ function findLiveMessageId(messages) {
621
+ for (const m of messages.slice().reverse()) {
622
+ if (m.role === "assistant" && m.clubFanDashboard?.phase === "live") return m.id;
607
623
  }
624
+ return null;
625
+ }
626
+ function useLiveDashboardPoll({ messages, dispatch, getLiveDashboard }) {
627
+ const messageId = findLiveMessageId(messages);
628
+ const inFlightRef = (0, import_react.useRef)(false);
629
+ (0, import_react.useEffect)(() => {
630
+ if (!messageId) return;
631
+ const tick = async () => {
632
+ if (inFlightRef.current) return;
633
+ inFlightRef.current = true;
634
+ try {
635
+ const payload = await getLiveDashboard();
636
+ if (payload && payload.phase === "live") {
637
+ dispatch({ kind: "LIVE_REFRESH", messageId, cards: payload.cards });
638
+ } else {
639
+ window.clearInterval(id);
640
+ }
641
+ } catch {
642
+ } finally {
643
+ inFlightRef.current = false;
644
+ }
645
+ };
646
+ const id = window.setInterval(() => {
647
+ void tick();
648
+ }, POLL_INTERVAL_MS);
649
+ return () => window.clearInterval(id);
650
+ }, [messageId, dispatch, getLiveDashboard]);
608
651
  }
609
652
 
610
653
  // src/useChatStream.ts
611
654
  var STREAM_FALLBACK_TIMEOUT_MS = 3e4;
612
655
  function useChatStream(conversation, starterTexts, greetingMessage) {
613
- const seed = (0, import_react.useMemo)(() => initialState(starterTexts.map(chipFromText)), []);
614
- const [state, dispatch] = (0, import_react.useReducer)(reducer, seed);
615
- const idRef = (0, import_react.useRef)(0);
616
- const handleRef = (0, import_react.useRef)(null);
617
- const fallbackTimerRef = (0, import_react.useRef)(null);
618
- const stateRef = (0, import_react.useRef)(state);
656
+ const seed = (0, import_react2.useMemo)(() => initialState(starterTexts.map(chipFromText)), []);
657
+ const [state, dispatch] = (0, import_react2.useReducer)(reducer, seed);
658
+ const idRef = (0, import_react2.useRef)(0);
659
+ const handleRef = (0, import_react2.useRef)(null);
660
+ const fallbackTimerRef = (0, import_react2.useRef)(null);
661
+ const stateRef = (0, import_react2.useRef)(state);
619
662
  stateRef.current = state;
620
- const greetedRef = (0, import_react.useRef)(false);
663
+ const greetedRef = (0, import_react2.useRef)(false);
621
664
  const nextId = () => `m${idRef.current++}`;
622
- const clearFallbackTimer = (0, import_react.useCallback)(() => {
665
+ const clearFallbackTimer = (0, import_react2.useCallback)(() => {
623
666
  if (fallbackTimerRef.current != null) {
624
667
  clearTimeout(fallbackTimerRef.current);
625
668
  fallbackTimerRef.current = null;
626
669
  }
627
670
  }, []);
628
- const armFallbackTimer = (0, import_react.useCallback)((onFire) => {
671
+ const armFallbackTimer = (0, import_react2.useCallback)((onFire) => {
629
672
  clearFallbackTimer();
630
673
  fallbackTimerRef.current = setTimeout(() => {
631
674
  fallbackTimerRef.current = null;
@@ -634,7 +677,7 @@ function useChatStream(conversation, starterTexts, greetingMessage) {
634
677
  onFire();
635
678
  }, STREAM_FALLBACK_TIMEOUT_MS);
636
679
  }, [clearFallbackTimer]);
637
- const consume = (0, import_react.useCallback)((handle, onError) => {
680
+ const consume = (0, import_react2.useCallback)((handle, onError) => {
638
681
  ;
639
682
  (async () => {
640
683
  try {
@@ -652,7 +695,7 @@ function useChatStream(conversation, starterTexts, greetingMessage) {
652
695
  }
653
696
  })();
654
697
  }, [clearFallbackTimer]);
655
- const run = (0, import_react.useCallback)((input, displayText) => {
698
+ const run = (0, import_react2.useCallback)((input, displayText) => {
656
699
  handleRef.current?.cancel();
657
700
  clearFallbackTimer();
658
701
  dispatch({ kind: "USER_SENT", input, userId: nextId(), assistantId: nextId(), displayText });
@@ -664,16 +707,16 @@ function useChatStream(conversation, starterTexts, greetingMessage) {
664
707
  });
665
708
  consume(handle, (err) => dispatch({ kind: "STREAM_ERROR", error: err }));
666
709
  }, [conversation, consume, clearFallbackTimer, armFallbackTimer]);
667
- const send = (0, import_react.useCallback)((text, opts) => {
710
+ const send = (0, import_react2.useCallback)((text, opts) => {
668
711
  const trimmed = text.trim();
669
712
  if (!trimmed) return;
670
713
  run({ message: trimmed, actionId: opts?.actionId, actionRef: opts?.actionRef }, opts?.displayText);
671
714
  }, [run]);
672
- const retry = (0, import_react.useCallback)(() => {
715
+ const retry = (0, import_react2.useCallback)(() => {
673
716
  const last = stateRef.current.lastUserInput;
674
717
  if (last) run(last, stateRef.current.lastDisplayText ?? void 0);
675
718
  }, [run]);
676
- (0, import_react.useEffect)(() => {
719
+ (0, import_react2.useEffect)(() => {
677
720
  const message = greetingMessage?.trim();
678
721
  if (!message || greetedRef.current) return;
679
722
  greetedRef.current = true;
@@ -689,24 +732,26 @@ function useChatStream(conversation, starterTexts, greetingMessage) {
689
732
  dispatch({ kind: "STOP" });
690
733
  }
691
734
  }, [greetingMessage, conversation, consume, clearFallbackTimer, armFallbackTimer]);
692
- const open = (0, import_react.useCallback)(() => dispatch({ kind: "OPEN" }), []);
693
- const close = (0, import_react.useCallback)(() => {
735
+ const open = (0, import_react2.useCallback)(() => dispatch({ kind: "OPEN" }), []);
736
+ const close = (0, import_react2.useCallback)(() => {
694
737
  handleRef.current?.cancel();
695
738
  handleRef.current = null;
696
739
  clearFallbackTimer();
697
740
  dispatch({ kind: "CLOSE" });
698
741
  }, [clearFallbackTimer]);
699
- const stop = (0, import_react.useCallback)(() => {
742
+ const stop = (0, import_react2.useCallback)(() => {
700
743
  handleRef.current?.cancel();
701
744
  handleRef.current = null;
702
745
  clearFallbackTimer();
703
746
  dispatch({ kind: "STOP" });
704
747
  }, [clearFallbackTimer]);
705
- const toggle = (0, import_react.useCallback)(() => {
748
+ const toggle = (0, import_react2.useCallback)(() => {
706
749
  if (stateRef.current.isOpen) close();
707
750
  else open();
708
751
  }, [close, open]);
709
- const dismissPopup = (0, import_react.useCallback)(() => dispatch({ kind: "DISMISS_POPUP" }), []);
752
+ const dismissPopup = (0, import_react2.useCallback)(() => dispatch({ kind: "DISMISS_POPUP" }), []);
753
+ const getLiveDashboard = (0, import_react2.useCallback)(() => conversation.getLiveDashboard(), [conversation]);
754
+ useLiveDashboardPoll({ messages: state.messages, dispatch, getLiveDashboard });
710
755
  return { state, open, close, stop, toggle, send, retry, dismissPopup };
711
756
  }
712
757
 
@@ -767,7 +812,7 @@ function ChatFab({ label, greeting, popupText, onOpen, onDismissPopup, dismissLa
767
812
  }
768
813
 
769
814
  // src/components/ChatPanel.tsx
770
- var import_react11 = require("react");
815
+ var import_react12 = require("react");
771
816
 
772
817
  // src/components/ChatHeader.tsx
773
818
  var import_jsx_runtime4 = require("react/jsx-runtime");
@@ -780,7 +825,7 @@ function ChatHeader({ onClose, closeLabel = "Close chat" }) {
780
825
  }
781
826
 
782
827
  // src/components/MessageList.tsx
783
- var import_react9 = require("react");
828
+ var import_react10 = require("react");
784
829
 
785
830
  // src/components/Markdown.tsx
786
831
  var import_react_markdown = __toESM(require("react-markdown"), 1);
@@ -798,7 +843,7 @@ function Markdown({ text }) {
798
843
  }
799
844
 
800
845
  // src/components/EntityCardList.tsx
801
- var import_react2 = require("react");
846
+ var import_react3 = require("react");
802
847
  var import_jsx_runtime6 = require("react/jsx-runtime");
803
848
  var MAX_VISIBLE = 3;
804
849
  var RECORD_RE = /^(\d+)-(\d+)-(\d+)$/;
@@ -830,7 +875,7 @@ function StatRows({ stats }) {
830
875
  }) });
831
876
  }
832
877
  function CardBody({ c }) {
833
- const [active, setActive] = (0, import_react2.useState)(0);
878
+ const [active, setActive] = (0, import_react3.useState)(0);
834
879
  if (c.scopes.length === 0) return null;
835
880
  const season = c.season ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-ecard-season", children: shortSeason(c.season) }) : null;
836
881
  if (c.scopes.length === 1) {
@@ -889,7 +934,7 @@ function EntityCardItem({ c }) {
889
934
  ] });
890
935
  }
891
936
  function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMoreLabel = (n) => `Show ${n} more` }) {
892
- const [expanded, setExpanded] = (0, import_react2.useState)(false);
937
+ const [expanded, setExpanded] = (0, import_react3.useState)(false);
893
938
  if (cards.length === 0) return null;
894
939
  const visible = expanded ? cards : cards.slice(0, MAX_VISIBLE);
895
940
  const overflow = cards.length - MAX_VISIBLE;
@@ -900,10 +945,10 @@ function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMore
900
945
  }
901
946
 
902
947
  // src/components/BettingInsightList.tsx
903
- var import_react6 = require("react");
948
+ var import_react7 = require("react");
904
949
 
905
950
  // src/components/AddToSlipButton.tsx
906
- var import_react4 = require("react");
951
+ var import_react5 = require("react");
907
952
  var import_widget_sdk4 = require("@sensiblestats/widget-sdk");
908
953
 
909
954
  // src/slip.ts
@@ -935,11 +980,11 @@ function resultToToast(result, shownOdds, ui) {
935
980
  }
936
981
 
937
982
  // src/components/Toast.tsx
938
- var import_react3 = require("react");
983
+ var import_react4 = require("react");
939
984
  var import_jsx_runtime7 = require("react/jsx-runtime");
940
985
  var DISMISS_MS = 4e3;
941
986
  function Toast({ toast, onDismiss }) {
942
- (0, import_react3.useEffect)(() => {
987
+ (0, import_react4.useEffect)(() => {
943
988
  if (!toast) return;
944
989
  const timer = setTimeout(onDismiss, DISMISS_MS);
945
990
  return () => clearTimeout(timer);
@@ -957,10 +1002,10 @@ function withTimeout(value) {
957
1002
  ]);
958
1003
  }
959
1004
  function SlipButton({ selection, shownOdds, marketName, slip, ui }) {
960
- const [pending, setPending] = (0, import_react4.useState)(false);
961
- const [settled, setSettled] = (0, import_react4.useState)(false);
962
- const [toast, setToast] = (0, import_react4.useState)(null);
963
- const dismiss = (0, import_react4.useCallback)(() => setToast(null), []);
1005
+ const [pending, setPending] = (0, import_react5.useState)(false);
1006
+ const [settled, setSettled] = (0, import_react5.useState)(false);
1007
+ const [toast, setToast] = (0, import_react5.useState)(null);
1008
+ const dismiss = (0, import_react5.useCallback)(() => setToast(null), []);
964
1009
  const handler = slip.onAddToSlip;
965
1010
  if (!ui || !handler || !selection) return null;
966
1011
  const onClick = async () => {
@@ -1024,14 +1069,14 @@ function OddsMovementBadge({
1024
1069
  }
1025
1070
 
1026
1071
  // src/components/Crest.tsx
1027
- var import_react5 = require("react");
1072
+ var import_react6 = require("react");
1028
1073
  var import_jsx_runtime10 = require("react/jsx-runtime");
1029
1074
  function initials2(name) {
1030
1075
  return (name ?? "").trim().slice(0, 1).toUpperCase() || "?";
1031
1076
  }
1032
1077
  function Crest2({ url, name, kind }) {
1033
1078
  const cls = kind === "comp" ? "ss-w-bi-comp-logo" : "ss-w-bi-crest";
1034
- const [failed, setFailed] = (0, import_react5.useState)(false);
1079
+ const [failed, setFailed] = (0, import_react6.useState)(false);
1035
1080
  if (url && !failed) {
1036
1081
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("img", { className: cls, src: url, alt: "", "aria-hidden": "true", onError: () => setFailed(true) });
1037
1082
  }
@@ -1047,7 +1092,7 @@ function StatRows2({ stats }) {
1047
1092
  ] }, `${s.label}:${s.value}`)) });
1048
1093
  }
1049
1094
  function ScopedStats({ scopes }) {
1050
- const [active, setActive] = (0, import_react6.useState)(0);
1095
+ const [active, setActive] = (0, import_react7.useState)(0);
1051
1096
  const current = scopes[active] ?? scopes[0] ?? { key: "", label: "", stats: [] };
1052
1097
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ss-w-bi-scoped", children: [
1053
1098
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "ss-w-bi-scopes", role: "group", children: scopes.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "ss-w-bi-chip", "aria-pressed": i === active, onClick: () => setActive(i), children: s.label }, s.key)) }),
@@ -1058,7 +1103,7 @@ function hasStatsContent(it) {
1058
1103
  return !!it.scopes && it.scopes.length >= 2 || it.stats.length > 0;
1059
1104
  }
1060
1105
  function ReasonStrip({ it, ui }) {
1061
- const [open, setOpen] = (0, import_react6.useState)(false);
1106
+ const [open, setOpen] = (0, import_react7.useState)(false);
1062
1107
  const hasScopes = !!it.scopes && it.scopes.length >= 2;
1063
1108
  const hasStats = hasStatsContent(it);
1064
1109
  const hasText = !!it.text;
@@ -1138,7 +1183,7 @@ function BettingInsightList({ insights, ui, slip }) {
1138
1183
  }
1139
1184
 
1140
1185
  // src/components/MarketOddsCardList.tsx
1141
- var import_react7 = require("react");
1186
+ var import_react8 = require("react");
1142
1187
  var import_jsx_runtime12 = require("react/jsx-runtime");
1143
1188
  function OddsRow({ vm, row, ui, slip }) {
1144
1189
  const selection = vm.addToSlipEnabled ? marketOddsRowToSlipSelection(vm, row, slip.conversationId) : null;
@@ -1150,7 +1195,7 @@ function OddsRow({ vm, row, ui, slip }) {
1150
1195
  ] });
1151
1196
  }
1152
1197
  function MarketOddsCard({ vm, ui, slip }) {
1153
- const [open, setOpen] = (0, import_react7.useState)(false);
1198
+ const [open, setOpen] = (0, import_react8.useState)(false);
1154
1199
  const kickoff = vm.kickoffUtc && ui?.bettingKickoff ? ui.bettingKickoff(vm.kickoffUtc) : void 0;
1155
1200
  const hasNamedTeams = !!vm.homeTeamName && !!vm.awayTeamName;
1156
1201
  const showHeader = !!vm.competition || vm.isLive || !!kickoff;
@@ -1407,7 +1452,7 @@ function FixtureCard({ card, onTap }) {
1407
1452
  }
1408
1453
 
1409
1454
  // src/components/dashboard/CountdownCard.tsx
1410
- var import_react8 = require("react");
1455
+ var import_react9 = require("react");
1411
1456
  var import_jsx_runtime18 = require("react/jsx-runtime");
1412
1457
  var DAY_MS = 864e5;
1413
1458
  function remainingSegments(kickoffUnix, nowMs, withSeconds) {
@@ -1426,12 +1471,12 @@ function remainingSegments(kickoffUnix, nowMs, withSeconds) {
1426
1471
  function CountdownCard({ card }) {
1427
1472
  const { data } = card;
1428
1473
  const unitLabel = { d: data.daysUnit, h: data.hoursUnit, m: data.minutesUnit, s: data.secondsUnit };
1429
- const [nowMs, setNowMs] = (0, import_react8.useState)(() => Date.now());
1430
- const [reduced] = (0, import_react8.useState)(
1474
+ const [nowMs, setNowMs] = (0, import_react9.useState)(() => Date.now());
1475
+ const [reduced] = (0, import_react9.useState)(
1431
1476
  () => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches
1432
1477
  );
1433
1478
  const withinDay = data.kickoffUnix * 1e3 - nowMs < DAY_MS;
1434
- (0, import_react8.useEffect)(() => {
1479
+ (0, import_react9.useEffect)(() => {
1435
1480
  if (reduced) return;
1436
1481
  const id = window.setInterval(() => setNowMs(Date.now()), withinDay ? 1e3 : 6e4);
1437
1482
  return () => window.clearInterval(id);
@@ -1439,7 +1484,7 @@ function CountdownCard({ card }) {
1439
1484
  const segments = remainingSegments(data.kickoffUnix, nowMs, withinDay && !reduced);
1440
1485
  return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "ss-w-fandash-card ss-w-fandash-countdown", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "ss-w-fandash-countdown-top", children: [
1441
1486
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title ?? "Kick-off in" }),
1442
- segments ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "ss-w-fandash-countdown-clock", children: segments.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react8.Fragment, { children: [
1487
+ segments ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "ss-w-fandash-countdown-clock", children: segments.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react9.Fragment, { children: [
1443
1488
  i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "ss-w-fandash-countdown-colon", children: ":" }) : null,
1444
1489
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "ss-w-fandash-countdown-seg", children: [
1445
1490
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "ss-w-fandash-countdown-value", children: s.value }),
@@ -1687,85 +1732,139 @@ function RestingFormCard({ card }) {
1687
1732
  ] });
1688
1733
  }
1689
1734
 
1690
- // src/components/dashboard/ClubFanDashboard.tsx
1735
+ // src/components/dashboard/LiveScoreCard.tsx
1691
1736
  var import_jsx_runtime34 = require("react/jsx-runtime");
1737
+ function LiveScoreCard({ card }) {
1738
+ const { data } = card;
1739
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-card ss-w-fandash-live-score", children: [
1740
+ card.title ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1741
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-postmatch-scoreboard", children: [
1742
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-postmatch-team", children: data.homeName }),
1743
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-postmatch-score", children: data.homeScore }),
1744
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-postmatch-sep", children: "\u2013" }),
1745
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-postmatch-score", children: data.awayScore }),
1746
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-postmatch-team ss-w-fandash-postmatch-team--away", children: data.awayName })
1747
+ ] }),
1748
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash-live-meta", children: [
1749
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-minute", children: data.minuteLabel }),
1750
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-status", children: data.statusLabel }),
1751
+ data.competition ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-live-competition", children: data.competition }) : null
1752
+ ] })
1753
+ ] });
1754
+ }
1755
+
1756
+ // src/components/dashboard/LiveTimelineCard.tsx
1757
+ var import_jsx_runtime35 = require("react/jsx-runtime");
1758
+ function LiveTimelineCard({ card }) {
1759
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "ss-w-fandash-card ss-w-fandash-live-timeline", children: [
1760
+ card.title ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1761
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { className: "ss-w-fandash-live-event-list", children: card.data.events.map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("li", { className: e.scoredByClub ? "ss-w-fandash-live-event ss-w-fandash-live-event--club" : "ss-w-fandash-live-event", children: [
1762
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-event-minute", children: e.minuteLabel }),
1763
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "ss-w-fandash-live-event-text", children: e.text })
1764
+ ] }, `${e.minuteLabel}-${i}`)) })
1765
+ ] });
1766
+ }
1767
+
1768
+ // src/components/dashboard/LiveStatsCard.tsx
1769
+ var import_jsx_runtime36 = require("react/jsx-runtime");
1770
+ function LiveStatsCard({ card }) {
1771
+ const { data } = card;
1772
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "ss-w-fandash-card ss-w-fandash-live-stats", children: [
1773
+ card.title ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
1774
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "ss-w-fandash-live-stat-rows", children: data.rows.map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "ss-w-fandash-live-stat-row", children: [
1775
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-stat-value", children: r.clubValue }),
1776
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-stat-label", children: r.label }),
1777
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-stat-value", children: r.oppValue })
1778
+ ] }, `${r.label}-${i}`)) }),
1779
+ data.momentumLabel ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "ss-w-fandash-live-momentum", children: data.momentumLabel }) : null
1780
+ ] });
1781
+ }
1782
+
1783
+ // src/components/dashboard/ClubFanDashboard.tsx
1784
+ var import_jsx_runtime37 = require("react/jsx-runtime");
1692
1785
  function ClubFanDashboard({ dashboard, onTap, ui }) {
1693
1786
  const orderedCards = [
1694
1787
  ...dashboard.cards.filter((c) => c.cardType === "countdown"),
1695
1788
  ...dashboard.cards.filter((c) => c.cardType !== "countdown")
1696
1789
  ];
1697
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "ss-w-fandash", children: [
1698
- dashboard.greeting ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "ss-w-fandash-greeting", children: dashboard.greeting }) : null,
1699
- /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "ss-w-fandash-cards", children: orderedCards.map((card, i) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(FanDashboardCard, { card, onTap, ui }, `${card.cardType}-${i}`)) })
1790
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "ss-w-fandash", children: [
1791
+ dashboard.greeting ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "ss-w-fandash-greeting", children: dashboard.greeting }) : null,
1792
+ /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "ss-w-fandash-cards", children: orderedCards.map((card, i) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(FanDashboardCard, { card, onTap, ui }, `${card.cardType}-${i}`)) })
1700
1793
  ] });
1701
1794
  }
1702
1795
  function FanDashboardCard({ card, onTap, ui }) {
1703
1796
  switch (card.cardType) {
1704
1797
  case "fixture":
1705
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(FixtureCard, { card, onTap });
1798
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(FixtureCard, { card, onTap });
1706
1799
  case "countdown":
1707
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(CountdownCard, { card });
1800
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(CountdownCard, { card });
1708
1801
  case "injuries":
1709
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(InjuriesCard, { card });
1802
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(InjuriesCard, { card });
1710
1803
  case "reasonToBet":
1711
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ReasonToBetCard, { card });
1804
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReasonToBetCard, { card });
1712
1805
  case "whereToWatch":
1713
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(WhereToWatchCard, { card });
1806
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(WhereToWatchCard, { card });
1714
1807
  case "congestedWeek":
1715
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(CongestedWeekCard, { card, onTap });
1808
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(CongestedWeekCard, { card, onTap });
1716
1809
  case "transfers":
1717
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(TransfersCard, { card });
1810
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(TransfersCard, { card });
1718
1811
  case "links":
1719
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(LinksCard, { card });
1812
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LinksCard, { card });
1720
1813
  case "postMatchResult":
1721
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(PostMatchResultCard, { card });
1814
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchResultCard, { card });
1722
1815
  case "postMatchGoals":
1723
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(PostMatchGoalsCard, { card, onTap });
1816
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchGoalsCard, { card, onTap });
1724
1817
  case "postMatchStats":
1725
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(PostMatchStatsCard, { card, onTap });
1818
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchStatsCard, { card, onTap });
1726
1819
  case "postMatchTopPerformer":
1727
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(PostMatchTopPerformerCard, { card, onTap });
1820
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchTopPerformerCard, { card, onTap });
1728
1821
  case "postMatchNext":
1729
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(PostMatchNextCard, { card });
1822
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchNextCard, { card });
1730
1823
  case "restingNext":
1731
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(RestingNextCard, { card, onTap });
1824
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingNextCard, { card, onTap });
1732
1825
  case "restingStandings":
1733
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(RestingStandingsCard, { card, onTap });
1826
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingStandingsCard, { card, onTap });
1734
1827
  case "restingSeason":
1735
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(RestingSeasonCard, { card, onTap });
1828
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingSeasonCard, { card, onTap });
1736
1829
  case "restingForm":
1737
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(RestingFormCard, { card });
1830
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingFormCard, { card });
1831
+ case "liveScore":
1832
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LiveScoreCard, { card });
1833
+ case "liveTimeline":
1834
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LiveTimelineCard, { card });
1835
+ case "liveStats":
1836
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LiveStatsCard, { card });
1738
1837
  default:
1739
1838
  return null;
1740
1839
  }
1741
1840
  }
1742
1841
 
1743
1842
  // src/components/ChatMessage.tsx
1744
- var import_jsx_runtime35 = require("react/jsx-runtime");
1843
+ var import_jsx_runtime38 = require("react/jsx-runtime");
1745
1844
  function ChatMessage({ message, disabled, onAct, onTap, ui, slip }) {
1746
1845
  if (message.role === "user") {
1747
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "ss-w-bubble", children: message.text }) });
1846
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "ss-w-msg ss-w-user", "data-msg-id": message.id, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "ss-w-bubble", children: message.text }) });
1748
1847
  }
1749
1848
  const onFanDashboardTap = (card) => {
1750
1849
  onAct({ message: card.actionLabel, actionRef: card.actionRef, label: card.actionLabel || card.title || "" });
1751
1850
  };
1752
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "ss-w-msg ss-w-bot", children: [
1753
- message.text && !message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Markdown, { text: message.text }) : null,
1754
- message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
1755
- message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ClubFanDashboard, { dashboard: message.clubFanDashboard, onTap: onFanDashboardTap, ui }) : null,
1756
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
1757
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(BettingInsightList, { insights: message.insights, ui, slip }),
1758
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
1759
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(OddsMovementCardList, { cards: message.oddsMovement, ui }),
1760
- /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
1851
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "ss-w-msg ss-w-bot", "data-msg-id": message.id, children: [
1852
+ message.text && !message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Markdown, { text: message.text }) : null,
1853
+ message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
1854
+ message.clubFanDashboard ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ClubFanDashboard, { dashboard: message.clubFanDashboard, onTap: onFanDashboardTap, ui }) : null,
1855
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
1856
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(BettingInsightList, { insights: message.insights, ui, slip }),
1857
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
1858
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(OddsMovementCardList, { cards: message.oddsMovement, ui }),
1859
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
1761
1860
  ] });
1762
1861
  }
1763
1862
 
1764
1863
  // src/components/IntentChips.tsx
1765
- var import_jsx_runtime36 = require("react/jsx-runtime");
1864
+ var import_jsx_runtime39 = require("react/jsx-runtime");
1766
1865
  function IntentChips({ chips, onPick }) {
1767
1866
  if (chips.length === 0) return null;
1768
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
1867
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
1769
1868
  }
1770
1869
 
1771
1870
  // src/scroll.ts
@@ -1775,18 +1874,32 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
1775
1874
  }
1776
1875
 
1777
1876
  // src/components/MessageList.tsx
1778
- var import_jsx_runtime37 = require("react/jsx-runtime");
1877
+ var import_jsx_runtime40 = require("react/jsx-runtime");
1878
+ function turnAnchor(messages) {
1879
+ const last = messages[messages.length - 1];
1880
+ if (!last || last.role !== "assistant") return null;
1881
+ const prev = messages[messages.length - 2];
1882
+ return { turnId: last.id, anchorId: prev && prev.role === "user" ? prev.id : last.id };
1883
+ }
1779
1884
  function MessageList({ controller, ui, slip, onDashboardTap }) {
1780
1885
  const { state, send } = controller;
1781
- const listRef = (0, import_react9.useRef)(null);
1782
- const endRef = (0, import_react9.useRef)(null);
1783
- const pinnedRef = (0, import_react9.useRef)(true);
1784
- (0, import_react9.useEffect)(() => {
1886
+ const listRef = (0, import_react10.useRef)(null);
1887
+ const endRef = (0, import_react10.useRef)(null);
1888
+ const pinnedRef = (0, import_react10.useRef)(true);
1889
+ const anchoredTurnRef = (0, import_react10.useRef)(null);
1890
+ (0, import_react10.useEffect)(() => {
1891
+ const anchor = turnAnchor(state.messages);
1892
+ if (anchor && anchor.turnId !== anchoredTurnRef.current) {
1893
+ anchoredTurnRef.current = anchor.turnId;
1894
+ pinnedRef.current = false;
1895
+ listRef.current?.querySelector(`[data-msg-id="${anchor.anchorId}"]`)?.scrollIntoView({ block: "start" });
1896
+ return;
1897
+ }
1785
1898
  if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
1786
1899
  }, [state.messages, state.status]);
1787
1900
  const hasUserMessage = state.messages.some((m) => m.role === "user");
1788
1901
  const hasDashboard = state.messages.some((m) => m.role === "assistant" && (m.greetingDashboard || m.clubFanDashboard));
1789
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
1902
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
1790
1903
  "div",
1791
1904
  {
1792
1905
  className: "ss-w-list",
@@ -1796,7 +1909,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1796
1909
  if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
1797
1910
  },
1798
1911
  children: [
1799
- state.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
1912
+ state.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
1800
1913
  ChatMessage,
1801
1914
  {
1802
1915
  message: m,
@@ -1811,25 +1924,25 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1811
1924
  },
1812
1925
  m.id
1813
1926
  )),
1814
- state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ActionButtons, { actions: [], loading: true, onAct: () => {
1927
+ state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ActionButtons, { actions: [], loading: true, onAct: () => {
1815
1928
  } }) : null,
1816
- state.status ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "ss-w-status", children: state.status }) : null,
1817
- state.error ? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "ss-w-error", role: "alert", children: [
1818
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
1819
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
1929
+ state.status ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "ss-w-status", children: state.status }) : null,
1930
+ state.error ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "ss-w-error", role: "alert", children: [
1931
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
1932
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
1820
1933
  ] }) : null,
1821
- !hasUserMessage && !hasDashboard ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
1822
- /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { ref: endRef })
1934
+ !hasUserMessage && !hasDashboard ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
1935
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { ref: endRef })
1823
1936
  ]
1824
1937
  }
1825
1938
  );
1826
1939
  }
1827
1940
 
1828
1941
  // src/components/ChatInput.tsx
1829
- var import_react10 = require("react");
1830
- var import_jsx_runtime38 = require("react/jsx-runtime");
1942
+ var import_react11 = require("react");
1943
+ var import_jsx_runtime41 = require("react/jsx-runtime");
1831
1944
  function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
1832
- const [value, setValue] = (0, import_react10.useState)("");
1945
+ const [value, setValue] = (0, import_react11.useState)("");
1833
1946
  const submit = () => {
1834
1947
  const t = value.trim();
1835
1948
  if (!t) return;
@@ -1842,18 +1955,18 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
1842
1955
  submit();
1843
1956
  }
1844
1957
  };
1845
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "ss-w-input", children: [
1846
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
1847
- streaming ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(SendIcon, {}) })
1958
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "ss-w-input", children: [
1959
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
1960
+ streaming ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(SendIcon, {}) })
1848
1961
  ] });
1849
1962
  }
1850
1963
 
1851
1964
  // src/components/ChatPanel.tsx
1852
- var import_jsx_runtime39 = require("react/jsx-runtime");
1965
+ var import_jsx_runtime42 = require("react/jsx-runtime");
1853
1966
  var FULLSCREEN_QUERY = "(max-width: 639px)";
1854
1967
  function useIsModal() {
1855
- const [modal, setModal] = (0, import_react11.useState)(false);
1856
- (0, import_react11.useEffect)(() => {
1968
+ const [modal, setModal] = (0, import_react12.useState)(false);
1969
+ (0, import_react12.useEffect)(() => {
1857
1970
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
1858
1971
  const mql = window.matchMedia(FULLSCREEN_QUERY);
1859
1972
  const sync = () => setModal(mql.matches);
@@ -1866,17 +1979,17 @@ function useIsModal() {
1866
1979
  function ChatPanel({ controller, ui, slip, onDashboardTap }) {
1867
1980
  const { state, close, stop, send } = controller;
1868
1981
  const isModal = useIsModal();
1869
- (0, import_react11.useEffect)(() => {
1982
+ (0, import_react12.useEffect)(() => {
1870
1983
  const onKey = (e) => {
1871
1984
  if (e.key === "Escape") close();
1872
1985
  };
1873
1986
  window.addEventListener("keydown", onKey);
1874
1987
  return () => window.removeEventListener("keydown", onKey);
1875
1988
  }, [close]);
1876
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
1877
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
1878
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(MessageList, { controller, ui, slip, onDashboardTap }),
1879
- /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
1989
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
1990
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
1991
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(MessageList, { controller, ui, slip, onDashboardTap }),
1992
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
1880
1993
  ] });
1881
1994
  }
1882
1995
 
@@ -1940,7 +2053,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
1940
2053
  .ss-w-close { margin-left: auto; background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 16px; display: grid; place-items: center; }
1941
2054
 
1942
2055
  /* messages */
1943
- .ss-w-list { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 12px; }
2056
+ .ss-w-list { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 12px; scroll-padding-top: 14px; }
1944
2057
  .ss-w-msg { display: flex; }
1945
2058
  .ss-w-user { justify-content: flex-end; }
1946
2059
  .ss-w-user .ss-w-bubble { background: color-mix(in srgb, var(--ss-w-accent) 14%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 30%, var(--ss-w-line)); border-radius: 14px; padding: 8px 12px; max-width: 80%; }
@@ -2388,10 +2501,35 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
2388
2501
 
2389
2502
  /* resting form: reuses the fixture last-five pill classes (ss-w-fandash-form / -form-pill) */
2390
2503
  .ss-w-fandash-resting-form .ss-w-fandash-form { margin-top: 2px; }
2504
+
2505
+ /* live score: reuses the post-match scoreboard classes for the team/score row, with its own
2506
+ meta row -- minute called out in the accent color, status and competition beside it */
2507
+ .ss-w-fandash-live-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 11.5px; color: var(--ss-w-faint); }
2508
+ .ss-w-fandash-live-minute { flex: none; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); }
2509
+ .ss-w-fandash-live-status { font-weight: 650; color: var(--ss-w-ink); }
2510
+ .ss-w-fandash-live-competition { color: var(--ss-w-faint); }
2511
+
2512
+ /* live timeline: a plain event list, the club's own events picked out in the accent color --
2513
+ same mechanism as the post-match goal list, plus a minute column */
2514
+ .ss-w-fandash-live-event-list { list-style: none; display: flex; flex-direction: column; gap: 4px; }
2515
+ .ss-w-fandash-live-event { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; color: var(--ss-w-faint); }
2516
+ .ss-w-fandash-live-event--club { color: var(--ss-w-ink); font-weight: 650; }
2517
+ .ss-w-fandash-live-event-minute { flex: none; font-variant-numeric: tabular-nums; color: var(--ss-w-faint); }
2518
+ .ss-w-fandash-live-event--club .ss-w-fandash-live-event-minute { color: var(--ss-w-accent); }
2519
+ .ss-w-fandash-live-event-text { min-width: 0; }
2520
+
2521
+ /* live stats: club value / stat label / opponent value, same 3-column layout as post-match
2522
+ stats, plus an optional momentum line underneath */
2523
+ .ss-w-fandash-live-stat-rows { display: flex; flex-direction: column; gap: 5px; }
2524
+ .ss-w-fandash-live-stat-row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px; }
2525
+ .ss-w-fandash-live-stat-value { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-ink); }
2526
+ .ss-w-fandash-live-stat-row .ss-w-fandash-live-stat-value:last-child { text-align: right; }
2527
+ .ss-w-fandash-live-stat-label { font-size: 10.5px; text-align: center; color: var(--ss-w-faint); text-transform: uppercase; letter-spacing: .03em; }
2528
+ .ss-w-fandash-live-momentum { font-size: 11.5px; font-weight: 650; color: var(--ss-w-accent); }
2391
2529
  `;
2392
2530
 
2393
2531
  // src/StatsWidget.tsx
2394
- var import_jsx_runtime40 = require("react/jsx-runtime");
2532
+ var import_jsx_runtime43 = require("react/jsx-runtime");
2395
2533
  function buildClient(props) {
2396
2534
  try {
2397
2535
  if ("client" in props && props.client) {
@@ -2416,11 +2554,11 @@ function injectStyles(root) {
2416
2554
  function StatsWidget(props) {
2417
2555
  const injectedClient = "client" in props ? props.client : void 0;
2418
2556
  const cfg = props.config;
2419
- const built = (0, import_react12.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
2420
- const ui = (0, import_react12.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
2421
- const rootRef = (0, import_react12.useRef)(null);
2557
+ const built = (0, import_react13.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
2558
+ const ui = (0, import_react13.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
2559
+ const rootRef = (0, import_react13.useRef)(null);
2422
2560
  const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0);
2423
- const slip = (0, import_react12.useMemo)(
2561
+ const slip = (0, import_react13.useMemo)(
2424
2562
  () => ({
2425
2563
  onAddToSlip: props.config?.onAddToSlip,
2426
2564
  conversationId: controller.state.conversationId ?? void 0,
@@ -2433,18 +2571,18 @@ function StatsWidget(props) {
2433
2571
  }),
2434
2572
  [props.config?.onAddToSlip, controller.state.conversationId, built]
2435
2573
  );
2436
- const onDashboardTap = (0, import_react12.useMemo)(
2574
+ const onDashboardTap = (0, import_react13.useMemo)(
2437
2575
  () => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input)) : void 0,
2438
2576
  [built]
2439
2577
  );
2440
- (0, import_react12.useEffect)(() => {
2578
+ (0, import_react13.useEffect)(() => {
2441
2579
  if (!built || !ui.injectStyles) return;
2442
2580
  const node = rootRef.current?.getRootNode();
2443
2581
  if (node) injectStyles(node);
2444
2582
  }, [built, ui.injectStyles]);
2445
2583
  if (!built) return null;
2446
2584
  const attrs = themeAttrs(ui);
2447
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { ref: rootRef, className: "ss-w-root", "data-ss-w-theme": attrs["data-ss-w-theme"], "data-ss-w-pos": attrs["data-ss-w-pos"], style: attrs.style, children: controller.state.isOpen ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ChatPanel, { controller, ui, slip, onDashboardTap }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
2585
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { ref: rootRef, className: "ss-w-root", "data-ss-w-theme": attrs["data-ss-w-theme"], "data-ss-w-pos": attrs["data-ss-w-pos"], style: attrs.style, children: controller.state.isOpen ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ChatPanel, { controller, ui, slip, onDashboardTap }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
2448
2586
  ChatFab,
2449
2587
  {
2450
2588
  label: ui.launcherLabel,