@sensiblestats/widget-react 0.24.0 → 0.26.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/CHANGELOG.md +22 -0
- package/dist/index.cjs +319 -111
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +281 -73
- package/dist/index.js.map +1 -1
- package/dist/styles.css +42 -0
- package/package.json +2 -2
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
|
|
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
|
|
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
|
+
};
|
|
607
614
|
}
|
|
608
615
|
}
|
|
609
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;
|
|
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]);
|
|
651
|
+
}
|
|
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,
|
|
614
|
-
const [state, dispatch] = (0,
|
|
615
|
-
const idRef = (0,
|
|
616
|
-
const handleRef = (0,
|
|
617
|
-
const fallbackTimerRef = (0,
|
|
618
|
-
const stateRef = (0,
|
|
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,
|
|
663
|
+
const greetedRef = (0, import_react2.useRef)(false);
|
|
621
664
|
const nextId = () => `m${idRef.current++}`;
|
|
622
|
-
const clearFallbackTimer = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
693
|
-
const close = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
948
|
+
var import_react7 = require("react");
|
|
904
949
|
|
|
905
950
|
// src/components/AddToSlipButton.tsx
|
|
906
|
-
var
|
|
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
|
|
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,
|
|
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,
|
|
961
|
-
const [settled, setSettled] = (0,
|
|
962
|
-
const [toast, setToast] = (0,
|
|
963
|
-
const dismiss = (0,
|
|
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
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
1430
|
-
const [reduced] = (0,
|
|
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,
|
|
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)(
|
|
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 }),
|
|
@@ -1628,77 +1673,198 @@ function PostMatchNextCard({ card }) {
|
|
|
1628
1673
|
] });
|
|
1629
1674
|
}
|
|
1630
1675
|
|
|
1631
|
-
// src/components/dashboard/
|
|
1676
|
+
// src/components/dashboard/RestingNextCard.tsx
|
|
1632
1677
|
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1678
|
+
function RestingNextCard({ card, onTap }) {
|
|
1679
|
+
const { data } = card;
|
|
1680
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("button", { type: "button", className: "ss-w-fandash-card ss-w-fandash-card--tap ss-w-fandash-resting-next", onClick: () => onTap(card), children: [
|
|
1681
|
+
card.title ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
1682
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { className: "ss-w-fandash-resting-next-row", children: [
|
|
1683
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "ss-w-fandash-resting-next-when", children: data.whenLabel }),
|
|
1684
|
+
data.competition ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "ss-w-fandash-resting-next-competition", children: data.competition }) : null
|
|
1685
|
+
] }),
|
|
1686
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "ss-w-fandash-card-cta", children: card.actionLabel })
|
|
1687
|
+
] });
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
// src/components/dashboard/RestingStandingsCard.tsx
|
|
1691
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1692
|
+
function RestingStandingsCard({ card, onTap }) {
|
|
1693
|
+
const { data } = card;
|
|
1694
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("button", { type: "button", className: "ss-w-fandash-card ss-w-fandash-card--tap ss-w-fandash-resting-standings", onClick: () => onTap(card), children: [
|
|
1695
|
+
card.title ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
1696
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "ss-w-fandash-resting-standings-row", children: [
|
|
1697
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "ss-w-fandash-resting-standings-position", children: data.positionLabel }),
|
|
1698
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "ss-w-fandash-resting-standings-record", children: data.recordLabel })
|
|
1699
|
+
] }),
|
|
1700
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "ss-w-fandash-card-cta", children: card.actionLabel })
|
|
1701
|
+
] });
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
// src/components/dashboard/RestingSeasonCard.tsx
|
|
1705
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1706
|
+
function RestingSeasonCard({ card, onTap }) {
|
|
1707
|
+
const { data } = card;
|
|
1708
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("button", { type: "button", className: "ss-w-fandash-card ss-w-fandash-card--tap ss-w-fandash-resting-season", onClick: () => onTap(card), children: [
|
|
1709
|
+
card.title ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
1710
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "ss-w-fandash-resting-season-rows", children: [
|
|
1711
|
+
data.scorerLabel ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "ss-w-fandash-resting-season-row", children: data.scorerLabel }) : null,
|
|
1712
|
+
data.playerLabel ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "ss-w-fandash-resting-season-row", children: data.playerLabel }) : null,
|
|
1713
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "ss-w-fandash-resting-season-row", children: data.tallyLabel })
|
|
1714
|
+
] }),
|
|
1715
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "ss-w-fandash-card-cta", children: card.actionLabel })
|
|
1716
|
+
] });
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
// src/components/dashboard/RestingFormCard.tsx
|
|
1720
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1721
|
+
function resultClass2(result) {
|
|
1722
|
+
const upper = result.toUpperCase();
|
|
1723
|
+
if (upper === "W") return "ss-w-fandash-form-pill--win";
|
|
1724
|
+
if (upper === "L") return "ss-w-fandash-form-pill--loss";
|
|
1725
|
+
return "ss-w-fandash-form-pill--draw";
|
|
1726
|
+
}
|
|
1727
|
+
function RestingFormCard({ card }) {
|
|
1728
|
+
const { data } = card;
|
|
1729
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "ss-w-fandash-card ss-w-fandash-resting-form", children: [
|
|
1730
|
+
card.title ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
1731
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "ss-w-fandash-form", children: data.letters.map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: `ss-w-fandash-form-pill ${resultClass2(r)}`, children: r }, `${r}-${i}`)) })
|
|
1732
|
+
] });
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
// src/components/dashboard/LiveScoreCard.tsx
|
|
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");
|
|
1633
1785
|
function ClubFanDashboard({ dashboard, onTap, ui }) {
|
|
1634
1786
|
const orderedCards = [
|
|
1635
1787
|
...dashboard.cards.filter((c) => c.cardType === "countdown"),
|
|
1636
1788
|
...dashboard.cards.filter((c) => c.cardType !== "countdown")
|
|
1637
1789
|
];
|
|
1638
|
-
return /* @__PURE__ */ (0,
|
|
1639
|
-
dashboard.greeting ? /* @__PURE__ */ (0,
|
|
1640
|
-
/* @__PURE__ */ (0,
|
|
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}`)) })
|
|
1641
1793
|
] });
|
|
1642
1794
|
}
|
|
1643
1795
|
function FanDashboardCard({ card, onTap, ui }) {
|
|
1644
1796
|
switch (card.cardType) {
|
|
1645
1797
|
case "fixture":
|
|
1646
|
-
return /* @__PURE__ */ (0,
|
|
1798
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(FixtureCard, { card, onTap });
|
|
1647
1799
|
case "countdown":
|
|
1648
|
-
return /* @__PURE__ */ (0,
|
|
1800
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(CountdownCard, { card });
|
|
1649
1801
|
case "injuries":
|
|
1650
|
-
return /* @__PURE__ */ (0,
|
|
1802
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(InjuriesCard, { card });
|
|
1651
1803
|
case "reasonToBet":
|
|
1652
|
-
return /* @__PURE__ */ (0,
|
|
1804
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReasonToBetCard, { card });
|
|
1653
1805
|
case "whereToWatch":
|
|
1654
|
-
return /* @__PURE__ */ (0,
|
|
1806
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(WhereToWatchCard, { card });
|
|
1655
1807
|
case "congestedWeek":
|
|
1656
|
-
return /* @__PURE__ */ (0,
|
|
1808
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(CongestedWeekCard, { card, onTap });
|
|
1657
1809
|
case "transfers":
|
|
1658
|
-
return /* @__PURE__ */ (0,
|
|
1810
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(TransfersCard, { card });
|
|
1659
1811
|
case "links":
|
|
1660
|
-
return /* @__PURE__ */ (0,
|
|
1812
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LinksCard, { card });
|
|
1661
1813
|
case "postMatchResult":
|
|
1662
|
-
return /* @__PURE__ */ (0,
|
|
1814
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchResultCard, { card });
|
|
1663
1815
|
case "postMatchGoals":
|
|
1664
|
-
return /* @__PURE__ */ (0,
|
|
1816
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchGoalsCard, { card, onTap });
|
|
1665
1817
|
case "postMatchStats":
|
|
1666
|
-
return /* @__PURE__ */ (0,
|
|
1818
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchStatsCard, { card, onTap });
|
|
1667
1819
|
case "postMatchTopPerformer":
|
|
1668
|
-
return /* @__PURE__ */ (0,
|
|
1820
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchTopPerformerCard, { card, onTap });
|
|
1669
1821
|
case "postMatchNext":
|
|
1670
|
-
return /* @__PURE__ */ (0,
|
|
1822
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchNextCard, { card });
|
|
1823
|
+
case "restingNext":
|
|
1824
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingNextCard, { card, onTap });
|
|
1825
|
+
case "restingStandings":
|
|
1826
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingStandingsCard, { card, onTap });
|
|
1827
|
+
case "restingSeason":
|
|
1828
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingSeasonCard, { card, onTap });
|
|
1829
|
+
case "restingForm":
|
|
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 });
|
|
1671
1837
|
default:
|
|
1672
1838
|
return null;
|
|
1673
1839
|
}
|
|
1674
1840
|
}
|
|
1675
1841
|
|
|
1676
1842
|
// src/components/ChatMessage.tsx
|
|
1677
|
-
var
|
|
1843
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
1678
1844
|
function ChatMessage({ message, disabled, onAct, onTap, ui, slip }) {
|
|
1679
1845
|
if (message.role === "user") {
|
|
1680
|
-
return /* @__PURE__ */ (0,
|
|
1846
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "ss-w-bubble", children: message.text }) });
|
|
1681
1847
|
}
|
|
1682
1848
|
const onFanDashboardTap = (card) => {
|
|
1683
1849
|
onAct({ message: card.actionLabel, actionRef: card.actionRef, label: card.actionLabel || card.title || "" });
|
|
1684
1850
|
};
|
|
1685
|
-
return /* @__PURE__ */ (0,
|
|
1686
|
-
message.text && !message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0,
|
|
1687
|
-
message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0,
|
|
1688
|
-
message.clubFanDashboard ? /* @__PURE__ */ (0,
|
|
1689
|
-
/* @__PURE__ */ (0,
|
|
1690
|
-
/* @__PURE__ */ (0,
|
|
1691
|
-
/* @__PURE__ */ (0,
|
|
1692
|
-
/* @__PURE__ */ (0,
|
|
1693
|
-
/* @__PURE__ */ (0,
|
|
1851
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "ss-w-msg ss-w-bot", 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 })
|
|
1694
1860
|
] });
|
|
1695
1861
|
}
|
|
1696
1862
|
|
|
1697
1863
|
// src/components/IntentChips.tsx
|
|
1698
|
-
var
|
|
1864
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
1699
1865
|
function IntentChips({ chips, onPick }) {
|
|
1700
1866
|
if (chips.length === 0) return null;
|
|
1701
|
-
return /* @__PURE__ */ (0,
|
|
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}`)) });
|
|
1702
1868
|
}
|
|
1703
1869
|
|
|
1704
1870
|
// src/scroll.ts
|
|
@@ -1708,18 +1874,18 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
|
|
|
1708
1874
|
}
|
|
1709
1875
|
|
|
1710
1876
|
// src/components/MessageList.tsx
|
|
1711
|
-
var
|
|
1877
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
1712
1878
|
function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
1713
1879
|
const { state, send } = controller;
|
|
1714
|
-
const listRef = (0,
|
|
1715
|
-
const endRef = (0,
|
|
1716
|
-
const pinnedRef = (0,
|
|
1717
|
-
(0,
|
|
1880
|
+
const listRef = (0, import_react10.useRef)(null);
|
|
1881
|
+
const endRef = (0, import_react10.useRef)(null);
|
|
1882
|
+
const pinnedRef = (0, import_react10.useRef)(true);
|
|
1883
|
+
(0, import_react10.useEffect)(() => {
|
|
1718
1884
|
if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
|
|
1719
1885
|
}, [state.messages, state.status]);
|
|
1720
1886
|
const hasUserMessage = state.messages.some((m) => m.role === "user");
|
|
1721
1887
|
const hasDashboard = state.messages.some((m) => m.role === "assistant" && (m.greetingDashboard || m.clubFanDashboard));
|
|
1722
|
-
return /* @__PURE__ */ (0,
|
|
1888
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
1723
1889
|
"div",
|
|
1724
1890
|
{
|
|
1725
1891
|
className: "ss-w-list",
|
|
@@ -1729,7 +1895,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1729
1895
|
if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
|
|
1730
1896
|
},
|
|
1731
1897
|
children: [
|
|
1732
|
-
state.messages.map((m) => /* @__PURE__ */ (0,
|
|
1898
|
+
state.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
1733
1899
|
ChatMessage,
|
|
1734
1900
|
{
|
|
1735
1901
|
message: m,
|
|
@@ -1744,25 +1910,25 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1744
1910
|
},
|
|
1745
1911
|
m.id
|
|
1746
1912
|
)),
|
|
1747
|
-
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0,
|
|
1913
|
+
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ActionButtons, { actions: [], loading: true, onAct: () => {
|
|
1748
1914
|
} }) : null,
|
|
1749
|
-
state.status ? /* @__PURE__ */ (0,
|
|
1750
|
-
state.error ? /* @__PURE__ */ (0,
|
|
1751
|
-
/* @__PURE__ */ (0,
|
|
1752
|
-
/* @__PURE__ */ (0,
|
|
1915
|
+
state.status ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "ss-w-status", children: state.status }) : null,
|
|
1916
|
+
state.error ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "ss-w-error", role: "alert", children: [
|
|
1917
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
|
|
1918
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
|
|
1753
1919
|
] }) : null,
|
|
1754
|
-
!hasUserMessage && !hasDashboard ? /* @__PURE__ */ (0,
|
|
1755
|
-
/* @__PURE__ */ (0,
|
|
1920
|
+
!hasUserMessage && !hasDashboard ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
|
|
1921
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { ref: endRef })
|
|
1756
1922
|
]
|
|
1757
1923
|
}
|
|
1758
1924
|
);
|
|
1759
1925
|
}
|
|
1760
1926
|
|
|
1761
1927
|
// src/components/ChatInput.tsx
|
|
1762
|
-
var
|
|
1763
|
-
var
|
|
1928
|
+
var import_react11 = require("react");
|
|
1929
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
1764
1930
|
function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
|
|
1765
|
-
const [value, setValue] = (0,
|
|
1931
|
+
const [value, setValue] = (0, import_react11.useState)("");
|
|
1766
1932
|
const submit = () => {
|
|
1767
1933
|
const t = value.trim();
|
|
1768
1934
|
if (!t) return;
|
|
@@ -1775,18 +1941,18 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
|
|
|
1775
1941
|
submit();
|
|
1776
1942
|
}
|
|
1777
1943
|
};
|
|
1778
|
-
return /* @__PURE__ */ (0,
|
|
1779
|
-
/* @__PURE__ */ (0,
|
|
1780
|
-
streaming ? /* @__PURE__ */ (0,
|
|
1944
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "ss-w-input", children: [
|
|
1945
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
|
|
1946
|
+
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, {}) })
|
|
1781
1947
|
] });
|
|
1782
1948
|
}
|
|
1783
1949
|
|
|
1784
1950
|
// src/components/ChatPanel.tsx
|
|
1785
|
-
var
|
|
1951
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
1786
1952
|
var FULLSCREEN_QUERY = "(max-width: 639px)";
|
|
1787
1953
|
function useIsModal() {
|
|
1788
|
-
const [modal, setModal] = (0,
|
|
1789
|
-
(0,
|
|
1954
|
+
const [modal, setModal] = (0, import_react12.useState)(false);
|
|
1955
|
+
(0, import_react12.useEffect)(() => {
|
|
1790
1956
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
1791
1957
|
const mql = window.matchMedia(FULLSCREEN_QUERY);
|
|
1792
1958
|
const sync = () => setModal(mql.matches);
|
|
@@ -1799,17 +1965,17 @@ function useIsModal() {
|
|
|
1799
1965
|
function ChatPanel({ controller, ui, slip, onDashboardTap }) {
|
|
1800
1966
|
const { state, close, stop, send } = controller;
|
|
1801
1967
|
const isModal = useIsModal();
|
|
1802
|
-
(0,
|
|
1968
|
+
(0, import_react12.useEffect)(() => {
|
|
1803
1969
|
const onKey = (e) => {
|
|
1804
1970
|
if (e.key === "Escape") close();
|
|
1805
1971
|
};
|
|
1806
1972
|
window.addEventListener("keydown", onKey);
|
|
1807
1973
|
return () => window.removeEventListener("keydown", onKey);
|
|
1808
1974
|
}, [close]);
|
|
1809
|
-
return /* @__PURE__ */ (0,
|
|
1810
|
-
/* @__PURE__ */ (0,
|
|
1811
|
-
/* @__PURE__ */ (0,
|
|
1812
|
-
/* @__PURE__ */ (0,
|
|
1975
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
|
|
1976
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
|
|
1977
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(MessageList, { controller, ui, slip, onDashboardTap }),
|
|
1978
|
+
/* @__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 })
|
|
1813
1979
|
] });
|
|
1814
1980
|
}
|
|
1815
1981
|
|
|
@@ -2304,10 +2470,52 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
2304
2470
|
.ss-w-fandash-postmatch-next-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
|
2305
2471
|
.ss-w-fandash-postmatch-next-opponent { font-size: 13px; font-weight: 650; overflow-wrap: anywhere; }
|
|
2306
2472
|
.ss-w-fandash-postmatch-next-when { flex: none; font-size: 11.5px; color: var(--ss-w-faint); }
|
|
2473
|
+
|
|
2474
|
+
/* resting next: the between-matches fixture teaser -- when-label left, competition badge right */
|
|
2475
|
+
.ss-w-fandash-resting-next-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
|
2476
|
+
.ss-w-fandash-resting-next-when { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; font-weight: 650; }
|
|
2477
|
+
.ss-w-fandash-resting-next-competition { flex: none; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--ss-w-faint); }
|
|
2478
|
+
|
|
2479
|
+
/* resting standings: position figure left, W-D-L/points record right */
|
|
2480
|
+
.ss-w-fandash-resting-standings-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
|
|
2481
|
+
.ss-w-fandash-resting-standings-position { font-size: 15px; font-weight: 750; color: var(--ss-w-ink); }
|
|
2482
|
+
.ss-w-fandash-resting-standings-record { font-size: 11.5px; color: var(--ss-w-faint); text-align: right; }
|
|
2483
|
+
|
|
2484
|
+
/* resting season: top scorer / key player / season tally as stacked rows */
|
|
2485
|
+
.ss-w-fandash-resting-season-rows { display: flex; flex-direction: column; gap: 4px; }
|
|
2486
|
+
.ss-w-fandash-resting-season-row { font-size: 12.5px; color: var(--ss-w-ink); }
|
|
2487
|
+
|
|
2488
|
+
/* resting form: reuses the fixture last-five pill classes (ss-w-fandash-form / -form-pill) */
|
|
2489
|
+
.ss-w-fandash-resting-form .ss-w-fandash-form { margin-top: 2px; }
|
|
2490
|
+
|
|
2491
|
+
/* live score: reuses the post-match scoreboard classes for the team/score row, with its own
|
|
2492
|
+
meta row -- minute called out in the accent color, status and competition beside it */
|
|
2493
|
+
.ss-w-fandash-live-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 11.5px; color: var(--ss-w-faint); }
|
|
2494
|
+
.ss-w-fandash-live-minute { flex: none; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); }
|
|
2495
|
+
.ss-w-fandash-live-status { font-weight: 650; color: var(--ss-w-ink); }
|
|
2496
|
+
.ss-w-fandash-live-competition { color: var(--ss-w-faint); }
|
|
2497
|
+
|
|
2498
|
+
/* live timeline: a plain event list, the club's own events picked out in the accent color --
|
|
2499
|
+
same mechanism as the post-match goal list, plus a minute column */
|
|
2500
|
+
.ss-w-fandash-live-event-list { list-style: none; display: flex; flex-direction: column; gap: 4px; }
|
|
2501
|
+
.ss-w-fandash-live-event { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; color: var(--ss-w-faint); }
|
|
2502
|
+
.ss-w-fandash-live-event--club { color: var(--ss-w-ink); font-weight: 650; }
|
|
2503
|
+
.ss-w-fandash-live-event-minute { flex: none; font-variant-numeric: tabular-nums; color: var(--ss-w-faint); }
|
|
2504
|
+
.ss-w-fandash-live-event--club .ss-w-fandash-live-event-minute { color: var(--ss-w-accent); }
|
|
2505
|
+
.ss-w-fandash-live-event-text { min-width: 0; }
|
|
2506
|
+
|
|
2507
|
+
/* live stats: club value / stat label / opponent value, same 3-column layout as post-match
|
|
2508
|
+
stats, plus an optional momentum line underneath */
|
|
2509
|
+
.ss-w-fandash-live-stat-rows { display: flex; flex-direction: column; gap: 5px; }
|
|
2510
|
+
.ss-w-fandash-live-stat-row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px; }
|
|
2511
|
+
.ss-w-fandash-live-stat-value { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-ink); }
|
|
2512
|
+
.ss-w-fandash-live-stat-row .ss-w-fandash-live-stat-value:last-child { text-align: right; }
|
|
2513
|
+
.ss-w-fandash-live-stat-label { font-size: 10.5px; text-align: center; color: var(--ss-w-faint); text-transform: uppercase; letter-spacing: .03em; }
|
|
2514
|
+
.ss-w-fandash-live-momentum { font-size: 11.5px; font-weight: 650; color: var(--ss-w-accent); }
|
|
2307
2515
|
`;
|
|
2308
2516
|
|
|
2309
2517
|
// src/StatsWidget.tsx
|
|
2310
|
-
var
|
|
2518
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
2311
2519
|
function buildClient(props) {
|
|
2312
2520
|
try {
|
|
2313
2521
|
if ("client" in props && props.client) {
|
|
@@ -2332,11 +2540,11 @@ function injectStyles(root) {
|
|
|
2332
2540
|
function StatsWidget(props) {
|
|
2333
2541
|
const injectedClient = "client" in props ? props.client : void 0;
|
|
2334
2542
|
const cfg = props.config;
|
|
2335
|
-
const built = (0,
|
|
2336
|
-
const ui = (0,
|
|
2337
|
-
const rootRef = (0,
|
|
2543
|
+
const built = (0, import_react13.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
|
|
2544
|
+
const ui = (0, import_react13.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
|
|
2545
|
+
const rootRef = (0, import_react13.useRef)(null);
|
|
2338
2546
|
const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0);
|
|
2339
|
-
const slip = (0,
|
|
2547
|
+
const slip = (0, import_react13.useMemo)(
|
|
2340
2548
|
() => ({
|
|
2341
2549
|
onAddToSlip: props.config?.onAddToSlip,
|
|
2342
2550
|
conversationId: controller.state.conversationId ?? void 0,
|
|
@@ -2349,18 +2557,18 @@ function StatsWidget(props) {
|
|
|
2349
2557
|
}),
|
|
2350
2558
|
[props.config?.onAddToSlip, controller.state.conversationId, built]
|
|
2351
2559
|
);
|
|
2352
|
-
const onDashboardTap = (0,
|
|
2560
|
+
const onDashboardTap = (0, import_react13.useMemo)(
|
|
2353
2561
|
() => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input)) : void 0,
|
|
2354
2562
|
[built]
|
|
2355
2563
|
);
|
|
2356
|
-
(0,
|
|
2564
|
+
(0, import_react13.useEffect)(() => {
|
|
2357
2565
|
if (!built || !ui.injectStyles) return;
|
|
2358
2566
|
const node = rootRef.current?.getRootNode();
|
|
2359
2567
|
if (node) injectStyles(node);
|
|
2360
2568
|
}, [built, ui.injectStyles]);
|
|
2361
2569
|
if (!built) return null;
|
|
2362
2570
|
const attrs = themeAttrs(ui);
|
|
2363
|
-
return /* @__PURE__ */ (0,
|
|
2571
|
+
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)(
|
|
2364
2572
|
ChatFab,
|
|
2365
2573
|
{
|
|
2366
2574
|
label: ui.launcherLabel,
|