@sensiblestats/widget-react 0.7.1 → 0.8.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 +17 -0
- package/dist/index.cjs +238 -92
- 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 +225 -79
- package/dist/index.js.map +1 -1
- package/dist/styles.css +35 -17
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -16,9 +16,15 @@ var STRINGS = {
|
|
|
16
16
|
retry: "Retry",
|
|
17
17
|
showLess: "Show less",
|
|
18
18
|
showMore: (n) => `Show ${n} more`,
|
|
19
|
+
showStats: "Show stats",
|
|
20
|
+
hideStats: "Hide stats",
|
|
19
21
|
oddsLabel: "Odds",
|
|
20
22
|
liveLabel: "Live",
|
|
21
23
|
addToSlip: "Add to slip",
|
|
24
|
+
marketPrices: "Market prices",
|
|
25
|
+
moreLines: "More lines",
|
|
26
|
+
hideLines: "Hide lines",
|
|
27
|
+
updatedLabel: "updated",
|
|
22
28
|
inSlip: "In slip",
|
|
23
29
|
slipAdded: (n) => n === void 0 ? "Added to slip" : `Added to slip (${n} selection${n === 1 ? "" : "s"})`,
|
|
24
30
|
slipDuplicate: "Already in your slip",
|
|
@@ -40,9 +46,15 @@ var STRINGS = {
|
|
|
40
46
|
retry: "Tekrar dene",
|
|
41
47
|
showLess: "Daha az g\xF6ster",
|
|
42
48
|
showMore: (n) => `${n} tane daha g\xF6ster`,
|
|
49
|
+
showStats: "\u0130statistikleri g\xF6ster",
|
|
50
|
+
hideStats: "\u0130statistikleri gizle",
|
|
43
51
|
oddsLabel: "Oran",
|
|
44
52
|
liveLabel: "Canl\u0131",
|
|
45
53
|
addToSlip: "Kupona ekle",
|
|
54
|
+
marketPrices: "Piyasa fiyatlar\u0131",
|
|
55
|
+
moreLines: "Di\u011Fer \xE7izgiler",
|
|
56
|
+
hideLines: "\xC7izgileri gizle",
|
|
57
|
+
updatedLabel: "g\xFCncellendi",
|
|
46
58
|
inSlip: "Kuponda",
|
|
47
59
|
slipAdded: (n) => n === void 0 ? "Kupona eklendi" : `Kupona eklendi (${n} se\xE7im)`,
|
|
48
60
|
slipDuplicate: "Bu se\xE7im kuponunuzda zaten var",
|
|
@@ -81,8 +93,14 @@ function normalizeUi(cfg) {
|
|
|
81
93
|
retry: strings.retry,
|
|
82
94
|
showLess: strings.showLess,
|
|
83
95
|
showMore: strings.showMore,
|
|
96
|
+
showStats: strings.showStats,
|
|
97
|
+
hideStats: strings.hideStats,
|
|
84
98
|
oddsLabel: strings.oddsLabel,
|
|
85
99
|
liveLabel: strings.liveLabel,
|
|
100
|
+
marketPrices: strings.marketPrices,
|
|
101
|
+
moreLines: strings.moreLines,
|
|
102
|
+
hideLines: strings.hideLines,
|
|
103
|
+
updatedLabel: strings.updatedLabel,
|
|
86
104
|
addToSlip: strings.addToSlip,
|
|
87
105
|
inSlip: strings.inSlip,
|
|
88
106
|
slipAdded: strings.slipAdded,
|
|
@@ -212,6 +230,47 @@ function toSlipSelection(vm, conversationId) {
|
|
|
212
230
|
if (conversationId !== void 0) selection.conversationId = conversationId;
|
|
213
231
|
return selection;
|
|
214
232
|
}
|
|
233
|
+
function toMarketOddsVM(card) {
|
|
234
|
+
const rows = Array.isArray(card.rows) ? card.rows : [];
|
|
235
|
+
const toRow = (r, i) => {
|
|
236
|
+
const selection = [str(r.selectionLabel), str(r.line)].filter(Boolean).join(" ");
|
|
237
|
+
const price = typeof r.price === "number" && Number.isFinite(r.price) ? r.price : void 0;
|
|
238
|
+
return {
|
|
239
|
+
key: `${selection}:${str(r.line) ?? ""}:${i}`,
|
|
240
|
+
selection,
|
|
241
|
+
odds: price !== void 0 ? price.toFixed(2) : "",
|
|
242
|
+
oddsDecimal: price,
|
|
243
|
+
matchOddsId: typeof r.matchOddsId === "number" ? r.matchOddsId : void 0,
|
|
244
|
+
quotedAtUtc: str(r.quotedAtUtc),
|
|
245
|
+
isMain: r.isMainLine !== false
|
|
246
|
+
};
|
|
247
|
+
};
|
|
248
|
+
const vms = rows.map(toRow).filter((r) => r.selection && r.odds);
|
|
249
|
+
const home = str(card.homeTeamName);
|
|
250
|
+
const away = str(card.awayTeamName);
|
|
251
|
+
const updated = rows.map((r) => str(r.quotedAtUtc)).filter((v) => !!v).sort();
|
|
252
|
+
return {
|
|
253
|
+
id: `${typeof card.matchId === "number" ? card.matchId : 0}:${str(card.marketDeveloperName) ?? ""}`,
|
|
254
|
+
market: str(card.marketLabel) ?? str(card.marketDeveloperName) ?? "",
|
|
255
|
+
fixture: home && away ? `${home} vs ${away}` : void 0,
|
|
256
|
+
competition: str(card.competitionName),
|
|
257
|
+
bookmaker: rows.map((r) => str(r.bookmakerName)).find(Boolean),
|
|
258
|
+
updatedAt: updated.length > 0 ? updated[updated.length - 1] : void 0,
|
|
259
|
+
isLive: rows.some((r) => r.isLive === true),
|
|
260
|
+
matchId: typeof card.matchId === "number" ? card.matchId : 0,
|
|
261
|
+
mainRows: vms.filter((r) => r.isMain),
|
|
262
|
+
moreRows: vms.filter((r) => !r.isMain),
|
|
263
|
+
disclaimer: str(card.disclaimer),
|
|
264
|
+
addToSlipEnabled: card.addToSlipEnabled === true
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
function marketOddsRowToSlipSelection(vm, row, conversationId) {
|
|
268
|
+
if (row.matchOddsId === void 0 || row.oddsDecimal === void 0) return null;
|
|
269
|
+
const selection = { matchOddsId: row.matchOddsId, matchId: vm.matchId, oddsDecimal: row.oddsDecimal };
|
|
270
|
+
if (row.quotedAtUtc !== void 0) selection.quotedAtUtc = row.quotedAtUtc;
|
|
271
|
+
if (conversationId !== void 0) selection.conversationId = conversationId;
|
|
272
|
+
return selection;
|
|
273
|
+
}
|
|
215
274
|
|
|
216
275
|
// src/reducer.ts
|
|
217
276
|
var POPUP_MAX = 160;
|
|
@@ -243,6 +302,8 @@ function applyEvent(state, event) {
|
|
|
243
302
|
return { ...state, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, actions: [...t.actions, toActionVM(event.data.action)] })) };
|
|
244
303
|
case "betting_insight":
|
|
245
304
|
return { ...state, messages: mapActive(state, (t) => ({ ...t, insights: [...t.insights, toInsightVM(event.data.insight)] })) };
|
|
305
|
+
case "market_odds":
|
|
306
|
+
return { ...state, messages: mapActive(state, (t) => ({ ...t, marketOdds: [...t.marketOdds, toMarketOddsVM(event.data.card)] })) };
|
|
246
307
|
case "intent_chip": {
|
|
247
308
|
const chip = toChipVM(event.data.intent);
|
|
248
309
|
return chip ? { ...state, starters: [...state.starters, chip] } : state;
|
|
@@ -278,11 +339,11 @@ function reducer(state, action) {
|
|
|
278
339
|
return { ...state, isOpen: false, isStreaming: false, status: null, actionsLoading: false };
|
|
279
340
|
case "USER_SENT": {
|
|
280
341
|
const user = { role: "user", id: action.userId, text: action.displayText ?? action.input.message };
|
|
281
|
-
const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], done: false };
|
|
342
|
+
const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], marketOdds: [], done: false };
|
|
282
343
|
return { ...state, messages: [...state.messages, user, assistant], isStreaming: true, status: null, actionsLoading: false, starters: [], error: null, lastUserInput: action.input, lastDisplayText: action.displayText ?? null };
|
|
283
344
|
}
|
|
284
345
|
case "GREETING_SENT": {
|
|
285
|
-
const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], done: false, isGreeting: true };
|
|
346
|
+
const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], marketOdds: [], done: false, isGreeting: true };
|
|
286
347
|
return { ...state, messages: [...state.messages, assistant], isStreaming: true, status: null, error: null };
|
|
287
348
|
}
|
|
288
349
|
case "DISMISS_POPUP":
|
|
@@ -404,7 +465,7 @@ function ChatFab({ label, greeting, popupText, onOpen, onDismissPopup, dismissLa
|
|
|
404
465
|
}
|
|
405
466
|
|
|
406
467
|
// src/components/ChatPanel.tsx
|
|
407
|
-
import { useEffect as useEffect4, useState as
|
|
468
|
+
import { useEffect as useEffect4, useState as useState6 } from "react";
|
|
408
469
|
|
|
409
470
|
// src/components/ChatHeader.tsx
|
|
410
471
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -527,13 +588,12 @@ function withTimeout(value) {
|
|
|
527
588
|
new Promise((_, reject) => setTimeout(() => reject(new Error("add-to-slip timed out")), ADD_TO_SLIP_TIMEOUT_MS))
|
|
528
589
|
]);
|
|
529
590
|
}
|
|
530
|
-
function
|
|
591
|
+
function SlipButton({ selection, shownOdds, marketName, slip, ui }) {
|
|
531
592
|
const [pending, setPending] = useState2(false);
|
|
532
593
|
const [settled, setSettled] = useState2(false);
|
|
533
594
|
const [toast, setToast] = useState2(null);
|
|
534
595
|
const dismiss = useCallback2(() => setToast(null), []);
|
|
535
596
|
const handler = slip.onAddToSlip;
|
|
536
|
-
const selection = it.addToSlipEnabled ? toSlipSelection(it, slip.conversationId) : null;
|
|
537
597
|
if (!ui || !handler || !selection) return null;
|
|
538
598
|
const onClick = async () => {
|
|
539
599
|
setPending(true);
|
|
@@ -541,11 +601,11 @@ function AddToSlipButton({ it, slip, ui }) {
|
|
|
541
601
|
const raw = await withTimeout(Promise.resolve(handler(selection)));
|
|
542
602
|
if (raw != null && !isAddToSlipResult(raw)) throw new Error("malformed add-to-slip result");
|
|
543
603
|
const result = raw ?? void 0;
|
|
544
|
-
setToast(resultToToast(result,
|
|
604
|
+
setToast(resultToToast(result, shownOdds || selection.oddsDecimal.toFixed(2), ui));
|
|
545
605
|
setSettled(isTerminal(result));
|
|
546
606
|
if (wasAdded(result) && slip.onMarketClicked) {
|
|
547
607
|
try {
|
|
548
|
-
slip.onMarketClicked(
|
|
608
|
+
slip.onMarketClicked(marketName);
|
|
549
609
|
} catch {
|
|
550
610
|
}
|
|
551
611
|
}
|
|
@@ -569,29 +629,47 @@ function AddToSlipButton({ it, slip, ui }) {
|
|
|
569
629
|
/* @__PURE__ */ jsx8(Toast, { toast, onDismiss: dismiss })
|
|
570
630
|
] });
|
|
571
631
|
}
|
|
632
|
+
function AddToSlipButton({ it, slip, ui }) {
|
|
633
|
+
const selection = it.addToSlipEnabled ? toSlipSelection(it, slip.conversationId) : null;
|
|
634
|
+
return /* @__PURE__ */ jsx8(SlipButton, { selection, shownOdds: it.odds ?? "", marketName: it.market, slip, ui });
|
|
635
|
+
}
|
|
572
636
|
|
|
573
637
|
// src/components/BettingInsightList.tsx
|
|
574
638
|
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
return /* @__PURE__ */ jsxs6("div", { className: "ss-w-bi-rows", children: [
|
|
581
|
-
shown.map((s) => /* @__PURE__ */ jsxs6("div", { className: "ss-w-bi-row", children: [
|
|
582
|
-
/* @__PURE__ */ jsx9("span", { className: "ss-w-bi-row-lbl", children: s.label }),
|
|
583
|
-
/* @__PURE__ */ jsx9("span", { className: "ss-w-bi-row-dots", "aria-hidden": "true" }),
|
|
584
|
-
/* @__PURE__ */ jsx9("span", { className: "ss-w-bi-row-num", children: s.value })
|
|
585
|
-
] }, `${s.label}:${s.value}`)),
|
|
586
|
-
overflow > 0 ? /* @__PURE__ */ jsx9("button", { type: "button", className: "ss-w-bi-more", onClick: () => setExpanded((v) => !v), children: expanded ? ui?.showLess ?? "Show less" : (ui?.showMore ?? ((n) => `Show ${n} more`))(overflow) }) : null
|
|
587
|
-
] });
|
|
639
|
+
function StatRows({ stats }) {
|
|
640
|
+
return /* @__PURE__ */ jsx9("div", { className: "ss-w-bi-rows", children: stats.map((s) => /* @__PURE__ */ jsxs6("div", { className: "ss-w-bi-row", children: [
|
|
641
|
+
/* @__PURE__ */ jsx9("span", { className: "ss-w-bi-row-lbl", children: s.label }),
|
|
642
|
+
/* @__PURE__ */ jsx9("span", { className: "ss-w-bi-row-num", children: s.value })
|
|
643
|
+
] }, `${s.label}:${s.value}`)) });
|
|
588
644
|
}
|
|
589
|
-
function ScopedStats({ scopes
|
|
645
|
+
function ScopedStats({ scopes }) {
|
|
590
646
|
const [active, setActive] = useState3(0);
|
|
591
647
|
const current = scopes[active] ?? scopes[0] ?? { key: "", label: "", stats: [] };
|
|
592
648
|
return /* @__PURE__ */ jsxs6("div", { className: "ss-w-bi-scoped", children: [
|
|
593
649
|
/* @__PURE__ */ jsx9("div", { className: "ss-w-bi-scopes", role: "group", children: scopes.map((s, i) => /* @__PURE__ */ jsx9("button", { type: "button", className: "ss-w-bi-chip", "aria-pressed": i === active, onClick: () => setActive(i), children: s.label }, s.key)) }),
|
|
594
|
-
/* @__PURE__ */ jsx9(StatRows, { stats: current.stats
|
|
650
|
+
/* @__PURE__ */ jsx9(StatRows, { stats: current.stats }, current.key)
|
|
651
|
+
] });
|
|
652
|
+
}
|
|
653
|
+
function StatsDisclosure({ it, ui }) {
|
|
654
|
+
const [open, setOpen] = useState3(false);
|
|
655
|
+
const hasScopes = !!it.scopes && it.scopes.length >= 2;
|
|
656
|
+
const hasStats = hasScopes || it.stats.length > 0;
|
|
657
|
+
if (!hasStats) return null;
|
|
658
|
+
return /* @__PURE__ */ jsxs6("div", { className: "ss-w-bi-stats", children: [
|
|
659
|
+
/* @__PURE__ */ jsxs6(
|
|
660
|
+
"button",
|
|
661
|
+
{
|
|
662
|
+
type: "button",
|
|
663
|
+
className: "ss-w-bi-toggle",
|
|
664
|
+
"aria-expanded": open,
|
|
665
|
+
onClick: () => setOpen((v) => !v),
|
|
666
|
+
children: [
|
|
667
|
+
/* @__PURE__ */ jsx9("span", { children: open ? ui?.hideStats ?? "Hide stats" : ui?.showStats ?? "Show stats" }),
|
|
668
|
+
/* @__PURE__ */ jsx9("span", { className: `ss-w-bi-chev${open ? " ss-w-bi-chev-up" : ""}`, "aria-hidden": "true" })
|
|
669
|
+
]
|
|
670
|
+
}
|
|
671
|
+
),
|
|
672
|
+
open ? hasScopes ? /* @__PURE__ */ jsx9(ScopedStats, { scopes: it.scopes }) : /* @__PURE__ */ jsx9(StatRows, { stats: it.stats }) : null
|
|
595
673
|
] });
|
|
596
674
|
}
|
|
597
675
|
function InsightCard({ it, ui, slip }) {
|
|
@@ -617,7 +695,7 @@ function InsightCard({ it, ui, slip }) {
|
|
|
617
695
|
] }) : null
|
|
618
696
|
] }),
|
|
619
697
|
it.text ? /* @__PURE__ */ jsx9("p", { className: "ss-w-bi-text", children: it.text }) : null,
|
|
620
|
-
|
|
698
|
+
/* @__PURE__ */ jsx9(StatsDisclosure, { it, ui }),
|
|
621
699
|
it.disclaimer ? /* @__PURE__ */ jsx9("p", { className: "ss-w-bi-disc", children: it.disclaimer }) : null,
|
|
622
700
|
/* @__PURE__ */ jsx9(AddToSlipButton, { it, slip, ui })
|
|
623
701
|
] });
|
|
@@ -627,38 +705,88 @@ function BettingInsightList({ insights, ui, slip }) {
|
|
|
627
705
|
return /* @__PURE__ */ jsx9("div", { className: "ss-w-binsights", children: insights.map((it) => /* @__PURE__ */ jsx9(InsightCard, { it, ui, slip }, it.id)) });
|
|
628
706
|
}
|
|
629
707
|
|
|
708
|
+
// src/components/MarketOddsCardList.tsx
|
|
709
|
+
import { useState as useState4 } from "react";
|
|
710
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
711
|
+
function OddsRow({ vm, row, ui, slip }) {
|
|
712
|
+
const selection = vm.addToSlipEnabled ? marketOddsRowToSlipSelection(vm, row, slip.conversationId) : null;
|
|
713
|
+
return /* @__PURE__ */ jsxs7("div", { className: "ss-w-mo-row", children: [
|
|
714
|
+
/* @__PURE__ */ jsx10("span", { className: "ss-w-mo-sel", children: row.selection }),
|
|
715
|
+
/* @__PURE__ */ jsx10("span", { className: "ss-w-mo-price", children: row.odds }),
|
|
716
|
+
/* @__PURE__ */ jsx10(SlipButton, { selection, shownOdds: row.odds, marketName: vm.market, slip, ui })
|
|
717
|
+
] });
|
|
718
|
+
}
|
|
719
|
+
function formatUpdated(iso) {
|
|
720
|
+
if (!iso) return void 0;
|
|
721
|
+
const date = new Date(iso);
|
|
722
|
+
if (Number.isNaN(date.getTime())) return void 0;
|
|
723
|
+
return date.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit" });
|
|
724
|
+
}
|
|
725
|
+
function MarketOddsCard({ vm, ui, slip }) {
|
|
726
|
+
const [open, setOpen] = useState4(false);
|
|
727
|
+
const updated = formatUpdated(vm.updatedAt);
|
|
728
|
+
return /* @__PURE__ */ jsxs7("div", { className: "ss-w-mocard", children: [
|
|
729
|
+
/* @__PURE__ */ jsxs7("div", { className: "ss-w-mo-head", children: [
|
|
730
|
+
/* @__PURE__ */ jsx10("span", { className: "ss-w-mo-market", children: vm.market }),
|
|
731
|
+
/* @__PURE__ */ jsx10("span", { className: "ss-w-mo-tag", children: ui?.marketPrices ?? "Market prices" })
|
|
732
|
+
] }),
|
|
733
|
+
vm.fixture || vm.competition ? /* @__PURE__ */ jsxs7("div", { className: "ss-w-mo-fixture", children: [
|
|
734
|
+
vm.fixture ? /* @__PURE__ */ jsx10("span", { children: vm.fixture }) : null,
|
|
735
|
+
vm.competition ? /* @__PURE__ */ jsx10("span", { className: "ss-w-mo-comp", children: vm.competition }) : null
|
|
736
|
+
] }) : null,
|
|
737
|
+
/* @__PURE__ */ jsxs7("div", { className: "ss-w-mo-rows", children: [
|
|
738
|
+
vm.mainRows.map((row) => /* @__PURE__ */ jsx10(OddsRow, { vm, row, ui, slip }, row.key)),
|
|
739
|
+
open ? vm.moreRows.map((row) => /* @__PURE__ */ jsx10(OddsRow, { vm, row, ui, slip }, row.key)) : null
|
|
740
|
+
] }),
|
|
741
|
+
vm.moreRows.length > 0 ? /* @__PURE__ */ jsxs7("button", { type: "button", className: "ss-w-bi-toggle", "aria-expanded": open, onClick: () => setOpen((v) => !v), children: [
|
|
742
|
+
/* @__PURE__ */ jsx10("span", { children: open ? ui?.hideLines ?? "Hide lines" : `${ui?.moreLines ?? "More lines"} (${vm.moreRows.length})` }),
|
|
743
|
+
/* @__PURE__ */ jsx10("span", { className: `ss-w-bi-chev${open ? " ss-w-bi-chev-up" : ""}`, "aria-hidden": "true" })
|
|
744
|
+
] }) : null,
|
|
745
|
+
vm.bookmaker || updated ? /* @__PURE__ */ jsxs7("div", { className: "ss-w-mo-foot", children: [
|
|
746
|
+
vm.bookmaker ? /* @__PURE__ */ jsx10("span", { children: vm.bookmaker }) : null,
|
|
747
|
+
updated ? /* @__PURE__ */ jsx10("span", { children: `${ui?.updatedLabel ?? "updated"} ${updated}` }) : null
|
|
748
|
+
] }) : null,
|
|
749
|
+
vm.disclaimer ? /* @__PURE__ */ jsx10("p", { className: "ss-w-bi-disc", children: vm.disclaimer }) : null
|
|
750
|
+
] });
|
|
751
|
+
}
|
|
752
|
+
function MarketOddsCardList({ cards, ui, slip }) {
|
|
753
|
+
if (!cards || cards.length === 0) return null;
|
|
754
|
+
return /* @__PURE__ */ jsx10("div", { className: "ss-w-mocards", children: cards.map((vm) => /* @__PURE__ */ jsx10(MarketOddsCard, { vm, ui, slip }, vm.id)) });
|
|
755
|
+
}
|
|
756
|
+
|
|
630
757
|
// src/components/ActionButtons.tsx
|
|
631
|
-
import { Fragment, jsx as
|
|
758
|
+
import { Fragment, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
632
759
|
function ActionButtons({ actions, loading, disabled, onAct }) {
|
|
633
760
|
if (actions.length === 0 && !loading) return null;
|
|
634
|
-
return /* @__PURE__ */
|
|
635
|
-
actions.map((a, i) => /* @__PURE__ */
|
|
636
|
-
actions.length === 0 && loading ? /* @__PURE__ */
|
|
637
|
-
/* @__PURE__ */
|
|
638
|
-
/* @__PURE__ */
|
|
761
|
+
return /* @__PURE__ */ jsxs8("div", { className: "ss-w-actions", children: [
|
|
762
|
+
actions.map((a, i) => /* @__PURE__ */ jsx11("button", { type: "button", className: "ss-w-abtn", disabled, onClick: () => onAct(a), children: a.label }, `${a.label}-${i}`)),
|
|
763
|
+
actions.length === 0 && loading ? /* @__PURE__ */ jsxs8(Fragment, { children: [
|
|
764
|
+
/* @__PURE__ */ jsx11("span", { className: "ss-w-abtn-shimmer" }),
|
|
765
|
+
/* @__PURE__ */ jsx11("span", { className: "ss-w-abtn-shimmer" })
|
|
639
766
|
] }) : null
|
|
640
767
|
] });
|
|
641
768
|
}
|
|
642
769
|
|
|
643
770
|
// src/components/ChatMessage.tsx
|
|
644
|
-
import { jsx as
|
|
771
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
645
772
|
function ChatMessage({ message, disabled, onAct, ui, slip }) {
|
|
646
773
|
if (message.role === "user") {
|
|
647
|
-
return /* @__PURE__ */
|
|
774
|
+
return /* @__PURE__ */ jsx12("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx12("div", { className: "ss-w-bubble", children: message.text }) });
|
|
648
775
|
}
|
|
649
|
-
return /* @__PURE__ */
|
|
650
|
-
message.text ? /* @__PURE__ */
|
|
651
|
-
/* @__PURE__ */
|
|
652
|
-
/* @__PURE__ */
|
|
653
|
-
/* @__PURE__ */
|
|
776
|
+
return /* @__PURE__ */ jsxs9("div", { className: "ss-w-msg ss-w-bot", children: [
|
|
777
|
+
message.text ? /* @__PURE__ */ jsx12(Markdown, { text: message.text }) : null,
|
|
778
|
+
/* @__PURE__ */ jsx12(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
|
|
779
|
+
/* @__PURE__ */ jsx12(BettingInsightList, { insights: message.insights, ui, slip }),
|
|
780
|
+
/* @__PURE__ */ jsx12(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
|
|
781
|
+
/* @__PURE__ */ jsx12(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
|
|
654
782
|
] });
|
|
655
783
|
}
|
|
656
784
|
|
|
657
785
|
// src/components/IntentChips.tsx
|
|
658
|
-
import { jsx as
|
|
786
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
659
787
|
function IntentChips({ chips, onPick }) {
|
|
660
788
|
if (chips.length === 0) return null;
|
|
661
|
-
return /* @__PURE__ */
|
|
789
|
+
return /* @__PURE__ */ jsx13("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx13("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
|
|
662
790
|
}
|
|
663
791
|
|
|
664
792
|
// src/scroll.ts
|
|
@@ -668,7 +796,7 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
|
|
|
668
796
|
}
|
|
669
797
|
|
|
670
798
|
// src/components/MessageList.tsx
|
|
671
|
-
import { jsx as
|
|
799
|
+
import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
672
800
|
function MessageList({ controller, ui, slip }) {
|
|
673
801
|
const { state, send } = controller;
|
|
674
802
|
const listRef = useRef2(null);
|
|
@@ -678,7 +806,7 @@ function MessageList({ controller, ui, slip }) {
|
|
|
678
806
|
if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
|
|
679
807
|
}, [state.messages, state.status]);
|
|
680
808
|
const empty = state.messages.length === 0;
|
|
681
|
-
return /* @__PURE__ */
|
|
809
|
+
return /* @__PURE__ */ jsxs10(
|
|
682
810
|
"div",
|
|
683
811
|
{
|
|
684
812
|
className: "ss-w-list",
|
|
@@ -688,7 +816,7 @@ function MessageList({ controller, ui, slip }) {
|
|
|
688
816
|
if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
|
|
689
817
|
},
|
|
690
818
|
children: [
|
|
691
|
-
state.messages.map((m) => /* @__PURE__ */
|
|
819
|
+
state.messages.map((m) => /* @__PURE__ */ jsx14(
|
|
692
820
|
ChatMessage,
|
|
693
821
|
{
|
|
694
822
|
message: m,
|
|
@@ -699,25 +827,25 @@ function MessageList({ controller, ui, slip }) {
|
|
|
699
827
|
},
|
|
700
828
|
m.id
|
|
701
829
|
)),
|
|
702
|
-
state.isStreaming && state.actionsLoading ? /* @__PURE__ */
|
|
830
|
+
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx14(ActionButtons, { actions: [], loading: true, onAct: () => {
|
|
703
831
|
} }) : null,
|
|
704
|
-
state.status ? /* @__PURE__ */
|
|
705
|
-
state.error ? /* @__PURE__ */
|
|
706
|
-
/* @__PURE__ */
|
|
707
|
-
/* @__PURE__ */
|
|
832
|
+
state.status ? /* @__PURE__ */ jsx14("div", { className: "ss-w-status", children: state.status }) : null,
|
|
833
|
+
state.error ? /* @__PURE__ */ jsxs10("div", { className: "ss-w-error", role: "alert", children: [
|
|
834
|
+
/* @__PURE__ */ jsx14("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
|
|
835
|
+
/* @__PURE__ */ jsx14("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
|
|
708
836
|
] }) : null,
|
|
709
|
-
empty ? /* @__PURE__ */
|
|
710
|
-
/* @__PURE__ */
|
|
837
|
+
empty ? /* @__PURE__ */ jsx14(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
|
|
838
|
+
/* @__PURE__ */ jsx14("div", { ref: endRef })
|
|
711
839
|
]
|
|
712
840
|
}
|
|
713
841
|
);
|
|
714
842
|
}
|
|
715
843
|
|
|
716
844
|
// src/components/ChatInput.tsx
|
|
717
|
-
import { useState as
|
|
718
|
-
import { jsx as
|
|
845
|
+
import { useState as useState5 } from "react";
|
|
846
|
+
import { jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
719
847
|
function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
|
|
720
|
-
const [value, setValue] =
|
|
848
|
+
const [value, setValue] = useState5("");
|
|
721
849
|
const submit = () => {
|
|
722
850
|
const t = value.trim();
|
|
723
851
|
if (!t) return;
|
|
@@ -730,17 +858,17 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
|
|
|
730
858
|
submit();
|
|
731
859
|
}
|
|
732
860
|
};
|
|
733
|
-
return /* @__PURE__ */
|
|
734
|
-
/* @__PURE__ */
|
|
735
|
-
streaming ? /* @__PURE__ */
|
|
861
|
+
return /* @__PURE__ */ jsxs11("div", { className: "ss-w-input", children: [
|
|
862
|
+
/* @__PURE__ */ jsx15("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
|
|
863
|
+
streaming ? /* @__PURE__ */ jsx15("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ jsx15("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx15("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ jsx15(SendIcon, {}) })
|
|
736
864
|
] });
|
|
737
865
|
}
|
|
738
866
|
|
|
739
867
|
// src/components/ChatPanel.tsx
|
|
740
|
-
import { jsx as
|
|
868
|
+
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
741
869
|
var FULLSCREEN_QUERY = "(max-width: 639px)";
|
|
742
870
|
function useIsModal() {
|
|
743
|
-
const [modal, setModal] =
|
|
871
|
+
const [modal, setModal] = useState6(false);
|
|
744
872
|
useEffect4(() => {
|
|
745
873
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
746
874
|
const mql = window.matchMedia(FULLSCREEN_QUERY);
|
|
@@ -761,10 +889,10 @@ function ChatPanel({ controller, ui, slip }) {
|
|
|
761
889
|
window.addEventListener("keydown", onKey);
|
|
762
890
|
return () => window.removeEventListener("keydown", onKey);
|
|
763
891
|
}, [close]);
|
|
764
|
-
return /* @__PURE__ */
|
|
765
|
-
/* @__PURE__ */
|
|
766
|
-
/* @__PURE__ */
|
|
767
|
-
/* @__PURE__ */
|
|
892
|
+
return /* @__PURE__ */ jsxs12("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
|
|
893
|
+
/* @__PURE__ */ jsx16(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
|
|
894
|
+
/* @__PURE__ */ jsx16(MessageList, { controller, ui, slip }),
|
|
895
|
+
/* @__PURE__ */ jsx16(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
|
|
768
896
|
] });
|
|
769
897
|
}
|
|
770
898
|
|
|
@@ -876,27 +1004,45 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
876
1004
|
.ss-w-bi-live { font-size: 8.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: #fff; background: #c0392b; border-radius: 4px; padding: 1px 5px; display: inline-flex; align-items: center; gap: 4px; flex: none; }
|
|
877
1005
|
.ss-w-bi-live-dot { width: 5px; height: 5px; border-radius: 50%; background: #fff; }
|
|
878
1006
|
.ss-w-bi-head { display: flex; align-items: flex-start; gap: 10px; }
|
|
879
|
-
.ss-w-bi-mkt { display: flex; flex-direction: column; gap:
|
|
880
|
-
.ss-w-bi-market { font-size:
|
|
881
|
-
.ss-w-bi-sel { font-size:
|
|
882
|
-
.ss-w-bi-odds { margin-left: auto; flex: none; display: flex; flex-direction: column; align-items: center; justify-content: center; background: color-mix(in srgb, var(--ss-w-accent) 12%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-accent)
|
|
883
|
-
.ss-w-bi-odds-cap { font-size:
|
|
884
|
-
.ss-w-bi-odds b { font-size:
|
|
1007
|
+
.ss-w-bi-mkt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
1008
|
+
.ss-w-bi-market { font-size: 15px; font-weight: 700; line-height: 1.25; color: var(--ss-w-ink); }
|
|
1009
|
+
.ss-w-bi-sel { font-size: 12px; line-height: 1.3; color: var(--ss-w-faint); }
|
|
1010
|
+
.ss-w-bi-odds { margin-left: auto; flex: none; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 1px; background: color-mix(in srgb, var(--ss-w-accent) 12%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 30%, var(--ss-w-line)); border-radius: 10px; padding: 4px 11px; min-width: 62px; }
|
|
1011
|
+
.ss-w-bi-odds-cap { font-size: 9px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--ss-w-faint); }
|
|
1012
|
+
.ss-w-bi-odds b { font-size: 19px; font-family: ui-monospace, monospace; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); line-height: 1.1; }
|
|
885
1013
|
.ss-w-bi-book { font-size: 9.5px; color: var(--ss-w-faint); }
|
|
886
|
-
.ss-w-bi-text { font-size: 12px; color: var(--ss-w-ink); }
|
|
1014
|
+
.ss-w-bi-text { font-size: 12px; line-height: 1.45; color: var(--ss-w-ink); }
|
|
1015
|
+
.ss-w-bi-stats { display: flex; flex-direction: column; gap: 8px; }
|
|
1016
|
+
.ss-w-bi-toggle { align-self: flex-start; display: inline-flex; align-items: center; gap: 5px; background: none; border: none; color: var(--ss-w-accent); font: inherit; font-size: 11.5px; font-weight: 600; padding: 2px 0; cursor: pointer; }
|
|
1017
|
+
.ss-w-bi-toggle:hover { text-decoration: underline; }
|
|
1018
|
+
.ss-w-bi-chev { width: 7px; height: 7px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: translateY(-2px) rotate(45deg); transition: transform .15s ease; }
|
|
1019
|
+
.ss-w-bi-chev-up { transform: translateY(1px) rotate(-135deg); }
|
|
887
1020
|
.ss-w-bi-rows { display: flex; flex-direction: column; }
|
|
888
|
-
.ss-w-bi-row { display: flex; align-items: baseline; gap:
|
|
1021
|
+
.ss-w-bi-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 5px 0; border-top: 1px solid var(--ss-w-line); }
|
|
889
1022
|
.ss-w-bi-row:first-child { border-top: none; }
|
|
890
|
-
.ss-w-bi-row-lbl { font-size:
|
|
891
|
-
.ss-w-bi-row-
|
|
892
|
-
.ss-w-bi-row-num { font-family: ui-monospace, monospace; font-weight: 700; font-variant-numeric: tabular-nums; font-size: 12.5px; flex: none; color: var(--ss-w-ink); }
|
|
893
|
-
.ss-w-bi-more { align-self: flex-start; background: none; border: none; color: var(--ss-w-accent); font: inherit; font-size: 11px; font-weight: 560; padding: 2px 0; cursor: pointer; }
|
|
894
|
-
.ss-w-bi-more:hover { text-decoration: underline; }
|
|
1023
|
+
.ss-w-bi-row-lbl { font-size: 11.5px; color: var(--ss-w-faint); min-width: 0; }
|
|
1024
|
+
.ss-w-bi-row-num { font-family: ui-monospace, monospace; font-weight: 700; font-variant-numeric: tabular-nums; font-size: 13px; flex: none; color: var(--ss-w-ink); }
|
|
895
1025
|
.ss-w-bi-disc { font-size: 10px; font-style: italic; color: var(--ss-w-faint); }
|
|
896
|
-
.ss-w-bi-scoped { display: flex; flex-direction: column; gap:
|
|
897
|
-
.ss-w-bi-scopes { display: flex; align-items: center; gap:
|
|
898
|
-
.ss-w-bi-chip { font-size: 11px; font-weight:
|
|
899
|
-
.ss-w-bi-chip[aria-pressed="true"] { color:
|
|
1026
|
+
.ss-w-bi-scoped { display: flex; flex-direction: column; gap: 8px; }
|
|
1027
|
+
.ss-w-bi-scopes { display: inline-flex; align-items: center; gap: 2px; flex-wrap: wrap; align-self: flex-start; background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 9px; padding: 2px; }
|
|
1028
|
+
.ss-w-bi-chip { font-size: 11px; font-weight: 600; border: none; border-radius: 7px; padding: 4px 10px; color: var(--ss-w-faint); background: none; cursor: pointer; }
|
|
1029
|
+
.ss-w-bi-chip[aria-pressed="true"] { color: var(--ss-w-ink); background: var(--ss-w-panel); box-shadow: 0 1px 2px rgba(0,0,0,.08); }
|
|
1030
|
+
|
|
1031
|
+
/* market odds card */
|
|
1032
|
+
.ss-w-mocards { display: flex; flex-direction: column; gap: 7px; }
|
|
1033
|
+
.ss-w-mocard { background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; border-left: 3px solid var(--ss-w-accent); padding: 9px 11px; display: flex; flex-direction: column; gap: 6px; }
|
|
1034
|
+
.ss-w-mo-head { display: flex; align-items: center; gap: 8px; }
|
|
1035
|
+
.ss-w-mo-market { font-size: 13px; font-weight: 700; color: var(--ss-w-ink); }
|
|
1036
|
+
.ss-w-mo-tag { margin-left: auto; flex: none; font-size: 9px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--ss-w-faint); border: 1px solid var(--ss-w-line); border-radius: 8px; padding: 2px 7px; }
|
|
1037
|
+
.ss-w-mo-fixture { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--ss-w-faint); min-width: 0; }
|
|
1038
|
+
.ss-w-mo-comp { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1039
|
+
.ss-w-mo-rows { display: flex; flex-direction: column; gap: 4px; }
|
|
1040
|
+
.ss-w-mo-row { display: flex; align-items: center; gap: 10px; padding: 4px 0; border-bottom: 1px dashed var(--ss-w-line); }
|
|
1041
|
+
.ss-w-mo-row:last-child { border-bottom: none; }
|
|
1042
|
+
.ss-w-mo-sel { font-size: 12.5px; font-weight: 600; color: var(--ss-w-ink); }
|
|
1043
|
+
.ss-w-mo-price { margin-left: auto; font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 14px; font-weight: 700; color: var(--ss-w-accent); }
|
|
1044
|
+
.ss-w-mo-row .ss-w-slip { margin: 0; }
|
|
1045
|
+
.ss-w-mo-foot { display: flex; align-items: center; gap: 8px; font-size: 10.5px; color: var(--ss-w-faint); }
|
|
900
1046
|
|
|
901
1047
|
/* action buttons */
|
|
902
1048
|
.ss-w-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
|
|
@@ -935,7 +1081,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
935
1081
|
`;
|
|
936
1082
|
|
|
937
1083
|
// src/StatsWidget.tsx
|
|
938
|
-
import { jsx as
|
|
1084
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
939
1085
|
function buildClient(props) {
|
|
940
1086
|
try {
|
|
941
1087
|
if ("client" in props && props.client) {
|
|
@@ -984,7 +1130,7 @@ function StatsWidget(props) {
|
|
|
984
1130
|
}, [built, ui.injectStyles]);
|
|
985
1131
|
if (!built) return null;
|
|
986
1132
|
const attrs = themeAttrs(ui);
|
|
987
|
-
return /* @__PURE__ */
|
|
1133
|
+
return /* @__PURE__ */ jsx17("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__ */ jsx17(ChatPanel, { controller, ui, slip }) : /* @__PURE__ */ jsx17(
|
|
988
1134
|
ChatFab,
|
|
989
1135
|
{
|
|
990
1136
|
label: ui.launcherLabel,
|