@sensiblestats/widget-react 0.37.2 → 0.38.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 +12 -0
- package/dist/index.cjs +214 -60
- 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 +214 -60
- package/dist/index.js.map +1 -1
- package/dist/styles.css +8 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ var STRINGS = {
|
|
|
20
20
|
launcherLabel: "Chat with SensibleStats",
|
|
21
21
|
placeholder: "Ask about a team, player or match\u2026",
|
|
22
22
|
closeChat: "Close chat",
|
|
23
|
+
newConversation: "New conversation",
|
|
23
24
|
chatDialogLabel: "SensibleStats chat",
|
|
24
25
|
stopResponse: "Stop response",
|
|
25
26
|
sendMessage: "Send message",
|
|
@@ -92,6 +93,7 @@ var STRINGS = {
|
|
|
92
93
|
launcherLabel: "SensibleStats ile sohbet et",
|
|
93
94
|
placeholder: "Bir tak\u0131m, oyuncu veya ma\xE7 hakk\u0131nda sor\u2026",
|
|
94
95
|
closeChat: "Sohbeti kapat",
|
|
96
|
+
newConversation: "Yeni sohbet",
|
|
95
97
|
chatDialogLabel: "SensibleStats sohbet",
|
|
96
98
|
stopResponse: "Yan\u0131t\u0131 durdur",
|
|
97
99
|
sendMessage: "Mesaj g\xF6nder",
|
|
@@ -187,6 +189,7 @@ function normalizeUi(cfg) {
|
|
|
187
189
|
starters: cfg.starters ?? [],
|
|
188
190
|
injectStyles: cfg.injectStyles ?? true,
|
|
189
191
|
closeChat: strings.closeChat,
|
|
192
|
+
newConversation: strings.newConversation,
|
|
190
193
|
chatDialogLabel: strings.chatDialogLabel,
|
|
191
194
|
stopResponse: strings.stopResponse,
|
|
192
195
|
sendMessage: strings.sendMessage,
|
|
@@ -605,7 +608,7 @@ function marketOddsRowToSlipSelection(vm, row, conversationId) {
|
|
|
605
608
|
// src/reducer.ts
|
|
606
609
|
var POPUP_MAX = 160;
|
|
607
610
|
function initialState(starters) {
|
|
608
|
-
return { isOpen: false, messages: [], isStreaming: false, status: null, actionsLoading: false, starters, error: null, lastUserInput: null, lastDisplayText: null, popupText: null, conversationId: null };
|
|
611
|
+
return { isOpen: false, messages: [], isStreaming: false, status: null, actionsLoading: false, starters, configuredStarters: starters, error: null, lastUserInput: null, lastDisplayText: null, popupText: null, conversationId: null };
|
|
609
612
|
}
|
|
610
613
|
function activeTurn(state) {
|
|
611
614
|
const last = state.messages[state.messages.length - 1];
|
|
@@ -685,6 +688,19 @@ function reducer(state, action) {
|
|
|
685
688
|
}
|
|
686
689
|
case "DISMISS_POPUP":
|
|
687
690
|
return { ...state, popupText: null };
|
|
691
|
+
case "RESET":
|
|
692
|
+
return { ...initialState(state.configuredStarters), isOpen: state.isOpen };
|
|
693
|
+
case "RESTORE":
|
|
694
|
+
return {
|
|
695
|
+
...state,
|
|
696
|
+
messages: action.messages,
|
|
697
|
+
conversationId: action.conversationId,
|
|
698
|
+
isStreaming: false,
|
|
699
|
+
status: null,
|
|
700
|
+
actionsLoading: false,
|
|
701
|
+
starters: action.messages.length ? [] : state.configuredStarters,
|
|
702
|
+
error: null
|
|
703
|
+
};
|
|
688
704
|
case "EVENT":
|
|
689
705
|
return applyEvent(state, action.event);
|
|
690
706
|
case "STREAM_ERROR":
|
|
@@ -804,17 +820,82 @@ function useLiveDashboardPoll({ messages, dispatch, getLiveDashboard }) {
|
|
|
804
820
|
}, [anchorKey, messages, forceUpdate]);
|
|
805
821
|
}
|
|
806
822
|
|
|
823
|
+
// src/conversationStore.ts
|
|
824
|
+
var TTL_MS = 36e5;
|
|
825
|
+
var MAX_MESSAGES = 30;
|
|
826
|
+
function readConversation(storageKey, now) {
|
|
827
|
+
let raw;
|
|
828
|
+
try {
|
|
829
|
+
raw = window.localStorage.getItem(storageKey);
|
|
830
|
+
} catch {
|
|
831
|
+
return null;
|
|
832
|
+
}
|
|
833
|
+
if (!raw) return null;
|
|
834
|
+
let parsed;
|
|
835
|
+
try {
|
|
836
|
+
parsed = JSON.parse(raw);
|
|
837
|
+
} catch {
|
|
838
|
+
return "corrupt";
|
|
839
|
+
}
|
|
840
|
+
if (parsed?.v !== 1 || !Array.isArray(parsed.messages) || typeof parsed.updatedAt !== "number") return "corrupt";
|
|
841
|
+
if (now - parsed.updatedAt > TTL_MS) return "expired";
|
|
842
|
+
return { conversationId: parsed.conversationId ?? null, messages: parsed.messages };
|
|
843
|
+
}
|
|
844
|
+
function writeConversation(storageKey, conversationId, messages, now) {
|
|
845
|
+
let capped = messages.slice(-MAX_MESSAGES);
|
|
846
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
847
|
+
try {
|
|
848
|
+
window.localStorage.setItem(storageKey, JSON.stringify({ v: 1, updatedAt: now, conversationId, messages: capped }));
|
|
849
|
+
return;
|
|
850
|
+
} catch {
|
|
851
|
+
if (capped.length <= 1) return;
|
|
852
|
+
capped = capped.slice(Math.ceil(capped.length / 2));
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
function clearConversation(storageKey) {
|
|
857
|
+
try {
|
|
858
|
+
window.localStorage.removeItem(storageKey);
|
|
859
|
+
} catch {
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
807
863
|
// src/useChatStream.ts
|
|
864
|
+
function hasAnswerContent(t) {
|
|
865
|
+
return !!t.text || t.cards.length > 0 || t.insights.length > 0 || t.marketOdds.length > 0 || t.oddsMovement.length > 0 || !!t.greetingDashboard || !!t.clubFanDashboard;
|
|
866
|
+
}
|
|
808
867
|
var STREAM_FALLBACK_TIMEOUT_MS = 3e4;
|
|
809
|
-
function
|
|
810
|
-
|
|
811
|
-
const
|
|
812
|
-
|
|
868
|
+
function seedIdCounter(messages) {
|
|
869
|
+
let max = -1;
|
|
870
|
+
for (const m of messages) {
|
|
871
|
+
const n = Number(m.id.slice(1));
|
|
872
|
+
if (m.id[0] === "m" && Number.isFinite(n) && n > max) max = n;
|
|
873
|
+
}
|
|
874
|
+
return max + 1;
|
|
875
|
+
}
|
|
876
|
+
function useChatStream(conversation, starterTexts, greetingMessage, storageKey) {
|
|
877
|
+
const boot = useMemo(() => {
|
|
878
|
+
const payload = storageKey ? readConversation(storageKey, Date.now()) : null;
|
|
879
|
+
const restored = payload != null && payload !== "expired" && payload !== "corrupt";
|
|
880
|
+
const base = initialState(starterTexts.map(chipFromText));
|
|
881
|
+
if (restored) {
|
|
882
|
+
return { state: { ...base, messages: payload.messages, conversationId: payload.conversationId, starters: [] }, restored: true, stale: false };
|
|
883
|
+
}
|
|
884
|
+
return { state: base, restored: false, stale: payload === "expired" || payload === "corrupt" };
|
|
885
|
+
}, []);
|
|
886
|
+
const [state, dispatch] = useReducer2(reducer, boot.state);
|
|
887
|
+
const idRef = useRef2(seedIdCounter(boot.state.messages));
|
|
813
888
|
const handleRef = useRef2(null);
|
|
814
889
|
const fallbackTimerRef = useRef2(null);
|
|
815
890
|
const stateRef = useRef2(state);
|
|
816
891
|
stateRef.current = state;
|
|
817
|
-
const greetedRef = useRef2(
|
|
892
|
+
const greetedRef = useRef2(boot.restored);
|
|
893
|
+
const hydratedRef = useRef2(false);
|
|
894
|
+
const restoreSeededRef = useRef2(false);
|
|
895
|
+
if (!restoreSeededRef.current) {
|
|
896
|
+
restoreSeededRef.current = true;
|
|
897
|
+
if (boot.restored && boot.state.conversationId) conversation.conversationId = boot.state.conversationId;
|
|
898
|
+
}
|
|
818
899
|
const nextId = () => `m${idRef.current++}`;
|
|
819
900
|
const clearFallbackTimer = useCallback(() => {
|
|
820
901
|
if (fallbackTimerRef.current != null) {
|
|
@@ -870,10 +951,9 @@ function useChatStream(conversation, starterTexts, greetingMessage) {
|
|
|
870
951
|
const last = stateRef.current.lastUserInput;
|
|
871
952
|
if (last) run(last, stateRef.current.lastDisplayText ?? void 0);
|
|
872
953
|
}, [run]);
|
|
873
|
-
|
|
954
|
+
const fireGreeting = useCallback(() => {
|
|
874
955
|
const message = greetingMessage?.trim();
|
|
875
|
-
if (!message
|
|
876
|
-
greetedRef.current = true;
|
|
956
|
+
if (!message) return;
|
|
877
957
|
try {
|
|
878
958
|
handleRef.current?.cancel();
|
|
879
959
|
clearFallbackTimer();
|
|
@@ -886,6 +966,55 @@ function useChatStream(conversation, starterTexts, greetingMessage) {
|
|
|
886
966
|
dispatch({ kind: "STOP" });
|
|
887
967
|
}
|
|
888
968
|
}, [greetingMessage, conversation, consume, clearFallbackTimer, armFallbackTimer]);
|
|
969
|
+
useEffect2(() => {
|
|
970
|
+
if (greetedRef.current) return;
|
|
971
|
+
greetedRef.current = true;
|
|
972
|
+
fireGreeting();
|
|
973
|
+
}, [fireGreeting]);
|
|
974
|
+
useEffect2(() => {
|
|
975
|
+
if (boot.stale && storageKey) clearConversation(storageKey);
|
|
976
|
+
}, []);
|
|
977
|
+
useEffect2(() => {
|
|
978
|
+
if (!storageKey || state.isStreaming) return;
|
|
979
|
+
if (hydratedRef.current) {
|
|
980
|
+
hydratedRef.current = false;
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
const msgs = state.messages;
|
|
984
|
+
const persistable = msgs.filter((m, i) => {
|
|
985
|
+
if (m.role === "user") {
|
|
986
|
+
const next = msgs[i + 1];
|
|
987
|
+
return !!next && next.role === "assistant" && next.done && !next.isGreeting && hasAnswerContent(next);
|
|
988
|
+
}
|
|
989
|
+
return m.done && !m.isGreeting && hasAnswerContent(m);
|
|
990
|
+
});
|
|
991
|
+
if (persistable.length === 0) return;
|
|
992
|
+
writeConversation(storageKey, state.conversationId, persistable, Date.now());
|
|
993
|
+
}, [storageKey, state.isStreaming, state.messages, state.conversationId]);
|
|
994
|
+
useEffect2(() => {
|
|
995
|
+
if (!storageKey) return;
|
|
996
|
+
const onStorage = (e) => {
|
|
997
|
+
if (e.key !== storageKey || e.storageArea !== window.localStorage) return;
|
|
998
|
+
if (stateRef.current.isStreaming) return;
|
|
999
|
+
if (e.newValue == null) {
|
|
1000
|
+
idRef.current = 0;
|
|
1001
|
+
conversation.conversationId = void 0;
|
|
1002
|
+
hydratedRef.current = true;
|
|
1003
|
+
dispatch({ kind: "RESET" });
|
|
1004
|
+
greetedRef.current = true;
|
|
1005
|
+
fireGreeting();
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
const parsed = readConversation(storageKey, Date.now());
|
|
1009
|
+
if (!parsed || parsed === "expired" || parsed === "corrupt") return;
|
|
1010
|
+
idRef.current = seedIdCounter(parsed.messages);
|
|
1011
|
+
conversation.conversationId = parsed.conversationId ?? void 0;
|
|
1012
|
+
hydratedRef.current = true;
|
|
1013
|
+
dispatch({ kind: "RESTORE", messages: parsed.messages, conversationId: parsed.conversationId });
|
|
1014
|
+
};
|
|
1015
|
+
window.addEventListener("storage", onStorage);
|
|
1016
|
+
return () => window.removeEventListener("storage", onStorage);
|
|
1017
|
+
}, [storageKey, conversation, fireGreeting]);
|
|
889
1018
|
const open = useCallback(() => dispatch({ kind: "OPEN" }), []);
|
|
890
1019
|
const close = useCallback(() => {
|
|
891
1020
|
handleRef.current?.cancel();
|
|
@@ -904,9 +1033,20 @@ function useChatStream(conversation, starterTexts, greetingMessage) {
|
|
|
904
1033
|
else open();
|
|
905
1034
|
}, [close, open]);
|
|
906
1035
|
const dismissPopup = useCallback(() => dispatch({ kind: "DISMISS_POPUP" }), []);
|
|
1036
|
+
const reset = useCallback(() => {
|
|
1037
|
+
handleRef.current?.cancel();
|
|
1038
|
+
handleRef.current = null;
|
|
1039
|
+
clearFallbackTimer();
|
|
1040
|
+
if (storageKey) clearConversation(storageKey);
|
|
1041
|
+
idRef.current = 0;
|
|
1042
|
+
conversation.conversationId = void 0;
|
|
1043
|
+
dispatch({ kind: "RESET" });
|
|
1044
|
+
greetedRef.current = true;
|
|
1045
|
+
fireGreeting();
|
|
1046
|
+
}, [storageKey, conversation, clearFallbackTimer, fireGreeting]);
|
|
907
1047
|
const getLiveDashboard = useCallback(() => conversation.getLiveDashboard(), [conversation]);
|
|
908
1048
|
useLiveDashboardPoll({ messages: state.messages, dispatch, getLiveDashboard });
|
|
909
|
-
return { state, open, close, stop, toggle, send, retry, dismissPopup };
|
|
1049
|
+
return { state, open, close, stop, toggle, send, retry, dismissPopup, reset };
|
|
910
1050
|
}
|
|
911
1051
|
|
|
912
1052
|
// src/useLiveMonitor.ts
|
|
@@ -1251,6 +1391,9 @@ function CloseIcon() {
|
|
|
1251
1391
|
function ChevronIcon() {
|
|
1252
1392
|
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
|
|
1253
1393
|
}
|
|
1394
|
+
function NewChatIcon() {
|
|
1395
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fill: "none", d: "M12 5v14M5 12h14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) });
|
|
1396
|
+
}
|
|
1254
1397
|
var DASHBOARD_EMOJI = {
|
|
1255
1398
|
football: "\u26BD",
|
|
1256
1399
|
"goal-net": "\u{1F945}",
|
|
@@ -1319,10 +1462,11 @@ import { useEffect as useEffect9, useState as useState17 } from "react";
|
|
|
1319
1462
|
|
|
1320
1463
|
// src/components/ChatHeader.tsx
|
|
1321
1464
|
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1322
|
-
function ChatHeader({ onClose, closeLabel = "Close chat", logoUrl, headerTitle }) {
|
|
1465
|
+
function ChatHeader({ onClose, closeLabel = "Close chat", logoUrl, headerTitle, onNewConversation, canReset, newConversationLabel = "New conversation" }) {
|
|
1323
1466
|
return /* @__PURE__ */ jsxs3("header", { className: "ss-w-header", children: [
|
|
1324
1467
|
/* @__PURE__ */ jsx5(BrandMark, { logoUrl, className: "ss-w-header-mark" }),
|
|
1325
1468
|
/* @__PURE__ */ jsx5("span", { className: "ss-w-header-title", children: headerTitle ?? "SensibleStats" }),
|
|
1469
|
+
canReset && onNewConversation ? /* @__PURE__ */ jsx5("button", { type: "button", className: "ss-w-newconv", "aria-label": newConversationLabel, onClick: onNewConversation, children: /* @__PURE__ */ jsx5(NewChatIcon, {}) }) : null,
|
|
1326
1470
|
/* @__PURE__ */ jsx5("button", { type: "button", className: "ss-w-close", "aria-label": closeLabel, onClick: onClose, children: /* @__PURE__ */ jsx5(CloseIcon, {}) })
|
|
1327
1471
|
] });
|
|
1328
1472
|
}
|
|
@@ -1764,7 +1908,7 @@ function OddsRow({ vm, row, ui, slip, inSlipIds }) {
|
|
|
1764
1908
|
/* @__PURE__ */ jsx14(SlipButton, { selection, shownOdds: row.odds, marketName: vm.market, slip, ui })
|
|
1765
1909
|
] });
|
|
1766
1910
|
}
|
|
1767
|
-
function MarketOddsCard({ vm, ui, slip, monitor, inSlipIds, hideHeader }) {
|
|
1911
|
+
function MarketOddsCard({ vm, ui, slip, monitor, inSlipIds, hideHeader, pinRef }) {
|
|
1768
1912
|
const [open, setOpen] = useState9(false);
|
|
1769
1913
|
const kickoff = vm.kickoffUtc && ui?.bettingKickoff ? ui.bettingKickoff(vm.kickoffUtc) : void 0;
|
|
1770
1914
|
const hasNamedTeams = !!vm.homeTeamName && !!vm.awayTeamName;
|
|
@@ -1792,7 +1936,7 @@ function MarketOddsCard({ vm, ui, slip, monitor, inSlipIds, hideHeader }) {
|
|
|
1792
1936
|
{
|
|
1793
1937
|
ui,
|
|
1794
1938
|
monitor,
|
|
1795
|
-
pin: { kind: "market", matchId: vm.matchId, marketDeveloperName: vm.marketDeveloperName, line: vm.mainLine }
|
|
1939
|
+
pin: pinRef ?? { kind: "market", matchId: vm.matchId, marketDeveloperName: vm.marketDeveloperName, line: vm.mainLine }
|
|
1796
1940
|
}
|
|
1797
1941
|
) : null
|
|
1798
1942
|
] })
|
|
@@ -1820,9 +1964,9 @@ function MarketOddsCard({ vm, ui, slip, monitor, inSlipIds, hideHeader }) {
|
|
|
1820
1964
|
vm.disclaimer ? /* @__PURE__ */ jsx14("p", { className: "ss-w-bi-disc", children: vm.disclaimer }) : null
|
|
1821
1965
|
] });
|
|
1822
1966
|
}
|
|
1823
|
-
function MarketOddsCardList({ cards, ui, slip, monitor, inSlipIds, hideHeader }) {
|
|
1967
|
+
function MarketOddsCardList({ cards, ui, slip, monitor, inSlipIds, hideHeader, pinRef }) {
|
|
1824
1968
|
if (!cards || cards.length === 0) return null;
|
|
1825
|
-
return /* @__PURE__ */ jsx14("div", { className: "ss-w-mocards", children: cards.map((vm) => /* @__PURE__ */ jsx14(MarketOddsCard, { vm, ui, slip, monitor, inSlipIds, hideHeader }, vm.id)) });
|
|
1969
|
+
return /* @__PURE__ */ jsx14("div", { className: "ss-w-mocards", children: cards.map((vm) => /* @__PURE__ */ jsx14(MarketOddsCard, { vm, ui, slip, monitor, inSlipIds, hideHeader, pinRef }, vm.id)) });
|
|
1826
1970
|
}
|
|
1827
1971
|
|
|
1828
1972
|
// src/components/OddsMovementCard.tsx
|
|
@@ -3107,36 +3251,24 @@ function MonitorMatchCard({ match, promotedPins, monitor, ui, slip }) {
|
|
|
3107
3251
|
|
|
3108
3252
|
// src/components/MonitorTabs.tsx
|
|
3109
3253
|
import { jsx as jsx48, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
3110
|
-
function
|
|
3111
|
-
const
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
m.phase === "preMatch" ? kick && /* @__PURE__ */ jsxs42("span", { className: "ss-w-mon-pill", children: [
|
|
3118
|
-
kick.dayLabel,
|
|
3254
|
+
function OddsLiveStrip({ m, ui }) {
|
|
3255
|
+
const showScore = m.phase !== "preMatch" && typeof m.homeScore === "number";
|
|
3256
|
+
const stale = m.status === "stale";
|
|
3257
|
+
if (!showScore && !m.minuteLabel && !(m.isSuspended && !m.isSettled) && !m.isSettled && !stale) return null;
|
|
3258
|
+
return /* @__PURE__ */ jsxs42("div", { className: "ss-w-mk-live", children: [
|
|
3259
|
+
showScore && /* @__PURE__ */ jsxs42("b", { className: "ss-w-mk-live-score", children: [
|
|
3260
|
+
m.homeTeamName,
|
|
3119
3261
|
" ",
|
|
3120
|
-
kick.time
|
|
3121
|
-
] }) : typeof m.homeScore === "number" && /* @__PURE__ */ jsxs42("b", { children: [
|
|
3122
3262
|
m.homeScore,
|
|
3123
3263
|
"\u2013",
|
|
3124
|
-
m.awayScore
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
/* @__PURE__ */ jsx48("span", { className: "ss-w-mon-name", children: m.awayTeamName }),
|
|
3128
|
-
/* @__PURE__ */ jsx48(Crest2, { kind: "team", url: m.awayCrestUrl ?? void 0, name: m.awayTeamName ?? void 0 })
|
|
3264
|
+
m.awayScore,
|
|
3265
|
+
" ",
|
|
3266
|
+
m.awayTeamName
|
|
3129
3267
|
] }),
|
|
3130
3268
|
m.minuteLabel && /* @__PURE__ */ jsx48("span", { className: "ss-w-mon-min", children: m.minuteLabel }),
|
|
3131
|
-
/* @__PURE__ */
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
m.status === "stale" && /* @__PURE__ */ jsx48("span", { className: "ss-w-mon-asof", children: ui.asOf(new Date(m.asOfUtc).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })) }),
|
|
3135
|
-
(() => {
|
|
3136
|
-
const pin = monitor.pins.find((p) => p.key === m.key);
|
|
3137
|
-
return pin ? /* @__PURE__ */ jsx48(PinToggle, { ui, monitor, pin }) : null;
|
|
3138
|
-
})()
|
|
3139
|
-
] })
|
|
3269
|
+
m.isSuspended && !m.isSettled && /* @__PURE__ */ jsx48("span", { className: "ss-w-mon-susp", children: ui.suspendedLabel }),
|
|
3270
|
+
m.isSettled && /* @__PURE__ */ jsx48("span", { className: "ss-w-mon-pill", children: ui.fullTimeLabel }),
|
|
3271
|
+
stale && /* @__PURE__ */ jsx48("span", { className: "ss-w-mon-asof", children: ui.asOf(new Date(m.asOfUtc).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })) })
|
|
3140
3272
|
] });
|
|
3141
3273
|
}
|
|
3142
3274
|
function MatchesPanel({ monitor, ui, slip }) {
|
|
@@ -3169,20 +3301,25 @@ function withSinceViewMovement(vm, prev) {
|
|
|
3169
3301
|
function OddsPanel({ monitor, ui, slip }) {
|
|
3170
3302
|
const markets = (monitor.snapshot?.markets ?? []).filter((m) => monitor.pins.some((p) => p.key === m.key));
|
|
3171
3303
|
if (markets.length === 0) return /* @__PURE__ */ jsx48("div", { className: "ss-w-mon-empty", children: ui.emptyOddsTab });
|
|
3172
|
-
return /* @__PURE__ */ jsx48("div", { className: "ss-w-mon-list", children: markets.map((m) =>
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3304
|
+
return /* @__PURE__ */ jsx48("div", { className: "ss-w-mon-list", children: markets.map((m) => {
|
|
3305
|
+
const pin = monitor.pins.find((p) => p.key === m.key);
|
|
3306
|
+
return /* @__PURE__ */ jsxs42("div", { className: "ss-w-mon-oddspin", "data-settled": m.isSettled || void 0, children: [
|
|
3307
|
+
/* @__PURE__ */ jsx48(OddsLiveStrip, { m, ui }),
|
|
3308
|
+
m.card && /* @__PURE__ */ jsx48(
|
|
3309
|
+
MarketOddsCardList,
|
|
3310
|
+
{
|
|
3311
|
+
cards: [withSinceViewMovement(toMarketOddsVM(m.card), monitor.lastViewedPrices[m.key])],
|
|
3312
|
+
ui,
|
|
3313
|
+
slip,
|
|
3314
|
+
monitor,
|
|
3315
|
+
pinRef: pin ?? void 0,
|
|
3316
|
+
inSlipIds: monitor.inSlip
|
|
3317
|
+
}
|
|
3318
|
+
),
|
|
3319
|
+
m.history && /* @__PURE__ */ jsx48(OddsMovementCardList, { cards: [toOddsMovementVM(m.history)], ui }),
|
|
3320
|
+
!m.card && !m.history && /* @__PURE__ */ jsx48("div", { className: "ss-w-mon-empty-card", children: ui.oddsUnavailable })
|
|
3321
|
+
] }, m.key);
|
|
3322
|
+
}) });
|
|
3186
3323
|
}
|
|
3187
3324
|
|
|
3188
3325
|
// src/components/ChatPanel.tsx
|
|
@@ -3201,7 +3338,7 @@ function useIsModal() {
|
|
|
3201
3338
|
return modal;
|
|
3202
3339
|
}
|
|
3203
3340
|
function ChatPanel({ controller, ui, slip, onDashboardTap, monitor }) {
|
|
3204
|
-
const { state, close, stop, send } = controller;
|
|
3341
|
+
const { state, close, stop, send, reset } = controller;
|
|
3205
3342
|
const isModal = useIsModal();
|
|
3206
3343
|
useEffect9(() => {
|
|
3207
3344
|
const onKey = (e) => {
|
|
@@ -3218,7 +3355,18 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor }) {
|
|
|
3218
3355
|
if (monitor && monitor.activeTab !== "chat" && state.isStreaming) monitor.markChatActivity();
|
|
3219
3356
|
}, [monitor, state.isStreaming, state.messages.length]);
|
|
3220
3357
|
return /* @__PURE__ */ jsxs43("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
|
|
3221
|
-
/* @__PURE__ */ jsx49(
|
|
3358
|
+
/* @__PURE__ */ jsx49(
|
|
3359
|
+
ChatHeader,
|
|
3360
|
+
{
|
|
3361
|
+
onClose: close,
|
|
3362
|
+
closeLabel: ui.closeChat,
|
|
3363
|
+
logoUrl: ui.logoUrl,
|
|
3364
|
+
headerTitle: ui.headerTitle,
|
|
3365
|
+
onNewConversation: reset,
|
|
3366
|
+
canReset: state.messages.length > 0,
|
|
3367
|
+
newConversationLabel: ui.newConversation
|
|
3368
|
+
}
|
|
3369
|
+
),
|
|
3222
3370
|
monitor && /* @__PURE__ */ jsx49(
|
|
3223
3371
|
TabBar,
|
|
3224
3372
|
{
|
|
@@ -3333,8 +3481,9 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
3333
3481
|
}
|
|
3334
3482
|
.ss-w-header { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 1px solid var(--ss-w-line); }
|
|
3335
3483
|
.ss-w-header-mark { width: 18px; height: 18px; display: block; object-fit: contain; }
|
|
3336
|
-
.ss-w-header-title { font-weight: 660; }
|
|
3337
|
-
.ss-w-
|
|
3484
|
+
.ss-w-header-title { font-weight: 660; flex: 1; }
|
|
3485
|
+
.ss-w-newconv { background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 16px; display: grid; place-items: center; }
|
|
3486
|
+
.ss-w-close { background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 16px; display: grid; place-items: center; }
|
|
3338
3487
|
|
|
3339
3488
|
/* messages */
|
|
3340
3489
|
/* .ss-w-mon-list (Matches/Odds tab scrollers) shares .ss-w-list's box/scroll rules by design --
|
|
@@ -4022,7 +4171,11 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
4022
4171
|
a bare context line with nothing under it. */
|
|
4023
4172
|
.ss-w-mon-empty-card { padding: 8px 0 2px; font-size: 12px; color: var(--ss-w-faint); }
|
|
4024
4173
|
.ss-w-mon-inslip { display: inline-flex; flex: none; align-items: center; gap: 3px; font-size: 10.5px; font-weight: 700; color: var(--ss-w-pos); }
|
|
4025
|
-
|
|
4174
|
+
/* Odds-tab item is bare: the visible card is the market-odds card itself (no outer wrapper card),
|
|
4175
|
+
with an optional slim live strip above it. */
|
|
4176
|
+
.ss-w-mon-oddspin { display: flex; flex-direction: column; gap: 8px; }
|
|
4177
|
+
.ss-w-mk-live { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding: 0 2px; font-size: 11.5px; color: var(--ss-w-faint); }
|
|
4178
|
+
.ss-w-mk-live-score { color: var(--ss-w-ink); font-weight: 700; font-variant-numeric: tabular-nums; }
|
|
4026
4179
|
.ss-w-mon-oddspin[data-settled] .ss-w-mo-price,
|
|
4027
4180
|
.ss-w-mon-oddspin[data-settled] .ss-w-mon-price { text-decoration: line-through; color: var(--ss-w-faint); }
|
|
4028
4181
|
|
|
@@ -4077,7 +4230,8 @@ function StatsWidget(props) {
|
|
|
4077
4230
|
const markIntroDone = useCallback5(() => {
|
|
4078
4231
|
introDoneRef.current = true;
|
|
4079
4232
|
}, []);
|
|
4080
|
-
const
|
|
4233
|
+
const convStorageKey = `ss-w-conv:${cfg?.operatorId ?? ""}:${cfg?.userContext?.operatorUserId ?? "anon"}`;
|
|
4234
|
+
const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0, convStorageKey);
|
|
4081
4235
|
const monitor = useLiveMonitor({
|
|
4082
4236
|
getLiveMonitor: (pins) => built ? built.conversation.getLiveMonitor(pins) : Promise.resolve(null),
|
|
4083
4237
|
getMarketOdds: (matchId, dev) => built ? built.conversation.getMarketOdds(matchId, dev) : Promise.resolve(null),
|