@sensiblestats/widget-react 0.25.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 +11 -0
- package/dist/index.cjs +239 -115
- 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 +201 -77
- package/dist/index.js.map +1 -1
- package/dist/styles.css +25 -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 }),
|
|
@@ -1687,85 +1732,139 @@ function RestingFormCard({ card }) {
|
|
|
1687
1732
|
] });
|
|
1688
1733
|
}
|
|
1689
1734
|
|
|
1690
|
-
// src/components/dashboard/
|
|
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,
|
|
1698
|
-
dashboard.greeting ? /* @__PURE__ */ (0,
|
|
1699
|
-
/* @__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}`)) })
|
|
1700
1793
|
] });
|
|
1701
1794
|
}
|
|
1702
1795
|
function FanDashboardCard({ card, onTap, ui }) {
|
|
1703
1796
|
switch (card.cardType) {
|
|
1704
1797
|
case "fixture":
|
|
1705
|
-
return /* @__PURE__ */ (0,
|
|
1798
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(FixtureCard, { card, onTap });
|
|
1706
1799
|
case "countdown":
|
|
1707
|
-
return /* @__PURE__ */ (0,
|
|
1800
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(CountdownCard, { card });
|
|
1708
1801
|
case "injuries":
|
|
1709
|
-
return /* @__PURE__ */ (0,
|
|
1802
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(InjuriesCard, { card });
|
|
1710
1803
|
case "reasonToBet":
|
|
1711
|
-
return /* @__PURE__ */ (0,
|
|
1804
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ReasonToBetCard, { card });
|
|
1712
1805
|
case "whereToWatch":
|
|
1713
|
-
return /* @__PURE__ */ (0,
|
|
1806
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(WhereToWatchCard, { card });
|
|
1714
1807
|
case "congestedWeek":
|
|
1715
|
-
return /* @__PURE__ */ (0,
|
|
1808
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(CongestedWeekCard, { card, onTap });
|
|
1716
1809
|
case "transfers":
|
|
1717
|
-
return /* @__PURE__ */ (0,
|
|
1810
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(TransfersCard, { card });
|
|
1718
1811
|
case "links":
|
|
1719
|
-
return /* @__PURE__ */ (0,
|
|
1812
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(LinksCard, { card });
|
|
1720
1813
|
case "postMatchResult":
|
|
1721
|
-
return /* @__PURE__ */ (0,
|
|
1814
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchResultCard, { card });
|
|
1722
1815
|
case "postMatchGoals":
|
|
1723
|
-
return /* @__PURE__ */ (0,
|
|
1816
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchGoalsCard, { card, onTap });
|
|
1724
1817
|
case "postMatchStats":
|
|
1725
|
-
return /* @__PURE__ */ (0,
|
|
1818
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchStatsCard, { card, onTap });
|
|
1726
1819
|
case "postMatchTopPerformer":
|
|
1727
|
-
return /* @__PURE__ */ (0,
|
|
1820
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchTopPerformerCard, { card, onTap });
|
|
1728
1821
|
case "postMatchNext":
|
|
1729
|
-
return /* @__PURE__ */ (0,
|
|
1822
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(PostMatchNextCard, { card });
|
|
1730
1823
|
case "restingNext":
|
|
1731
|
-
return /* @__PURE__ */ (0,
|
|
1824
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingNextCard, { card, onTap });
|
|
1732
1825
|
case "restingStandings":
|
|
1733
|
-
return /* @__PURE__ */ (0,
|
|
1826
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingStandingsCard, { card, onTap });
|
|
1734
1827
|
case "restingSeason":
|
|
1735
|
-
return /* @__PURE__ */ (0,
|
|
1828
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(RestingSeasonCard, { card, onTap });
|
|
1736
1829
|
case "restingForm":
|
|
1737
|
-
return /* @__PURE__ */ (0,
|
|
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
|
|
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,
|
|
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 }) });
|
|
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,
|
|
1753
|
-
message.text && !message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0,
|
|
1754
|
-
message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ (0,
|
|
1755
|
-
message.clubFanDashboard ? /* @__PURE__ */ (0,
|
|
1756
|
-
/* @__PURE__ */ (0,
|
|
1757
|
-
/* @__PURE__ */ (0,
|
|
1758
|
-
/* @__PURE__ */ (0,
|
|
1759
|
-
/* @__PURE__ */ (0,
|
|
1760
|
-
/* @__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 })
|
|
1761
1860
|
] });
|
|
1762
1861
|
}
|
|
1763
1862
|
|
|
1764
1863
|
// src/components/IntentChips.tsx
|
|
1765
|
-
var
|
|
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,
|
|
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,18 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
|
|
|
1775
1874
|
}
|
|
1776
1875
|
|
|
1777
1876
|
// src/components/MessageList.tsx
|
|
1778
|
-
var
|
|
1877
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
1779
1878
|
function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
1780
1879
|
const { state, send } = controller;
|
|
1781
|
-
const listRef = (0,
|
|
1782
|
-
const endRef = (0,
|
|
1783
|
-
const pinnedRef = (0,
|
|
1784
|
-
(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)(() => {
|
|
1785
1884
|
if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
|
|
1786
1885
|
}, [state.messages, state.status]);
|
|
1787
1886
|
const hasUserMessage = state.messages.some((m) => m.role === "user");
|
|
1788
1887
|
const hasDashboard = state.messages.some((m) => m.role === "assistant" && (m.greetingDashboard || m.clubFanDashboard));
|
|
1789
|
-
return /* @__PURE__ */ (0,
|
|
1888
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
1790
1889
|
"div",
|
|
1791
1890
|
{
|
|
1792
1891
|
className: "ss-w-list",
|
|
@@ -1796,7 +1895,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1796
1895
|
if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
|
|
1797
1896
|
},
|
|
1798
1897
|
children: [
|
|
1799
|
-
state.messages.map((m) => /* @__PURE__ */ (0,
|
|
1898
|
+
state.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
1800
1899
|
ChatMessage,
|
|
1801
1900
|
{
|
|
1802
1901
|
message: m,
|
|
@@ -1811,25 +1910,25 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1811
1910
|
},
|
|
1812
1911
|
m.id
|
|
1813
1912
|
)),
|
|
1814
|
-
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0,
|
|
1913
|
+
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ActionButtons, { actions: [], loading: true, onAct: () => {
|
|
1815
1914
|
} }) : null,
|
|
1816
|
-
state.status ? /* @__PURE__ */ (0,
|
|
1817
|
-
state.error ? /* @__PURE__ */ (0,
|
|
1818
|
-
/* @__PURE__ */ (0,
|
|
1819
|
-
/* @__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 })
|
|
1820
1919
|
] }) : null,
|
|
1821
|
-
!hasUserMessage && !hasDashboard ? /* @__PURE__ */ (0,
|
|
1822
|
-
/* @__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 })
|
|
1823
1922
|
]
|
|
1824
1923
|
}
|
|
1825
1924
|
);
|
|
1826
1925
|
}
|
|
1827
1926
|
|
|
1828
1927
|
// src/components/ChatInput.tsx
|
|
1829
|
-
var
|
|
1830
|
-
var
|
|
1928
|
+
var import_react11 = require("react");
|
|
1929
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
1831
1930
|
function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
|
|
1832
|
-
const [value, setValue] = (0,
|
|
1931
|
+
const [value, setValue] = (0, import_react11.useState)("");
|
|
1833
1932
|
const submit = () => {
|
|
1834
1933
|
const t = value.trim();
|
|
1835
1934
|
if (!t) return;
|
|
@@ -1842,18 +1941,18 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
|
|
|
1842
1941
|
submit();
|
|
1843
1942
|
}
|
|
1844
1943
|
};
|
|
1845
|
-
return /* @__PURE__ */ (0,
|
|
1846
|
-
/* @__PURE__ */ (0,
|
|
1847
|
-
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, {}) })
|
|
1848
1947
|
] });
|
|
1849
1948
|
}
|
|
1850
1949
|
|
|
1851
1950
|
// src/components/ChatPanel.tsx
|
|
1852
|
-
var
|
|
1951
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
1853
1952
|
var FULLSCREEN_QUERY = "(max-width: 639px)";
|
|
1854
1953
|
function useIsModal() {
|
|
1855
|
-
const [modal, setModal] = (0,
|
|
1856
|
-
(0,
|
|
1954
|
+
const [modal, setModal] = (0, import_react12.useState)(false);
|
|
1955
|
+
(0, import_react12.useEffect)(() => {
|
|
1857
1956
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
1858
1957
|
const mql = window.matchMedia(FULLSCREEN_QUERY);
|
|
1859
1958
|
const sync = () => setModal(mql.matches);
|
|
@@ -1866,17 +1965,17 @@ function useIsModal() {
|
|
|
1866
1965
|
function ChatPanel({ controller, ui, slip, onDashboardTap }) {
|
|
1867
1966
|
const { state, close, stop, send } = controller;
|
|
1868
1967
|
const isModal = useIsModal();
|
|
1869
|
-
(0,
|
|
1968
|
+
(0, import_react12.useEffect)(() => {
|
|
1870
1969
|
const onKey = (e) => {
|
|
1871
1970
|
if (e.key === "Escape") close();
|
|
1872
1971
|
};
|
|
1873
1972
|
window.addEventListener("keydown", onKey);
|
|
1874
1973
|
return () => window.removeEventListener("keydown", onKey);
|
|
1875
1974
|
}, [close]);
|
|
1876
|
-
return /* @__PURE__ */ (0,
|
|
1877
|
-
/* @__PURE__ */ (0,
|
|
1878
|
-
/* @__PURE__ */ (0,
|
|
1879
|
-
/* @__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 })
|
|
1880
1979
|
] });
|
|
1881
1980
|
}
|
|
1882
1981
|
|
|
@@ -2388,10 +2487,35 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
2388
2487
|
|
|
2389
2488
|
/* resting form: reuses the fixture last-five pill classes (ss-w-fandash-form / -form-pill) */
|
|
2390
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); }
|
|
2391
2515
|
`;
|
|
2392
2516
|
|
|
2393
2517
|
// src/StatsWidget.tsx
|
|
2394
|
-
var
|
|
2518
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
2395
2519
|
function buildClient(props) {
|
|
2396
2520
|
try {
|
|
2397
2521
|
if ("client" in props && props.client) {
|
|
@@ -2416,11 +2540,11 @@ function injectStyles(root) {
|
|
|
2416
2540
|
function StatsWidget(props) {
|
|
2417
2541
|
const injectedClient = "client" in props ? props.client : void 0;
|
|
2418
2542
|
const cfg = props.config;
|
|
2419
|
-
const built = (0,
|
|
2420
|
-
const ui = (0,
|
|
2421
|
-
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);
|
|
2422
2546
|
const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0);
|
|
2423
|
-
const slip = (0,
|
|
2547
|
+
const slip = (0, import_react13.useMemo)(
|
|
2424
2548
|
() => ({
|
|
2425
2549
|
onAddToSlip: props.config?.onAddToSlip,
|
|
2426
2550
|
conversationId: controller.state.conversationId ?? void 0,
|
|
@@ -2433,18 +2557,18 @@ function StatsWidget(props) {
|
|
|
2433
2557
|
}),
|
|
2434
2558
|
[props.config?.onAddToSlip, controller.state.conversationId, built]
|
|
2435
2559
|
);
|
|
2436
|
-
const onDashboardTap = (0,
|
|
2560
|
+
const onDashboardTap = (0, import_react13.useMemo)(
|
|
2437
2561
|
() => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input)) : void 0,
|
|
2438
2562
|
[built]
|
|
2439
2563
|
);
|
|
2440
|
-
(0,
|
|
2564
|
+
(0, import_react13.useEffect)(() => {
|
|
2441
2565
|
if (!built || !ui.injectStyles) return;
|
|
2442
2566
|
const node = rootRef.current?.getRootNode();
|
|
2443
2567
|
if (node) injectStyles(node);
|
|
2444
2568
|
}, [built, ui.injectStyles]);
|
|
2445
2569
|
if (!built) return null;
|
|
2446
2570
|
const attrs = themeAttrs(ui);
|
|
2447
|
-
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)(
|
|
2448
2572
|
ChatFab,
|
|
2449
2573
|
{
|
|
2450
2574
|
label: ui.launcherLabel,
|