@sensiblestats/widget-react 0.68.0 → 0.70.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 +34 -0
- package/dist/index.cjs +1846 -962
- 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 +1788 -904
- package/dist/index.js.map +1 -1
- package/dist/styles.css +72 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/StatsWidget.tsx
|
|
2
|
-
import { useCallback as
|
|
2
|
+
import { useCallback as useCallback14, useEffect as useEffect26, useMemo as useMemo5, useRef as useRef22, useState as useState44 } from "react";
|
|
3
3
|
import { StatsWidgetClient } from "@sensiblestats/widget-sdk";
|
|
4
4
|
|
|
5
5
|
// src/kickoff.ts
|
|
@@ -34,6 +34,12 @@ var STRINGS = {
|
|
|
34
34
|
showMore: (n) => `Show ${n} more`,
|
|
35
35
|
oddsLabel: "Odds",
|
|
36
36
|
liveLabel: "Live",
|
|
37
|
+
liveCard: {
|
|
38
|
+
stale: (time) => `Prices from ${time}`,
|
|
39
|
+
suspended: "Market suspended",
|
|
40
|
+
noPrice: "No live price available",
|
|
41
|
+
fullTime: "Full time"
|
|
42
|
+
},
|
|
37
43
|
yourMarketTag: "Your market",
|
|
38
44
|
marketFloorLabel: "Market price \xB7 not a value pick",
|
|
39
45
|
addToSlip: "Add to slip",
|
|
@@ -117,6 +123,30 @@ var STRINGS = {
|
|
|
117
123
|
gamePlay: "Play",
|
|
118
124
|
gameLocked: "Locked",
|
|
119
125
|
gameVoided: "Voided",
|
|
126
|
+
presets: {
|
|
127
|
+
lang: "en",
|
|
128
|
+
setNumbers: "Set the numbers",
|
|
129
|
+
buildCombo: "Build a combo",
|
|
130
|
+
dialName: "Set the numbers and Build a combo",
|
|
131
|
+
maxPriceRow: "{count} picks, max {price}",
|
|
132
|
+
minPriceRow: "Picks from {price} up",
|
|
133
|
+
comboRow: "A combo of {legs} matches priced at about {total}",
|
|
134
|
+
maxPriceDisplay: (count, price) => `${count} picks, max ${price.toFixed(2)}`,
|
|
135
|
+
maxPriceMessage: (count, price) => `Show me ${count} picks priced at ${price.toFixed(2)} or less.`,
|
|
136
|
+
minPriceDisplay: (count, price) => `${count} picks from ${price.toFixed(2)} up`,
|
|
137
|
+
minPriceMessage: (count, price) => `Show me ${count} picks priced at ${price.toFixed(2)} or more.`,
|
|
138
|
+
comboDisplay: (legs, total) => `A combo of ${legs} priced at about ${total.toFixed(1)}`,
|
|
139
|
+
comboMessage: (legs, total) => `Build me a combo of ${legs} priced at about ${total.toFixed(1)}.`,
|
|
140
|
+
unavailable: "Not available today.",
|
|
141
|
+
send: "Send",
|
|
142
|
+
close: "Close",
|
|
143
|
+
totalLabel: "Total",
|
|
144
|
+
matchesLabel: "Matches",
|
|
145
|
+
fewerMatches: "Fewer matches",
|
|
146
|
+
moreMatches: "More matches",
|
|
147
|
+
changeNumber: (value) => `${value}, tap to change`,
|
|
148
|
+
changeCombo: "Change total or matches"
|
|
149
|
+
},
|
|
120
150
|
games: {
|
|
121
151
|
lang: "en",
|
|
122
152
|
dailyChallenge: "Daily challenge",
|
|
@@ -334,6 +364,12 @@ var STRINGS = {
|
|
|
334
364
|
showMore: (n) => `${n} tane daha g\xF6ster`,
|
|
335
365
|
oddsLabel: "Oran",
|
|
336
366
|
liveLabel: "Canl\u0131",
|
|
367
|
+
liveCard: {
|
|
368
|
+
stale: (time) => `${time} itibar\u0131yla fiyatlar`,
|
|
369
|
+
suspended: "Market ask\u0131ya al\u0131nd\u0131",
|
|
370
|
+
noPrice: "Canl\u0131 fiyat mevcut de\u011Fil",
|
|
371
|
+
fullTime: "Ma\xE7 bitti"
|
|
372
|
+
},
|
|
337
373
|
yourMarketTag: "\u0130lgilendi\u011Fin bahis",
|
|
338
374
|
marketFloorLabel: "Piyasa oran\u0131 \xB7 de\u011Fer bahsi de\u011Fil",
|
|
339
375
|
addToSlip: "Kupona ekle",
|
|
@@ -417,6 +453,30 @@ var STRINGS = {
|
|
|
417
453
|
gamePlay: "Oyna",
|
|
418
454
|
gameLocked: "Kilitli",
|
|
419
455
|
gameVoided: "\u0130ptal edildi",
|
|
456
|
+
presets: {
|
|
457
|
+
lang: "tr",
|
|
458
|
+
setNumbers: "Say\u0131lar\u0131 belirle",
|
|
459
|
+
buildCombo: "Kombin olu\u015Ftur",
|
|
460
|
+
dialName: "Say\u0131lar\u0131 belirle ve Kombin olu\u015Ftur",
|
|
461
|
+
maxPriceRow: "{count} se\xE7im, en fazla {price}",
|
|
462
|
+
minPriceRow: "{price} ve \xFCzeri se\xE7imler",
|
|
463
|
+
comboRow: "Yakla\u015F\u0131k {total} oranl\u0131 {legs} ma\xE7l\u0131k kombin",
|
|
464
|
+
maxPriceDisplay: (count, price) => `${count} se\xE7im, en fazla ${price.toFixed(2)}`,
|
|
465
|
+
maxPriceMessage: (count, price) => `${price.toFixed(2)} veya alt\u0131 oranl\u0131 ${count} se\xE7im g\xF6ster.`,
|
|
466
|
+
minPriceDisplay: (count, price) => `${count} se\xE7im, ${price.toFixed(2)} ve \xFCzeri`,
|
|
467
|
+
minPriceMessage: (count, price) => `${price.toFixed(2)} veya \xFCzeri oranl\u0131 ${count} se\xE7im g\xF6ster.`,
|
|
468
|
+
comboDisplay: (legs, total) => `${legs} ma\xE7l\u0131k kombin, yakla\u015F\u0131k ${total.toFixed(1)}`,
|
|
469
|
+
comboMessage: (legs, total) => `Yakla\u015F\u0131k ${total.toFixed(1)} oranl\u0131 ${legs} ma\xE7l\u0131k bir kombin haz\u0131rla.`,
|
|
470
|
+
unavailable: "Bug\xFCn mevcut de\u011Fil.",
|
|
471
|
+
send: "G\xF6nder",
|
|
472
|
+
close: "Kapat",
|
|
473
|
+
totalLabel: "Toplam",
|
|
474
|
+
matchesLabel: "Ma\xE7 say\u0131s\u0131",
|
|
475
|
+
fewerMatches: "Daha az ma\xE7",
|
|
476
|
+
moreMatches: "Daha fazla ma\xE7",
|
|
477
|
+
changeNumber: (value) => `${value}, de\u011Fi\u015Ftirmek i\xE7in dokun`,
|
|
478
|
+
changeCombo: "Toplam\u0131 veya ma\xE7lar\u0131 de\u011Fi\u015Ftir"
|
|
479
|
+
},
|
|
420
480
|
games: {
|
|
421
481
|
lang: "tr",
|
|
422
482
|
dailyChallenge: "G\xFCn\xFCn meydan okumas\u0131",
|
|
@@ -635,6 +695,7 @@ function normalizeUi(cfg) {
|
|
|
635
695
|
accent: cfg.accent,
|
|
636
696
|
logoUrl: cfg.logoUrl,
|
|
637
697
|
headerTitle: cfg.headerTitle,
|
|
698
|
+
locale: cfg.locale ?? "en",
|
|
638
699
|
rawBettingLabels: isEnglishLocale(cfg.locale),
|
|
639
700
|
cta: cfg.cta,
|
|
640
701
|
surface: cfg.surface,
|
|
@@ -741,7 +802,9 @@ function normalizeUi(cfg) {
|
|
|
741
802
|
gamePlay: strings.gamePlay,
|
|
742
803
|
gameLocked: strings.gameLocked,
|
|
743
804
|
gameVoided: strings.gameVoided,
|
|
744
|
-
games: strings.games
|
|
805
|
+
games: strings.games,
|
|
806
|
+
presets: strings.presets,
|
|
807
|
+
liveCard: strings.liveCard
|
|
745
808
|
};
|
|
746
809
|
}
|
|
747
810
|
function toClientOptions(cfg) {
|
|
@@ -753,708 +816,725 @@ function toUserContext(cfg) {
|
|
|
753
816
|
return cfg.userContext && Object.keys(cfg.userContext).length > 0 ? cfg.userContext : void 0;
|
|
754
817
|
}
|
|
755
818
|
|
|
756
|
-
// src/
|
|
757
|
-
var
|
|
758
|
-
var
|
|
759
|
-
var
|
|
760
|
-
var
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
if (
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
const
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
819
|
+
// src/chart.ts
|
|
820
|
+
var CHART_VIEW_W = 320;
|
|
821
|
+
var CHART_VIEW_H = 140;
|
|
822
|
+
var PLOT_X0 = 40;
|
|
823
|
+
var PLOT_X1 = 312;
|
|
824
|
+
var PLOT_Y0 = 10;
|
|
825
|
+
var PLOT_Y1 = 110;
|
|
826
|
+
var MARKER_MAX_POINTS = 8;
|
|
827
|
+
var FLAT_DOMAIN_PAD = 0.05;
|
|
828
|
+
var round2 = (n) => Math.round(n * 100) / 100;
|
|
829
|
+
function projectSeries(samples) {
|
|
830
|
+
if (samples.length < 2) return null;
|
|
831
|
+
const tMin = samples[0].atMs;
|
|
832
|
+
const tMax = samples[samples.length - 1].atMs;
|
|
833
|
+
if (tMax <= tMin) return null;
|
|
834
|
+
const prices = samples.map((s) => s.price);
|
|
835
|
+
const pMin = Math.min(...prices);
|
|
836
|
+
const pMax = Math.max(...prices);
|
|
837
|
+
let yMin;
|
|
838
|
+
let yMax;
|
|
839
|
+
if (pMax === pMin) {
|
|
840
|
+
yMin = pMin - FLAT_DOMAIN_PAD;
|
|
841
|
+
yMax = pMax + FLAT_DOMAIN_PAD;
|
|
842
|
+
} else {
|
|
843
|
+
const pad = (pMax - pMin) * 0.1;
|
|
844
|
+
yMin = pMin - pad;
|
|
845
|
+
yMax = pMax + pad;
|
|
846
|
+
}
|
|
847
|
+
const x = (atMs) => round2(PLOT_X0 + (atMs - tMin) / (tMax - tMin) * (PLOT_X1 - PLOT_X0));
|
|
848
|
+
const y = (price) => round2(PLOT_Y1 - (price - yMin) / (yMax - yMin) * (PLOT_Y1 - PLOT_Y0));
|
|
849
|
+
const projected = samples.map((s) => ({ x: x(s.atMs), y: y(s.price) }));
|
|
850
|
+
const path = projected.map((p, i) => `${i === 0 ? "M" : "L"}${p.x} ${p.y}`).join(" ");
|
|
851
|
+
return {
|
|
852
|
+
path,
|
|
853
|
+
markers: samples.length <= MARKER_MAX_POINTS ? projected : [],
|
|
854
|
+
// The real extremes, never the padded bounds: a gridline label is a price, and the padded
|
|
855
|
+
// bounds are prices nobody was ever offered. A flat series has one real extreme, not two —
|
|
856
|
+
// two identical gridlines would collide on both React key and rendered text.
|
|
857
|
+
gridlines: pMax === pMin ? [{ y: y(pMax), price: pMax }] : [
|
|
858
|
+
{ y: y(pMax), price: pMax },
|
|
859
|
+
{ y: y(pMin), price: pMin }
|
|
860
|
+
],
|
|
861
|
+
viewBox: `0 0 ${CHART_VIEW_W} ${CHART_VIEW_H}`,
|
|
862
|
+
startMs: tMin,
|
|
863
|
+
endMs: tMax
|
|
864
|
+
};
|
|
780
865
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
return
|
|
866
|
+
|
|
867
|
+
// src/model.ts
|
|
868
|
+
function str(v) {
|
|
869
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
785
870
|
}
|
|
786
|
-
function
|
|
787
|
-
|
|
788
|
-
switch (result.status) {
|
|
789
|
-
case "added":
|
|
790
|
-
case "duplicate":
|
|
791
|
-
return result.status;
|
|
792
|
-
case "price_changed":
|
|
793
|
-
return result.accepted ? "added" : "price_changed";
|
|
794
|
-
case "rejected":
|
|
795
|
-
return result.reason;
|
|
796
|
-
}
|
|
871
|
+
function num(v) {
|
|
872
|
+
return typeof v === "number" && Number.isFinite(v) ? String(v) : void 0;
|
|
797
873
|
}
|
|
798
|
-
function
|
|
799
|
-
if (!
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
case "price_changed":
|
|
806
|
-
return result.accepted ? { kind: "success", text: ui.slipPriceChanged(shownOdds, result.newOddsDecimal.toFixed(2)) } : { kind: "error", text: ui.slipPriceNotAccepted(shownOdds, result.newOddsDecimal.toFixed(2)) };
|
|
807
|
-
case "rejected":
|
|
808
|
-
if (result.reason === "login_required") return { kind: "error", text: ui.slipLoginRequired };
|
|
809
|
-
if (result.reason === "suspended") return { kind: "error", text: ui.slipSuspended };
|
|
810
|
-
return { kind: "error", text: result.message ?? ui.slipFailed };
|
|
874
|
+
function readStatCells(raw) {
|
|
875
|
+
if (!Array.isArray(raw)) return [];
|
|
876
|
+
const cells = [];
|
|
877
|
+
for (const entry of raw) {
|
|
878
|
+
const label = str(entry.label);
|
|
879
|
+
const value = num(entry.value) ?? str(entry.value);
|
|
880
|
+
if (label !== void 0 && value !== void 0) cells.push({ label, value });
|
|
811
881
|
}
|
|
882
|
+
return cells;
|
|
812
883
|
}
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
var SS_LOGO_DATA_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAjoAAAI6CAYAAAAuSOLAAAAAAXNSR0IArs4c6QAAAJplWElmTU0AKgAAAAgAAwESAAMAAAABAAEAAAExAAIAAAA9AAAAModpAAQAAAABAAAAcAAAAABBZG9iZSBQaG90b3Nob3AgMjYuMTAgKDIwMjUwNzMwLm0uMzE2NiAwN2IxMjM0KSAgKE1hY2ludG9zaCkAAAADoAEAAwAAAAEAAQAAoAIABAAAAAEAAAI6oAMABAAAAAEAAAI6AAAAACwGtVwAAAQwaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA2LjAuMCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgICAgICAgICB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx4bXBNTTpEb2N1bWVudElEPnhtcC5kaWQ6QzBGRTM5NjM5NEJEMTFGMEJGNjZEMjUyN0U0MzU5RUM8L3htcE1NOkRvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpJbnN0YW5jZUlEPnhtcC5paWQ6QzBGRTM5NjI5NEJEMTFGMEJGNjZEMjUyN0U0MzU5RUM8L3htcE1NOkluc3RhbmNlSUQ+CiAgICAgICAgIDx4bXBNTTpEZXJpdmVkRnJvbSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgIDxzdFJlZjppbnN0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";
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
884
|
+
function toCardVM(card) {
|
|
885
|
+
const entityType = str(card.entityType)?.toLowerCase();
|
|
886
|
+
const kind = entityType === "player" ? "player" : entityType === "team" ? "team" : "generic";
|
|
887
|
+
const scopes = Array.isArray(card.scopes) ? card.scopes.map((s) => {
|
|
888
|
+
const label = str(s.label);
|
|
889
|
+
if (!label) return null;
|
|
890
|
+
return {
|
|
891
|
+
competitionId: typeof s.competitionId === "number" ? s.competitionId : null,
|
|
892
|
+
label,
|
|
893
|
+
image: str(s.imagePath),
|
|
894
|
+
stats: readStatCells(s.stats)
|
|
895
|
+
};
|
|
896
|
+
}).filter((s) => s !== null) : [];
|
|
897
|
+
return {
|
|
898
|
+
id: String(card.entityId),
|
|
899
|
+
kind,
|
|
900
|
+
name: card.canonicalName,
|
|
901
|
+
image: str(card.imagePath) ?? str(card.imageUrl) ?? str(card.image_url) ?? str(card.logoUrl) ?? str(card.logo),
|
|
902
|
+
sub: str(card.subtitle) ?? str(card.teamName) ?? str(card.leagueName) ?? str(card.position),
|
|
903
|
+
season: str(card.seasonName),
|
|
904
|
+
scopes
|
|
905
|
+
};
|
|
830
906
|
}
|
|
831
|
-
function
|
|
832
|
-
|
|
907
|
+
function toActionVM(action) {
|
|
908
|
+
const label = str(action.actionLabel) ?? str(action.label) ?? str(action.text) ?? str(action.title) ?? "Continue";
|
|
909
|
+
const actionId = str(action.actionId) ?? str(action.id);
|
|
910
|
+
const actionRef = str(action.actionRef);
|
|
911
|
+
const message = str(action.actionValue) ?? str(action.message) ?? str(action.query) ?? label;
|
|
912
|
+
return { label, actionId, actionRef, message };
|
|
833
913
|
}
|
|
834
|
-
function
|
|
835
|
-
|
|
914
|
+
function toChipVM(intent) {
|
|
915
|
+
const text = str(intent.text) ?? str(intent.label) ?? str(intent.title);
|
|
916
|
+
return text ? { text } : null;
|
|
836
917
|
}
|
|
837
|
-
function
|
|
838
|
-
return
|
|
918
|
+
function chipFromText(text) {
|
|
919
|
+
return { text };
|
|
839
920
|
}
|
|
840
|
-
function
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1.6" }),
|
|
844
|
-
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "19", r: "1.6" })
|
|
845
|
-
] }) });
|
|
921
|
+
function joinSelection(label, line) {
|
|
922
|
+
if (label && line && label.includes(line)) return label;
|
|
923
|
+
return [label, line].filter(Boolean).join(" ") || void 0;
|
|
846
924
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
925
|
+
function toInsightVM(insight) {
|
|
926
|
+
const market = str(insight.marketLabel) ?? str(insight.marketDeveloperName) ?? "";
|
|
927
|
+
const selectionLabel = str(insight.selectionLabel);
|
|
928
|
+
const selectionLine = str(insight.line);
|
|
929
|
+
const selection = joinSelection(selectionLabel, selectionLine);
|
|
930
|
+
const value = insight.odds?.value;
|
|
931
|
+
const odds = typeof value === "number" && Number.isFinite(value) ? value.toFixed(2) : void 0;
|
|
932
|
+
const oddsValue = insight.odds?.value;
|
|
933
|
+
const matchOddsId = typeof insight.odds?.matchOddsId === "number" ? insight.odds.matchOddsId : void 0;
|
|
934
|
+
const home = str(insight.homeTeamName);
|
|
935
|
+
const away = str(insight.awayTeamName);
|
|
936
|
+
const fixture = home && away ? `${home} vs ${away}` : void 0;
|
|
937
|
+
const competition = str(insight.competitionName);
|
|
938
|
+
const previousValue = insight.odds?.previousValue;
|
|
939
|
+
const previous = typeof previousValue === "number" && Number.isFinite(previousValue) ? previousValue : void 0;
|
|
940
|
+
const direction = insight.odds?.movementDirection === "shortened" || insight.odds?.movementDirection === "drifted" ? insight.odds.movementDirection : void 0;
|
|
941
|
+
return {
|
|
942
|
+
id: str(insight.insightId) ?? `${str(insight.ruleId) ?? "insight"}:${num(insight.matchId) ?? ""}`,
|
|
943
|
+
market,
|
|
944
|
+
selection,
|
|
945
|
+
odds,
|
|
946
|
+
// The wire payload carries bookmakerId (not a name); render a name only if one is present.
|
|
947
|
+
bookmaker: str(insight.odds?.bookmakerName),
|
|
948
|
+
isLive: insight.odds?.isLive === true,
|
|
949
|
+
phase: str(insight.phase),
|
|
950
|
+
fixture,
|
|
951
|
+
competition,
|
|
952
|
+
text: str(insight.text) ?? "",
|
|
953
|
+
disclaimer: str(insight.disclaimer),
|
|
954
|
+
matchId: typeof insight.matchId === "number" ? insight.matchId : 0,
|
|
955
|
+
matchOddsId,
|
|
956
|
+
marketDeveloperName: str(insight.marketDeveloperName),
|
|
957
|
+
selectionLabel: str(insight.selectionLabel),
|
|
958
|
+
line: str(insight.line),
|
|
959
|
+
addToSlipEnabled: insight.addToSlipEnabled === true,
|
|
960
|
+
pinEnabled: insight.pinEnabled === true,
|
|
961
|
+
isMarketFloor: insight.isMarketFloor === true,
|
|
962
|
+
oddsDecimal: typeof oddsValue === "number" && Number.isFinite(oddsValue) ? oddsValue : void 0,
|
|
963
|
+
quotedAtUtc: str(insight.odds?.quotedAtUtc),
|
|
964
|
+
previousOdds: previous !== void 0 && direction !== void 0 ? previous.toFixed(2) : void 0,
|
|
965
|
+
movementDirection: previous !== void 0 && direction !== void 0 ? direction : void 0,
|
|
966
|
+
homeTeamName: home,
|
|
967
|
+
awayTeamName: away,
|
|
968
|
+
homeCrest: str(insight.homeTeamImagePath),
|
|
969
|
+
awayCrest: str(insight.awayTeamImagePath),
|
|
970
|
+
competitionCrest: str(insight.competitionImagePath),
|
|
971
|
+
kickoffUtc: str(insight.kickoffUtc),
|
|
972
|
+
kickoffLabel: str(insight.kickoffLabel)
|
|
973
|
+
};
|
|
865
974
|
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
975
|
+
function toSlipSelection(vm, conversationId) {
|
|
976
|
+
if (vm.matchOddsId === void 0 || vm.oddsDecimal === void 0) return null;
|
|
977
|
+
const selection = {
|
|
978
|
+
matchOddsId: vm.matchOddsId,
|
|
979
|
+
matchId: vm.matchId,
|
|
980
|
+
oddsDecimal: vm.oddsDecimal
|
|
981
|
+
};
|
|
982
|
+
if (vm.quotedAtUtc !== void 0) selection.quotedAtUtc = vm.quotedAtUtc;
|
|
983
|
+
if (vm.id !== void 0) selection.insightId = vm.id;
|
|
984
|
+
if (conversationId !== void 0) selection.conversationId = conversationId;
|
|
985
|
+
return selection;
|
|
874
986
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
if (!menuOpen) return;
|
|
898
|
-
const onPointer = (e) => {
|
|
899
|
-
if (!wrapRef.current?.contains(e.target)) setMenuOpen(false);
|
|
900
|
-
};
|
|
901
|
-
const onKey = (e) => {
|
|
902
|
-
if (e.key === "Escape") {
|
|
903
|
-
setMenuOpen(false);
|
|
904
|
-
kebabRef.current?.focus();
|
|
905
|
-
}
|
|
906
|
-
};
|
|
907
|
-
document.addEventListener("mousedown", onPointer);
|
|
908
|
-
document.addEventListener("keydown", onKey);
|
|
909
|
-
return () => {
|
|
910
|
-
document.removeEventListener("mousedown", onPointer);
|
|
911
|
-
document.removeEventListener("keydown", onKey);
|
|
987
|
+
function toMarketOddsVM(card) {
|
|
988
|
+
const rows = Array.isArray(card.rows) ? card.rows : [];
|
|
989
|
+
const toRow = (r, i) => {
|
|
990
|
+
const selection = joinSelection(str(r.selectionLabel), str(r.line)) ?? "";
|
|
991
|
+
const price = typeof r.price === "number" && Number.isFinite(r.price) ? r.price : void 0;
|
|
992
|
+
const previous = typeof r.previousPrice === "number" && Number.isFinite(r.previousPrice) ? r.previousPrice : void 0;
|
|
993
|
+
const direction = r.movementDirection === "shortened" || r.movementDirection === "drifted" ? r.movementDirection : void 0;
|
|
994
|
+
return {
|
|
995
|
+
key: `${selection}:${str(r.line) ?? ""}:${i}`,
|
|
996
|
+
selection,
|
|
997
|
+
selectionLabel: str(r.selectionLabel),
|
|
998
|
+
line: str(r.line),
|
|
999
|
+
odds: price !== void 0 ? price.toFixed(2) : "",
|
|
1000
|
+
oddsDecimal: price,
|
|
1001
|
+
matchOddsId: typeof r.matchOddsId === "number" ? r.matchOddsId : void 0,
|
|
1002
|
+
quotedAtUtc: str(r.quotedAtUtc),
|
|
1003
|
+
isMain: r.isMainLine !== false,
|
|
1004
|
+
// === true, not !== false: a server that has not shipped the field yet leaves it undefined,
|
|
1005
|
+
// and undefined must read as open rather than hiding every price on the card.
|
|
1006
|
+
isSuspended: r.isSuspended === true,
|
|
1007
|
+
previousOdds: previous !== void 0 && direction !== void 0 ? previous.toFixed(2) : void 0,
|
|
1008
|
+
movementDirection: previous !== void 0 && direction !== void 0 ? direction : void 0
|
|
912
1009
|
};
|
|
913
|
-
}, [menuOpen]);
|
|
914
|
-
const runNewConversation = () => {
|
|
915
|
-
setMenuOpen(false);
|
|
916
|
-
onNewConversation?.();
|
|
917
1010
|
};
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1011
|
+
const vms = rows.map(toRow).filter((r) => r.selection && r.odds && !r.isSuspended);
|
|
1012
|
+
const home = str(card.homeTeamName);
|
|
1013
|
+
const away = str(card.awayTeamName);
|
|
1014
|
+
const updated = rows.map((r) => str(r.quotedAtUtc)).filter((v) => !!v).sort();
|
|
1015
|
+
return {
|
|
1016
|
+
id: `${typeof card.matchId === "number" ? card.matchId : 0}:${str(card.marketDeveloperName) ?? ""}`,
|
|
1017
|
+
market: str(card.marketLabel) ?? str(card.marketDeveloperName) ?? "",
|
|
1018
|
+
fixture: home && away ? `${home} vs ${away}` : void 0,
|
|
1019
|
+
homeTeamName: home,
|
|
1020
|
+
awayTeamName: away,
|
|
1021
|
+
homeCrest: str(card.homeTeamImagePath),
|
|
1022
|
+
awayCrest: str(card.awayTeamImagePath),
|
|
1023
|
+
competitionCrest: str(card.competitionImagePath),
|
|
1024
|
+
competition: str(card.competitionName),
|
|
1025
|
+
kickoffUtc: str(card.kickoffUtc),
|
|
1026
|
+
kickoffLabel: str(card.kickoffLabel),
|
|
1027
|
+
bookmaker: rows.map((r) => str(r.bookmakerName)).find(Boolean),
|
|
1028
|
+
updatedAt: updated.length > 0 ? updated[updated.length - 1] : void 0,
|
|
1029
|
+
isLive: rows.some((r) => r.isLive === true),
|
|
1030
|
+
matchId: typeof card.matchId === "number" ? card.matchId : 0,
|
|
1031
|
+
mainRows: vms.filter((r) => r.isMain),
|
|
1032
|
+
moreRows: vms.filter((r) => !r.isMain),
|
|
1033
|
+
disclaimer: str(card.disclaimer),
|
|
1034
|
+
addToSlipEnabled: card.addToSlipEnabled === true,
|
|
1035
|
+
marketDeveloperName: str(card.marketDeveloperName),
|
|
1036
|
+
mainLine: str(card.mainLine),
|
|
1037
|
+
pinEnabled: card.pinEnabled === true,
|
|
1038
|
+
statText: card.insight && typeof card.insight === "object" ? str(card.insight.text) : void 0,
|
|
1039
|
+
tag: card.tag === "your_market" ? "your_market" : void 0,
|
|
1040
|
+
live: card.live ?? null
|
|
1041
|
+
};
|
|
949
1042
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
1043
|
+
function toOddsMovementVM(card) {
|
|
1044
|
+
const raw = Array.isArray(card.points) ? card.points : [];
|
|
1045
|
+
const samples = [];
|
|
1046
|
+
for (const p of raw) {
|
|
1047
|
+
const price = typeof p.price === "number" && Number.isFinite(p.price) ? p.price : void 0;
|
|
1048
|
+
const atMs = typeof p.atUtc === "string" ? Date.parse(p.atUtc) : NaN;
|
|
1049
|
+
if (price === void 0 || Number.isNaN(atMs)) continue;
|
|
1050
|
+
samples.push({ atMs, price });
|
|
1051
|
+
}
|
|
1052
|
+
samples.sort((a, b) => a.atMs - b.atMs);
|
|
1053
|
+
const asNumber = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
1054
|
+
const opening = asNumber(card.openingPrice);
|
|
1055
|
+
const current = asNumber(card.currentPrice);
|
|
1056
|
+
const home = str(card.homeTeamName);
|
|
1057
|
+
const away = str(card.awayTeamName);
|
|
1058
|
+
const selection = joinSelection(str(card.selectionLabel), str(card.line)) ?? "";
|
|
1059
|
+
const bookmakerId = typeof card.bookmakerId === "number" ? card.bookmakerId : "";
|
|
1060
|
+
return {
|
|
1061
|
+
id: `${typeof card.matchId === "number" ? card.matchId : 0}:${str(card.marketDeveloperName) ?? ""}:${selection}:${bookmakerId}`,
|
|
1062
|
+
matchId: typeof card.matchId === "number" ? card.matchId : 0,
|
|
1063
|
+
market: str(card.marketLabel) ?? str(card.marketDeveloperName) ?? "",
|
|
1064
|
+
marketDeveloperName: str(card.marketDeveloperName),
|
|
1065
|
+
selection,
|
|
1066
|
+
selectionLabel: str(card.selectionLabel),
|
|
1067
|
+
line: str(card.line),
|
|
1068
|
+
fixture: home && away ? `${home} vs ${away}` : void 0,
|
|
1069
|
+
competition: str(card.competitionName),
|
|
1070
|
+
bookmaker: str(card.bookmakerName),
|
|
1071
|
+
opening: opening !== void 0 ? opening.toFixed(2) : "",
|
|
1072
|
+
current: current !== void 0 ? current.toFixed(2) : "",
|
|
1073
|
+
direction: card.movementDirection === "shortened" || card.movementDirection === "drifted" ? card.movementDirection : void 0,
|
|
1074
|
+
windowHours: asNumber(card.windowHours) ?? 0,
|
|
1075
|
+
excludedCount: asNumber(card.excludedSuspendedCount) ?? 0,
|
|
1076
|
+
isSuspended: card.isSuspended === true,
|
|
1077
|
+
disclaimer: str(card.disclaimer),
|
|
1078
|
+
geometry: projectSeries(samples),
|
|
1079
|
+
observationCount: samples.length,
|
|
1080
|
+
pinEnabled: card.pinEnabled === true,
|
|
1081
|
+
matchOddsId: typeof card.matchOddsId === "number" ? card.matchOddsId : void 0
|
|
1082
|
+
};
|
|
960
1083
|
}
|
|
961
|
-
function
|
|
962
|
-
|
|
963
|
-
|
|
1084
|
+
function marketOddsRowToSlipSelection(vm, row, conversationId) {
|
|
1085
|
+
if (row.matchOddsId === void 0 || row.oddsDecimal === void 0) return null;
|
|
1086
|
+
const selection = { matchOddsId: row.matchOddsId, matchId: vm.matchId, oddsDecimal: row.oddsDecimal };
|
|
1087
|
+
if (row.quotedAtUtc !== void 0) selection.quotedAtUtc = row.quotedAtUtc;
|
|
1088
|
+
if (conversationId !== void 0) selection.conversationId = conversationId;
|
|
1089
|
+
return selection;
|
|
964
1090
|
}
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
968
|
-
const m = messages[i];
|
|
969
|
-
if (!m || m.role !== "assistant" || !m.isGreeting) continue;
|
|
970
|
-
const dashboard = m.greetingDashboard;
|
|
971
|
-
if (!dashboard) return [];
|
|
972
|
-
const asks = [];
|
|
973
|
-
dashboard.tiles.forEach((tile, index) => {
|
|
974
|
-
const label = tile.subtitle?.trim();
|
|
975
|
-
if (asks.length < MAX_BUBBLE_ASKS && isPriceTile(tile) && label) asks.push({ tile, index, position: asks.length, layout: dashboard.layout, label });
|
|
976
|
-
});
|
|
977
|
-
return asks;
|
|
978
|
-
}
|
|
979
|
-
return [];
|
|
1091
|
+
function finite(v) {
|
|
1092
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
980
1093
|
}
|
|
981
|
-
function
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
const
|
|
986
|
-
|
|
987
|
-
|
|
1094
|
+
function toComboLegVM(leg) {
|
|
1095
|
+
if (!leg || typeof leg !== "object") return null;
|
|
1096
|
+
const price = finite(leg.price);
|
|
1097
|
+
const matchOddsId = finite(leg.matchOddsId);
|
|
1098
|
+
const matchId = finite(leg.matchId);
|
|
1099
|
+
const home = str(leg.homeTeamName);
|
|
1100
|
+
const away = str(leg.awayTeamName);
|
|
1101
|
+
const selectionLabel = str(leg.selectionLabel);
|
|
1102
|
+
const insight = leg.insight && typeof leg.insight === "object" ? leg.insight : void 0;
|
|
1103
|
+
const insightId = str(insight?.insightId);
|
|
1104
|
+
const statText = str(insight?.text);
|
|
1105
|
+
if (price === void 0 || matchOddsId === void 0 || matchId === void 0 || !home || !away || !selectionLabel || !insightId || !statText) return null;
|
|
1106
|
+
const line = str(leg.line);
|
|
1107
|
+
return {
|
|
1108
|
+
key: `${matchOddsId}`,
|
|
1109
|
+
matchId,
|
|
1110
|
+
homeTeamName: home,
|
|
1111
|
+
awayTeamName: away,
|
|
1112
|
+
homeCrest: str(leg.homeTeamImagePath),
|
|
1113
|
+
awayCrest: str(leg.awayTeamImagePath),
|
|
1114
|
+
competition: str(leg.competitionName),
|
|
1115
|
+
kickoffUtc: str(leg.kickoffUtc),
|
|
1116
|
+
kickoffLabel: str(leg.kickoffLabel),
|
|
1117
|
+
market: str(leg.marketLabel) ?? str(leg.marketDeveloperName) ?? "",
|
|
1118
|
+
marketDeveloperName: str(leg.marketDeveloperName) ?? "",
|
|
1119
|
+
selection: joinSelection(selectionLabel, line) ?? selectionLabel,
|
|
1120
|
+
selectionLabel,
|
|
1121
|
+
line,
|
|
1122
|
+
odds: price.toFixed(2),
|
|
1123
|
+
oddsDecimal: price,
|
|
1124
|
+
matchOddsId,
|
|
1125
|
+
quotedAtUtc: str(leg.quotedAtUtc),
|
|
1126
|
+
insightId,
|
|
1127
|
+
statText
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
function toComboCardVM(card) {
|
|
1131
|
+
const rawLegs = Array.isArray(card?.legs) ? card.legs : [];
|
|
1132
|
+
const multiplication = str(card?.multiplication);
|
|
1133
|
+
if (rawLegs.length < 2 || !multiplication) return null;
|
|
1134
|
+
const legs = [];
|
|
1135
|
+
for (const raw of rawLegs) {
|
|
1136
|
+
const vm = toComboLegVM(raw);
|
|
1137
|
+
if (!vm) return null;
|
|
1138
|
+
legs.push(vm);
|
|
1139
|
+
}
|
|
1140
|
+
return {
|
|
1141
|
+
id: str(card.comboId) ?? legs.map((l) => l.matchOddsId).join("-"),
|
|
1142
|
+
legs,
|
|
1143
|
+
multiplication,
|
|
1144
|
+
replacedNote: str(card.replacedNote),
|
|
1145
|
+
addToSlipEnabled: card.addToSlipEnabled === true,
|
|
1146
|
+
target: finite(card.target),
|
|
1147
|
+
legCount: finite(card.legCount)
|
|
1148
|
+
};
|
|
988
1149
|
}
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
code: ({ children }) => /* @__PURE__ */ jsx4("code", { className: "ss-w-md-code", children })
|
|
1000
|
-
};
|
|
1001
|
-
function Markdown({ text }) {
|
|
1002
|
-
return /* @__PURE__ */ jsx4("div", { className: "ss-w-md", children: /* @__PURE__ */ jsx4(ReactMarkdown, { remarkPlugins: [remarkGfm], components, children: text }) });
|
|
1150
|
+
function comboLegToSlipSelection(leg, conversationId) {
|
|
1151
|
+
const selection = {
|
|
1152
|
+
matchOddsId: leg.matchOddsId,
|
|
1153
|
+
matchId: leg.matchId,
|
|
1154
|
+
oddsDecimal: leg.oddsDecimal,
|
|
1155
|
+
insightId: leg.insightId
|
|
1156
|
+
};
|
|
1157
|
+
if (leg.quotedAtUtc !== void 0) selection.quotedAtUtc = leg.quotedAtUtc;
|
|
1158
|
+
if (conversationId !== void 0) selection.conversationId = conversationId;
|
|
1159
|
+
return selection;
|
|
1003
1160
|
}
|
|
1004
1161
|
|
|
1005
|
-
// src/
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
var
|
|
1009
|
-
var
|
|
1010
|
-
function
|
|
1011
|
-
|
|
1162
|
+
// src/slip.ts
|
|
1163
|
+
var ADD_TO_SLIP_TIMEOUT_MS = 1e4;
|
|
1164
|
+
var MAX_SELECTION = 64;
|
|
1165
|
+
var MAX_LINE = 32;
|
|
1166
|
+
var MAX_ODDS = 1e4;
|
|
1167
|
+
function slipPickFields(selection, label, line) {
|
|
1168
|
+
const fields = {};
|
|
1169
|
+
if (Number.isSafeInteger(selection.matchId) && selection.matchId > 0) fields.matchId = selection.matchId;
|
|
1170
|
+
if (label && label.trim() && label.length <= MAX_SELECTION) fields.selection = label;
|
|
1171
|
+
if (line && line.trim() && line.length <= MAX_LINE) fields.line = line;
|
|
1172
|
+
const odds = selection.oddsDecimal;
|
|
1173
|
+
if (Number.isFinite(odds) && odds > 1 && odds <= MAX_ODDS) fields.odds = odds;
|
|
1174
|
+
return fields;
|
|
1012
1175
|
}
|
|
1013
|
-
function
|
|
1014
|
-
|
|
1176
|
+
function chipSelection(market, matchId, conversationId) {
|
|
1177
|
+
if (!market || !market.addToSlipEnabled || typeof market.matchOddsId !== "number" || typeof market.price !== "number") return null;
|
|
1178
|
+
const selection = { matchOddsId: market.matchOddsId, matchId, oddsDecimal: market.price };
|
|
1179
|
+
if (conversationId !== void 0) selection.conversationId = conversationId;
|
|
1180
|
+
return selection;
|
|
1015
1181
|
}
|
|
1016
|
-
function
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
className: "ss-w-ecard-crest",
|
|
1021
|
-
style: { backgroundImage: `url(${image})` },
|
|
1022
|
-
...label ? { role: "img", title: label, "aria-label": label } : { "aria-hidden": "true" }
|
|
1023
|
-
}
|
|
1024
|
-
);
|
|
1182
|
+
function wasAdded(result) {
|
|
1183
|
+
if (!result) return true;
|
|
1184
|
+
if (result.status === "price_changed") return result.accepted;
|
|
1185
|
+
return result.status === "added";
|
|
1025
1186
|
}
|
|
1026
|
-
function
|
|
1027
|
-
if (
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
d > 0 ? /* @__PURE__ */ jsx5("span", { className: "ss-w-form-d", style: { flexGrow: d } }) : null,
|
|
1031
|
-
l > 0 ? /* @__PURE__ */ jsx5("span", { className: "ss-w-form-l", style: { flexGrow: l } }) : null
|
|
1032
|
-
] });
|
|
1187
|
+
function isTerminal(result) {
|
|
1188
|
+
if (!result) return true;
|
|
1189
|
+
if (result.status === "price_changed") return result.accepted;
|
|
1190
|
+
return result.status === "added" || result.status === "duplicate";
|
|
1033
1191
|
}
|
|
1034
|
-
function
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1192
|
+
function slipOutcome(result) {
|
|
1193
|
+
if (!result) return "added";
|
|
1194
|
+
switch (result.status) {
|
|
1195
|
+
case "added":
|
|
1196
|
+
case "duplicate":
|
|
1197
|
+
return result.status;
|
|
1198
|
+
case "price_changed":
|
|
1199
|
+
return result.accepted ? "added" : "price_changed";
|
|
1200
|
+
case "rejected":
|
|
1201
|
+
return result.reason;
|
|
1041
1202
|
}
|
|
1042
|
-
return /* @__PURE__ */ jsxs3("div", { className: "ss-w-ecard-cell", children: [
|
|
1043
|
-
/* @__PURE__ */ jsx5("span", { className: "ss-w-ecard-cval", children: s.value }),
|
|
1044
|
-
/* @__PURE__ */ jsx5("span", { className: "ss-w-ecard-clabel", children: s.label })
|
|
1045
|
-
] });
|
|
1046
1203
|
}
|
|
1047
|
-
function
|
|
1048
|
-
if (
|
|
1049
|
-
|
|
1050
|
-
|
|
1204
|
+
function resultToToast(result, shownOdds, ui) {
|
|
1205
|
+
if (!result) return { kind: "success", text: ui.slipAdded };
|
|
1206
|
+
switch (result.status) {
|
|
1207
|
+
case "added":
|
|
1208
|
+
return { kind: "success", text: ui.slipAdded };
|
|
1209
|
+
case "duplicate":
|
|
1210
|
+
return { kind: "success", text: ui.slipDuplicate };
|
|
1211
|
+
case "price_changed":
|
|
1212
|
+
return result.accepted ? { kind: "success", text: ui.slipPriceChanged(shownOdds, result.newOddsDecimal.toFixed(2)) } : { kind: "error", text: ui.slipPriceNotAccepted(shownOdds, result.newOddsDecimal.toFixed(2)) };
|
|
1213
|
+
case "rejected":
|
|
1214
|
+
if (result.reason === "login_required") return { kind: "error", text: ui.slipLoginRequired };
|
|
1215
|
+
if (result.reason === "suspended") return { kind: "error", text: ui.slipSuspended };
|
|
1216
|
+
return { kind: "error", text: result.message ?? ui.slipFailed };
|
|
1217
|
+
}
|
|
1051
1218
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
/* @__PURE__ */ jsxs3("span", { className: "ss-w-ecard-idtext", children: [
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
c.season ? /* @__PURE__ */ jsx5("span", { className: "ss-w-ecard-season", children: shortSeason(c.season) }) : null
|
|
1070
|
-
] })
|
|
1071
|
-
] });
|
|
1219
|
+
|
|
1220
|
+
// src/useHostEvents.ts
|
|
1221
|
+
import { useCallback as useCallback7, useEffect as useEffect16, useMemo as useMemo2, useRef as useRef12, useState as useState36 } from "react";
|
|
1222
|
+
|
|
1223
|
+
// src/components/ChatPanel.tsx
|
|
1224
|
+
import { useCallback as useCallback6, useEffect as useEffect15, useMemo, useRef as useRef11, useState as useState35 } from "react";
|
|
1225
|
+
|
|
1226
|
+
// src/components/ChatHeader.tsx
|
|
1227
|
+
import { useEffect, useRef, useState as useState2 } from "react";
|
|
1228
|
+
|
|
1229
|
+
// src/assets/logo.ts
|
|
1230
|
+
var SS_LOGO_DATA_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAjoAAAI6CAYAAAAuSOLAAAAAAXNSR0IArs4c6QAAAJplWElmTU0AKgAAAAgAAwESAAMAAAABAAEAAAExAAIAAAA9AAAAModpAAQAAAABAAAAcAAAAABBZG9iZSBQaG90b3Nob3AgMjYuMTAgKDIwMjUwNzMwLm0uMzE2NiAwN2IxMjM0KSAgKE1hY2ludG9zaCkAAAADoAEAAwAAAAEAAQAAoAIABAAAAAEAAAI6oAMABAAAAAEAAAI6AAAAACwGtVwAAAQwaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA2LjAuMCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgICAgICAgICB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx4bXBNTTpEb2N1bWVudElEPnhtcC5kaWQ6QzBGRTM5NjM5NEJEMTFGMEJGNjZEMjUyN0U0MzU5RUM8L3htcE1NOkRvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpJbnN0YW5jZUlEPnhtcC5paWQ6QzBGRTM5NjI5NEJEMTFGMEJGNjZEMjUyN0U0MzU5RUM8L3htcE1NOkluc3RhbmNlSUQ+CiAgICAgICAgIDx4bXBNTTpEZXJpdmVkRnJvbSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgIDxzdFJlZjppbnN0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";
|
|
1231
|
+
|
|
1232
|
+
// src/components/Icons.tsx
|
|
1233
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
1234
|
+
function SsMark({ className }) {
|
|
1235
|
+
return /* @__PURE__ */ jsx("img", { className, src: SS_LOGO_DATA_URI, alt: "", "aria-hidden": "true" });
|
|
1072
1236
|
}
|
|
1073
|
-
function
|
|
1074
|
-
return /* @__PURE__ */
|
|
1075
|
-
"button",
|
|
1076
|
-
{
|
|
1077
|
-
type: "button",
|
|
1078
|
-
role: "tab",
|
|
1079
|
-
className: "ss-w-ecard-scope",
|
|
1080
|
-
"aria-selected": i === active,
|
|
1081
|
-
"aria-label": s.label,
|
|
1082
|
-
title: s.label,
|
|
1083
|
-
onClick: () => onSelect(i),
|
|
1084
|
-
children: s.image ? /* @__PURE__ */ jsx5(Crest, { image: s.image }) : s.label
|
|
1085
|
-
},
|
|
1086
|
-
String(s.competitionId ?? "all")
|
|
1087
|
-
)) });
|
|
1237
|
+
function SendIcon() {
|
|
1238
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fill: "currentColor", d: "M3 20l18-8L3 4v6l12 2-12 2z" }) });
|
|
1088
1239
|
}
|
|
1089
|
-
function
|
|
1090
|
-
|
|
1091
|
-
const hasBody = c.scopes.length > 0;
|
|
1092
|
-
if (!hasBody) {
|
|
1093
|
-
return /* @__PURE__ */ jsx5("div", { className: "ss-w-ecard ss-w-ecard-bare", "data-kind": c.kind, children: /* @__PURE__ */ jsx5(CardHeader, { c, showScopeCrest: false }) });
|
|
1094
|
-
}
|
|
1095
|
-
const multi = c.scopes.length > 1;
|
|
1096
|
-
const scope = c.scopes[multi ? c.scopes[active] ? active : 0 : 0];
|
|
1097
|
-
return /* @__PURE__ */ jsxs3("div", { className: "ss-w-ecard", "data-kind": c.kind, children: [
|
|
1098
|
-
/* @__PURE__ */ jsx5(CardHeader, { c, showScopeCrest: !multi }),
|
|
1099
|
-
multi ? /* @__PURE__ */ jsx5(ScopeTabs, { scopes: c.scopes, active, onSelect: setActive }) : null,
|
|
1100
|
-
/* @__PURE__ */ jsx5(StatStrip, { stats: scope.stats })
|
|
1101
|
-
] });
|
|
1240
|
+
function CloseIcon() {
|
|
1241
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fill: "none", d: "M18 6L6 18M6 6l12 12", stroke: "currentColor", strokeWidth: "2" }) });
|
|
1102
1242
|
}
|
|
1103
|
-
function
|
|
1104
|
-
|
|
1105
|
-
if (cards.length === 0) return null;
|
|
1106
|
-
const visible = expanded ? cards : cards.slice(0, MAX_VISIBLE);
|
|
1107
|
-
const overflow = cards.length - MAX_VISIBLE;
|
|
1108
|
-
return /* @__PURE__ */ jsxs3("div", { className: "ss-w-cards", children: [
|
|
1109
|
-
visible.map((c) => /* @__PURE__ */ jsx5(EntityCardItem, { c }, c.id)),
|
|
1110
|
-
overflow > 0 ? /* @__PURE__ */ jsx5("button", { type: "button", className: "ss-w-cards-more", disabled, onClick: () => setExpanded((v) => !v), children: expanded ? showLessLabel : showMoreLabel(overflow) }) : null
|
|
1111
|
-
] });
|
|
1243
|
+
function ChevronIcon() {
|
|
1244
|
+
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" }) });
|
|
1112
1245
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
var CHART_VIEW_W = 320;
|
|
1120
|
-
var CHART_VIEW_H = 140;
|
|
1121
|
-
var PLOT_X0 = 40;
|
|
1122
|
-
var PLOT_X1 = 312;
|
|
1123
|
-
var PLOT_Y0 = 10;
|
|
1124
|
-
var PLOT_Y1 = 110;
|
|
1125
|
-
var MARKER_MAX_POINTS = 8;
|
|
1126
|
-
var FLAT_DOMAIN_PAD = 0.05;
|
|
1127
|
-
var round2 = (n) => Math.round(n * 100) / 100;
|
|
1128
|
-
function projectSeries(samples) {
|
|
1129
|
-
if (samples.length < 2) return null;
|
|
1130
|
-
const tMin = samples[0].atMs;
|
|
1131
|
-
const tMax = samples[samples.length - 1].atMs;
|
|
1132
|
-
if (tMax <= tMin) return null;
|
|
1133
|
-
const prices = samples.map((s) => s.price);
|
|
1134
|
-
const pMin = Math.min(...prices);
|
|
1135
|
-
const pMax = Math.max(...prices);
|
|
1136
|
-
let yMin;
|
|
1137
|
-
let yMax;
|
|
1138
|
-
if (pMax === pMin) {
|
|
1139
|
-
yMin = pMin - FLAT_DOMAIN_PAD;
|
|
1140
|
-
yMax = pMax + FLAT_DOMAIN_PAD;
|
|
1141
|
-
} else {
|
|
1142
|
-
const pad = (pMax - pMin) * 0.1;
|
|
1143
|
-
yMin = pMin - pad;
|
|
1144
|
-
yMax = pMax + pad;
|
|
1145
|
-
}
|
|
1146
|
-
const x = (atMs) => round2(PLOT_X0 + (atMs - tMin) / (tMax - tMin) * (PLOT_X1 - PLOT_X0));
|
|
1147
|
-
const y = (price) => round2(PLOT_Y1 - (price - yMin) / (yMax - yMin) * (PLOT_Y1 - PLOT_Y0));
|
|
1148
|
-
const projected = samples.map((s) => ({ x: x(s.atMs), y: y(s.price) }));
|
|
1149
|
-
const path = projected.map((p, i) => `${i === 0 ? "M" : "L"}${p.x} ${p.y}`).join(" ");
|
|
1150
|
-
return {
|
|
1151
|
-
path,
|
|
1152
|
-
markers: samples.length <= MARKER_MAX_POINTS ? projected : [],
|
|
1153
|
-
// The real extremes, never the padded bounds: a gridline label is a price, and the padded
|
|
1154
|
-
// bounds are prices nobody was ever offered. A flat series has one real extreme, not two —
|
|
1155
|
-
// two identical gridlines would collide on both React key and rendered text.
|
|
1156
|
-
gridlines: pMax === pMin ? [{ y: y(pMax), price: pMax }] : [
|
|
1157
|
-
{ y: y(pMax), price: pMax },
|
|
1158
|
-
{ y: y(pMin), price: pMin }
|
|
1159
|
-
],
|
|
1160
|
-
viewBox: `0 0 ${CHART_VIEW_W} ${CHART_VIEW_H}`,
|
|
1161
|
-
startMs: tMin,
|
|
1162
|
-
endMs: tMax
|
|
1163
|
-
};
|
|
1246
|
+
function KebabIcon() {
|
|
1247
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ jsxs("g", { fill: "currentColor", children: [
|
|
1248
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "1.6" }),
|
|
1249
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1.6" }),
|
|
1250
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "19", r: "1.6" })
|
|
1251
|
+
] }) });
|
|
1164
1252
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1253
|
+
function SlidersIcon() {
|
|
1254
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ jsxs("g", { fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
1255
|
+
/* @__PURE__ */ jsx("path", { d: "M4 6h9M17 6h3M4 12h3M11 12h9M4 18h11M19 18h1" }),
|
|
1256
|
+
/* @__PURE__ */ jsx("circle", { cx: "15", cy: "6", r: "2" }),
|
|
1257
|
+
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "12", r: "2" }),
|
|
1258
|
+
/* @__PURE__ */ jsx("circle", { cx: "17", cy: "18", r: "2" })
|
|
1259
|
+
] }) });
|
|
1169
1260
|
}
|
|
1170
|
-
function
|
|
1171
|
-
return
|
|
1261
|
+
function ComboIcon() {
|
|
1262
|
+
return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ jsxs("g", { fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinejoin: "round", children: [
|
|
1263
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "11", height: "11", rx: "2" }),
|
|
1264
|
+
/* @__PURE__ */ jsx("rect", { x: "10", y: "10", width: "11", height: "11", rx: "2" })
|
|
1265
|
+
] }) });
|
|
1172
1266
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1267
|
+
var DASHBOARD_EMOJI = {
|
|
1268
|
+
football: "\u26BD",
|
|
1269
|
+
"goal-net": "\u{1F945}",
|
|
1270
|
+
btts: "\u{1F91D}",
|
|
1271
|
+
card: "\u{1F7E8}",
|
|
1272
|
+
"corner-flag": "\u{1F6A9}",
|
|
1273
|
+
player: "\u{1F464}",
|
|
1274
|
+
target: "\u{1F3AF}",
|
|
1275
|
+
trophy: "\u{1F3C6}",
|
|
1276
|
+
"chart-line": "\u{1F4C8}",
|
|
1277
|
+
"chart-bar": "\u{1F4CA}",
|
|
1278
|
+
chat: "\u{1F4AC}",
|
|
1279
|
+
fire: "\u{1F525}",
|
|
1280
|
+
sparkle: "\u2728"
|
|
1281
|
+
};
|
|
1282
|
+
function DashboardIcon({ name }) {
|
|
1283
|
+
const glyph = Object.prototype.hasOwnProperty.call(DASHBOARD_EMOJI, name) ? DASHBOARD_EMOJI[name] : DASHBOARD_EMOJI.target;
|
|
1284
|
+
return /* @__PURE__ */ jsx("span", { className: "ss-w-dash-emoji", "aria-hidden": "true", children: glyph });
|
|
1182
1285
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1286
|
+
|
|
1287
|
+
// src/components/BrandMark.tsx
|
|
1288
|
+
import { useState } from "react";
|
|
1289
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
1290
|
+
function BrandMark({ logoUrl, className }) {
|
|
1291
|
+
const [failed, setFailed] = useState(false);
|
|
1292
|
+
if (!logoUrl || failed) return /* @__PURE__ */ jsx2(SsMark, { className });
|
|
1293
|
+
return /* @__PURE__ */ jsx2("img", { className, src: logoUrl, alt: "", "aria-hidden": "true", onError: () => setFailed(true) });
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
// src/components/ChatHeader.tsx
|
|
1297
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
1298
|
+
function ChatHeader({
|
|
1299
|
+
onClose,
|
|
1300
|
+
closeLabel = "Close chat",
|
|
1301
|
+
logoUrl,
|
|
1302
|
+
headerTitle,
|
|
1303
|
+
onNewConversation,
|
|
1304
|
+
canReset,
|
|
1305
|
+
newConversationLabel = "New conversation",
|
|
1306
|
+
moreOptionsLabel = "More options"
|
|
1307
|
+
}) {
|
|
1308
|
+
const [menuOpen, setMenuOpen] = useState2(false);
|
|
1309
|
+
const wrapRef = useRef(null);
|
|
1310
|
+
const kebabRef = useRef(null);
|
|
1311
|
+
const firstItemRef = useRef(null);
|
|
1312
|
+
const hasMenu = !!(canReset && onNewConversation);
|
|
1313
|
+
useEffect(() => {
|
|
1314
|
+
if (menuOpen) firstItemRef.current?.focus();
|
|
1315
|
+
}, [menuOpen]);
|
|
1316
|
+
useEffect(() => {
|
|
1317
|
+
if (!menuOpen) return;
|
|
1318
|
+
const onPointer = (e) => {
|
|
1319
|
+
if (!wrapRef.current?.contains(e.target)) setMenuOpen(false);
|
|
1194
1320
|
};
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1321
|
+
const onKey = (e) => {
|
|
1322
|
+
if (e.key === "Escape") {
|
|
1323
|
+
setMenuOpen(false);
|
|
1324
|
+
kebabRef.current?.focus();
|
|
1325
|
+
}
|
|
1326
|
+
};
|
|
1327
|
+
document.addEventListener("mousedown", onPointer);
|
|
1328
|
+
document.addEventListener("keydown", onKey);
|
|
1329
|
+
return () => {
|
|
1330
|
+
document.removeEventListener("mousedown", onPointer);
|
|
1331
|
+
document.removeEventListener("keydown", onKey);
|
|
1332
|
+
};
|
|
1333
|
+
}, [menuOpen]);
|
|
1334
|
+
const runNewConversation = () => {
|
|
1335
|
+
setMenuOpen(false);
|
|
1336
|
+
onNewConversation?.();
|
|
1204
1337
|
};
|
|
1338
|
+
return /* @__PURE__ */ jsxs2("header", { className: "ss-w-header", children: [
|
|
1339
|
+
/* @__PURE__ */ jsx3(BrandMark, { logoUrl, className: "ss-w-header-mark" }),
|
|
1340
|
+
/* @__PURE__ */ jsx3("span", { className: "ss-w-header-title", children: headerTitle ?? "SensibleStats" }),
|
|
1341
|
+
hasMenu ? /* @__PURE__ */ jsxs2("div", { className: "ss-w-menu-wrap", ref: wrapRef, children: [
|
|
1342
|
+
/* @__PURE__ */ jsx3(
|
|
1343
|
+
"button",
|
|
1344
|
+
{
|
|
1345
|
+
type: "button",
|
|
1346
|
+
className: "ss-w-kebab",
|
|
1347
|
+
ref: kebabRef,
|
|
1348
|
+
"aria-haspopup": "menu",
|
|
1349
|
+
"aria-expanded": menuOpen,
|
|
1350
|
+
"aria-label": moreOptionsLabel,
|
|
1351
|
+
onClick: () => setMenuOpen((open) => !open),
|
|
1352
|
+
children: /* @__PURE__ */ jsx3(KebabIcon, {})
|
|
1353
|
+
}
|
|
1354
|
+
),
|
|
1355
|
+
menuOpen ? /* @__PURE__ */ jsx3("div", { className: "ss-w-menu", role: "menu", "aria-label": moreOptionsLabel, children: /* @__PURE__ */ jsx3(
|
|
1356
|
+
"button",
|
|
1357
|
+
{
|
|
1358
|
+
type: "button",
|
|
1359
|
+
className: "ss-w-menu-item",
|
|
1360
|
+
role: "menuitem",
|
|
1361
|
+
ref: firstItemRef,
|
|
1362
|
+
onClick: runNewConversation,
|
|
1363
|
+
children: /* @__PURE__ */ jsx3("span", { children: newConversationLabel })
|
|
1364
|
+
}
|
|
1365
|
+
) }) : null
|
|
1366
|
+
] }) : null,
|
|
1367
|
+
/* @__PURE__ */ jsx3("button", { type: "button", className: "ss-w-close", "aria-label": closeLabel, onClick: onClose, children: /* @__PURE__ */ jsx3(CloseIcon, {}) })
|
|
1368
|
+
] });
|
|
1205
1369
|
}
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1370
|
+
|
|
1371
|
+
// src/components/MessageList.tsx
|
|
1372
|
+
import { useEffect as useEffect7, useRef as useRef4 } from "react";
|
|
1373
|
+
|
|
1374
|
+
// src/presets.ts
|
|
1375
|
+
var CLIENT_CAPABILITIES = ["price_presets"];
|
|
1376
|
+
var PRICE_TILE_PREFIX = "goal-preset-";
|
|
1377
|
+
function isPriceTile(tile) {
|
|
1378
|
+
const id = tile.signal?.entityId;
|
|
1379
|
+
return typeof id === "string" && id.startsWith(PRICE_TILE_PREFIX);
|
|
1212
1380
|
}
|
|
1213
|
-
function
|
|
1214
|
-
const
|
|
1215
|
-
return
|
|
1381
|
+
function tileDisplayText(tile) {
|
|
1382
|
+
const subtitle = tile.subtitle?.trim();
|
|
1383
|
+
return isPriceTile(tile) && subtitle ? subtitle : tile.title;
|
|
1216
1384
|
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1385
|
+
var MAX_BUBBLE_ASKS = 2;
|
|
1386
|
+
function bubbleAsks(messages) {
|
|
1387
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1388
|
+
const m = messages[i];
|
|
1389
|
+
if (!m || m.role !== "assistant" || !m.isGreeting) continue;
|
|
1390
|
+
const dashboard = m.greetingDashboard;
|
|
1391
|
+
if (!dashboard) return [];
|
|
1392
|
+
const asks = [];
|
|
1393
|
+
dashboard.tiles.forEach((tile, index) => {
|
|
1394
|
+
const label = tile.subtitle?.trim();
|
|
1395
|
+
if (asks.length < MAX_BUBBLE_ASKS && isPriceTile(tile) && label) asks.push({ tile, index, position: asks.length, layout: dashboard.layout, label });
|
|
1396
|
+
});
|
|
1397
|
+
return asks;
|
|
1398
|
+
}
|
|
1399
|
+
return [];
|
|
1219
1400
|
}
|
|
1220
|
-
function
|
|
1221
|
-
|
|
1222
|
-
|
|
1401
|
+
function runBubbleAsk(ask, deps) {
|
|
1402
|
+
deps.log?.log({ name: "popup_ask_tap", outcome: String(ask.position) });
|
|
1403
|
+
deps.open();
|
|
1404
|
+
deps.setActiveTab("chat");
|
|
1405
|
+
const displayText = tileDisplayText(ask.tile);
|
|
1406
|
+
deps.send(ask.tile.actionValue, ask.tile.actionRef ? { actionRef: ask.tile.actionRef, displayText } : { displayText });
|
|
1407
|
+
deps.onDashboardTap?.(ask.tile, ask.index, ask.layout);
|
|
1223
1408
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
isLive: insight.odds?.isLive === true,
|
|
1248
|
-
phase: str(insight.phase),
|
|
1249
|
-
fixture,
|
|
1250
|
-
competition,
|
|
1251
|
-
text: str(insight.text) ?? "",
|
|
1252
|
-
disclaimer: str(insight.disclaimer),
|
|
1253
|
-
matchId: typeof insight.matchId === "number" ? insight.matchId : 0,
|
|
1254
|
-
matchOddsId,
|
|
1255
|
-
marketDeveloperName: str(insight.marketDeveloperName),
|
|
1256
|
-
selectionLabel: str(insight.selectionLabel),
|
|
1257
|
-
line: str(insight.line),
|
|
1258
|
-
addToSlipEnabled: insight.addToSlipEnabled === true,
|
|
1259
|
-
pinEnabled: insight.pinEnabled === true,
|
|
1260
|
-
isMarketFloor: insight.isMarketFloor === true,
|
|
1261
|
-
oddsDecimal: typeof oddsValue === "number" && Number.isFinite(oddsValue) ? oddsValue : void 0,
|
|
1262
|
-
quotedAtUtc: str(insight.odds?.quotedAtUtc),
|
|
1263
|
-
previousOdds: previous !== void 0 && direction !== void 0 ? previous.toFixed(2) : void 0,
|
|
1264
|
-
movementDirection: previous !== void 0 && direction !== void 0 ? direction : void 0,
|
|
1265
|
-
homeTeamName: home,
|
|
1266
|
-
awayTeamName: away,
|
|
1267
|
-
homeCrest: str(insight.homeTeamImagePath),
|
|
1268
|
-
awayCrest: str(insight.awayTeamImagePath),
|
|
1269
|
-
competitionCrest: str(insight.competitionImagePath),
|
|
1270
|
-
kickoffUtc: str(insight.kickoffUtc),
|
|
1271
|
-
kickoffLabel: str(insight.kickoffLabel)
|
|
1272
|
-
};
|
|
1409
|
+
|
|
1410
|
+
// src/components/Markdown.tsx
|
|
1411
|
+
import ReactMarkdown from "react-markdown";
|
|
1412
|
+
import remarkGfm from "remark-gfm";
|
|
1413
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
1414
|
+
var components = {
|
|
1415
|
+
a: ({ children, href }) => /* @__PURE__ */ jsx4("a", { href, target: "_blank", rel: "noopener noreferrer", className: "ss-w-md-link", children }),
|
|
1416
|
+
table: ({ children }) => /* @__PURE__ */ jsx4("div", { className: "ss-w-table-wrap", children: /* @__PURE__ */ jsx4("table", { className: "ss-w-md-table", children }) }),
|
|
1417
|
+
ul: ({ children }) => /* @__PURE__ */ jsx4("ul", { className: "ss-w-md-ul", children }),
|
|
1418
|
+
ol: ({ children }) => /* @__PURE__ */ jsx4("ol", { className: "ss-w-md-ol", children }),
|
|
1419
|
+
code: ({ children }) => /* @__PURE__ */ jsx4("code", { className: "ss-w-md-code", children })
|
|
1420
|
+
};
|
|
1421
|
+
function Markdown({ text }) {
|
|
1422
|
+
return /* @__PURE__ */ jsx4("div", { className: "ss-w-md", children: /* @__PURE__ */ jsx4(ReactMarkdown, { remarkPlugins: [remarkGfm], components, children: text }) });
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
// src/components/EntityCardList.tsx
|
|
1426
|
+
import { useState as useState3 } from "react";
|
|
1427
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1428
|
+
var MAX_VISIBLE = 3;
|
|
1429
|
+
var RECORD_RE = /^(\d+)-(\d+)-(\d+)$/;
|
|
1430
|
+
function initials(name) {
|
|
1431
|
+
return name.split(/\s+/).slice(0, 2).map((w) => w[0] ?? "").join("").toUpperCase();
|
|
1273
1432
|
}
|
|
1274
|
-
function
|
|
1275
|
-
|
|
1276
|
-
const selection = {
|
|
1277
|
-
matchOddsId: vm.matchOddsId,
|
|
1278
|
-
matchId: vm.matchId,
|
|
1279
|
-
oddsDecimal: vm.oddsDecimal
|
|
1280
|
-
};
|
|
1281
|
-
if (vm.quotedAtUtc !== void 0) selection.quotedAtUtc = vm.quotedAtUtc;
|
|
1282
|
-
if (vm.id !== void 0) selection.insightId = vm.id;
|
|
1283
|
-
if (conversationId !== void 0) selection.conversationId = conversationId;
|
|
1284
|
-
return selection;
|
|
1433
|
+
function shortSeason(season) {
|
|
1434
|
+
return season.replace(/\d{4}/g, (year) => year.slice(2));
|
|
1285
1435
|
}
|
|
1286
|
-
function
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
selection,
|
|
1296
|
-
selectionLabel: str(r.selectionLabel),
|
|
1297
|
-
line: str(r.line),
|
|
1298
|
-
odds: price !== void 0 ? price.toFixed(2) : "",
|
|
1299
|
-
oddsDecimal: price,
|
|
1300
|
-
matchOddsId: typeof r.matchOddsId === "number" ? r.matchOddsId : void 0,
|
|
1301
|
-
quotedAtUtc: str(r.quotedAtUtc),
|
|
1302
|
-
isMain: r.isMainLine !== false,
|
|
1303
|
-
// === true, not !== false: a server that has not shipped the field yet leaves it undefined,
|
|
1304
|
-
// and undefined must read as open rather than hiding every price on the card.
|
|
1305
|
-
isSuspended: r.isSuspended === true,
|
|
1306
|
-
previousOdds: previous !== void 0 && direction !== void 0 ? previous.toFixed(2) : void 0,
|
|
1307
|
-
movementDirection: previous !== void 0 && direction !== void 0 ? direction : void 0
|
|
1308
|
-
};
|
|
1309
|
-
};
|
|
1310
|
-
const vms = rows.map(toRow).filter((r) => r.selection && r.odds && !r.isSuspended);
|
|
1311
|
-
const home = str(card.homeTeamName);
|
|
1312
|
-
const away = str(card.awayTeamName);
|
|
1313
|
-
const updated = rows.map((r) => str(r.quotedAtUtc)).filter((v) => !!v).sort();
|
|
1314
|
-
return {
|
|
1315
|
-
id: `${typeof card.matchId === "number" ? card.matchId : 0}:${str(card.marketDeveloperName) ?? ""}`,
|
|
1316
|
-
market: str(card.marketLabel) ?? str(card.marketDeveloperName) ?? "",
|
|
1317
|
-
fixture: home && away ? `${home} vs ${away}` : void 0,
|
|
1318
|
-
homeTeamName: home,
|
|
1319
|
-
awayTeamName: away,
|
|
1320
|
-
homeCrest: str(card.homeTeamImagePath),
|
|
1321
|
-
awayCrest: str(card.awayTeamImagePath),
|
|
1322
|
-
competitionCrest: str(card.competitionImagePath),
|
|
1323
|
-
competition: str(card.competitionName),
|
|
1324
|
-
kickoffUtc: str(card.kickoffUtc),
|
|
1325
|
-
kickoffLabel: str(card.kickoffLabel),
|
|
1326
|
-
bookmaker: rows.map((r) => str(r.bookmakerName)).find(Boolean),
|
|
1327
|
-
updatedAt: updated.length > 0 ? updated[updated.length - 1] : void 0,
|
|
1328
|
-
isLive: rows.some((r) => r.isLive === true),
|
|
1329
|
-
matchId: typeof card.matchId === "number" ? card.matchId : 0,
|
|
1330
|
-
mainRows: vms.filter((r) => r.isMain),
|
|
1331
|
-
moreRows: vms.filter((r) => !r.isMain),
|
|
1332
|
-
disclaimer: str(card.disclaimer),
|
|
1333
|
-
addToSlipEnabled: card.addToSlipEnabled === true,
|
|
1334
|
-
marketDeveloperName: str(card.marketDeveloperName),
|
|
1335
|
-
mainLine: str(card.mainLine),
|
|
1336
|
-
pinEnabled: card.pinEnabled === true,
|
|
1337
|
-
statText: card.insight && typeof card.insight === "object" ? str(card.insight.text) : void 0,
|
|
1338
|
-
tag: card.tag === "your_market" ? "your_market" : void 0
|
|
1339
|
-
};
|
|
1436
|
+
function Crest({ image, label }) {
|
|
1437
|
+
return /* @__PURE__ */ jsx5(
|
|
1438
|
+
"span",
|
|
1439
|
+
{
|
|
1440
|
+
className: "ss-w-ecard-crest",
|
|
1441
|
+
style: { backgroundImage: `url(${image})` },
|
|
1442
|
+
...label ? { role: "img", title: label, "aria-label": label } : { "aria-hidden": "true" }
|
|
1443
|
+
}
|
|
1444
|
+
);
|
|
1340
1445
|
}
|
|
1341
|
-
function
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1446
|
+
function FormBar({ w, d, l }) {
|
|
1447
|
+
if (w + d + l === 0) return null;
|
|
1448
|
+
return /* @__PURE__ */ jsxs3("span", { className: "ss-w-form", "aria-hidden": "true", children: [
|
|
1449
|
+
w > 0 ? /* @__PURE__ */ jsx5("span", { className: "ss-w-form-w", style: { flexGrow: w } }) : null,
|
|
1450
|
+
d > 0 ? /* @__PURE__ */ jsx5("span", { className: "ss-w-form-d", style: { flexGrow: d } }) : null,
|
|
1451
|
+
l > 0 ? /* @__PURE__ */ jsx5("span", { className: "ss-w-form-l", style: { flexGrow: l } }) : null
|
|
1452
|
+
] });
|
|
1453
|
+
}
|
|
1454
|
+
function StatCellView({ s }) {
|
|
1455
|
+
const m = RECORD_RE.exec(s.value);
|
|
1456
|
+
if (m) {
|
|
1457
|
+
return /* @__PURE__ */ jsxs3("div", { className: "ss-w-ecard-cell", children: [
|
|
1458
|
+
/* @__PURE__ */ jsx5("span", { className: "ss-w-ecard-cval", children: /* @__PURE__ */ jsx5(FormBar, { w: Number(m[1]), d: Number(m[2]), l: Number(m[3]) }) }),
|
|
1459
|
+
/* @__PURE__ */ jsx5("span", { className: "ss-w-ecard-clabel", children: s.value })
|
|
1460
|
+
] });
|
|
1349
1461
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
const home = str(card.homeTeamName);
|
|
1355
|
-
const away = str(card.awayTeamName);
|
|
1356
|
-
const selection = joinSelection(str(card.selectionLabel), str(card.line)) ?? "";
|
|
1357
|
-
const bookmakerId = typeof card.bookmakerId === "number" ? card.bookmakerId : "";
|
|
1358
|
-
return {
|
|
1359
|
-
id: `${typeof card.matchId === "number" ? card.matchId : 0}:${str(card.marketDeveloperName) ?? ""}:${selection}:${bookmakerId}`,
|
|
1360
|
-
matchId: typeof card.matchId === "number" ? card.matchId : 0,
|
|
1361
|
-
market: str(card.marketLabel) ?? str(card.marketDeveloperName) ?? "",
|
|
1362
|
-
marketDeveloperName: str(card.marketDeveloperName),
|
|
1363
|
-
selection,
|
|
1364
|
-
selectionLabel: str(card.selectionLabel),
|
|
1365
|
-
line: str(card.line),
|
|
1366
|
-
fixture: home && away ? `${home} vs ${away}` : void 0,
|
|
1367
|
-
competition: str(card.competitionName),
|
|
1368
|
-
bookmaker: str(card.bookmakerName),
|
|
1369
|
-
opening: opening !== void 0 ? opening.toFixed(2) : "",
|
|
1370
|
-
current: current !== void 0 ? current.toFixed(2) : "",
|
|
1371
|
-
direction: card.movementDirection === "shortened" || card.movementDirection === "drifted" ? card.movementDirection : void 0,
|
|
1372
|
-
windowHours: asNumber(card.windowHours) ?? 0,
|
|
1373
|
-
excludedCount: asNumber(card.excludedSuspendedCount) ?? 0,
|
|
1374
|
-
isSuspended: card.isSuspended === true,
|
|
1375
|
-
disclaimer: str(card.disclaimer),
|
|
1376
|
-
geometry: projectSeries(samples),
|
|
1377
|
-
observationCount: samples.length,
|
|
1378
|
-
pinEnabled: card.pinEnabled === true,
|
|
1379
|
-
matchOddsId: typeof card.matchOddsId === "number" ? card.matchOddsId : void 0
|
|
1380
|
-
};
|
|
1462
|
+
return /* @__PURE__ */ jsxs3("div", { className: "ss-w-ecard-cell", children: [
|
|
1463
|
+
/* @__PURE__ */ jsx5("span", { className: "ss-w-ecard-cval", children: s.value }),
|
|
1464
|
+
/* @__PURE__ */ jsx5("span", { className: "ss-w-ecard-clabel", children: s.label })
|
|
1465
|
+
] });
|
|
1381
1466
|
}
|
|
1382
|
-
function
|
|
1383
|
-
if (
|
|
1384
|
-
const
|
|
1385
|
-
|
|
1386
|
-
if (conversationId !== void 0) selection.conversationId = conversationId;
|
|
1387
|
-
return selection;
|
|
1467
|
+
function StatStrip({ stats }) {
|
|
1468
|
+
if (stats.length === 0) return null;
|
|
1469
|
+
const cols = Math.min(stats.length, 4);
|
|
1470
|
+
return /* @__PURE__ */ jsx5("div", { className: "ss-w-ecard-strip", style: { gridTemplateColumns: `repeat(${cols}, 1fr)` }, children: stats.map((s) => /* @__PURE__ */ jsx5(StatCellView, { s }, s.label + s.value)) });
|
|
1388
1471
|
}
|
|
1389
|
-
function
|
|
1390
|
-
|
|
1472
|
+
function CardHeader({ c, showScopeCrest }) {
|
|
1473
|
+
const onlyScope = c.scopes.length === 1 ? c.scopes[0] : null;
|
|
1474
|
+
return /* @__PURE__ */ jsxs3("div", { className: "ss-w-ecard-hd", children: [
|
|
1475
|
+
/* @__PURE__ */ jsx5(
|
|
1476
|
+
"span",
|
|
1477
|
+
{
|
|
1478
|
+
className: c.kind === "team" ? "ss-w-thumb ss-w-team" : "ss-w-thumb",
|
|
1479
|
+
style: c.image ? { backgroundImage: `url(${c.image})` } : void 0,
|
|
1480
|
+
children: c.image ? "" : initials(c.name)
|
|
1481
|
+
}
|
|
1482
|
+
),
|
|
1483
|
+
/* @__PURE__ */ jsxs3("span", { className: "ss-w-ecard-idtext", children: [
|
|
1484
|
+
/* @__PURE__ */ jsx5("span", { className: "ss-w-nm", children: c.name }),
|
|
1485
|
+
c.sub ? /* @__PURE__ */ jsx5("span", { className: "ss-w-sub", children: c.sub }) : null
|
|
1486
|
+
] }),
|
|
1487
|
+
/* @__PURE__ */ jsxs3("span", { className: "ss-w-ecard-meta", children: [
|
|
1488
|
+
showScopeCrest && onlyScope?.image ? /* @__PURE__ */ jsx5(Crest, { image: onlyScope.image, label: onlyScope.label }) : null,
|
|
1489
|
+
c.season ? /* @__PURE__ */ jsx5("span", { className: "ss-w-ecard-season", children: shortSeason(c.season) }) : null
|
|
1490
|
+
] })
|
|
1491
|
+
] });
|
|
1391
1492
|
}
|
|
1392
|
-
function
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
matchId,
|
|
1408
|
-
homeTeamName: home,
|
|
1409
|
-
awayTeamName: away,
|
|
1410
|
-
homeCrest: str(leg.homeTeamImagePath),
|
|
1411
|
-
awayCrest: str(leg.awayTeamImagePath),
|
|
1412
|
-
competition: str(leg.competitionName),
|
|
1413
|
-
kickoffUtc: str(leg.kickoffUtc),
|
|
1414
|
-
kickoffLabel: str(leg.kickoffLabel),
|
|
1415
|
-
market: str(leg.marketLabel) ?? str(leg.marketDeveloperName) ?? "",
|
|
1416
|
-
marketDeveloperName: str(leg.marketDeveloperName) ?? "",
|
|
1417
|
-
selection: joinSelection(selectionLabel, line) ?? selectionLabel,
|
|
1418
|
-
selectionLabel,
|
|
1419
|
-
line,
|
|
1420
|
-
odds: price.toFixed(2),
|
|
1421
|
-
oddsDecimal: price,
|
|
1422
|
-
matchOddsId,
|
|
1423
|
-
quotedAtUtc: str(leg.quotedAtUtc),
|
|
1424
|
-
insightId,
|
|
1425
|
-
statText
|
|
1426
|
-
};
|
|
1493
|
+
function ScopeTabs({ scopes, active, onSelect }) {
|
|
1494
|
+
return /* @__PURE__ */ jsx5("div", { className: "ss-w-ecard-scopes", role: "tablist", children: scopes.map((s, i) => /* @__PURE__ */ jsx5(
|
|
1495
|
+
"button",
|
|
1496
|
+
{
|
|
1497
|
+
type: "button",
|
|
1498
|
+
role: "tab",
|
|
1499
|
+
className: "ss-w-ecard-scope",
|
|
1500
|
+
"aria-selected": i === active,
|
|
1501
|
+
"aria-label": s.label,
|
|
1502
|
+
title: s.label,
|
|
1503
|
+
onClick: () => onSelect(i),
|
|
1504
|
+
children: s.image ? /* @__PURE__ */ jsx5(Crest, { image: s.image }) : s.label
|
|
1505
|
+
},
|
|
1506
|
+
String(s.competitionId ?? "all")
|
|
1507
|
+
)) });
|
|
1427
1508
|
}
|
|
1428
|
-
function
|
|
1429
|
-
const
|
|
1430
|
-
const
|
|
1431
|
-
if (
|
|
1432
|
-
|
|
1433
|
-
for (const raw of rawLegs) {
|
|
1434
|
-
const vm = toComboLegVM(raw);
|
|
1435
|
-
if (!vm) return null;
|
|
1436
|
-
legs.push(vm);
|
|
1509
|
+
function EntityCardItem({ c }) {
|
|
1510
|
+
const [active, setActive] = useState3(0);
|
|
1511
|
+
const hasBody = c.scopes.length > 0;
|
|
1512
|
+
if (!hasBody) {
|
|
1513
|
+
return /* @__PURE__ */ jsx5("div", { className: "ss-w-ecard ss-w-ecard-bare", "data-kind": c.kind, children: /* @__PURE__ */ jsx5(CardHeader, { c, showScopeCrest: false }) });
|
|
1437
1514
|
}
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
};
|
|
1515
|
+
const multi = c.scopes.length > 1;
|
|
1516
|
+
const scope = c.scopes[multi ? c.scopes[active] ? active : 0 : 0];
|
|
1517
|
+
return /* @__PURE__ */ jsxs3("div", { className: "ss-w-ecard", "data-kind": c.kind, children: [
|
|
1518
|
+
/* @__PURE__ */ jsx5(CardHeader, { c, showScopeCrest: !multi }),
|
|
1519
|
+
multi ? /* @__PURE__ */ jsx5(ScopeTabs, { scopes: c.scopes, active, onSelect: setActive }) : null,
|
|
1520
|
+
/* @__PURE__ */ jsx5(StatStrip, { stats: scope.stats })
|
|
1521
|
+
] });
|
|
1445
1522
|
}
|
|
1446
|
-
function
|
|
1447
|
-
const
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
return selection;
|
|
1523
|
+
function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMoreLabel = (n) => `Show ${n} more` }) {
|
|
1524
|
+
const [expanded, setExpanded] = useState3(false);
|
|
1525
|
+
if (cards.length === 0) return null;
|
|
1526
|
+
const visible = expanded ? cards : cards.slice(0, MAX_VISIBLE);
|
|
1527
|
+
const overflow = cards.length - MAX_VISIBLE;
|
|
1528
|
+
return /* @__PURE__ */ jsxs3("div", { className: "ss-w-cards", children: [
|
|
1529
|
+
visible.map((c) => /* @__PURE__ */ jsx5(EntityCardItem, { c }, c.id)),
|
|
1530
|
+
overflow > 0 ? /* @__PURE__ */ jsx5("button", { type: "button", className: "ss-w-cards-more", disabled, onClick: () => setExpanded((v) => !v), children: expanded ? showLessLabel : showMoreLabel(overflow) }) : null
|
|
1531
|
+
] });
|
|
1456
1532
|
}
|
|
1457
1533
|
|
|
1534
|
+
// src/components/AddToSlipButton.tsx
|
|
1535
|
+
import { useCallback, useState as useState4 } from "react";
|
|
1536
|
+
import { isAddToSlipResult } from "@sensiblestats/widget-sdk";
|
|
1537
|
+
|
|
1458
1538
|
// src/eventLogContext.ts
|
|
1459
1539
|
import { createContext, useContext } from "react";
|
|
1460
1540
|
var EventLogContext = createContext(null);
|
|
@@ -1956,6 +2036,13 @@ function BettingInsightList({ insights, ui, slip, monitor }) {
|
|
|
1956
2036
|
return /* @__PURE__ */ jsx13("div", { className: "ss-w-binsights", children: insights.map((it) => /* @__PURE__ */ jsx13(InsightCard, { it, ui, slip, monitor }, it.id)) });
|
|
1957
2037
|
}
|
|
1958
2038
|
|
|
2039
|
+
// src/presetToolsContext.ts
|
|
2040
|
+
import { createContext as createContext4, useContext as useContext4 } from "react";
|
|
2041
|
+
var PresetToolsContext = createContext4(null);
|
|
2042
|
+
function usePresetTools() {
|
|
2043
|
+
return useContext4(PresetToolsContext);
|
|
2044
|
+
}
|
|
2045
|
+
|
|
1959
2046
|
// src/components/ComboCardList.tsx
|
|
1960
2047
|
import { jsx as jsx14, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1961
2048
|
function ComboLegRow({ leg, addToSlipEnabled, ui, slip }) {
|
|
@@ -1998,10 +2085,13 @@ function ComboLegRow({ leg, addToSlipEnabled, ui, slip }) {
|
|
|
1998
2085
|
] });
|
|
1999
2086
|
}
|
|
2000
2087
|
function ComboCardView({ vm, ui, slip }) {
|
|
2088
|
+
const tools = usePresetTools();
|
|
2089
|
+
const { target, legCount } = vm;
|
|
2001
2090
|
return /* @__PURE__ */ jsxs9("div", { className: "ss-w-combo", children: [
|
|
2002
2091
|
/* @__PURE__ */ jsx14("ol", { className: "ss-w-combo-legs", role: "list", children: vm.legs.map((leg) => /* @__PURE__ */ jsx14(ComboLegRow, { leg, addToSlipEnabled: vm.addToSlipEnabled, ui, slip }, leg.key)) }),
|
|
2003
2092
|
/* @__PURE__ */ jsx14("div", { className: "ss-w-combo-math", children: vm.multiplication }),
|
|
2004
|
-
vm.replacedNote ? /* @__PURE__ */ jsx14("p", { className: "ss-w-combo-note", children: vm.replacedNote }) : null
|
|
2093
|
+
vm.replacedNote ? /* @__PURE__ */ jsx14("p", { className: "ss-w-combo-note", children: vm.replacedNote }) : null,
|
|
2094
|
+
tools && target !== void 0 && legCount !== void 0 ? /* @__PURE__ */ jsx14("button", { type: "button", className: "ss-w-combo-change", onClick: () => tools.openBuilder(target, legCount), children: tools.changeLabel }) : null
|
|
2005
2095
|
] });
|
|
2006
2096
|
}
|
|
2007
2097
|
function ComboCardList({ cards, ui, slip }) {
|
|
@@ -2010,10 +2100,10 @@ function ComboCardList({ cards, ui, slip }) {
|
|
|
2010
2100
|
}
|
|
2011
2101
|
|
|
2012
2102
|
// src/components/MarketOddsCardList.tsx
|
|
2013
|
-
import { useState as useState8 } from "react";
|
|
2103
|
+
import { useEffect as useEffect3, useState as useState8 } from "react";
|
|
2014
2104
|
import { Fragment as Fragment2, jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2015
|
-
function OddsRow({ vm, row, ui, slip, inSlipIds, history }) {
|
|
2016
|
-
const selection = vm.addToSlipEnabled ? marketOddsRowToSlipSelection(vm, row, slip.conversationId) : null;
|
|
2105
|
+
function OddsRow({ vm, row, ui, slip, inSlipIds, history, suppressSlip }) {
|
|
2106
|
+
const selection = vm.addToSlipEnabled && !suppressSlip ? marketOddsRowToSlipSelection(vm, row, slip.conversationId) : null;
|
|
2017
2107
|
const inSlip = !!inSlipIds && row.matchOddsId != null && !!inSlipIds[row.matchOddsId];
|
|
2018
2108
|
return /* @__PURE__ */ jsxs10("div", { className: "ss-w-mo-row", children: [
|
|
2019
2109
|
/* @__PURE__ */ jsx15("span", { className: "ss-w-mo-sel", children: row.selection }),
|
|
@@ -2031,17 +2121,34 @@ function OddsRow({ vm, row, ui, slip, inSlipIds, history }) {
|
|
|
2031
2121
|
"\u2713 ",
|
|
2032
2122
|
ui.inSlipLabel
|
|
2033
2123
|
] }) : null,
|
|
2034
|
-
/* @__PURE__ */ jsx15(SlipButton, { selection, shownOdds: row.odds, marketDeveloperName: vm.marketDeveloperName, selectionLabel: row.selectionLabel, line: row.line, slip, ui })
|
|
2124
|
+
!suppressSlip ? /* @__PURE__ */ jsx15(SlipButton, { selection, shownOdds: row.odds, marketDeveloperName: vm.marketDeveloperName, selectionLabel: row.selectionLabel, line: row.line, slip, ui }) : null
|
|
2035
2125
|
] });
|
|
2036
2126
|
}
|
|
2127
|
+
var STALE_AFTER_MS = 12e4;
|
|
2128
|
+
function fmtHhMm(iso, locale) {
|
|
2129
|
+
return new Date(iso).toLocaleTimeString(locale, { hour: "2-digit", minute: "2-digit", hour12: false });
|
|
2130
|
+
}
|
|
2037
2131
|
function MarketOddsCard({ vm, ui, slip, monitor, inSlipIds, hideHeader, pinRef }) {
|
|
2038
2132
|
const [open, setOpen] = useState8(false);
|
|
2039
2133
|
const kickoffFallback = vm.kickoffUtc && ui?.bettingKickoff ? ui.bettingKickoff(vm.kickoffUtc) : void 0;
|
|
2040
2134
|
const kickoff = vm.kickoffLabel ?? (kickoffFallback ? `${kickoffFallback.dayLabel} ${kickoffFallback.time}` : void 0);
|
|
2041
2135
|
const hasNamedTeams = !!vm.homeTeamName && !!vm.awayTeamName;
|
|
2042
|
-
const
|
|
2136
|
+
const live = vm.live ?? null;
|
|
2137
|
+
const closedReason = live?.closedReason ?? null;
|
|
2138
|
+
const isClosed = closedReason !== null;
|
|
2139
|
+
const asOfMs = live ? new Date(live.asOfUtc).getTime() : NaN;
|
|
2140
|
+
const isStale = live != null && !isClosed && (vm.awaitingLiveUpdate === true || Date.now() - asOfMs > STALE_AFTER_MS);
|
|
2141
|
+
const staleTime = isStale && live ? fmtHhMm(live.asOfUtc, ui?.locale ?? "en") : null;
|
|
2142
|
+
const [, setStaleTick] = useState8(0);
|
|
2143
|
+
const watchStale = live != null && !isClosed && !isStale;
|
|
2144
|
+
useEffect3(() => {
|
|
2145
|
+
if (!watchStale) return;
|
|
2146
|
+
const id = window.setTimeout(() => setStaleTick((n) => n + 1), Math.max(0, asOfMs + STALE_AFTER_MS - Date.now()) + 1);
|
|
2147
|
+
return () => window.clearTimeout(id);
|
|
2148
|
+
}, [watchStale, asOfMs]);
|
|
2149
|
+
const showPin = !!monitor && vm.pinEnabled && !live;
|
|
2043
2150
|
const history = showPin && !pinRef && !hideHeader ? monitor : void 0;
|
|
2044
|
-
const showHeader = !!vm.competition || vm.isLive || !!kickoff || showPin;
|
|
2151
|
+
const showHeader = !!vm.competition || vm.isLive || !!kickoff || showPin || !!live;
|
|
2045
2152
|
return /* @__PURE__ */ jsxs10("div", { className: hideHeader ? "ss-w-mocard ss-w-mocard--bare" : "ss-w-mocard", children: [
|
|
2046
2153
|
!hideHeader && /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
2047
2154
|
showHeader ? /* @__PURE__ */ jsxs10("div", { className: "ss-w-bi-header", children: [
|
|
@@ -2050,7 +2157,14 @@ function MarketOddsCard({ vm, ui, slip, monitor, inSlipIds, hideHeader, pinRef }
|
|
|
2050
2157
|
/* @__PURE__ */ jsx15("span", { className: "ss-w-bi-comp", children: vm.competition })
|
|
2051
2158
|
] }) : null }),
|
|
2052
2159
|
/* @__PURE__ */ jsxs10("span", { className: "ss-w-bi-header-right", children: [
|
|
2053
|
-
|
|
2160
|
+
live && !isClosed ? /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
2161
|
+
/* @__PURE__ */ jsxs10("span", { className: "ss-w-bi-score", children: [
|
|
2162
|
+
live.homeScore,
|
|
2163
|
+
"\u2013",
|
|
2164
|
+
live.awayScore
|
|
2165
|
+
] }),
|
|
2166
|
+
live.minuteLabel ? /* @__PURE__ */ jsx15("span", { className: "ss-w-bi-min", children: live.minuteLabel }) : null
|
|
2167
|
+
] }) : !live && vm.isLive ? /* @__PURE__ */ jsxs10("span", { className: "ss-w-bi-live", children: [
|
|
2054
2168
|
/* @__PURE__ */ jsx15("span", { className: "ss-w-bi-live-dot", "aria-hidden": "true" }),
|
|
2055
2169
|
ui?.liveLabel ?? "Live"
|
|
2056
2170
|
] }) : null,
|
|
@@ -2080,14 +2194,17 @@ function MarketOddsCard({ vm, ui, slip, monitor, inSlipIds, hideHeader, pinRef }
|
|
|
2080
2194
|
] }) : null
|
|
2081
2195
|
] }),
|
|
2082
2196
|
vm.statText && !hideHeader && !pinRef ? /* @__PURE__ */ jsx15("div", { className: "ss-w-mo-stat", children: vm.statText }) : null,
|
|
2083
|
-
/* @__PURE__ */
|
|
2084
|
-
|
|
2085
|
-
|
|
2197
|
+
isClosed ? /* @__PURE__ */ jsx15("div", { className: "ss-w-mo-closed", children: closedReason === "suspended" ? /* @__PURE__ */ jsx15("span", { children: ui?.liveCard.suspended ?? "Market suspended" }) : closedReason === "no_live_price" ? /* @__PURE__ */ jsx15("span", { children: ui?.liveCard.noPrice ?? "No live price available" }) : closedReason === "full_time" ? /* @__PURE__ */ jsx15("span", { children: ui?.liveCard.fullTime ?? "Full time" }) : null }) : /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
2198
|
+
/* @__PURE__ */ jsxs10("div", { className: "ss-w-mo-rows", children: [
|
|
2199
|
+
vm.mainRows.map((row) => /* @__PURE__ */ jsx15(OddsRow, { vm, row, ui, slip, inSlipIds, history, suppressSlip: isStale }, row.key)),
|
|
2200
|
+
open ? vm.moreRows.map((row) => /* @__PURE__ */ jsx15(OddsRow, { vm, row, ui, slip, inSlipIds, history, suppressSlip: isStale }, row.key)) : null
|
|
2201
|
+
] }),
|
|
2202
|
+
vm.moreRows.length > 0 ? /* @__PURE__ */ jsxs10("button", { type: "button", className: "ss-w-mo-more", "aria-expanded": open, onClick: () => setOpen((v) => !v), children: [
|
|
2203
|
+
/* @__PURE__ */ jsx15("span", { children: open ? ui?.hideLines ?? "Hide lines" : `${ui?.moreLines ?? "More lines"} (${vm.moreRows.length})` }),
|
|
2204
|
+
/* @__PURE__ */ jsx15("span", { className: `ss-w-mo-more-chev${open ? " ss-w-mo-more-chev-up" : ""}`, "aria-hidden": "true" })
|
|
2205
|
+
] }) : null,
|
|
2206
|
+
staleTime ? /* @__PURE__ */ jsx15("p", { className: "ss-w-mo-stale", children: ui?.liveCard.stale(staleTime) ?? `Prices from ${staleTime}` }) : null
|
|
2086
2207
|
] }),
|
|
2087
|
-
vm.moreRows.length > 0 ? /* @__PURE__ */ jsxs10("button", { type: "button", className: "ss-w-mo-more", "aria-expanded": open, onClick: () => setOpen((v) => !v), children: [
|
|
2088
|
-
/* @__PURE__ */ jsx15("span", { children: open ? ui?.hideLines ?? "Hide lines" : `${ui?.moreLines ?? "More lines"} (${vm.moreRows.length})` }),
|
|
2089
|
-
/* @__PURE__ */ jsx15("span", { className: `ss-w-mo-more-chev${open ? " ss-w-mo-more-chev-up" : ""}`, "aria-hidden": "true" })
|
|
2090
|
-
] }) : null,
|
|
2091
2208
|
vm.bookmaker ? /* @__PURE__ */ jsx15("div", { className: "ss-w-mo-foot", children: /* @__PURE__ */ jsx15("span", { children: vm.bookmaker }) }) : null,
|
|
2092
2209
|
vm.disclaimer ? /* @__PURE__ */ jsx15("p", { className: "ss-w-bi-disc", children: vm.disclaimer }) : null
|
|
2093
2210
|
] });
|
|
@@ -2098,7 +2215,7 @@ function MarketOddsCardList({ cards, ui, slip, monitor, inSlipIds, hideHeader, p
|
|
|
2098
2215
|
}
|
|
2099
2216
|
|
|
2100
2217
|
// src/components/CompactOddsList.tsx
|
|
2101
|
-
import { useEffect as
|
|
2218
|
+
import { useEffect as useEffect4, useRef as useRef2, useState as useState9 } from "react";
|
|
2102
2219
|
|
|
2103
2220
|
// src/compactList.ts
|
|
2104
2221
|
var COMPACT_MIN_CARDS = 3;
|
|
@@ -2215,7 +2332,7 @@ function CompactOddsList({ list, ui, slip, monitor }) {
|
|
|
2215
2332
|
const hidden = list.rowCount - shown;
|
|
2216
2333
|
const [focusIndex, setFocusIndex] = useState9(null);
|
|
2217
2334
|
const focusRef = useRef2(null);
|
|
2218
|
-
|
|
2335
|
+
useEffect4(() => {
|
|
2219
2336
|
if (focusIndex !== null) focusRef.current?.focus();
|
|
2220
2337
|
}, [focusIndex]);
|
|
2221
2338
|
let left = shown;
|
|
@@ -2516,7 +2633,7 @@ function FixtureCard({ card }) {
|
|
|
2516
2633
|
}
|
|
2517
2634
|
|
|
2518
2635
|
// src/components/dashboard/CountdownCard.tsx
|
|
2519
|
-
import { Fragment as Fragment6, useEffect as
|
|
2636
|
+
import { Fragment as Fragment6, useEffect as useEffect5, useState as useState10 } from "react";
|
|
2520
2637
|
import { jsx as jsx22, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2521
2638
|
var DAY_MS = 864e5;
|
|
2522
2639
|
function remainingSegments(kickoffUnix, nowMs, withSeconds) {
|
|
@@ -2540,7 +2657,7 @@ function CountdownCard({ card }) {
|
|
|
2540
2657
|
() => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
2541
2658
|
);
|
|
2542
2659
|
const withinDay = data.kickoffUnix * 1e3 - nowMs < DAY_MS;
|
|
2543
|
-
|
|
2660
|
+
useEffect5(() => {
|
|
2544
2661
|
if (reduced) return;
|
|
2545
2662
|
const id = window.setInterval(() => setNowMs(Date.now()), withinDay ? 1e3 : 6e4);
|
|
2546
2663
|
return () => window.clearInterval(id);
|
|
@@ -2900,7 +3017,7 @@ function RestingFormCard({ card }) {
|
|
|
2900
3017
|
import { useRef as useRef3, useState as useState14 } from "react";
|
|
2901
3018
|
|
|
2902
3019
|
// src/useLiveClock.ts
|
|
2903
|
-
import { useEffect as
|
|
3020
|
+
import { useEffect as useEffect6, useState as useState13 } from "react";
|
|
2904
3021
|
var TICK_MS = 6e4;
|
|
2905
3022
|
var MAX_LIVE_TICK_MINUTES = 15;
|
|
2906
3023
|
function computeDisplayMinute(minute, clockRunning, receivedAtMs) {
|
|
@@ -2912,7 +3029,7 @@ function useLiveClock(minute, clockRunning, receivedAtMs) {
|
|
|
2912
3029
|
const [displayMinute, setDisplayMinute] = useState13(
|
|
2913
3030
|
() => computeDisplayMinute(minute, clockRunning, receivedAtMs)
|
|
2914
3031
|
);
|
|
2915
|
-
|
|
3032
|
+
useEffect6(() => {
|
|
2916
3033
|
setDisplayMinute(computeDisplayMinute(minute, clockRunning, receivedAtMs));
|
|
2917
3034
|
if (!clockRunning) return;
|
|
2918
3035
|
const id = window.setInterval(() => {
|
|
@@ -3276,7 +3393,7 @@ function MessageList({ controller, ui, slip, onDashboardTap, monitor }) {
|
|
|
3276
3393
|
const endRef = useRef4(null);
|
|
3277
3394
|
const pinnedRef = useRef4(true);
|
|
3278
3395
|
const anchoredTurnRef = useRef4(null);
|
|
3279
|
-
|
|
3396
|
+
useEffect7(() => {
|
|
3280
3397
|
const anchor = turnAnchor(state.messages);
|
|
3281
3398
|
const list = listRef.current;
|
|
3282
3399
|
if (anchor && anchor.turnId !== anchoredTurnRef.current) {
|
|
@@ -3333,11 +3450,15 @@ function MessageList({ controller, ui, slip, onDashboardTap, monitor }) {
|
|
|
3333
3450
|
}
|
|
3334
3451
|
|
|
3335
3452
|
// src/components/ChatInput.tsx
|
|
3336
|
-
import { useState as useState16 } from "react";
|
|
3453
|
+
import { forwardRef, useEffect as useEffect8, useImperativeHandle, useRef as useRef5, useState as useState16 } from "react";
|
|
3337
3454
|
import { jsx as jsx46, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
3338
|
-
|
|
3455
|
+
var ChatInput = forwardRef(function ChatInput2({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message", disabled = false, onType }, ref) {
|
|
3339
3456
|
const [value, setValue] = useState16("");
|
|
3457
|
+
const textareaRef = useRef5(null);
|
|
3458
|
+
const stopRef = useRef5(null);
|
|
3459
|
+
const textBoxWantsFocusRef = useRef5(false);
|
|
3340
3460
|
const submit = () => {
|
|
3461
|
+
if (disabled) return;
|
|
3341
3462
|
const t = value.trim();
|
|
3342
3463
|
if (!t) return;
|
|
3343
3464
|
onSend(t);
|
|
@@ -3349,11 +3470,48 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
|
|
|
3349
3470
|
submit();
|
|
3350
3471
|
}
|
|
3351
3472
|
};
|
|
3473
|
+
useImperativeHandle(ref, () => ({
|
|
3474
|
+
focus() {
|
|
3475
|
+
const box = textareaRef.current;
|
|
3476
|
+
if (box && !box.disabled) {
|
|
3477
|
+
textBoxWantsFocusRef.current = false;
|
|
3478
|
+
box.focus();
|
|
3479
|
+
return;
|
|
3480
|
+
}
|
|
3481
|
+
textBoxWantsFocusRef.current = true;
|
|
3482
|
+
stopRef.current?.focus();
|
|
3483
|
+
}
|
|
3484
|
+
}), []);
|
|
3485
|
+
useEffect8(() => {
|
|
3486
|
+
if (streaming || disabled || !textBoxWantsFocusRef.current) return;
|
|
3487
|
+
textBoxWantsFocusRef.current = false;
|
|
3488
|
+
const box = textareaRef.current;
|
|
3489
|
+
if (!box) return;
|
|
3490
|
+
const active = box.getRootNode().activeElement;
|
|
3491
|
+
const input = box.closest(".ss-w-input");
|
|
3492
|
+
const isWithinComponent = input?.contains(active);
|
|
3493
|
+
if (!active || active === box.ownerDocument.body || isWithinComponent) box.focus();
|
|
3494
|
+
}, [streaming, disabled]);
|
|
3352
3495
|
return /* @__PURE__ */ jsxs40("div", { className: "ss-w-input", children: [
|
|
3353
|
-
/* @__PURE__ */ jsx46(
|
|
3354
|
-
|
|
3496
|
+
/* @__PURE__ */ jsx46(
|
|
3497
|
+
"textarea",
|
|
3498
|
+
{
|
|
3499
|
+
ref: textareaRef,
|
|
3500
|
+
className: "ss-w-textarea",
|
|
3501
|
+
rows: 1,
|
|
3502
|
+
placeholder,
|
|
3503
|
+
value,
|
|
3504
|
+
disabled: streaming || disabled,
|
|
3505
|
+
onChange: (e) => {
|
|
3506
|
+
setValue(e.target.value);
|
|
3507
|
+
onType?.();
|
|
3508
|
+
},
|
|
3509
|
+
onKeyDown
|
|
3510
|
+
}
|
|
3511
|
+
),
|
|
3512
|
+
streaming ? /* @__PURE__ */ jsx46("button", { ref: stopRef, type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, disabled, onClick: onStop, children: /* @__PURE__ */ jsx46("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx46("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, disabled, onClick: submit, children: /* @__PURE__ */ jsx46(SendIcon, {}) })
|
|
3355
3513
|
] });
|
|
3356
|
-
}
|
|
3514
|
+
});
|
|
3357
3515
|
|
|
3358
3516
|
// src/components/TabBar.tsx
|
|
3359
3517
|
import { jsx as jsx47, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
@@ -3836,7 +3994,7 @@ function TodayPanel({ discovery, ui, monitor }) {
|
|
|
3836
3994
|
}
|
|
3837
3995
|
|
|
3838
3996
|
// src/components/GamesPanel.tsx
|
|
3839
|
-
import { useEffect as
|
|
3997
|
+
import { useEffect as useEffect12, useState as useState31 } from "react";
|
|
3840
3998
|
|
|
3841
3999
|
// src/components/GamesLoginWall.tsx
|
|
3842
4000
|
import { jsx as jsx55 } from "react/jsx-runtime";
|
|
@@ -3845,7 +4003,7 @@ function GamesLoginWall({ ui }) {
|
|
|
3845
4003
|
}
|
|
3846
4004
|
|
|
3847
4005
|
// src/components/GamesHub.tsx
|
|
3848
|
-
import { useEffect as
|
|
4006
|
+
import { useEffect as useEffect9, useState as useState18 } from "react";
|
|
3849
4007
|
|
|
3850
4008
|
// src/components/games/ui/gameMeta.ts
|
|
3851
4009
|
function prettifyGameId(gameId) {
|
|
@@ -4031,7 +4189,7 @@ function pickHeroGame(games) {
|
|
|
4031
4189
|
var tileKey = (g) => `${g.gameId}-${g.gameDay}-${g.fixtureId}`;
|
|
4032
4190
|
function GamesHub({ games, myWeeklyRow, weeklyPrize, onOpen, ui }) {
|
|
4033
4191
|
const [, setTick] = useState18(0);
|
|
4034
|
-
|
|
4192
|
+
useEffect9(() => {
|
|
4035
4193
|
const id = setInterval(() => setTick((t) => t + 1), 6e4);
|
|
4036
4194
|
return () => clearInterval(id);
|
|
4037
4195
|
}, []);
|
|
@@ -4091,10 +4249,10 @@ function GamesHub({ games, myWeeklyRow, weeklyPrize, onOpen, ui }) {
|
|
|
4091
4249
|
}
|
|
4092
4250
|
|
|
4093
4251
|
// src/components/GameScreenHost.tsx
|
|
4094
|
-
import { useEffect as
|
|
4252
|
+
import { useEffect as useEffect11, useRef as useRef8, useState as useState30 } from "react";
|
|
4095
4253
|
|
|
4096
4254
|
// src/useGamePlay.ts
|
|
4097
|
-
import { useCallback as useCallback4, useRef as
|
|
4255
|
+
import { useCallback as useCallback4, useRef as useRef6, useState as useState19 } from "react";
|
|
4098
4256
|
function toSubmitResult(payload) {
|
|
4099
4257
|
return {
|
|
4100
4258
|
// 'pending' means the play is recorded but awaiting resolution (fixture games)
|
|
@@ -4109,12 +4267,12 @@ function useGamePlay(conversation, { gameId, gameDay, fixtureId, locale, refresh
|
|
|
4109
4267
|
const [result, setResult] = useState19(null);
|
|
4110
4268
|
const [submitting, setSubmitting] = useState19(false);
|
|
4111
4269
|
const [rawStatus, setRawStatus] = useState19(null);
|
|
4112
|
-
const inFlightRef =
|
|
4113
|
-
const convRef =
|
|
4270
|
+
const inFlightRef = useRef6(false);
|
|
4271
|
+
const convRef = useRef6(conversation);
|
|
4114
4272
|
convRef.current = conversation;
|
|
4115
|
-
const optsRef =
|
|
4273
|
+
const optsRef = useRef6({ gameId, gameDay, fixtureId, locale, refresh });
|
|
4116
4274
|
optsRef.current = { gameId, gameDay, fixtureId, locale, refresh };
|
|
4117
|
-
const resultRef =
|
|
4275
|
+
const resultRef = useRef6(null);
|
|
4118
4276
|
resultRef.current = result;
|
|
4119
4277
|
const refetch = useCallback4(async () => {
|
|
4120
4278
|
const conv = convRef.current;
|
|
@@ -5044,7 +5202,7 @@ function CareerPathGame({
|
|
|
5044
5202
|
}
|
|
5045
5203
|
|
|
5046
5204
|
// src/components/games/BeatTheGridGame.tsx
|
|
5047
|
-
import React, { useEffect as
|
|
5205
|
+
import React, { useEffect as useEffect10, useId as useId4, useRef as useRef7, useState as useState24 } from "react";
|
|
5048
5206
|
import { Fragment as Fragment14, jsx as jsx74, jsxs as jsxs61 } from "react/jsx-runtime";
|
|
5049
5207
|
function criterionIconKind(label, key) {
|
|
5050
5208
|
const l = (key ?? label).toLowerCase();
|
|
@@ -5127,8 +5285,8 @@ function SearchSheet({
|
|
|
5127
5285
|
const s = ui.games.beatTheGrid;
|
|
5128
5286
|
const [query, setQuery] = useState24("");
|
|
5129
5287
|
const inputId = useId4();
|
|
5130
|
-
const inputRef =
|
|
5131
|
-
|
|
5288
|
+
const inputRef = useRef7(null);
|
|
5289
|
+
useEffect10(() => {
|
|
5132
5290
|
inputRef.current?.focus();
|
|
5133
5291
|
}, [row, col]);
|
|
5134
5292
|
const filtered = playerPool.filter(
|
|
@@ -6430,7 +6588,7 @@ function GameScreenHost({
|
|
|
6430
6588
|
}) {
|
|
6431
6589
|
const { gameId, fixtureId, gameDay = "", lockAtUtc = null } = selected;
|
|
6432
6590
|
const [configJson, setConfigJson] = useState30(null);
|
|
6433
|
-
|
|
6591
|
+
useEffect11(() => {
|
|
6434
6592
|
setConfigJson(null);
|
|
6435
6593
|
if (!conversation) return;
|
|
6436
6594
|
let cancelled = false;
|
|
@@ -6453,12 +6611,12 @@ function GameScreenHost({
|
|
|
6453
6611
|
fixtureId: fixtureId || void 0,
|
|
6454
6612
|
locale: ui.games.lang
|
|
6455
6613
|
});
|
|
6456
|
-
|
|
6614
|
+
useEffect11(() => {
|
|
6457
6615
|
if (conversation) hydrate();
|
|
6458
6616
|
}, [conversation, gameId]);
|
|
6459
|
-
const hydrateRef =
|
|
6617
|
+
const hydrateRef = useRef8(hydrate);
|
|
6460
6618
|
hydrateRef.current = hydrate;
|
|
6461
|
-
|
|
6619
|
+
useEffect11(() => {
|
|
6462
6620
|
if (!FIXTURE_GAME_IDS.has(gameId)) return;
|
|
6463
6621
|
if (rawStatus !== "pending") return;
|
|
6464
6622
|
const timerId = setInterval(() => {
|
|
@@ -6594,7 +6752,7 @@ function GamesPanel({
|
|
|
6594
6752
|
refreshHub
|
|
6595
6753
|
}) {
|
|
6596
6754
|
const [selected, setSelected] = useState31(null);
|
|
6597
|
-
|
|
6755
|
+
useEffect12(() => {
|
|
6598
6756
|
ensureGamesFonts();
|
|
6599
6757
|
}, []);
|
|
6600
6758
|
if (!loggedIn) return /* @__PURE__ */ jsx83(GamesLoginWall, { ui });
|
|
@@ -6789,12 +6947,440 @@ function LeaderboardPanel({ loggedIn, data, loading, onPlay, ui }) {
|
|
|
6789
6947
|
return /* @__PURE__ */ jsx87("div", { className: "ss-w-mon-list", children: /* @__PURE__ */ jsx87(Leaderboard, { data: data ?? EMPTY_PAYLOAD, ui, onPlay }) });
|
|
6790
6948
|
}
|
|
6791
6949
|
|
|
6792
|
-
// src/components/
|
|
6950
|
+
// src/components/PresetSpeedDial.tsx
|
|
6951
|
+
import { useId as useId9 } from "react";
|
|
6793
6952
|
import { jsx as jsx88, jsxs as jsxs72 } from "react/jsx-runtime";
|
|
6953
|
+
function PresetSpeedDial({ expanded, showDot, strings, onToggle, onOpen, rootRef, buttonRef }) {
|
|
6954
|
+
const pillsId = useId9();
|
|
6955
|
+
const tabIndex = expanded ? 0 : -1;
|
|
6956
|
+
return /* @__PURE__ */ jsxs72("div", { ref: rootRef, className: "ss-w-dial", children: [
|
|
6957
|
+
/* @__PURE__ */ jsxs72(
|
|
6958
|
+
"button",
|
|
6959
|
+
{
|
|
6960
|
+
ref: buttonRef,
|
|
6961
|
+
type: "button",
|
|
6962
|
+
className: "ss-w-dial-btn",
|
|
6963
|
+
"aria-expanded": expanded,
|
|
6964
|
+
"aria-controls": pillsId,
|
|
6965
|
+
"aria-label": strings.dialName,
|
|
6966
|
+
onClick: onToggle,
|
|
6967
|
+
children: [
|
|
6968
|
+
/* @__PURE__ */ jsx88("span", { className: "ss-w-dial-icon ss-w-dial-icon--open", children: /* @__PURE__ */ jsx88(SlidersIcon, {}) }),
|
|
6969
|
+
/* @__PURE__ */ jsx88("span", { className: "ss-w-dial-icon ss-w-dial-icon--close", children: /* @__PURE__ */ jsx88(CloseIcon, {}) }),
|
|
6970
|
+
showDot && !expanded ? /* @__PURE__ */ jsx88("span", { className: "ss-w-dial-dot", "aria-hidden": "true" }) : null
|
|
6971
|
+
]
|
|
6972
|
+
}
|
|
6973
|
+
),
|
|
6974
|
+
/* @__PURE__ */ jsxs72("div", { id: pillsId, className: "ss-w-dial-pills", "data-open": expanded ? "" : void 0, children: [
|
|
6975
|
+
/* @__PURE__ */ jsxs72("button", { type: "button", className: "ss-w-dial-pill", tabIndex, onClick: () => onOpen("numbers"), children: [
|
|
6976
|
+
/* @__PURE__ */ jsx88(SlidersIcon, {}),
|
|
6977
|
+
/* @__PURE__ */ jsx88("span", { className: "ss-w-dial-pill-label", children: strings.setNumbers })
|
|
6978
|
+
] }),
|
|
6979
|
+
/* @__PURE__ */ jsxs72("button", { type: "button", className: "ss-w-dial-pill", tabIndex, onClick: () => onOpen("combo"), children: [
|
|
6980
|
+
/* @__PURE__ */ jsx88(ComboIcon, {}),
|
|
6981
|
+
/* @__PURE__ */ jsx88("span", { className: "ss-w-dial-pill-label", children: strings.buildCombo })
|
|
6982
|
+
] })
|
|
6983
|
+
] })
|
|
6984
|
+
] });
|
|
6985
|
+
}
|
|
6986
|
+
|
|
6987
|
+
// src/components/PresetTools.tsx
|
|
6988
|
+
import { Fragment as Fragment20, useEffect as useEffect13, useId as useId10, useRef as useRef9, useState as useState33 } from "react";
|
|
6989
|
+
|
|
6990
|
+
// src/presetGrid.ts
|
|
6991
|
+
var DEFAULT_SINGLES_LIMITS = [1.3, 1.5, 1.7, 2];
|
|
6992
|
+
var DEFAULT_SINGLES_FLOORS = [1.5, 2, 2.5, 3];
|
|
6993
|
+
var DEFAULT_COMBO_TOTALS = [3, 5, 10, 20, 50];
|
|
6994
|
+
var FIXED_SINGLES_COUNT = 3;
|
|
6995
|
+
var MIN_COMBO_LEGS = 2;
|
|
6996
|
+
var MAX_COMBO_LEGS = 6;
|
|
6997
|
+
var START = { maxPrice: 1.5, minPrice: 2, comboTotal: 5, comboLegs: 3 };
|
|
6998
|
+
var finite2 = (v) => typeof v === "number" && Number.isFinite(v);
|
|
6999
|
+
var cents = (n) => Math.round(n * 100);
|
|
7000
|
+
function samePrice(a, b) {
|
|
7001
|
+
return cents(a) === cents(b);
|
|
7002
|
+
}
|
|
7003
|
+
function isPresetGrid(v) {
|
|
7004
|
+
if (!v || typeof v !== "object") return false;
|
|
7005
|
+
const g = v;
|
|
7006
|
+
return Array.isArray(g.singles) && Array.isArray(g.singlesFrom) && Array.isArray(g.combos) && finite2(g.maxLegs);
|
|
7007
|
+
}
|
|
7008
|
+
function drawable(p) {
|
|
7009
|
+
return !!p && finite2(p.price) && finite2(p.matchOddsId) && finite2(p.matchId) && typeof p.homeTeamName === "string" && typeof p.awayTeamName === "string";
|
|
7010
|
+
}
|
|
7011
|
+
function choices(defaults, present) {
|
|
7012
|
+
const out = [];
|
|
7013
|
+
for (const n of [...defaults, ...present]) {
|
|
7014
|
+
if (finite2(n) && n > 0 && !out.some((m) => samePrice(m, n))) out.push(n);
|
|
7015
|
+
}
|
|
7016
|
+
return out.sort((a, b) => a - b);
|
|
7017
|
+
}
|
|
7018
|
+
function singlesLimits(grid) {
|
|
7019
|
+
return choices(DEFAULT_SINGLES_LIMITS, grid.singles.map((c) => c?.price));
|
|
7020
|
+
}
|
|
7021
|
+
function singlesFloors(grid) {
|
|
7022
|
+
return choices(DEFAULT_SINGLES_FLOORS, grid.singlesFrom.map((c) => c?.price));
|
|
7023
|
+
}
|
|
7024
|
+
function comboTotals(grid) {
|
|
7025
|
+
return choices(DEFAULT_COMBO_TOTALS, grid.combos.map((c) => c?.total));
|
|
7026
|
+
}
|
|
7027
|
+
function comboLegChoices(grid) {
|
|
7028
|
+
const max = Math.min(MAX_COMBO_LEGS, Math.floor(grid.maxLegs));
|
|
7029
|
+
const out = [];
|
|
7030
|
+
for (let n = MIN_COMBO_LEGS; n <= max; n++) out.push(n);
|
|
7031
|
+
return out;
|
|
7032
|
+
}
|
|
7033
|
+
function cycle(values, current) {
|
|
7034
|
+
if (values.length === 0) return current;
|
|
7035
|
+
const i = values.findIndex((v) => samePrice(v, current));
|
|
7036
|
+
return values[(i + 1) % values.length] ?? current;
|
|
7037
|
+
}
|
|
7038
|
+
function startValue(values, preferred) {
|
|
7039
|
+
return values.find((v) => samePrice(v, preferred)) ?? values[0] ?? preferred;
|
|
7040
|
+
}
|
|
7041
|
+
function nearest(values, target) {
|
|
7042
|
+
const first = values[0];
|
|
7043
|
+
if (first === void 0) return target;
|
|
7044
|
+
if (!(target > 0)) return first;
|
|
7045
|
+
const distance = (v) => Math.abs(Math.log(v / target));
|
|
7046
|
+
return values.reduce((best, v) => distance(v) < distance(best) ? v : best, first);
|
|
7047
|
+
}
|
|
7048
|
+
function clampLegs(values, legs) {
|
|
7049
|
+
const lo = values[0];
|
|
7050
|
+
const hi = values[values.length - 1];
|
|
7051
|
+
if (lo === void 0 || hi === void 0) return legs;
|
|
7052
|
+
return Math.min(Math.max(Math.round(legs), lo), hi);
|
|
7053
|
+
}
|
|
7054
|
+
function findSingles(cells, price, count) {
|
|
7055
|
+
const cell = cells.find((c) => !!c && finite2(c.price) && samePrice(c.price, price) && c.count === count);
|
|
7056
|
+
return cell && Array.isArray(cell.picks) && cell.picks.length === count && cell.picks.every(drawable) ? cell : null;
|
|
7057
|
+
}
|
|
7058
|
+
function findCombo(grid, total, legs) {
|
|
7059
|
+
const cell = grid.combos.find((c) => !!c && finite2(c.total) && samePrice(c.total, total) && c.legs === legs);
|
|
7060
|
+
const combo = cell?.combo;
|
|
7061
|
+
const whole = !!combo && typeof combo.multiplication === "string" && combo.multiplication !== "" && Array.isArray(combo.legs) && combo.legs.length === legs && combo.legs.every(drawable);
|
|
7062
|
+
return cell && whole ? cell : null;
|
|
7063
|
+
}
|
|
7064
|
+
function idsOf(picks) {
|
|
7065
|
+
return { matchOddsIds: picks.map((p) => p.matchOddsId), matchIds: picks.map((p) => p.matchId) };
|
|
7066
|
+
}
|
|
7067
|
+
function maxPriceAsk(cell, s) {
|
|
7068
|
+
return {
|
|
7069
|
+
message: s.maxPriceMessage(cell.count, cell.price),
|
|
7070
|
+
displayText: s.maxPriceDisplay(cell.count, cell.price),
|
|
7071
|
+
preset: { kind: "max_price", price: cell.price, count: cell.count, ...idsOf(cell.picks) }
|
|
7072
|
+
};
|
|
7073
|
+
}
|
|
7074
|
+
function minPriceAsk(cell, s) {
|
|
7075
|
+
return {
|
|
7076
|
+
message: s.minPriceMessage(cell.count, cell.price),
|
|
7077
|
+
displayText: s.minPriceDisplay(cell.count, cell.price),
|
|
7078
|
+
preset: { kind: "min_price", price: cell.price, count: cell.count, ...idsOf(cell.picks) }
|
|
7079
|
+
};
|
|
7080
|
+
}
|
|
7081
|
+
function comboAsk(cell, s) {
|
|
7082
|
+
return {
|
|
7083
|
+
message: s.comboMessage(cell.legs, cell.total),
|
|
7084
|
+
displayText: s.comboDisplay(cell.legs, cell.total),
|
|
7085
|
+
preset: { kind: "combo", total: cell.total, legs: cell.legs, ...idsOf(cell.combo.legs) }
|
|
7086
|
+
};
|
|
7087
|
+
}
|
|
7088
|
+
|
|
7089
|
+
// src/components/PresetTools.tsx
|
|
7090
|
+
import { Fragment as Fragment21, jsx as jsx89, jsxs as jsxs73 } from "react/jsx-runtime";
|
|
7091
|
+
var FOCUSABLE = 'button:not([disabled]), [href], input:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
7092
|
+
function PresetToolModal({ title, closeLabel, onClose, children, footer }) {
|
|
7093
|
+
const dialogRef = useRef9(null);
|
|
7094
|
+
const titleId = useId10();
|
|
7095
|
+
useEffect13(() => {
|
|
7096
|
+
dialogRef.current?.focus();
|
|
7097
|
+
}, []);
|
|
7098
|
+
useEffect13(() => {
|
|
7099
|
+
const onKey = (e) => {
|
|
7100
|
+
if (e.key !== "Escape") return;
|
|
7101
|
+
e.stopPropagation();
|
|
7102
|
+
e.preventDefault();
|
|
7103
|
+
onClose();
|
|
7104
|
+
};
|
|
7105
|
+
window.addEventListener("keydown", onKey, true);
|
|
7106
|
+
return () => window.removeEventListener("keydown", onKey, true);
|
|
7107
|
+
}, [onClose]);
|
|
7108
|
+
const trapTab = (e) => {
|
|
7109
|
+
if (e.key !== "Tab") return;
|
|
7110
|
+
const dialog = dialogRef.current;
|
|
7111
|
+
if (!dialog) return;
|
|
7112
|
+
const items = Array.from(dialog.querySelectorAll(FOCUSABLE));
|
|
7113
|
+
const first = items[0];
|
|
7114
|
+
const last = items[items.length - 1];
|
|
7115
|
+
if (!first || !last) {
|
|
7116
|
+
e.preventDefault();
|
|
7117
|
+
return;
|
|
7118
|
+
}
|
|
7119
|
+
const active = dialog.getRootNode().activeElement;
|
|
7120
|
+
if (e.shiftKey && (active === first || active === dialog)) {
|
|
7121
|
+
e.preventDefault();
|
|
7122
|
+
last.focus();
|
|
7123
|
+
} else if (!e.shiftKey && active === last) {
|
|
7124
|
+
e.preventDefault();
|
|
7125
|
+
first.focus();
|
|
7126
|
+
}
|
|
7127
|
+
};
|
|
7128
|
+
return /* @__PURE__ */ jsx89("div", { className: "ss-w-tool-backdrop", children: /* @__PURE__ */ jsxs73("div", { ref: dialogRef, className: "ss-w-tool", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, tabIndex: -1, onKeyDown: trapTab, children: [
|
|
7129
|
+
/* @__PURE__ */ jsxs73("div", { className: "ss-w-tool-head", children: [
|
|
7130
|
+
/* @__PURE__ */ jsx89("h2", { id: titleId, className: "ss-w-tool-title", children: title }),
|
|
7131
|
+
/* @__PURE__ */ jsx89("button", { type: "button", className: "ss-w-tool-close", "aria-label": closeLabel, onClick: onClose, children: /* @__PURE__ */ jsx89(CloseIcon, {}) })
|
|
7132
|
+
] }),
|
|
7133
|
+
/* @__PURE__ */ jsx89("div", { className: "ss-w-tool-body", children }),
|
|
7134
|
+
footer ? /* @__PURE__ */ jsx89("div", { className: "ss-w-tool-foot", children: footer }) : null
|
|
7135
|
+
] }) });
|
|
7136
|
+
}
|
|
7137
|
+
function fillTemplate(template, slots) {
|
|
7138
|
+
return template.split(/(\{[a-z]+\})/).filter((part) => part !== "").map((part, i) => {
|
|
7139
|
+
const key = /^\{([a-z]+)\}$/.exec(part)?.[1];
|
|
7140
|
+
return /* @__PURE__ */ jsx89(Fragment20, { children: key !== void 0 && key in slots ? slots[key] : part }, i);
|
|
7141
|
+
});
|
|
7142
|
+
}
|
|
7143
|
+
function NumberButton({ value, label, onCycle }) {
|
|
7144
|
+
return /* @__PURE__ */ jsx89("button", { type: "button", className: "ss-w-tool-num", "aria-label": label, onClick: onCycle, children: value });
|
|
7145
|
+
}
|
|
7146
|
+
function SendButton({ label, display, disabled, onClick }) {
|
|
7147
|
+
return /* @__PURE__ */ jsx89("button", { type: "button", className: "ss-w-tool-send", "aria-label": `${label}: ${display}`, disabled, onClick, children: label });
|
|
7148
|
+
}
|
|
7149
|
+
function PickList({ picks, formatKickoff }) {
|
|
7150
|
+
return /* @__PURE__ */ jsx89("ul", { className: "ss-w-tool-picks", role: "list", children: picks.map((p) => /* @__PURE__ */ jsxs73("li", { className: "ss-w-tool-pick", children: [
|
|
7151
|
+
/* @__PURE__ */ jsxs73("span", { className: "ss-w-tool-pick-match", children: [
|
|
7152
|
+
p.homeTeamName,
|
|
7153
|
+
" \u2013 ",
|
|
7154
|
+
p.awayTeamName
|
|
7155
|
+
] }),
|
|
7156
|
+
/* @__PURE__ */ jsxs73("span", { className: "ss-w-tool-pick-sel", children: [
|
|
7157
|
+
p.marketLabel,
|
|
7158
|
+
": ",
|
|
7159
|
+
p.selectionLabel,
|
|
7160
|
+
p.line ? ` ${p.line}` : ""
|
|
7161
|
+
] }),
|
|
7162
|
+
/* @__PURE__ */ jsx89("span", { className: "ss-w-tool-pick-kick", children: formatKickoff(p.kickoffUtc) }),
|
|
7163
|
+
/* @__PURE__ */ jsx89("span", { className: "ss-w-tool-pick-price", children: p.price.toFixed(2) })
|
|
7164
|
+
] }, p.matchOddsId)) });
|
|
7165
|
+
}
|
|
7166
|
+
function ComboPreview({ cell, formatKickoff }) {
|
|
7167
|
+
return /* @__PURE__ */ jsxs73(Fragment21, { children: [
|
|
7168
|
+
/* @__PURE__ */ jsx89(PickList, { picks: cell.combo.legs, formatKickoff }),
|
|
7169
|
+
/* @__PURE__ */ jsx89("div", { className: "ss-w-tool-math", children: cell.combo.multiplication })
|
|
7170
|
+
] });
|
|
7171
|
+
}
|
|
7172
|
+
function Unavailable({ text }) {
|
|
7173
|
+
return /* @__PURE__ */ jsx89("p", { className: "ss-w-tool-unavailable", children: text });
|
|
7174
|
+
}
|
|
7175
|
+
function SetNumbersTool({ grid, strings, streaming, formatKickoff, onSend, onClose }) {
|
|
7176
|
+
const limits = singlesLimits(grid);
|
|
7177
|
+
const floors = singlesFloors(grid);
|
|
7178
|
+
const totals = comboTotals(grid);
|
|
7179
|
+
const legChoices = comboLegChoices(grid);
|
|
7180
|
+
const count = FIXED_SINGLES_COUNT;
|
|
7181
|
+
const [maxPrice, setMaxPrice] = useState33(() => startValue(limits, START.maxPrice));
|
|
7182
|
+
const [minPrice, setMinPrice] = useState33(() => startValue(floors, START.minPrice));
|
|
7183
|
+
const [total, setTotal] = useState33(() => startValue(totals, START.comboTotal));
|
|
7184
|
+
const [legs, setLegs] = useState33(() => startValue(legChoices, START.comboLegs));
|
|
7185
|
+
const [announcement, setAnnouncement] = useState33("");
|
|
7186
|
+
const maxCell = findSingles(grid.singles, maxPrice, count);
|
|
7187
|
+
const minCell = findSingles(grid.singlesFrom, minPrice, count);
|
|
7188
|
+
const comboCell = findCombo(grid, total, legs);
|
|
7189
|
+
const announce = (display, available) => setAnnouncement(available ? display : `${display}. ${strings.unavailable}`);
|
|
7190
|
+
const nextMax = () => {
|
|
7191
|
+
const v = cycle(limits, maxPrice);
|
|
7192
|
+
setMaxPrice(v);
|
|
7193
|
+
announce(strings.maxPriceDisplay(count, v), !!findSingles(grid.singles, v, count));
|
|
7194
|
+
};
|
|
7195
|
+
const nextMin = () => {
|
|
7196
|
+
const v = cycle(floors, minPrice);
|
|
7197
|
+
setMinPrice(v);
|
|
7198
|
+
announce(strings.minPriceDisplay(count, v), !!findSingles(grid.singlesFrom, v, count));
|
|
7199
|
+
};
|
|
7200
|
+
const nextLegs = () => {
|
|
7201
|
+
const v = cycle(legChoices, legs);
|
|
7202
|
+
setLegs(v);
|
|
7203
|
+
announce(strings.comboDisplay(v, total), !!findCombo(grid, total, v));
|
|
7204
|
+
};
|
|
7205
|
+
const nextTotal = () => {
|
|
7206
|
+
const v = cycle(totals, total);
|
|
7207
|
+
setTotal(v);
|
|
7208
|
+
announce(strings.comboDisplay(legs, v), !!findCombo(grid, v, legs));
|
|
7209
|
+
};
|
|
7210
|
+
return /* @__PURE__ */ jsxs73(PresetToolModal, { title: strings.setNumbers, closeLabel: strings.close, onClose, children: [
|
|
7211
|
+
/* @__PURE__ */ jsxs73("section", { className: "ss-w-tool-row", children: [
|
|
7212
|
+
/* @__PURE__ */ jsx89("p", { className: "ss-w-tool-ask", children: fillTemplate(strings.maxPriceRow, {
|
|
7213
|
+
count: String(count),
|
|
7214
|
+
price: /* @__PURE__ */ jsx89(NumberButton, { value: maxPrice.toFixed(2), label: strings.changeNumber(maxPrice.toFixed(2)), onCycle: nextMax })
|
|
7215
|
+
}) }),
|
|
7216
|
+
maxCell ? /* @__PURE__ */ jsx89(PickList, { picks: maxCell.picks, formatKickoff }) : /* @__PURE__ */ jsx89(Unavailable, { text: strings.unavailable }),
|
|
7217
|
+
/* @__PURE__ */ jsx89(SendButton, { label: strings.send, display: strings.maxPriceDisplay(count, maxPrice), disabled: !maxCell || streaming, onClick: () => {
|
|
7218
|
+
if (maxCell) onSend(maxPriceAsk(maxCell, strings));
|
|
7219
|
+
} })
|
|
7220
|
+
] }),
|
|
7221
|
+
/* @__PURE__ */ jsxs73("section", { className: "ss-w-tool-row", children: [
|
|
7222
|
+
/* @__PURE__ */ jsx89("p", { className: "ss-w-tool-ask", children: fillTemplate(strings.minPriceRow, {
|
|
7223
|
+
count: String(count),
|
|
7224
|
+
price: /* @__PURE__ */ jsx89(NumberButton, { value: minPrice.toFixed(2), label: strings.changeNumber(minPrice.toFixed(2)), onCycle: nextMin })
|
|
7225
|
+
}) }),
|
|
7226
|
+
minCell ? /* @__PURE__ */ jsx89(PickList, { picks: minCell.picks, formatKickoff }) : /* @__PURE__ */ jsx89(Unavailable, { text: strings.unavailable }),
|
|
7227
|
+
/* @__PURE__ */ jsx89(SendButton, { label: strings.send, display: strings.minPriceDisplay(count, minPrice), disabled: !minCell || streaming, onClick: () => {
|
|
7228
|
+
if (minCell) onSend(minPriceAsk(minCell, strings));
|
|
7229
|
+
} })
|
|
7230
|
+
] }),
|
|
7231
|
+
/* @__PURE__ */ jsxs73("section", { className: "ss-w-tool-row", children: [
|
|
7232
|
+
/* @__PURE__ */ jsx89("p", { className: "ss-w-tool-ask", children: fillTemplate(strings.comboRow, {
|
|
7233
|
+
legs: /* @__PURE__ */ jsx89(NumberButton, { value: String(legs), label: strings.changeNumber(String(legs)), onCycle: nextLegs }),
|
|
7234
|
+
total: /* @__PURE__ */ jsx89(NumberButton, { value: total.toFixed(1), label: strings.changeNumber(total.toFixed(1)), onCycle: nextTotal })
|
|
7235
|
+
}) }),
|
|
7236
|
+
comboCell ? /* @__PURE__ */ jsx89(ComboPreview, { cell: comboCell, formatKickoff }) : /* @__PURE__ */ jsx89(Unavailable, { text: strings.unavailable }),
|
|
7237
|
+
/* @__PURE__ */ jsx89(SendButton, { label: strings.send, display: strings.comboDisplay(legs, total), disabled: !comboCell || streaming, onClick: () => {
|
|
7238
|
+
if (comboCell) onSend(comboAsk(comboCell, strings));
|
|
7239
|
+
} })
|
|
7240
|
+
] }),
|
|
7241
|
+
/* @__PURE__ */ jsx89("p", { className: "ss-w-vh", "aria-live": "polite", children: announcement })
|
|
7242
|
+
] });
|
|
7243
|
+
}
|
|
7244
|
+
function BuildComboTool({ grid, strings, streaming, formatKickoff, initialTotal, initialLegs, onSend, onClose }) {
|
|
7245
|
+
const totals = comboTotals(grid);
|
|
7246
|
+
const legChoices = comboLegChoices(grid);
|
|
7247
|
+
const [total, setTotal] = useState33(() => initialTotal !== void 0 ? nearest(totals, initialTotal) : startValue(totals, START.comboTotal));
|
|
7248
|
+
const [legs, setLegs] = useState33(() => initialLegs !== void 0 ? clampLegs(legChoices, initialLegs) : startValue(legChoices, START.comboLegs));
|
|
7249
|
+
const [announcement, setAnnouncement] = useState33("");
|
|
7250
|
+
const cell = findCombo(grid, total, legs);
|
|
7251
|
+
const minLegs = legChoices[0] ?? legs;
|
|
7252
|
+
const maxLegs = legChoices[legChoices.length - 1] ?? legs;
|
|
7253
|
+
const display = strings.comboDisplay(legs, total);
|
|
7254
|
+
const choose = (nextTotal, nextLegs) => {
|
|
7255
|
+
setTotal(nextTotal);
|
|
7256
|
+
setLegs(nextLegs);
|
|
7257
|
+
const d = strings.comboDisplay(nextLegs, nextTotal);
|
|
7258
|
+
setAnnouncement(findCombo(grid, nextTotal, nextLegs) ? d : `${d}. ${strings.unavailable}`);
|
|
7259
|
+
};
|
|
7260
|
+
const footer = /* @__PURE__ */ jsx89(SendButton, { label: strings.send, display, disabled: !cell || streaming, onClick: () => {
|
|
7261
|
+
if (cell) onSend(comboAsk(cell, strings));
|
|
7262
|
+
} });
|
|
7263
|
+
return /* @__PURE__ */ jsxs73(PresetToolModal, { title: strings.buildCombo, closeLabel: strings.close, onClose, footer, children: [
|
|
7264
|
+
/* @__PURE__ */ jsxs73("div", { className: "ss-w-tool-section", role: "group", "aria-label": strings.totalLabel, children: [
|
|
7265
|
+
/* @__PURE__ */ jsx89("span", { className: "ss-w-tool-label", "aria-hidden": "true", children: strings.totalLabel }),
|
|
7266
|
+
/* @__PURE__ */ jsx89("div", { className: "ss-w-tool-chips", children: totals.map((t) => /* @__PURE__ */ jsx89("button", { type: "button", className: "ss-w-tool-chip", "aria-pressed": samePrice(t, total), onClick: () => choose(t, legs), children: t.toFixed(1) }, t)) })
|
|
7267
|
+
] }),
|
|
7268
|
+
/* @__PURE__ */ jsxs73("div", { className: "ss-w-tool-section", role: "group", "aria-label": strings.matchesLabel, children: [
|
|
7269
|
+
/* @__PURE__ */ jsx89("span", { className: "ss-w-tool-label", "aria-hidden": "true", children: strings.matchesLabel }),
|
|
7270
|
+
/* @__PURE__ */ jsxs73("div", { className: "ss-w-tool-stepper", children: [
|
|
7271
|
+
/* @__PURE__ */ jsx89("button", { type: "button", className: "ss-w-tool-step", "aria-label": strings.fewerMatches, disabled: legs <= minLegs, onClick: () => choose(total, legs - 1), children: "\u2212" }),
|
|
7272
|
+
/* @__PURE__ */ jsx89("span", { className: "ss-w-tool-count", children: legs }),
|
|
7273
|
+
/* @__PURE__ */ jsx89("button", { type: "button", className: "ss-w-tool-step", "aria-label": strings.moreMatches, disabled: legs >= maxLegs, onClick: () => choose(total, legs + 1), children: "+" })
|
|
7274
|
+
] })
|
|
7275
|
+
] }),
|
|
7276
|
+
cell ? /* @__PURE__ */ jsx89(ComboPreview, { cell, formatKickoff }) : /* @__PURE__ */ jsx89(Unavailable, { text: strings.unavailable }),
|
|
7277
|
+
/* @__PURE__ */ jsx89("p", { className: "ss-w-vh", "aria-live": "polite", children: announcement })
|
|
7278
|
+
] });
|
|
7279
|
+
}
|
|
7280
|
+
|
|
7281
|
+
// src/usePresetDial.ts
|
|
7282
|
+
import { useCallback as useCallback5, useEffect as useEffect14, useRef as useRef10, useState as useState34 } from "react";
|
|
7283
|
+
function sendInFlight(messages, isStreaming) {
|
|
7284
|
+
const last = messages[messages.length - 1];
|
|
7285
|
+
return isStreaming && !!last && last.role === "assistant" && !last.done && !last.isGreeting;
|
|
7286
|
+
}
|
|
7287
|
+
function lastUserId(messages) {
|
|
7288
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
7289
|
+
const m = messages[i];
|
|
7290
|
+
if (m && m.role === "user") return m.id;
|
|
7291
|
+
}
|
|
7292
|
+
return null;
|
|
7293
|
+
}
|
|
7294
|
+
var SCROLL_KEYS = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End", " "]);
|
|
7295
|
+
function usePresetDial({ layerRef, dialRef, dialButtonRef, messages, isStreaming, activeTab }) {
|
|
7296
|
+
const [expanded, setExpanded] = useState34(() => !sendInFlight(messages, isStreaming));
|
|
7297
|
+
const fold = useCallback5(() => setExpanded(false), []);
|
|
7298
|
+
const toggle = useCallback5(() => setExpanded((e) => !e), []);
|
|
7299
|
+
const currentUserId = lastUserId(messages);
|
|
7300
|
+
const userIdRef = useRef10(currentUserId);
|
|
7301
|
+
useEffect14(() => {
|
|
7302
|
+
if (currentUserId === userIdRef.current) return;
|
|
7303
|
+
userIdRef.current = currentUserId;
|
|
7304
|
+
setExpanded(false);
|
|
7305
|
+
}, [currentUserId]);
|
|
7306
|
+
const tabRef = useRef10(activeTab);
|
|
7307
|
+
useEffect14(() => {
|
|
7308
|
+
if (activeTab === tabRef.current) return;
|
|
7309
|
+
tabRef.current = activeTab;
|
|
7310
|
+
setExpanded(false);
|
|
7311
|
+
}, [activeTab]);
|
|
7312
|
+
const expandedRef = useRef10(expanded);
|
|
7313
|
+
expandedRef.current = expanded;
|
|
7314
|
+
useEffect14(() => {
|
|
7315
|
+
const onEscape = (e) => {
|
|
7316
|
+
const dial = dialRef.current;
|
|
7317
|
+
if (e.key !== "Escape" || !dial || !e.composedPath().includes(dial)) return;
|
|
7318
|
+
e.stopPropagation();
|
|
7319
|
+
e.preventDefault();
|
|
7320
|
+
if (!expandedRef.current) return;
|
|
7321
|
+
setExpanded(false);
|
|
7322
|
+
dialButtonRef.current?.focus();
|
|
7323
|
+
};
|
|
7324
|
+
window.addEventListener("keydown", onEscape, true);
|
|
7325
|
+
return () => window.removeEventListener("keydown", onEscape, true);
|
|
7326
|
+
}, [dialRef, dialButtonRef]);
|
|
7327
|
+
useEffect14(() => {
|
|
7328
|
+
if (!expanded) return;
|
|
7329
|
+
const layer = layerRef.current;
|
|
7330
|
+
const inDial = (e) => {
|
|
7331
|
+
const dial = dialRef.current;
|
|
7332
|
+
return !!dial && e.composedPath().includes(dial);
|
|
7333
|
+
};
|
|
7334
|
+
const inList = (e) => e.composedPath().some((n) => n instanceof Element && n.classList.contains("ss-w-list"));
|
|
7335
|
+
const onPress = (e) => {
|
|
7336
|
+
if (!inDial(e)) setExpanded(false);
|
|
7337
|
+
};
|
|
7338
|
+
const onScrollGesture = (e) => {
|
|
7339
|
+
if (inList(e) && !inDial(e)) setExpanded(false);
|
|
7340
|
+
};
|
|
7341
|
+
const onKey = (e) => {
|
|
7342
|
+
if (SCROLL_KEYS.has(e.key) && inList(e)) setExpanded(false);
|
|
7343
|
+
};
|
|
7344
|
+
const doc = layer?.ownerDocument ?? document;
|
|
7345
|
+
doc.addEventListener("pointerdown", onPress, true);
|
|
7346
|
+
layer?.addEventListener("wheel", onScrollGesture, { passive: true });
|
|
7347
|
+
layer?.addEventListener("touchmove", onScrollGesture, { passive: true });
|
|
7348
|
+
window.addEventListener("keydown", onKey, true);
|
|
7349
|
+
return () => {
|
|
7350
|
+
doc.removeEventListener("pointerdown", onPress, true);
|
|
7351
|
+
layer?.removeEventListener("wheel", onScrollGesture);
|
|
7352
|
+
layer?.removeEventListener("touchmove", onScrollGesture);
|
|
7353
|
+
window.removeEventListener("keydown", onKey, true);
|
|
7354
|
+
};
|
|
7355
|
+
}, [expanded, layerRef, dialRef]);
|
|
7356
|
+
return { expanded, toggle, fold };
|
|
7357
|
+
}
|
|
7358
|
+
|
|
7359
|
+
// src/presetSeen.ts
|
|
7360
|
+
var KEY = "ss-w-presets-tool-opened";
|
|
7361
|
+
var openedThisPage = false;
|
|
7362
|
+
function readToolSeen() {
|
|
7363
|
+
if (openedThisPage) return true;
|
|
7364
|
+
try {
|
|
7365
|
+
return window.localStorage.getItem(KEY) === "1";
|
|
7366
|
+
} catch {
|
|
7367
|
+
return false;
|
|
7368
|
+
}
|
|
7369
|
+
}
|
|
7370
|
+
function markToolSeen() {
|
|
7371
|
+
openedThisPage = true;
|
|
7372
|
+
try {
|
|
7373
|
+
window.localStorage.setItem(KEY, "1");
|
|
7374
|
+
} catch {
|
|
7375
|
+
}
|
|
7376
|
+
}
|
|
7377
|
+
|
|
7378
|
+
// src/components/ChatPanel.tsx
|
|
7379
|
+
import { jsx as jsx90, jsxs as jsxs74 } from "react/jsx-runtime";
|
|
6794
7380
|
var FULLSCREEN_QUERY = "(max-width: 639px)";
|
|
6795
7381
|
function useIsModal() {
|
|
6796
|
-
const [modal, setModal] =
|
|
6797
|
-
|
|
7382
|
+
const [modal, setModal] = useState35(false);
|
|
7383
|
+
useEffect15(() => {
|
|
6798
7384
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
6799
7385
|
const mql = window.matchMedia(FULLSCREEN_QUERY);
|
|
6800
7386
|
const sync = () => setModal(mql.matches);
|
|
@@ -6804,10 +7390,11 @@ function useIsModal() {
|
|
|
6804
7390
|
}, []);
|
|
6805
7391
|
return modal;
|
|
6806
7392
|
}
|
|
6807
|
-
|
|
7393
|
+
var HIDDEN_LAYER_STYLE = { position: "absolute", inset: 0, visibility: "hidden", display: "flex", flexDirection: "column" };
|
|
7394
|
+
function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, gamesResult, leaderboardResult, loggedIn, conversation, presetGrid }) {
|
|
6808
7395
|
const { state, close, stop, send, reset } = controller;
|
|
6809
7396
|
const isModal = useIsModal();
|
|
6810
|
-
|
|
7397
|
+
useEffect15(() => {
|
|
6811
7398
|
const onKey = (e) => {
|
|
6812
7399
|
if (e.key === "Escape") close();
|
|
6813
7400
|
};
|
|
@@ -6822,11 +7409,66 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
|
|
|
6822
7409
|
monitor?.setActiveTab("chat");
|
|
6823
7410
|
send(text, { grounding });
|
|
6824
7411
|
};
|
|
6825
|
-
|
|
7412
|
+
useEffect15(() => {
|
|
6826
7413
|
if (monitor && monitor.activeTab !== "chat" && state.isStreaming) monitor.markChatActivity();
|
|
6827
7414
|
}, [monitor, state.isStreaming, state.messages.length]);
|
|
6828
|
-
|
|
6829
|
-
|
|
7415
|
+
const onChatTab = !monitor || monitor.activeTab === "chat";
|
|
7416
|
+
const layerRef = useRef11(null);
|
|
7417
|
+
const dialRef = useRef11(null);
|
|
7418
|
+
const dialButtonRef = useRef11(null);
|
|
7419
|
+
const inputRef = useRef11(null);
|
|
7420
|
+
const focusAfterCloseRef = useRef11(null);
|
|
7421
|
+
const [tool, setTool] = useState35(null);
|
|
7422
|
+
const [toolSeen, setToolSeen] = useState35(readToolSeen);
|
|
7423
|
+
const dial = usePresetDial({
|
|
7424
|
+
layerRef,
|
|
7425
|
+
dialRef,
|
|
7426
|
+
dialButtonRef,
|
|
7427
|
+
messages: state.messages,
|
|
7428
|
+
isStreaming: state.isStreaming,
|
|
7429
|
+
activeTab: monitor?.activeTab ?? "chat"
|
|
7430
|
+
});
|
|
7431
|
+
const { fold } = dial;
|
|
7432
|
+
const showDial = !!presetGrid && onChatTab && !tool;
|
|
7433
|
+
const openTool = useCallback6((next) => {
|
|
7434
|
+
fold();
|
|
7435
|
+
markToolSeen();
|
|
7436
|
+
setToolSeen(true);
|
|
7437
|
+
setTool(next);
|
|
7438
|
+
}, [fold]);
|
|
7439
|
+
const onPill = useCallback6((kind) => openTool(kind === "numbers" ? { kind: "numbers" } : { kind: "combo" }), [openTool]);
|
|
7440
|
+
const toolsValue = useMemo(
|
|
7441
|
+
() => presetGrid ? { openBuilder: (total, legs) => openTool({ kind: "combo", total, legs }), changeLabel: ui.presets.changeCombo } : null,
|
|
7442
|
+
[presetGrid, openTool, ui.presets.changeCombo]
|
|
7443
|
+
);
|
|
7444
|
+
const closeTool = useCallback6(() => {
|
|
7445
|
+
focusAfterCloseRef.current = "dial";
|
|
7446
|
+
setTool(null);
|
|
7447
|
+
}, []);
|
|
7448
|
+
const sendAsk = useCallback6((ask) => {
|
|
7449
|
+
focusAfterCloseRef.current = "composer";
|
|
7450
|
+
setTool(null);
|
|
7451
|
+
monitor?.setActiveTab("chat");
|
|
7452
|
+
send(ask.message, { displayText: ask.displayText, preset: ask.preset });
|
|
7453
|
+
}, [monitor, send]);
|
|
7454
|
+
const formatKickoff = useCallback6((kickoffUtc) => {
|
|
7455
|
+
const k = ui.bettingKickoff(kickoffUtc);
|
|
7456
|
+
return `${k.dayLabel} ${k.time}`;
|
|
7457
|
+
}, [ui]);
|
|
7458
|
+
useEffect15(() => {
|
|
7459
|
+
if (tool) return;
|
|
7460
|
+
const target = focusAfterCloseRef.current;
|
|
7461
|
+
focusAfterCloseRef.current = null;
|
|
7462
|
+
if (target === "dial") dialButtonRef.current?.focus();
|
|
7463
|
+
else if (target === "composer") inputRef.current?.focus();
|
|
7464
|
+
}, [tool]);
|
|
7465
|
+
useEffect15(() => {
|
|
7466
|
+
if (!tool || onChatTab && presetGrid) return;
|
|
7467
|
+
focusAfterCloseRef.current = "composer";
|
|
7468
|
+
setTool(null);
|
|
7469
|
+
}, [tool, onChatTab, presetGrid]);
|
|
7470
|
+
return /* @__PURE__ */ jsxs74("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
|
|
7471
|
+
/* @__PURE__ */ jsx90(
|
|
6830
7472
|
ChatHeader,
|
|
6831
7473
|
{
|
|
6832
7474
|
onClose: close,
|
|
@@ -6839,7 +7481,7 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
|
|
|
6839
7481
|
moreOptionsLabel: ui.moreOptions
|
|
6840
7482
|
}
|
|
6841
7483
|
),
|
|
6842
|
-
monitor && /* @__PURE__ */
|
|
7484
|
+
monitor && /* @__PURE__ */ jsx90(
|
|
6843
7485
|
TabBar,
|
|
6844
7486
|
{
|
|
6845
7487
|
monitor,
|
|
@@ -6851,17 +7493,26 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
|
|
|
6851
7493
|
gamesEnabled: gamesResult?.enabled ?? false
|
|
6852
7494
|
}
|
|
6853
7495
|
),
|
|
6854
|
-
/* @__PURE__ */
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
7496
|
+
/* @__PURE__ */ jsxs74("div", { ref: layerRef, className: "ss-w-chat-layer", "data-dial": presetGrid ? "" : void 0, style: onChatTab ? void 0 : HIDDEN_LAYER_STYLE, children: [
|
|
7497
|
+
/* @__PURE__ */ jsx90(PresetToolsContext.Provider, { value: toolsValue, children: /* @__PURE__ */ jsx90(MessageList, { controller, ui, slip, onDashboardTap, monitor }) }),
|
|
7498
|
+
showDial ? /* @__PURE__ */ jsx90(
|
|
7499
|
+
PresetSpeedDial,
|
|
7500
|
+
{
|
|
7501
|
+
expanded: dial.expanded,
|
|
7502
|
+
showDot: !toolSeen,
|
|
7503
|
+
strings: ui.presets,
|
|
7504
|
+
onToggle: dial.toggle,
|
|
7505
|
+
onOpen: onPill,
|
|
7506
|
+
rootRef: dialRef,
|
|
7507
|
+
buttonRef: dialButtonRef
|
|
7508
|
+
}
|
|
7509
|
+
) : null,
|
|
7510
|
+
tool && presetGrid && onChatTab ? tool.kind === "numbers" ? /* @__PURE__ */ jsx90(SetNumbersTool, { grid: presetGrid, strings: ui.presets, streaming: state.isStreaming, formatKickoff, onSend: sendAsk, onClose: closeTool }) : /* @__PURE__ */ jsx90(BuildComboTool, { grid: presetGrid, strings: ui.presets, streaming: state.isStreaming, formatKickoff, initialTotal: tool.total, initialLegs: tool.legs, onSend: sendAsk, onClose: closeTool }) : null
|
|
7511
|
+
] }),
|
|
7512
|
+
monitor && discovery && monitor.activeTab === "today" && /* @__PURE__ */ jsx90(TodayPanel, { discovery, ui, monitor }),
|
|
7513
|
+
monitor && monitor.activeTab === "following" && /* @__PURE__ */ jsx90(MatchesPanel, { monitor, ui, slip, onAsk: handleAsk }),
|
|
7514
|
+
monitor && monitor.activeTab === "odds" && /* @__PURE__ */ jsx90(OddsPanel, { monitor, ui, slip }),
|
|
7515
|
+
monitor && monitor.activeTab === "games" && gamesResult && /* @__PURE__ */ jsx90(
|
|
6865
7516
|
GamesPanel,
|
|
6866
7517
|
{
|
|
6867
7518
|
loggedIn: !!loggedIn,
|
|
@@ -6875,7 +7526,7 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
|
|
|
6875
7526
|
}
|
|
6876
7527
|
}
|
|
6877
7528
|
),
|
|
6878
|
-
monitor && monitor.activeTab === "leaderboard" && leaderboardResult && /* @__PURE__ */
|
|
7529
|
+
monitor && monitor.activeTab === "leaderboard" && leaderboardResult && /* @__PURE__ */ jsx90(
|
|
6879
7530
|
LeaderboardPanel,
|
|
6880
7531
|
{
|
|
6881
7532
|
loggedIn: !!loggedIn,
|
|
@@ -6885,7 +7536,20 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
|
|
|
6885
7536
|
ui
|
|
6886
7537
|
}
|
|
6887
7538
|
),
|
|
6888
|
-
(!monitor || monitor.activeTab !== "games" && monitor.activeTab !== "leaderboard") && /* @__PURE__ */
|
|
7539
|
+
(!monitor || monitor.activeTab !== "games" && monitor.activeTab !== "leaderboard") && /* @__PURE__ */ jsx90(
|
|
7540
|
+
ChatInput,
|
|
7541
|
+
{
|
|
7542
|
+
ref: inputRef,
|
|
7543
|
+
placeholder: ui.placeholder,
|
|
7544
|
+
streaming: state.isStreaming,
|
|
7545
|
+
onSend: handleSend,
|
|
7546
|
+
onStop: stop,
|
|
7547
|
+
stopLabel: ui.stopResponse,
|
|
7548
|
+
sendLabel: ui.sendMessage,
|
|
7549
|
+
disabled: !!tool,
|
|
7550
|
+
onType: fold
|
|
7551
|
+
}
|
|
7552
|
+
)
|
|
6889
7553
|
] });
|
|
6890
7554
|
}
|
|
6891
7555
|
|
|
@@ -7094,15 +7758,15 @@ function toSlipEvent(name, entry) {
|
|
|
7094
7758
|
// src/useHostEvents.ts
|
|
7095
7759
|
function useHostEvents(opts) {
|
|
7096
7760
|
const { host, operatorId, currentUser, logger, isOpen, closePanel, onOpenMatch, openMatchLabel } = opts;
|
|
7097
|
-
const [added, setAdded] =
|
|
7098
|
-
const [settled, setSettled] =
|
|
7099
|
-
const markAdded =
|
|
7761
|
+
const [added, setAdded] = useState36({});
|
|
7762
|
+
const [settled, setSettled] = useState36({});
|
|
7763
|
+
const markAdded = useCallback7((matchOddsId) => {
|
|
7100
7764
|
setAdded((prev) => prev[matchOddsId] ? prev : { ...prev, [matchOddsId]: true });
|
|
7101
7765
|
}, []);
|
|
7102
|
-
const settle =
|
|
7766
|
+
const settle = useCallback7((matchOddsId) => {
|
|
7103
7767
|
setSettled((prev) => prev[matchOddsId] ? prev : { ...prev, [matchOddsId]: true });
|
|
7104
7768
|
}, []);
|
|
7105
|
-
const clearMarks =
|
|
7769
|
+
const clearMarks = useCallback7((ids) => {
|
|
7106
7770
|
const drop = (prev) => {
|
|
7107
7771
|
if (!ids.some((id) => prev[id])) return prev;
|
|
7108
7772
|
const next = { ...prev };
|
|
@@ -7113,26 +7777,26 @@ function useHostEvents(opts) {
|
|
|
7113
7777
|
setSettled(drop);
|
|
7114
7778
|
}, []);
|
|
7115
7779
|
const hasHost = host !== void 0;
|
|
7116
|
-
const ledger =
|
|
7780
|
+
const ledger = useMemo2(
|
|
7117
7781
|
() => hasHost ? createSlipLedger(`ss-w-slip:${operatorId}`) : null,
|
|
7118
7782
|
[hasHost, operatorId]
|
|
7119
7783
|
);
|
|
7120
|
-
const currentUserRef =
|
|
7784
|
+
const currentUserRef = useRef12(currentUser);
|
|
7121
7785
|
currentUserRef.current = currentUser;
|
|
7122
|
-
const recordAdd =
|
|
7786
|
+
const recordAdd = useCallback7((add) => {
|
|
7123
7787
|
ledger?.record({ id: add.selection.matchOddsId, market: add.market, ...add.pick, user: currentUserRef.current });
|
|
7124
7788
|
}, [ledger]);
|
|
7125
|
-
const slipState =
|
|
7789
|
+
const slipState = useMemo2(
|
|
7126
7790
|
() => ({ added, settled, settle, recordAdd }),
|
|
7127
7791
|
[added, settled, settle, recordAdd]
|
|
7128
7792
|
);
|
|
7129
7793
|
const hasOpenMatch = typeof onOpenMatch === "function";
|
|
7130
7794
|
const tracksViews = hasHost || hasOpenMatch;
|
|
7131
|
-
const views =
|
|
7795
|
+
const views = useMemo2(
|
|
7132
7796
|
() => tracksViews ? createMatchViews(`ss-w-views:${operatorId}`, { user: currentUser }) : null,
|
|
7133
7797
|
[tracksViews, operatorId, currentUser]
|
|
7134
7798
|
);
|
|
7135
|
-
const sinkRef =
|
|
7799
|
+
const sinkRef = useRef12(() => {
|
|
7136
7800
|
});
|
|
7137
7801
|
sinkRef.current = (command) => {
|
|
7138
7802
|
if (command.kind === "matchOpened") {
|
|
@@ -7153,11 +7817,11 @@ function useHostEvents(opts) {
|
|
|
7153
7817
|
logger?.log(toSlipEvent(name, entry));
|
|
7154
7818
|
}
|
|
7155
7819
|
};
|
|
7156
|
-
|
|
7820
|
+
useEffect16(() => {
|
|
7157
7821
|
if (!host) return;
|
|
7158
7822
|
return attachHost(host, (command) => sinkRef.current(command));
|
|
7159
7823
|
}, [host]);
|
|
7160
|
-
|
|
7824
|
+
useEffect16(() => {
|
|
7161
7825
|
if (!isOpen || !views) return;
|
|
7162
7826
|
const held = views.markOpened();
|
|
7163
7827
|
if (held.length === 0) return;
|
|
@@ -7166,7 +7830,7 @@ function useHostEvents(opts) {
|
|
|
7166
7830
|
}
|
|
7167
7831
|
void logger?.flushNow();
|
|
7168
7832
|
}, [isOpen, views, logger]);
|
|
7169
|
-
const openMatchRef =
|
|
7833
|
+
const openMatchRef = useRef12(() => {
|
|
7170
7834
|
});
|
|
7171
7835
|
openMatchRef.current = (matchId) => {
|
|
7172
7836
|
if (views?.view(matchId) === "log") {
|
|
@@ -7180,7 +7844,7 @@ function useHostEvents(opts) {
|
|
|
7180
7844
|
}
|
|
7181
7845
|
if (typeof window.matchMedia === "function" && window.matchMedia(FULLSCREEN_QUERY).matches) closePanel();
|
|
7182
7846
|
};
|
|
7183
|
-
const matchLink =
|
|
7847
|
+
const matchLink = useMemo2(
|
|
7184
7848
|
() => hasOpenMatch ? { open: (matchId) => openMatchRef.current(matchId), label: openMatchLabel } : null,
|
|
7185
7849
|
[hasOpenMatch, openMatchLabel]
|
|
7186
7850
|
);
|
|
@@ -7248,12 +7912,27 @@ function themeAttrs(ui) {
|
|
|
7248
7912
|
}
|
|
7249
7913
|
|
|
7250
7914
|
// src/useChatStream.ts
|
|
7251
|
-
import { useCallback as
|
|
7915
|
+
import { useCallback as useCallback8, useEffect as useEffect18, useMemo as useMemo3, useReducer as useReducer2, useRef as useRef14 } from "react";
|
|
7252
7916
|
import { WidgetSdkError as WidgetSdkError2 } from "@sensiblestats/widget-sdk";
|
|
7253
7917
|
|
|
7254
7918
|
// src/reducer.ts
|
|
7255
7919
|
import "@sensiblestats/widget-sdk";
|
|
7256
7920
|
var POPUP_MAX = 160;
|
|
7921
|
+
function markAwaitingLiveUpdate(messages, held = []) {
|
|
7922
|
+
if (!messages.some((m) => m.role === "assistant" && m.marketOdds.some((vm) => vm.live != null))) return messages;
|
|
7923
|
+
const heldFlags = /* @__PURE__ */ new Map();
|
|
7924
|
+
for (const m of held) {
|
|
7925
|
+
if (m.role !== "assistant") continue;
|
|
7926
|
+
for (const vm of m.marketOdds) if (vm.live != null) heldFlags.set(`${m.id}|${vm.id}`, vm.awaitingLiveUpdate === true);
|
|
7927
|
+
}
|
|
7928
|
+
return messages.map((m) => {
|
|
7929
|
+
if (m.role !== "assistant" || !m.marketOdds.some((vm) => vm.live != null)) return m;
|
|
7930
|
+
return {
|
|
7931
|
+
...m,
|
|
7932
|
+
marketOdds: m.marketOdds.map((vm) => vm.live != null ? { ...vm, awaitingLiveUpdate: heldFlags.get(`${m.id}|${vm.id}`) ?? true } : vm)
|
|
7933
|
+
};
|
|
7934
|
+
});
|
|
7935
|
+
}
|
|
7257
7936
|
function initialState(starters) {
|
|
7258
7937
|
return { isOpen: false, messages: [], isStreaming: false, status: null, actionsLoading: false, starters, configuredStarters: starters, error: null, lastUserInput: null, lastDisplayText: null, popupText: null, conversationId: null };
|
|
7259
7938
|
}
|
|
@@ -7345,7 +8024,7 @@ function reducer(state, action) {
|
|
|
7345
8024
|
case "RESTORE":
|
|
7346
8025
|
return {
|
|
7347
8026
|
...state,
|
|
7348
|
-
messages: action.messages,
|
|
8027
|
+
messages: markAwaitingLiveUpdate(action.messages, state.messages),
|
|
7349
8028
|
conversationId: action.conversationId,
|
|
7350
8029
|
isStreaming: false,
|
|
7351
8030
|
status: null,
|
|
@@ -7359,6 +8038,22 @@ function reducer(state, action) {
|
|
|
7359
8038
|
return { ...state, error: action.error, isStreaming: false, status: null, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, done: true })) };
|
|
7360
8039
|
case "STOP":
|
|
7361
8040
|
return { ...state, isStreaming: false, status: null, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, done: true })) };
|
|
8041
|
+
case "UPDATE_LIVE_CARD": {
|
|
8042
|
+
const { matchId, live, mainRows, moreRows, addToSlipEnabled } = action;
|
|
8043
|
+
return {
|
|
8044
|
+
...state,
|
|
8045
|
+
messages: state.messages.map((m) => {
|
|
8046
|
+
if (m.role !== "assistant") return m;
|
|
8047
|
+
if (!m.marketOdds.some((vm) => vm.matchId === matchId && vm.live != null)) return m;
|
|
8048
|
+
return {
|
|
8049
|
+
...m,
|
|
8050
|
+
marketOdds: m.marketOdds.map(
|
|
8051
|
+
(vm) => vm.matchId === matchId && vm.live != null ? { ...vm, live, mainRows, moreRows, addToSlipEnabled, awaitingLiveUpdate: false } : vm
|
|
8052
|
+
)
|
|
8053
|
+
};
|
|
8054
|
+
})
|
|
8055
|
+
};
|
|
8056
|
+
}
|
|
7362
8057
|
case "LIVE_REFRESH":
|
|
7363
8058
|
return {
|
|
7364
8059
|
...state,
|
|
@@ -7377,7 +8072,7 @@ function reducer(state, action) {
|
|
|
7377
8072
|
}
|
|
7378
8073
|
|
|
7379
8074
|
// src/useLiveDashboardPoll.ts
|
|
7380
|
-
import { useEffect as
|
|
8075
|
+
import { useEffect as useEffect17, useReducer, useRef as useRef13 } from "react";
|
|
7381
8076
|
var POLL_INTERVAL_MS = 6e4;
|
|
7382
8077
|
var LEAD_MS = 12e4;
|
|
7383
8078
|
var GIVE_UP_MS = 30 * 6e4;
|
|
@@ -7421,11 +8116,11 @@ function findEarliestWakeupMs(messages) {
|
|
|
7421
8116
|
function useLiveDashboardPoll({ messages, dispatch, getLiveDashboard }) {
|
|
7422
8117
|
const [, forceUpdate] = useReducer((x) => x + 1, 0);
|
|
7423
8118
|
const anchor = findAnchor(messages);
|
|
7424
|
-
const anchorRef =
|
|
8119
|
+
const anchorRef = useRef13(anchor);
|
|
7425
8120
|
anchorRef.current = anchor;
|
|
7426
|
-
const inFlightRef =
|
|
8121
|
+
const inFlightRef = useRef13(false);
|
|
7427
8122
|
const anchorKey = anchor ? `${anchor.messageId}:${anchor.kind}` : null;
|
|
7428
|
-
|
|
8123
|
+
useEffect17(() => {
|
|
7429
8124
|
if (!anchorKey || !anchorRef.current) return;
|
|
7430
8125
|
const tick = async () => {
|
|
7431
8126
|
if (inFlightRef.current) return;
|
|
@@ -7461,7 +8156,7 @@ function useLiveDashboardPoll({ messages, dispatch, getLiveDashboard }) {
|
|
|
7461
8156
|
}, POLL_INTERVAL_MS);
|
|
7462
8157
|
return () => window.clearInterval(id);
|
|
7463
8158
|
}, [anchorKey, dispatch, getLiveDashboard]);
|
|
7464
|
-
|
|
8159
|
+
useEffect17(() => {
|
|
7465
8160
|
if (anchorKey !== null) return;
|
|
7466
8161
|
const wakeAt = findEarliestWakeupMs(messages);
|
|
7467
8162
|
if (wakeAt === null) return;
|
|
@@ -7526,36 +8221,36 @@ function seedIdCounter(messages) {
|
|
|
7526
8221
|
return max + 1;
|
|
7527
8222
|
}
|
|
7528
8223
|
function useChatStream(conversation, starterTexts, greetingMessage, storageKey) {
|
|
7529
|
-
const boot =
|
|
8224
|
+
const boot = useMemo3(() => {
|
|
7530
8225
|
const payload = storageKey ? readConversation(storageKey, Date.now()) : null;
|
|
7531
8226
|
const restored = payload != null && payload !== "expired" && payload !== "corrupt";
|
|
7532
8227
|
const base = initialState(starterTexts.map(chipFromText));
|
|
7533
8228
|
if (restored) {
|
|
7534
|
-
return { state: { ...base, messages: payload.messages, conversationId: payload.conversationId, starters: [] }, restored: true, stale: false };
|
|
8229
|
+
return { state: { ...base, messages: markAwaitingLiveUpdate(payload.messages), conversationId: payload.conversationId, starters: [] }, restored: true, stale: false };
|
|
7535
8230
|
}
|
|
7536
8231
|
return { state: base, restored: false, stale: payload === "expired" || payload === "corrupt" };
|
|
7537
8232
|
}, []);
|
|
7538
8233
|
const [state, dispatch] = useReducer2(reducer, boot.state);
|
|
7539
|
-
const idRef =
|
|
7540
|
-
const handleRef =
|
|
7541
|
-
const fallbackTimerRef =
|
|
7542
|
-
const stateRef =
|
|
8234
|
+
const idRef = useRef14(seedIdCounter(boot.state.messages));
|
|
8235
|
+
const handleRef = useRef14(null);
|
|
8236
|
+
const fallbackTimerRef = useRef14(null);
|
|
8237
|
+
const stateRef = useRef14(state);
|
|
7543
8238
|
stateRef.current = state;
|
|
7544
|
-
const greetedRef =
|
|
7545
|
-
const hydratedRef =
|
|
7546
|
-
const restoreSeededRef =
|
|
8239
|
+
const greetedRef = useRef14(boot.restored);
|
|
8240
|
+
const hydratedRef = useRef14(false);
|
|
8241
|
+
const restoreSeededRef = useRef14(false);
|
|
7547
8242
|
if (!restoreSeededRef.current) {
|
|
7548
8243
|
restoreSeededRef.current = true;
|
|
7549
8244
|
if (boot.restored && boot.state.conversationId) conversation.conversationId = boot.state.conversationId;
|
|
7550
8245
|
}
|
|
7551
8246
|
const nextId = () => `m${idRef.current++}`;
|
|
7552
|
-
const clearFallbackTimer =
|
|
8247
|
+
const clearFallbackTimer = useCallback8(() => {
|
|
7553
8248
|
if (fallbackTimerRef.current != null) {
|
|
7554
8249
|
clearTimeout(fallbackTimerRef.current);
|
|
7555
8250
|
fallbackTimerRef.current = null;
|
|
7556
8251
|
}
|
|
7557
8252
|
}, []);
|
|
7558
|
-
const armFallbackTimer =
|
|
8253
|
+
const armFallbackTimer = useCallback8((onFire) => {
|
|
7559
8254
|
clearFallbackTimer();
|
|
7560
8255
|
fallbackTimerRef.current = setTimeout(() => {
|
|
7561
8256
|
fallbackTimerRef.current = null;
|
|
@@ -7564,7 +8259,7 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7564
8259
|
onFire();
|
|
7565
8260
|
}, STREAM_FALLBACK_TIMEOUT_MS);
|
|
7566
8261
|
}, [clearFallbackTimer]);
|
|
7567
|
-
const consume =
|
|
8262
|
+
const consume = useCallback8((handle, onError) => {
|
|
7568
8263
|
;
|
|
7569
8264
|
(async () => {
|
|
7570
8265
|
try {
|
|
@@ -7582,7 +8277,7 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7582
8277
|
}
|
|
7583
8278
|
})();
|
|
7584
8279
|
}, [clearFallbackTimer]);
|
|
7585
|
-
const run =
|
|
8280
|
+
const run = useCallback8((input, displayText) => {
|
|
7586
8281
|
handleRef.current?.cancel();
|
|
7587
8282
|
clearFallbackTimer();
|
|
7588
8283
|
dispatch({ kind: "USER_SENT", input, userId: nextId(), assistantId: nextId(), displayText });
|
|
@@ -7594,7 +8289,7 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7594
8289
|
});
|
|
7595
8290
|
consume(handle, (err) => dispatch({ kind: "STREAM_ERROR", error: err }));
|
|
7596
8291
|
}, [conversation, consume, clearFallbackTimer, armFallbackTimer]);
|
|
7597
|
-
const send =
|
|
8292
|
+
const send = useCallback8((text, opts) => {
|
|
7598
8293
|
const trimmed = text.trim();
|
|
7599
8294
|
if (!trimmed) return;
|
|
7600
8295
|
run({
|
|
@@ -7602,14 +8297,15 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7602
8297
|
actionId: opts?.actionId,
|
|
7603
8298
|
actionRef: opts?.actionRef,
|
|
7604
8299
|
grounding: opts?.grounding,
|
|
7605
|
-
clientCapabilities: [...CLIENT_CAPABILITIES]
|
|
8300
|
+
clientCapabilities: [...CLIENT_CAPABILITIES],
|
|
8301
|
+
...opts?.preset ? { preset: opts.preset } : {}
|
|
7606
8302
|
}, opts?.displayText);
|
|
7607
8303
|
}, [run]);
|
|
7608
|
-
const retry =
|
|
8304
|
+
const retry = useCallback8(() => {
|
|
7609
8305
|
const last = stateRef.current.lastUserInput;
|
|
7610
8306
|
if (last) run(last, stateRef.current.lastDisplayText ?? void 0);
|
|
7611
8307
|
}, [run]);
|
|
7612
|
-
const fireGreeting =
|
|
8308
|
+
const fireGreeting = useCallback8(() => {
|
|
7613
8309
|
const message = greetingMessage?.trim();
|
|
7614
8310
|
if (!message) return;
|
|
7615
8311
|
try {
|
|
@@ -7624,15 +8320,15 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7624
8320
|
dispatch({ kind: "STOP" });
|
|
7625
8321
|
}
|
|
7626
8322
|
}, [greetingMessage, conversation, consume, clearFallbackTimer, armFallbackTimer]);
|
|
7627
|
-
|
|
8323
|
+
useEffect18(() => {
|
|
7628
8324
|
if (greetedRef.current) return;
|
|
7629
8325
|
greetedRef.current = true;
|
|
7630
8326
|
fireGreeting();
|
|
7631
8327
|
}, [fireGreeting]);
|
|
7632
|
-
|
|
8328
|
+
useEffect18(() => {
|
|
7633
8329
|
if (boot.stale && storageKey) clearConversation(storageKey);
|
|
7634
8330
|
}, []);
|
|
7635
|
-
|
|
8331
|
+
useEffect18(() => {
|
|
7636
8332
|
if (!storageKey || state.isStreaming) return;
|
|
7637
8333
|
if (hydratedRef.current) {
|
|
7638
8334
|
hydratedRef.current = false;
|
|
@@ -7649,7 +8345,7 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7649
8345
|
if (persistable.length === 0) return;
|
|
7650
8346
|
writeConversation(storageKey, state.conversationId, persistable, Date.now());
|
|
7651
8347
|
}, [storageKey, state.isStreaming, state.messages, state.conversationId]);
|
|
7652
|
-
|
|
8348
|
+
useEffect18(() => {
|
|
7653
8349
|
if (!storageKey) return;
|
|
7654
8350
|
const onStorage = (e) => {
|
|
7655
8351
|
if (e.key !== storageKey || e.storageArea !== window.localStorage) return;
|
|
@@ -7673,25 +8369,25 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7673
8369
|
window.addEventListener("storage", onStorage);
|
|
7674
8370
|
return () => window.removeEventListener("storage", onStorage);
|
|
7675
8371
|
}, [storageKey, conversation, fireGreeting]);
|
|
7676
|
-
const open =
|
|
7677
|
-
const close =
|
|
8372
|
+
const open = useCallback8(() => dispatch({ kind: "OPEN" }), []);
|
|
8373
|
+
const close = useCallback8(() => {
|
|
7678
8374
|
handleRef.current?.cancel();
|
|
7679
8375
|
handleRef.current = null;
|
|
7680
8376
|
clearFallbackTimer();
|
|
7681
8377
|
dispatch({ kind: "CLOSE" });
|
|
7682
8378
|
}, [clearFallbackTimer]);
|
|
7683
|
-
const stop =
|
|
8379
|
+
const stop = useCallback8(() => {
|
|
7684
8380
|
handleRef.current?.cancel();
|
|
7685
8381
|
handleRef.current = null;
|
|
7686
8382
|
clearFallbackTimer();
|
|
7687
8383
|
dispatch({ kind: "STOP" });
|
|
7688
8384
|
}, [clearFallbackTimer]);
|
|
7689
|
-
const toggle =
|
|
8385
|
+
const toggle = useCallback8(() => {
|
|
7690
8386
|
if (stateRef.current.isOpen) close();
|
|
7691
8387
|
else open();
|
|
7692
8388
|
}, [close, open]);
|
|
7693
|
-
const dismissPopup =
|
|
7694
|
-
const reset =
|
|
8389
|
+
const dismissPopup = useCallback8(() => dispatch({ kind: "DISMISS_POPUP" }), []);
|
|
8390
|
+
const reset = useCallback8(() => {
|
|
7695
8391
|
handleRef.current?.cancel();
|
|
7696
8392
|
handleRef.current = null;
|
|
7697
8393
|
clearFallbackTimer();
|
|
@@ -7702,18 +8398,18 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7702
8398
|
greetedRef.current = true;
|
|
7703
8399
|
fireGreeting();
|
|
7704
8400
|
}, [storageKey, conversation, clearFallbackTimer, fireGreeting]);
|
|
7705
|
-
const getLiveDashboard =
|
|
8401
|
+
const getLiveDashboard = useCallback8(() => conversation.getLiveDashboard(), [conversation]);
|
|
7706
8402
|
useLiveDashboardPoll({ messages: state.messages, dispatch, getLiveDashboard });
|
|
7707
|
-
return { state, open, close, stop, toggle, send, retry, dismissPopup, reset };
|
|
8403
|
+
return { state, open, close, stop, toggle, send, retry, dismissPopup, reset, _dispatch: dispatch };
|
|
7708
8404
|
}
|
|
7709
8405
|
|
|
7710
8406
|
// src/useLiveMonitor.ts
|
|
7711
|
-
import { useCallback as
|
|
8407
|
+
import { useCallback as useCallback9, useEffect as useEffect19, useRef as useRef15, useState as useState38 } from "react";
|
|
7712
8408
|
|
|
7713
8409
|
// src/useTabs.ts
|
|
7714
|
-
import { useState as
|
|
8410
|
+
import { useState as useState37 } from "react";
|
|
7715
8411
|
function useTabs(initial = "chat") {
|
|
7716
|
-
const [activeTab, setActiveTab] =
|
|
8412
|
+
const [activeTab, setActiveTab] = useState37(initial);
|
|
7717
8413
|
return { activeTab, setActiveTab };
|
|
7718
8414
|
}
|
|
7719
8415
|
|
|
@@ -7721,6 +8417,7 @@ function useTabs(initial = "chat") {
|
|
|
7721
8417
|
var GOAL_OR_RED = /* @__PURE__ */ new Set(["goal", "red"]);
|
|
7722
8418
|
var MIN_POLL_MS = 5e3;
|
|
7723
8419
|
var DEFAULT_POLL_MS = 6e4;
|
|
8420
|
+
var LIVE_WATCH_POLL_MS = 12e3;
|
|
7724
8421
|
function eventIdentity(e) {
|
|
7725
8422
|
return `${e.type}:${e.side}:${e.minuteLabel}:${e.player ?? ""}`;
|
|
7726
8423
|
}
|
|
@@ -7737,36 +8434,38 @@ function phaseForPin(snapshot, pin) {
|
|
|
7737
8434
|
return snapshot.markets.find((mk) => mk.key === pin.key)?.phase;
|
|
7738
8435
|
}
|
|
7739
8436
|
function useLiveMonitor(opts) {
|
|
7740
|
-
const sessionIdRef =
|
|
7741
|
-
const [pins, setPins] =
|
|
7742
|
-
const [store] =
|
|
7743
|
-
const [enabled, setEnabled] =
|
|
7744
|
-
const [snapshot, setSnapshotState] =
|
|
8437
|
+
const sessionIdRef = useRef15(crypto.randomUUID());
|
|
8438
|
+
const [pins, setPins] = useState38([]);
|
|
8439
|
+
const [store] = useState38(() => createPinStore(opts.storageKey, sessionIdRef.current, setPins));
|
|
8440
|
+
const [enabled, setEnabled] = useState38(true);
|
|
8441
|
+
const [snapshot, setSnapshotState] = useState38(null);
|
|
7745
8442
|
const { activeTab, setActiveTab: setActiveTabState } = useTabs("chat");
|
|
7746
|
-
const [chatDot, setChatDot] =
|
|
7747
|
-
const [matchesDot, setMatchesDot] =
|
|
7748
|
-
const [oddsDot, setOddsDot] =
|
|
7749
|
-
const [todayDot, setTodayDot] =
|
|
7750
|
-
const [lastViewedPrices, setLastViewedPrices] =
|
|
7751
|
-
const [openMarket, setOpenMarket] =
|
|
7752
|
-
const [marketOdds, setMarketOdds] =
|
|
7753
|
-
const pinsRef =
|
|
8443
|
+
const [chatDot, setChatDot] = useState38(false);
|
|
8444
|
+
const [matchesDot, setMatchesDot] = useState38(false);
|
|
8445
|
+
const [oddsDot, setOddsDot] = useState38(false);
|
|
8446
|
+
const [todayDot, setTodayDot] = useState38(false);
|
|
8447
|
+
const [lastViewedPrices, setLastViewedPrices] = useState38({});
|
|
8448
|
+
const [openMarket, setOpenMarket] = useState38({});
|
|
8449
|
+
const [marketOdds, setMarketOdds] = useState38({});
|
|
8450
|
+
const pinsRef = useRef15(pins);
|
|
7754
8451
|
pinsRef.current = pins;
|
|
7755
|
-
const
|
|
8452
|
+
const watchesRef = useRef15(opts.watches ?? []);
|
|
8453
|
+
watchesRef.current = opts.watches ?? [];
|
|
8454
|
+
const activeTabRef = useRef15(activeTab);
|
|
7756
8455
|
activeTabRef.current = activeTab;
|
|
7757
|
-
const snapshotRef =
|
|
8456
|
+
const snapshotRef = useRef15(snapshot);
|
|
7758
8457
|
snapshotRef.current = snapshot;
|
|
7759
|
-
const lastViewedPricesRef =
|
|
8458
|
+
const lastViewedPricesRef = useRef15(lastViewedPrices);
|
|
7760
8459
|
lastViewedPricesRef.current = lastViewedPrices;
|
|
7761
|
-
const openMarketRef =
|
|
8460
|
+
const openMarketRef = useRef15(openMarket);
|
|
7762
8461
|
openMarketRef.current = openMarket;
|
|
7763
|
-
const marketOddsRef =
|
|
8462
|
+
const marketOddsRef = useRef15(marketOdds);
|
|
7764
8463
|
marketOddsRef.current = marketOdds;
|
|
7765
8464
|
const hasToday = opts.hasToday ?? false;
|
|
7766
8465
|
const marketKey = (matchId, dev) => `${matchId}:${dev}`;
|
|
7767
|
-
const getMarketOddsRef =
|
|
8466
|
+
const getMarketOddsRef = useRef15(opts.getMarketOdds);
|
|
7768
8467
|
getMarketOddsRef.current = opts.getMarketOdds;
|
|
7769
|
-
const fetchMarket =
|
|
8468
|
+
const fetchMarket = useCallback9((matchId, dev) => {
|
|
7770
8469
|
const key = marketKey(matchId, dev);
|
|
7771
8470
|
const existing = marketOddsRef.current[key];
|
|
7772
8471
|
if (existing?.loading) return;
|
|
@@ -7785,7 +8484,7 @@ function useLiveMonitor(opts) {
|
|
|
7785
8484
|
setMarketOdds((prev) => ({ ...prev, [key]: entry }));
|
|
7786
8485
|
});
|
|
7787
8486
|
}, []);
|
|
7788
|
-
const toggleMarket =
|
|
8487
|
+
const toggleMarket = useCallback9((matchId, dev) => {
|
|
7789
8488
|
const key = marketKey(matchId, dev);
|
|
7790
8489
|
const wasOpen = openMarketRef.current[matchId] === dev;
|
|
7791
8490
|
const nextValue = wasOpen ? null : dev;
|
|
@@ -7796,10 +8495,10 @@ function useLiveMonitor(opts) {
|
|
|
7796
8495
|
fetchMarket(matchId, dev);
|
|
7797
8496
|
}
|
|
7798
8497
|
}, [fetchMarket]);
|
|
7799
|
-
const retryMarket =
|
|
8498
|
+
const retryMarket = useCallback9((matchId, dev) => {
|
|
7800
8499
|
fetchMarket(matchId, dev);
|
|
7801
8500
|
}, [fetchMarket]);
|
|
7802
|
-
const applySnapshot =
|
|
8501
|
+
const applySnapshot = useCallback9((next) => {
|
|
7803
8502
|
if (next === null) {
|
|
7804
8503
|
setEnabled(false);
|
|
7805
8504
|
return;
|
|
@@ -7862,17 +8561,18 @@ function useLiveMonitor(opts) {
|
|
|
7862
8561
|
snapshotRef.current = next;
|
|
7863
8562
|
setSnapshotState(next);
|
|
7864
8563
|
}, [store]);
|
|
7865
|
-
const inFlightRef =
|
|
7866
|
-
const timerRef =
|
|
7867
|
-
const disabledRef =
|
|
7868
|
-
const mountedRef =
|
|
7869
|
-
const getLiveMonitorRef =
|
|
8564
|
+
const inFlightRef = useRef15(false);
|
|
8565
|
+
const timerRef = useRef15(void 0);
|
|
8566
|
+
const disabledRef = useRef15(false);
|
|
8567
|
+
const mountedRef = useRef15(false);
|
|
8568
|
+
const getLiveMonitorRef = useRef15(opts.getLiveMonitor);
|
|
7870
8569
|
getLiveMonitorRef.current = opts.getLiveMonitor;
|
|
7871
|
-
const tick =
|
|
7872
|
-
|
|
8570
|
+
const tick = useCallback9(async () => {
|
|
8571
|
+
const allPins = [...pinsRef.current, ...watchesRef.current];
|
|
8572
|
+
if (inFlightRef.current || disabledRef.current || allPins.length === 0) return;
|
|
7873
8573
|
inFlightRef.current = true;
|
|
7874
8574
|
try {
|
|
7875
|
-
const snap = await getLiveMonitorRef.current(
|
|
8575
|
+
const snap = await getLiveMonitorRef.current(allPins);
|
|
7876
8576
|
if (!mountedRef.current) return;
|
|
7877
8577
|
if (snap === null) {
|
|
7878
8578
|
disabledRef.current = true;
|
|
@@ -7891,44 +8591,53 @@ function useLiveMonitor(opts) {
|
|
|
7891
8591
|
scheduleRef.current();
|
|
7892
8592
|
}
|
|
7893
8593
|
}, [applySnapshot, fetchMarket]);
|
|
7894
|
-
const scheduleRef =
|
|
8594
|
+
const scheduleRef = useRef15(() => {
|
|
7895
8595
|
});
|
|
7896
8596
|
scheduleRef.current = () => {
|
|
7897
8597
|
window.clearTimeout(timerRef.current);
|
|
7898
|
-
|
|
7899
|
-
|
|
8598
|
+
const hasWork = pinsRef.current.length > 0 || watchesRef.current.length > 0;
|
|
8599
|
+
if (!mountedRef.current || disabledRef.current || !hasWork || document.visibilityState === "hidden") return;
|
|
8600
|
+
const fallbackMs = watchesRef.current.length > 0 ? LIVE_WATCH_POLL_MS : DEFAULT_POLL_MS;
|
|
8601
|
+
const hinted = snapshotRef.current ? snapshotRef.current.nextPollSeconds * 1e3 : fallbackMs;
|
|
7900
8602
|
timerRef.current = window.setTimeout(() => void tick(), Math.max(MIN_POLL_MS, hinted));
|
|
7901
8603
|
};
|
|
7902
|
-
|
|
8604
|
+
useEffect19(() => {
|
|
7903
8605
|
mountedRef.current = true;
|
|
7904
8606
|
return () => {
|
|
7905
8607
|
mountedRef.current = false;
|
|
7906
8608
|
};
|
|
7907
8609
|
}, []);
|
|
7908
|
-
const prevPinsLengthRef =
|
|
7909
|
-
|
|
7910
|
-
const wasEmpty = prevPinsLengthRef.current === 0;
|
|
8610
|
+
const prevPinsLengthRef = useRef15(0);
|
|
8611
|
+
useEffect19(() => {
|
|
8612
|
+
const wasEmpty = prevPinsLengthRef.current === 0 && watchesRef.current.length === 0;
|
|
7911
8613
|
prevPinsLengthRef.current = pins.length;
|
|
7912
|
-
if (pins.length === 0) {
|
|
8614
|
+
if (pins.length === 0 && watchesRef.current.length === 0) {
|
|
7913
8615
|
window.clearTimeout(timerRef.current);
|
|
7914
8616
|
return;
|
|
7915
8617
|
}
|
|
7916
8618
|
if (wasEmpty) void tick();
|
|
7917
8619
|
}, [pins.length, tick]);
|
|
7918
|
-
|
|
8620
|
+
const prevWatchesLengthRef = useRef15(0);
|
|
8621
|
+
const watchesLength = opts.watches?.length ?? 0;
|
|
8622
|
+
useEffect19(() => {
|
|
8623
|
+
const wasEmpty = prevWatchesLengthRef.current === 0 && pinsRef.current.length === 0;
|
|
8624
|
+
prevWatchesLengthRef.current = watchesLength;
|
|
8625
|
+
if (wasEmpty && watchesLength > 0) void tick();
|
|
8626
|
+
}, [watchesLength, tick]);
|
|
8627
|
+
useEffect19(() => {
|
|
7919
8628
|
const matchPins = pins.filter((p) => p.kind === "match").length;
|
|
7920
8629
|
const marketPins = pins.length - matchPins;
|
|
7921
8630
|
if (activeTab === "following" && matchPins === 0) setActiveTabState(hasToday ? "today" : "chat");
|
|
7922
8631
|
if (activeTab === "odds" && marketPins === 0) setActiveTabState(hasToday ? "today" : "chat");
|
|
7923
8632
|
if (activeTab === "today" && !hasToday) setActiveTabState("chat");
|
|
7924
8633
|
}, [pins, activeTab, hasToday]);
|
|
7925
|
-
const prevIsOpenRef =
|
|
7926
|
-
|
|
8634
|
+
const prevIsOpenRef = useRef15(false);
|
|
8635
|
+
useEffect19(() => {
|
|
7927
8636
|
const wasClosed = !prevIsOpenRef.current;
|
|
7928
8637
|
prevIsOpenRef.current = opts.isOpen;
|
|
7929
8638
|
if (wasClosed && opts.isOpen) void tick();
|
|
7930
8639
|
}, [opts.isOpen, tick]);
|
|
7931
|
-
|
|
8640
|
+
useEffect19(() => {
|
|
7932
8641
|
const onVisibility = () => {
|
|
7933
8642
|
if (document.visibilityState === "hidden") window.clearTimeout(timerRef.current);
|
|
7934
8643
|
else void tick();
|
|
@@ -7939,7 +8648,7 @@ function useLiveMonitor(opts) {
|
|
|
7939
8648
|
window.clearTimeout(timerRef.current);
|
|
7940
8649
|
};
|
|
7941
8650
|
}, [tick]);
|
|
7942
|
-
const setActiveTab =
|
|
8651
|
+
const setActiveTab = useCallback9((tab) => {
|
|
7943
8652
|
setActiveTabState(tab);
|
|
7944
8653
|
if (tab === "chat") setChatDot(false);
|
|
7945
8654
|
if (tab === "today") setTodayDot(false);
|
|
@@ -7960,14 +8669,14 @@ function useLiveMonitor(opts) {
|
|
|
7960
8669
|
void tick();
|
|
7961
8670
|
}
|
|
7962
8671
|
}, [tick]);
|
|
7963
|
-
const togglePin =
|
|
8672
|
+
const togglePin = useCallback9((pin) => {
|
|
7964
8673
|
const result = store.toggle(pin);
|
|
7965
8674
|
pinsRef.current = store.pins;
|
|
7966
8675
|
return result;
|
|
7967
8676
|
}, [store]);
|
|
7968
|
-
const isPinned =
|
|
7969
|
-
const markChatActivity =
|
|
7970
|
-
const markTodayActivity =
|
|
8677
|
+
const isPinned = useCallback9((key) => pinsRef.current.some((p) => p.key === key), []);
|
|
8678
|
+
const markChatActivity = useCallback9(() => setChatDot(true), []);
|
|
8679
|
+
const markTodayActivity = useCallback9(() => {
|
|
7971
8680
|
if (activeTabRef.current !== "today") setTodayDot(true);
|
|
7972
8681
|
}, []);
|
|
7973
8682
|
return {
|
|
@@ -7994,7 +8703,7 @@ function useLiveMonitor(opts) {
|
|
|
7994
8703
|
}
|
|
7995
8704
|
|
|
7996
8705
|
// src/useMatchDiscovery.ts
|
|
7997
|
-
import { useCallback as
|
|
8706
|
+
import { useCallback as useCallback10, useEffect as useEffect20, useMemo as useMemo4, useRef as useRef16, useState as useState39 } from "react";
|
|
7998
8707
|
|
|
7999
8708
|
// src/scheduleCache.ts
|
|
8000
8709
|
var PREFIX = "ss-w-schedule:";
|
|
@@ -8082,27 +8791,27 @@ function localDateOf(now) {
|
|
|
8082
8791
|
return `${y}-${m}-${d}`;
|
|
8083
8792
|
}
|
|
8084
8793
|
function useMatchDiscovery(opts) {
|
|
8085
|
-
const [groups, setGroups] =
|
|
8086
|
-
const [states, setStates] =
|
|
8087
|
-
const [failures, setFailures] =
|
|
8088
|
-
const [expanded, setExpanded] =
|
|
8089
|
-
const [touched, setTouched] =
|
|
8090
|
-
const groupsRef =
|
|
8794
|
+
const [groups, setGroups] = useState39([]);
|
|
8795
|
+
const [states, setStates] = useState39({});
|
|
8796
|
+
const [failures, setFailures] = useState39(0);
|
|
8797
|
+
const [expanded, setExpanded] = useState39({});
|
|
8798
|
+
const [touched, setTouched] = useState39({});
|
|
8799
|
+
const groupsRef = useRef16(groups);
|
|
8091
8800
|
groupsRef.current = groups;
|
|
8092
|
-
const statesRef =
|
|
8801
|
+
const statesRef = useRef16(states);
|
|
8093
8802
|
statesRef.current = states;
|
|
8094
|
-
const getStatesRef =
|
|
8803
|
+
const getStatesRef = useRef16(opts.getStates);
|
|
8095
8804
|
getStatesRef.current = opts.getStates;
|
|
8096
8805
|
const pinnedKey = opts.pinnedMatchIds.join(",");
|
|
8097
|
-
const [localDate, setLocalDate] =
|
|
8098
|
-
|
|
8806
|
+
const [localDate, setLocalDate] = useState39(() => localDateOf(/* @__PURE__ */ new Date()));
|
|
8807
|
+
useEffect20(() => {
|
|
8099
8808
|
const id = setInterval(() => {
|
|
8100
8809
|
const next = localDateOf(/* @__PURE__ */ new Date());
|
|
8101
8810
|
setLocalDate((prev) => prev === next ? prev : next);
|
|
8102
8811
|
}, ROLLOVER_CHECK_MS);
|
|
8103
8812
|
return () => clearInterval(id);
|
|
8104
8813
|
}, []);
|
|
8105
|
-
|
|
8814
|
+
useEffect20(() => {
|
|
8106
8815
|
let cancelled = false;
|
|
8107
8816
|
const utcOffsetMinutes = -(/* @__PURE__ */ new Date()).getTimezoneOffset();
|
|
8108
8817
|
const cached = readSchedule(opts.operatorId, localDate, Date.now());
|
|
@@ -8127,7 +8836,7 @@ function useMatchDiscovery(opts) {
|
|
|
8127
8836
|
cancelled = true;
|
|
8128
8837
|
};
|
|
8129
8838
|
}, [opts.operatorId, localDate]);
|
|
8130
|
-
|
|
8839
|
+
useEffect20(() => {
|
|
8131
8840
|
setExpanded((prev) => {
|
|
8132
8841
|
const next = {};
|
|
8133
8842
|
for (const group of groups) {
|
|
@@ -8138,7 +8847,7 @@ function useMatchDiscovery(opts) {
|
|
|
8138
8847
|
return next;
|
|
8139
8848
|
});
|
|
8140
8849
|
}, [groups, pinnedKey, touched]);
|
|
8141
|
-
const tick =
|
|
8850
|
+
const tick = useCallback10(async () => {
|
|
8142
8851
|
const ids = dueMatchIds(groupsRef.current, statesRef.current, Date.now());
|
|
8143
8852
|
if (ids.length === 0) return;
|
|
8144
8853
|
try {
|
|
@@ -8154,7 +8863,7 @@ function useMatchDiscovery(opts) {
|
|
|
8154
8863
|
setFailures((n) => n + 1);
|
|
8155
8864
|
}
|
|
8156
8865
|
}, []);
|
|
8157
|
-
|
|
8866
|
+
useEffect20(() => {
|
|
8158
8867
|
if (!opts.isOpen) return;
|
|
8159
8868
|
void tick();
|
|
8160
8869
|
const id = setInterval(() => {
|
|
@@ -8163,11 +8872,11 @@ function useMatchDiscovery(opts) {
|
|
|
8163
8872
|
return () => clearInterval(id);
|
|
8164
8873
|
}, [opts.isOpen, tick, groups]);
|
|
8165
8874
|
const stale = failures >= STALE_AFTER_FAILURES;
|
|
8166
|
-
const liveCount =
|
|
8875
|
+
const liveCount = useMemo4(
|
|
8167
8876
|
() => stale ? 0 : Object.values(states).filter((s) => s.phase === "live").length,
|
|
8168
8877
|
[states, stale]
|
|
8169
8878
|
);
|
|
8170
|
-
const toggleGroup =
|
|
8879
|
+
const toggleGroup = useCallback10((competitionId) => {
|
|
8171
8880
|
setTouched((prev) => ({ ...prev, [competitionId]: true }));
|
|
8172
8881
|
setExpanded((prev) => ({ ...prev, [competitionId]: !prev[competitionId] }));
|
|
8173
8882
|
}, []);
|
|
@@ -8175,25 +8884,25 @@ function useMatchDiscovery(opts) {
|
|
|
8175
8884
|
}
|
|
8176
8885
|
|
|
8177
8886
|
// src/useGames.ts
|
|
8178
|
-
import { useCallback as
|
|
8887
|
+
import { useCallback as useCallback11, useEffect as useEffect21, useRef as useRef17, useState as useState40 } from "react";
|
|
8179
8888
|
var GAMES_POLL_MS = 6e4;
|
|
8180
8889
|
var DEFAULT_TZ = "Europe/London";
|
|
8181
8890
|
function computeGameDay(timezone) {
|
|
8182
8891
|
return new Intl.DateTimeFormat("en-CA", { timeZone: timezone }).format(/* @__PURE__ */ new Date());
|
|
8183
8892
|
}
|
|
8184
8893
|
function useGames(conversation, { activeTab, operatorTimezone }) {
|
|
8185
|
-
const [enabled, setEnabled] =
|
|
8186
|
-
const [games, setGames] =
|
|
8187
|
-
const [loading, setLoading] =
|
|
8188
|
-
const [refreshCount, setRefreshCount] =
|
|
8189
|
-
const inFlightRef =
|
|
8190
|
-
const hardOffRef =
|
|
8191
|
-
const conversationRef =
|
|
8894
|
+
const [enabled, setEnabled] = useState40(false);
|
|
8895
|
+
const [games, setGames] = useState40([]);
|
|
8896
|
+
const [loading, setLoading] = useState40(false);
|
|
8897
|
+
const [refreshCount, setRefreshCount] = useState40(0);
|
|
8898
|
+
const inFlightRef = useRef17(false);
|
|
8899
|
+
const hardOffRef = useRef17(false);
|
|
8900
|
+
const conversationRef = useRef17(conversation);
|
|
8192
8901
|
conversationRef.current = conversation;
|
|
8193
|
-
const tzRef =
|
|
8902
|
+
const tzRef = useRef17(operatorTimezone ?? DEFAULT_TZ);
|
|
8194
8903
|
tzRef.current = operatorTimezone ?? DEFAULT_TZ;
|
|
8195
8904
|
const ready = conversation != null;
|
|
8196
|
-
|
|
8905
|
+
useEffect21(() => {
|
|
8197
8906
|
if (hardOffRef.current) return;
|
|
8198
8907
|
let active = true;
|
|
8199
8908
|
const doFetch = async () => {
|
|
@@ -8236,22 +8945,22 @@ function useGames(conversation, { activeTab, operatorTimezone }) {
|
|
|
8236
8945
|
if (intervalId !== void 0) window.clearInterval(intervalId);
|
|
8237
8946
|
};
|
|
8238
8947
|
}, [activeTab, refreshCount, ready]);
|
|
8239
|
-
const refresh =
|
|
8948
|
+
const refresh = useCallback11(() => {
|
|
8240
8949
|
setRefreshCount((c) => c + 1);
|
|
8241
8950
|
}, []);
|
|
8242
8951
|
return { enabled, games, loading, refresh };
|
|
8243
8952
|
}
|
|
8244
8953
|
|
|
8245
8954
|
// src/useLeaderboard.ts
|
|
8246
|
-
import { useCallback as
|
|
8955
|
+
import { useCallback as useCallback12, useEffect as useEffect22, useRef as useRef18, useState as useState41 } from "react";
|
|
8247
8956
|
function useLeaderboard(conversation, { activeTab }) {
|
|
8248
|
-
const [data, setData] =
|
|
8249
|
-
const [loading, setLoading] =
|
|
8250
|
-
const [refreshCount, setRefreshCount] =
|
|
8251
|
-
const inFlightRef =
|
|
8252
|
-
const conversationRef =
|
|
8957
|
+
const [data, setData] = useState41(null);
|
|
8958
|
+
const [loading, setLoading] = useState41(false);
|
|
8959
|
+
const [refreshCount, setRefreshCount] = useState41(0);
|
|
8960
|
+
const inFlightRef = useRef18(false);
|
|
8961
|
+
const conversationRef = useRef18(conversation);
|
|
8253
8962
|
conversationRef.current = conversation;
|
|
8254
|
-
|
|
8963
|
+
useEffect22(() => {
|
|
8255
8964
|
if (activeTab !== "leaderboard" && activeTab !== "games") return;
|
|
8256
8965
|
let active = true;
|
|
8257
8966
|
const doFetch = async () => {
|
|
@@ -8274,18 +8983,65 @@ function useLeaderboard(conversation, { activeTab }) {
|
|
|
8274
8983
|
inFlightRef.current = false;
|
|
8275
8984
|
};
|
|
8276
8985
|
}, [activeTab, refreshCount]);
|
|
8277
|
-
const refresh =
|
|
8986
|
+
const refresh = useCallback12(() => {
|
|
8278
8987
|
setRefreshCount((c) => c + 1);
|
|
8279
8988
|
}, []);
|
|
8280
8989
|
return { data, loading, refresh };
|
|
8281
8990
|
}
|
|
8282
8991
|
|
|
8992
|
+
// src/usePresetGrid.ts
|
|
8993
|
+
import { useEffect as useEffect23, useRef as useRef19, useState as useState42 } from "react";
|
|
8994
|
+
var GRID_MAX_AGE_MS = 6e5;
|
|
8995
|
+
function usePresetGrid({ getPricePresets, isOpen, locale, now = Date.now }) {
|
|
8996
|
+
const [grid, setGrid] = useState42(null);
|
|
8997
|
+
const keptRef = useRef19(null);
|
|
8998
|
+
const localeRef = useRef19(locale);
|
|
8999
|
+
const inFlightRef = useRef19(false);
|
|
9000
|
+
const mountedRef = useRef19(true);
|
|
9001
|
+
const getRef = useRef19(getPricePresets);
|
|
9002
|
+
getRef.current = getPricePresets;
|
|
9003
|
+
const nowRef = useRef19(now);
|
|
9004
|
+
nowRef.current = now;
|
|
9005
|
+
useEffect23(() => {
|
|
9006
|
+
mountedRef.current = true;
|
|
9007
|
+
return () => {
|
|
9008
|
+
mountedRef.current = false;
|
|
9009
|
+
};
|
|
9010
|
+
}, []);
|
|
9011
|
+
useEffect23(() => {
|
|
9012
|
+
if (localeRef.current !== locale) {
|
|
9013
|
+
localeRef.current = locale;
|
|
9014
|
+
keptRef.current = null;
|
|
9015
|
+
setGrid(null);
|
|
9016
|
+
}
|
|
9017
|
+
if (!isOpen || inFlightRef.current) return;
|
|
9018
|
+
const kept = keptRef.current;
|
|
9019
|
+
const age = kept ? nowRef.current() - Date.parse(kept.builtAtUtc) : Number.NaN;
|
|
9020
|
+
if (age <= GRID_MAX_AGE_MS) return;
|
|
9021
|
+
inFlightRef.current = true;
|
|
9022
|
+
Promise.resolve().then(() => getRef.current(locale)).then((body) => {
|
|
9023
|
+
if (body === null) {
|
|
9024
|
+
keptRef.current = null;
|
|
9025
|
+
if (mountedRef.current) setGrid(null);
|
|
9026
|
+
return;
|
|
9027
|
+
}
|
|
9028
|
+
if (!isPresetGrid(body)) return;
|
|
9029
|
+
keptRef.current = body;
|
|
9030
|
+
if (mountedRef.current) setGrid(body);
|
|
9031
|
+
}).catch(() => {
|
|
9032
|
+
}).finally(() => {
|
|
9033
|
+
inFlightRef.current = false;
|
|
9034
|
+
});
|
|
9035
|
+
}, [isOpen, locale]);
|
|
9036
|
+
return grid;
|
|
9037
|
+
}
|
|
9038
|
+
|
|
8283
9039
|
// src/popupTelemetry.ts
|
|
8284
|
-
import { useCallback as
|
|
9040
|
+
import { useCallback as useCallback13, useEffect as useEffect24, useRef as useRef20 } from "react";
|
|
8285
9041
|
function usePopupTelemetry(log, isOpen, popupText, dismissPopup) {
|
|
8286
|
-
const shownRef =
|
|
8287
|
-
const closedTextRef =
|
|
8288
|
-
|
|
9042
|
+
const shownRef = useRef20(/* @__PURE__ */ new Set());
|
|
9043
|
+
const closedTextRef = useRef20(null);
|
|
9044
|
+
useEffect24(() => {
|
|
8289
9045
|
if (isOpen) {
|
|
8290
9046
|
if (closedTextRef.current) log?.log({ name: "popup_opened" });
|
|
8291
9047
|
closedTextRef.current = null;
|
|
@@ -8297,65 +9053,65 @@ function usePopupTelemetry(log, isOpen, popupText, dismissPopup) {
|
|
|
8297
9053
|
log?.log({ name: "popup_shown" });
|
|
8298
9054
|
}
|
|
8299
9055
|
}, [log, isOpen, popupText]);
|
|
8300
|
-
return
|
|
9056
|
+
return useCallback13(() => {
|
|
8301
9057
|
log?.log({ name: "popup_dismissed" });
|
|
8302
9058
|
dismissPopup();
|
|
8303
9059
|
}, [log, dismissPopup]);
|
|
8304
9060
|
}
|
|
8305
9061
|
|
|
8306
9062
|
// src/components/ChatFab.tsx
|
|
8307
|
-
import { useEffect as
|
|
9063
|
+
import { useEffect as useEffect25, useRef as useRef21, useState as useState43 } from "react";
|
|
8308
9064
|
|
|
8309
9065
|
// src/components/GreetingBubble.tsx
|
|
8310
|
-
import { jsx as
|
|
9066
|
+
import { jsx as jsx91, jsxs as jsxs75 } from "react/jsx-runtime";
|
|
8311
9067
|
function GreetingBubble({ text, onOpen, onDismiss, dismissLabel = "Dismiss", asks }) {
|
|
8312
|
-
const body = /* @__PURE__ */
|
|
8313
|
-
/* @__PURE__ */
|
|
8314
|
-
/* @__PURE__ */
|
|
8315
|
-
/* @__PURE__ */
|
|
8316
|
-
/* @__PURE__ */
|
|
9068
|
+
const body = /* @__PURE__ */ jsxs75("button", { type: "button", className: "ss-w-greeting-body", onClick: onOpen, children: [
|
|
9069
|
+
/* @__PURE__ */ jsxs75("span", { className: "ss-w-greeting-dots", "aria-hidden": "true", children: [
|
|
9070
|
+
/* @__PURE__ */ jsx91("i", {}),
|
|
9071
|
+
/* @__PURE__ */ jsx91("i", {}),
|
|
9072
|
+
/* @__PURE__ */ jsx91("i", {})
|
|
8317
9073
|
] }),
|
|
8318
|
-
/* @__PURE__ */
|
|
9074
|
+
/* @__PURE__ */ jsx91("span", { className: "ss-w-greeting-text", children: text })
|
|
8319
9075
|
] });
|
|
8320
|
-
const dismiss = onDismiss ? /* @__PURE__ */
|
|
9076
|
+
const dismiss = onDismiss ? /* @__PURE__ */ jsx91("button", { type: "button", className: "ss-w-greeting-x", "aria-label": dismissLabel, onClick: onDismiss, children: "\xD7" }) : null;
|
|
8321
9077
|
if (!asks || asks.length === 0) {
|
|
8322
|
-
return /* @__PURE__ */
|
|
9078
|
+
return /* @__PURE__ */ jsxs75("div", { className: "ss-w-greeting", role: "note", children: [
|
|
8323
9079
|
body,
|
|
8324
9080
|
dismiss
|
|
8325
9081
|
] });
|
|
8326
9082
|
}
|
|
8327
|
-
return /* @__PURE__ */
|
|
8328
|
-
/* @__PURE__ */
|
|
9083
|
+
return /* @__PURE__ */ jsxs75("div", { className: "ss-w-greeting ss-w-greeting--asks", role: "note", children: [
|
|
9084
|
+
/* @__PURE__ */ jsxs75("div", { className: "ss-w-greeting-top", children: [
|
|
8329
9085
|
body,
|
|
8330
9086
|
dismiss
|
|
8331
9087
|
] }),
|
|
8332
|
-
/* @__PURE__ */
|
|
9088
|
+
/* @__PURE__ */ jsx91("div", { className: "ss-w-greeting-asks", children: asks.map((ask, i) => /* @__PURE__ */ jsx91("button", { type: "button", className: "ss-w-greeting-ask", onClick: ask.onTap, children: ask.label }, `${i}:${ask.label}`)) })
|
|
8333
9089
|
] });
|
|
8334
9090
|
}
|
|
8335
9091
|
|
|
8336
9092
|
// src/components/ChatFab.tsx
|
|
8337
|
-
import { jsx as
|
|
9093
|
+
import { jsx as jsx92, jsxs as jsxs76 } from "react/jsx-runtime";
|
|
8338
9094
|
function ChatFab({ label, greeting, popupText, logoUrl, onOpen, onDismissPopup, dismissLabel = "Dismiss", introAllowed = false, onIntroStart, asks }) {
|
|
8339
9095
|
const bubble = popupText ?? greeting;
|
|
8340
|
-
const [intro, setIntro] =
|
|
8341
|
-
const armedRef =
|
|
8342
|
-
|
|
9096
|
+
const [intro, setIntro] = useState43(false);
|
|
9097
|
+
const armedRef = useRef21(introAllowed);
|
|
9098
|
+
useEffect25(() => {
|
|
8343
9099
|
if (armedRef.current && bubble) {
|
|
8344
9100
|
armedRef.current = false;
|
|
8345
9101
|
setIntro(true);
|
|
8346
9102
|
onIntroStart?.();
|
|
8347
9103
|
}
|
|
8348
9104
|
}, [bubble, onIntroStart]);
|
|
8349
|
-
return /* @__PURE__ */
|
|
8350
|
-
bubble ? /* @__PURE__ */
|
|
8351
|
-
/* @__PURE__ */
|
|
9105
|
+
return /* @__PURE__ */ jsxs76("div", { className: intro ? "ss-w-fab-wrap ss-w-intro" : "ss-w-fab-wrap", children: [
|
|
9106
|
+
bubble ? /* @__PURE__ */ jsx92(GreetingBubble, { text: bubble, onOpen, onDismiss: popupText ? onDismissPopup : void 0, dismissLabel, asks: popupText ? asks : void 0 }) : null,
|
|
9107
|
+
/* @__PURE__ */ jsx92("button", { type: "button", className: "ss-w-fab", "aria-label": label, onClick: onOpen, children: /* @__PURE__ */ jsx92(BrandMark, { logoUrl, className: "ss-w-fab-mark" }) })
|
|
8352
9108
|
] });
|
|
8353
9109
|
}
|
|
8354
9110
|
|
|
8355
9111
|
// src/components/HostEventsProvider.tsx
|
|
8356
|
-
import { jsx as
|
|
9112
|
+
import { jsx as jsx93 } from "react/jsx-runtime";
|
|
8357
9113
|
function HostEventsProvider({ value, children }) {
|
|
8358
|
-
return /* @__PURE__ */
|
|
9114
|
+
return /* @__PURE__ */ jsx93(SlipStateContext.Provider, { value: value.slipState, children: /* @__PURE__ */ jsx93(MatchLinkContext.Provider, { value: value.matchLink, children }) });
|
|
8359
9115
|
}
|
|
8360
9116
|
|
|
8361
9117
|
// src/styles/css.generated.ts
|
|
@@ -8751,6 +9507,12 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
8751
9507
|
.ss-w-mo-more-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; }
|
|
8752
9508
|
.ss-w-mo-more-chev-up { transform: translateY(1px) rotate(-135deg); }
|
|
8753
9509
|
.ss-w-mo-rows { display: flex; flex-direction: column; gap: 4px; }
|
|
9510
|
+
/* Stage 4: live card score+minute in header (R18). */
|
|
9511
|
+
.ss-w-bi-score { font-size: 13px; font-weight: 700; color: var(--ss-w-ink); white-space: nowrap; }
|
|
9512
|
+
.ss-w-bi-min { font-size: 11px; font-weight: 600; color: var(--ss-w-faint); white-space: nowrap; }
|
|
9513
|
+
/* D12: closed state message row; D13: stale-price notice. */
|
|
9514
|
+
.ss-w-mo-closed { padding: 6px 0; font-size: 12.5px; color: var(--ss-w-faint); }
|
|
9515
|
+
.ss-w-mo-stale { margin: 0; font-size: 11px; color: var(--ss-w-faint); }
|
|
8754
9516
|
/* The card's stat, in the Live tab accordion's insight style. No row below it is marked (R17). */
|
|
8755
9517
|
.ss-w-mo-stat { font-size: 12px; color: var(--ss-w-ink); background: color-mix(in srgb, var(--ss-w-accent) 9%, var(--ss-w-panel)); border-radius: 8px; padding: 7px 9px; }
|
|
8756
9518
|
.ss-w-mo-row { display: flex; align-items: center; gap: 10px; padding: 4px 0; border-bottom: 1px dashed var(--ss-w-line); }
|
|
@@ -8786,6 +9548,71 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
8786
9548
|
.ss-w-combo-leg-stat { font-size: 12px; line-height: 1.45; color: var(--ss-w-ink); background: var(--ss-w-bubble); border-radius: 8px; padding: 6px 9px; }
|
|
8787
9549
|
.ss-w-combo-math { font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 14px; font-weight: 700; line-height: 1.4; color: var(--ss-w-ink); text-align: right; background: color-mix(in srgb, var(--ss-w-accent) 9%, var(--ss-w-panel)); border-radius: 8px; padding: 8px 10px; overflow-wrap: anywhere; }
|
|
8788
9550
|
.ss-w-combo-note { font-size: 11px; line-height: 1.4; color: var(--ss-w-faint); }
|
|
9551
|
+
.ss-w-combo-change { align-self: flex-start; min-height: 36px; padding: 6px 12px; font: inherit; font-size: 12px; font-weight: 650; color: var(--ss-w-accent); background: var(--ss-w-panel); border: 1px solid var(--ss-w-accent); border-radius: 16px; cursor: pointer; }
|
|
9552
|
+
|
|
9553
|
+
/* onboarding presets stage 2: the two tools (spec Stage 2 Behaviour, boards 1c and 2c). A sheet
|
|
9554
|
+
over the message list, inside .ss-w-chat-layer; the body scrolls and the builder's Send sits in
|
|
9555
|
+
the footer, so it stays visible on a phone. Colours come from the theme tokens. */
|
|
9556
|
+
.ss-w-tool-backdrop { position: absolute; inset: 0; z-index: 3; display: flex; flex-direction: column; justify-content: flex-end; background: rgba(0,0,0,.35); }
|
|
9557
|
+
.ss-w-tool { display: flex; flex-direction: column; min-height: 0; max-height: 100%; background: var(--ss-w-panel); color: var(--ss-w-ink); border-top: 1px solid var(--ss-w-line); border-radius: 16px 16px 0 0; box-shadow: 0 -8px 24px rgba(0,0,0,.18); outline: none; }
|
|
9558
|
+
.ss-w-tool-head { flex: none; display: flex; align-items: center; gap: 8px; padding: 12px 14px 8px; }
|
|
9559
|
+
.ss-w-tool-title { flex: 1; font-size: 15px; font-weight: 700; line-height: 1.3; }
|
|
9560
|
+
.ss-w-tool-close { flex: none; width: 36px; height: 36px; display: grid; place-items: center; font-size: 16px; color: var(--ss-w-faint); background: var(--ss-w-bubble); border: none; border-radius: 50%; cursor: pointer; }
|
|
9561
|
+
.ss-w-tool-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; display: flex; flex-direction: column; gap: 12px; padding: 4px 14px 14px; }
|
|
9562
|
+
.ss-w-tool-body > * { flex: none; }
|
|
9563
|
+
.ss-w-tool-foot { flex: none; padding: 10px 14px 12px; border-top: 1px solid var(--ss-w-line); }
|
|
9564
|
+
.ss-w-tool-row { display: flex; flex-direction: column; gap: 8px; padding: 10px 11px; border: 1px solid var(--ss-w-line); border-left: 3px solid var(--ss-w-accent); border-radius: 12px; }
|
|
9565
|
+
.ss-w-tool-ask { font-size: 14px; font-weight: 650; line-height: 1.7; color: var(--ss-w-ink); overflow-wrap: anywhere; }
|
|
9566
|
+
.ss-w-tool-num { display: inline-block; min-width: 44px; min-height: 32px; margin: 0 2px; padding: 3px 8px; font: inherit; font-variant-numeric: tabular-nums; font-weight: 800; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 10%, var(--ss-w-panel)); border: 1px dashed color-mix(in srgb, var(--ss-w-accent) 55%, var(--ss-w-line)); border-radius: 8px; cursor: pointer; }
|
|
9567
|
+
.ss-w-tool-picks { list-style: none; display: flex; flex-direction: column; gap: 4px; }
|
|
9568
|
+
.ss-w-tool-pick { display: grid; grid-template-columns: 1fr auto; column-gap: 8px; padding: 5px 8px; font-size: 12px; line-height: 1.35; background: var(--ss-w-bubble); border-radius: 8px; }
|
|
9569
|
+
.ss-w-tool-pick-match { grid-column: 1; font-weight: 650; color: var(--ss-w-ink); overflow-wrap: anywhere; }
|
|
9570
|
+
.ss-w-tool-pick-sel { grid-column: 1; color: var(--ss-w-faint); overflow-wrap: anywhere; }
|
|
9571
|
+
.ss-w-tool-pick-kick { grid-column: 1; font-size: 11px; color: var(--ss-w-faint); }
|
|
9572
|
+
.ss-w-tool-pick-price { grid-column: 2; grid-row: 1 / span 3; align-self: center; font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 14px; font-weight: 800; color: var(--ss-w-accent); }
|
|
9573
|
+
.ss-w-tool-math { padding: 6px 8px; font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 700; text-align: right; color: var(--ss-w-ink); background: color-mix(in srgb, var(--ss-w-accent) 9%, var(--ss-w-panel)); border-radius: 8px; overflow-wrap: anywhere; }
|
|
9574
|
+
.ss-w-tool-unavailable { padding: 6px 8px; font-size: 12px; color: var(--ss-w-faint); border: 1px dashed var(--ss-w-line); border-radius: 8px; }
|
|
9575
|
+
.ss-w-tool-send { align-self: flex-end; min-height: 40px; padding: 8px 18px; font: inherit; font-size: 13px; font-weight: 700; color: #fff; background: var(--ss-w-accent); border: none; border-radius: 20px; cursor: pointer; }
|
|
9576
|
+
.ss-w-tool-send:disabled { color: var(--ss-w-faint); background: var(--ss-w-bubble); cursor: not-allowed; }
|
|
9577
|
+
.ss-w-tool-foot .ss-w-tool-send { width: 100%; }
|
|
9578
|
+
.ss-w-tool-section { display: flex; flex-direction: column; gap: 6px; }
|
|
9579
|
+
.ss-w-tool-label { font-size: 11.5px; font-weight: 650; color: var(--ss-w-faint); }
|
|
9580
|
+
.ss-w-tool-chips { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
9581
|
+
.ss-w-tool-chip { min-width: 52px; min-height: 36px; padding: 6px 10px; font: inherit; font-variant-numeric: tabular-nums; font-weight: 700; color: var(--ss-w-ink); background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 18px; cursor: pointer; }
|
|
9582
|
+
.ss-w-tool-chip[aria-pressed="true"] { color: #fff; background: var(--ss-w-accent); border-color: var(--ss-w-accent); }
|
|
9583
|
+
.ss-w-tool-stepper { display: inline-flex; align-items: center; gap: 10px; }
|
|
9584
|
+
.ss-w-tool-step { width: 40px; height: 40px; display: grid; place-items: center; font: inherit; font-size: 18px; font-weight: 700; color: var(--ss-w-accent); background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 50%; cursor: pointer; }
|
|
9585
|
+
.ss-w-tool-step:disabled { opacity: .5; cursor: not-allowed; }
|
|
9586
|
+
.ss-w-tool-count { min-width: 24px; text-align: center; font-size: 16px; font-weight: 800; font-variant-numeric: tabular-nums; }
|
|
9587
|
+
|
|
9588
|
+
/* onboarding presets stage 2: the chat layer and the speed dial (spec Stage 2 Behaviour). The
|
|
9589
|
+
layer is the message list's positioned box (P6), so the dial sits against the list's bottom
|
|
9590
|
+
right and rises with the composer: right 8px puts the 44px button's centre on Send's (12px
|
|
9591
|
+
padding + 18px), bottom 10px is above the composer's top border. While the button shows the list
|
|
9592
|
+
gets 54px more bottom padding so its last message can scroll clear. */
|
|
9593
|
+
.ss-w-chat-layer { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; }
|
|
9594
|
+
.ss-w-chat-layer[data-dial] .ss-w-list { padding-bottom: 68px; }
|
|
9595
|
+
.ss-w-dial { position: absolute; left: 12px; right: 8px; bottom: 10px; z-index: 2; display: flex; flex-direction: column-reverse; align-items: flex-end; gap: 8px; pointer-events: none; }
|
|
9596
|
+
.ss-w-dial-btn, .ss-w-dial-pill { pointer-events: auto; }
|
|
9597
|
+
.ss-w-dial-btn { position: relative; flex: none; width: 44px; height: 44px; display: grid; place-items: center; padding: 0; font-size: 20px; color: var(--ss-w-accent); background: var(--ss-w-panel); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 40%, var(--ss-w-line)); border-radius: 50%; box-shadow: 0 4px 14px rgba(0,0,0,.18); cursor: pointer; }
|
|
9598
|
+
.ss-w-dial-btn[aria-expanded="true"] { color: var(--ss-w-panel); background: var(--ss-w-accent); border-color: var(--ss-w-accent); }
|
|
9599
|
+
.ss-w-dial-btn:focus-visible, .ss-w-dial-pill:focus-visible { outline: 2px solid var(--ss-w-accent); outline-offset: 2px; }
|
|
9600
|
+
.ss-w-dial-icon { grid-area: 1 / 1; display: grid; place-items: center; transition: transform 200ms ease, opacity 200ms ease; }
|
|
9601
|
+
.ss-w-dial-icon--close { opacity: 0; transform: rotate(-90deg); }
|
|
9602
|
+
.ss-w-dial-btn[aria-expanded="true"] .ss-w-dial-icon--open { opacity: 0; transform: rotate(90deg); }
|
|
9603
|
+
.ss-w-dial-btn[aria-expanded="true"] .ss-w-dial-icon--close { opacity: 1; transform: none; }
|
|
9604
|
+
.ss-w-dial-dot { position: absolute; top: 3px; right: 3px; width: 10px; height: 10px; background: var(--ss-w-accent); border: 2px solid var(--ss-w-panel); border-radius: 50%; }
|
|
9605
|
+
.ss-w-dial-pills { max-width: 100%; display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }
|
|
9606
|
+
.ss-w-dial-pill { max-width: 100%; min-height: 44px; display: inline-flex; align-items: center; gap: 8px; padding: 6px 16px 6px 12px; font: inherit; font-size: 12.5px; font-weight: 650; line-height: 1.25; text-align: left; white-space: normal; overflow-wrap: anywhere; color: var(--ss-w-ink); background: var(--ss-w-panel); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 35%, var(--ss-w-line)); border-radius: 22px; box-shadow: 0 4px 14px rgba(0,0,0,.16); cursor: pointer; opacity: 0; visibility: hidden; transform: translateY(14px) scale(.5); transform-origin: bottom right; transition: opacity 200ms ease, transform 200ms ease, visibility 0s linear 200ms; }
|
|
9607
|
+
.ss-w-dial-pill svg { flex: none; font-size: 18px; color: var(--ss-w-accent); }
|
|
9608
|
+
.ss-w-dial-pill-label { min-width: 0; }
|
|
9609
|
+
.ss-w-dial-pills[data-open] .ss-w-dial-pill { opacity: 1; visibility: visible; transform: none; transition: opacity 200ms ease, transform 200ms ease, visibility 0s; }
|
|
9610
|
+
@media (prefers-reduced-motion: reduce) {
|
|
9611
|
+
.ss-w-dial-pill { transform: none; transition: opacity 200ms ease, visibility 0s linear 200ms; }
|
|
9612
|
+
.ss-w-dial-pills[data-open] .ss-w-dial-pill { transition: opacity 200ms ease, visibility 0s; }
|
|
9613
|
+
.ss-w-dial-icon { transition: opacity 200ms ease; }
|
|
9614
|
+
.ss-w-dial-icon--close, .ss-w-dial-btn[aria-expanded="true"] .ss-w-dial-icon--open { transform: none; }
|
|
9615
|
+
}
|
|
8789
9616
|
|
|
8790
9617
|
/* Compact price list (D16): three or more cards of one turn, one row per fixture. Team names wrap
|
|
8791
9618
|
to a second line and are never cut; the time sits above the match pin, with no column of its own. */
|
|
@@ -8872,6 +9699,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
8872
9699
|
.ss-w-textarea { font-size: 16px; }
|
|
8873
9700
|
}
|
|
8874
9701
|
.ss-w-send { width: 36px; height: 36px; flex: none; border: none; border-radius: 50%; background: var(--ss-w-accent); color: #fff; cursor: pointer; display: grid; place-items: center; font-size: 16px; }
|
|
9702
|
+
.ss-w-send:disabled { opacity: .45; cursor: not-allowed; }
|
|
8875
9703
|
.ss-w-stop-glyph { width: 11px; height: 11px; background: currentColor; border-radius: 2px; }
|
|
8876
9704
|
|
|
8877
9705
|
/* add to slip */
|
|
@@ -9911,7 +10739,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
9911
10739
|
`;
|
|
9912
10740
|
|
|
9913
10741
|
// src/StatsWidget.tsx
|
|
9914
|
-
import { jsx as
|
|
10742
|
+
import { jsx as jsx94 } from "react/jsx-runtime";
|
|
9915
10743
|
function buildClient(props) {
|
|
9916
10744
|
try {
|
|
9917
10745
|
if ("client" in props && props.client) {
|
|
@@ -9936,39 +10764,54 @@ function injectStyles(root) {
|
|
|
9936
10764
|
function StatsWidget(props) {
|
|
9937
10765
|
const injectedClient = "client" in props ? props.client : void 0;
|
|
9938
10766
|
const cfg = props.config;
|
|
9939
|
-
const built =
|
|
9940
|
-
const logger =
|
|
10767
|
+
const built = useMemo5(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
|
|
10768
|
+
const logger = useMemo5(
|
|
9941
10769
|
() => built ? built.client.createEventLogger({
|
|
9942
10770
|
operatorUserId: cfg?.userContext?.operatorUserId,
|
|
9943
10771
|
anonymousUserId: cfg?.userContext?.anonymousUserId
|
|
9944
10772
|
}) : null,
|
|
9945
10773
|
[built?.client]
|
|
9946
10774
|
);
|
|
9947
|
-
const ui =
|
|
9948
|
-
const rootRef =
|
|
9949
|
-
const introDoneRef =
|
|
9950
|
-
const markIntroDone =
|
|
10775
|
+
const ui = useMemo5(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
|
|
10776
|
+
const rootRef = useRef22(null);
|
|
10777
|
+
const introDoneRef = useRef22(false);
|
|
10778
|
+
const markIntroDone = useCallback14(() => {
|
|
9951
10779
|
introDoneRef.current = true;
|
|
9952
10780
|
}, []);
|
|
9953
10781
|
const convStorageKey = `ss-w-conv:${cfg?.operatorId ?? ""}:${cfg?.userContext?.operatorUserId ?? "anon"}`;
|
|
9954
10782
|
const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0, convStorageKey);
|
|
9955
|
-
const [hasToday, setHasToday] =
|
|
10783
|
+
const [hasToday, setHasToday] = useState44(false);
|
|
10784
|
+
const liveCardWatches = useMemo5(() => {
|
|
10785
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10786
|
+
const out = [];
|
|
10787
|
+
for (const msg of controller.state.messages) {
|
|
10788
|
+
if (msg.role !== "assistant") continue;
|
|
10789
|
+
for (const vm of msg.marketOdds) {
|
|
10790
|
+
if (vm.live == null || seen.has(vm.matchId)) continue;
|
|
10791
|
+
seen.add(vm.matchId);
|
|
10792
|
+
out.push({ key: `live:card:${vm.matchId}`, kind: "market", matchId: vm.matchId, marketDeveloperName: vm.marketDeveloperName ?? "FULLTIME_RESULT" });
|
|
10793
|
+
}
|
|
10794
|
+
}
|
|
10795
|
+
return out;
|
|
10796
|
+
}, [controller.state.messages]);
|
|
10797
|
+
const locale = cfg?.locale;
|
|
9956
10798
|
const monitor = useLiveMonitor({
|
|
9957
|
-
getLiveMonitor: (pins) => built ? built.conversation.getLiveMonitor(pins) : Promise.resolve(null),
|
|
10799
|
+
getLiveMonitor: (pins) => built ? built.conversation.getLiveMonitor(pins, locale) : Promise.resolve(null),
|
|
9958
10800
|
getMarketOdds: (matchId, dev) => built ? built.conversation.getMarketOdds(matchId, dev) : Promise.resolve(null),
|
|
9959
10801
|
storageKey: `ss-w-pins:${cfg?.operatorId ?? ""}:${cfg?.userContext?.operatorUserId ?? "anon"}`,
|
|
9960
10802
|
isOpen: controller.state.isOpen,
|
|
9961
|
-
hasToday
|
|
10803
|
+
hasToday,
|
|
10804
|
+
watches: liveCardWatches
|
|
9962
10805
|
});
|
|
9963
|
-
const getSchedule =
|
|
10806
|
+
const getSchedule = useCallback14(
|
|
9964
10807
|
(localDate, utcOffsetMinutes) => built ? built.conversation.getSchedule(localDate, utcOffsetMinutes) : Promise.resolve(null),
|
|
9965
10808
|
[built]
|
|
9966
10809
|
);
|
|
9967
|
-
const getStates =
|
|
10810
|
+
const getStates = useCallback14(
|
|
9968
10811
|
(matchIds) => built ? built.conversation.getStates(matchIds) : Promise.resolve(null),
|
|
9969
10812
|
[built]
|
|
9970
10813
|
);
|
|
9971
|
-
const pinnedMatchIds =
|
|
10814
|
+
const pinnedMatchIds = useMemo5(
|
|
9972
10815
|
() => monitor.pins.filter((p) => p.kind === "match").map((p) => p.matchId),
|
|
9973
10816
|
[monitor.pins]
|
|
9974
10817
|
);
|
|
@@ -9980,14 +10823,54 @@ function StatsWidget(props) {
|
|
|
9980
10823
|
pinnedMatchIds
|
|
9981
10824
|
});
|
|
9982
10825
|
const todayHasContent = visibleGroupsOf(discovery.groups, discovery.states).length > 0;
|
|
9983
|
-
|
|
10826
|
+
useEffect26(() => {
|
|
9984
10827
|
setHasToday(todayHasContent);
|
|
9985
10828
|
}, [todayHasContent]);
|
|
10829
|
+
const prevLiveSnapshotRef = useRef22(monitor.snapshot);
|
|
10830
|
+
useEffect26(() => {
|
|
10831
|
+
const snap = monitor.snapshot;
|
|
10832
|
+
if (!snap || snap === prevLiveSnapshotRef.current) return;
|
|
10833
|
+
prevLiveSnapshotRef.current = snap;
|
|
10834
|
+
for (const market of snap.markets) {
|
|
10835
|
+
if (!market.key.startsWith("live:card:")) continue;
|
|
10836
|
+
const matchId = parseInt(market.key.slice("live:card:".length), 10);
|
|
10837
|
+
if (!Number.isFinite(matchId)) continue;
|
|
10838
|
+
let mainRows = [];
|
|
10839
|
+
let moreRows = [];
|
|
10840
|
+
let addToSlipEnabled = false;
|
|
10841
|
+
if (market.card) {
|
|
10842
|
+
const updated = toMarketOddsVM(market.card);
|
|
10843
|
+
mainRows = updated.mainRows;
|
|
10844
|
+
moreRows = updated.moreRows;
|
|
10845
|
+
addToSlipEnabled = updated.addToSlipEnabled;
|
|
10846
|
+
}
|
|
10847
|
+
let closedReason = null;
|
|
10848
|
+
if (market.phase === "fullTime" || market.phase === "postponed") closedReason = "full_time";
|
|
10849
|
+
else if (market.isSuspended) closedReason = "suspended";
|
|
10850
|
+
else if (!market.card) closedReason = "no_live_price";
|
|
10851
|
+
const live = {
|
|
10852
|
+
phase: market.phase,
|
|
10853
|
+
homeScore: market.homeScore ?? null,
|
|
10854
|
+
awayScore: market.awayScore ?? null,
|
|
10855
|
+
minuteLabel: market.minuteLabel ?? null,
|
|
10856
|
+
isSuspended: market.isSuspended,
|
|
10857
|
+
isSettled: market.isSettled,
|
|
10858
|
+
asOfUtc: market.asOfUtc,
|
|
10859
|
+
closedReason
|
|
10860
|
+
};
|
|
10861
|
+
controller._dispatch?.({ kind: "UPDATE_LIVE_CARD", matchId, live, mainRows, moreRows, addToSlipEnabled });
|
|
10862
|
+
}
|
|
10863
|
+
}, [monitor.snapshot]);
|
|
9986
10864
|
const gamesResult = useGames(built?.conversation ?? null, { activeTab: monitor.activeTab });
|
|
9987
10865
|
const leaderboardResult = useLeaderboard(built?.conversation ?? null, { activeTab: monitor.activeTab });
|
|
10866
|
+
const getPricePresets = useCallback14(
|
|
10867
|
+
(locale2) => built ? built.conversation.getPricePresets(locale2) : Promise.resolve(null),
|
|
10868
|
+
[built]
|
|
10869
|
+
);
|
|
10870
|
+
const presetGrid = usePresetGrid({ getPricePresets, isOpen: controller.state.isOpen, locale: ui.presets.lang });
|
|
9988
10871
|
const markTodayActivity = monitor.markTodayActivity;
|
|
9989
|
-
const prevLiveCountRef =
|
|
9990
|
-
|
|
10872
|
+
const prevLiveCountRef = useRef22(0);
|
|
10873
|
+
useEffect26(() => {
|
|
9991
10874
|
const rose = prevLiveCountRef.current === 0 && discovery.liveCount > 0;
|
|
9992
10875
|
prevLiveCountRef.current = discovery.liveCount;
|
|
9993
10876
|
if (rose) markTodayActivity();
|
|
@@ -10003,7 +10886,7 @@ function StatsWidget(props) {
|
|
|
10003
10886
|
openMatchLabel: ui.openMatch
|
|
10004
10887
|
});
|
|
10005
10888
|
const markAdded = hostEvents.markAdded;
|
|
10006
|
-
const slip =
|
|
10889
|
+
const slip = useMemo5(
|
|
10007
10890
|
() => {
|
|
10008
10891
|
const handler = props.config?.onAddToSlip;
|
|
10009
10892
|
return {
|
|
@@ -10022,7 +10905,7 @@ function StatsWidget(props) {
|
|
|
10022
10905
|
},
|
|
10023
10906
|
[props.config?.onAddToSlip, controller.state.conversationId, markAdded]
|
|
10024
10907
|
);
|
|
10025
|
-
const onDashboardTap =
|
|
10908
|
+
const onDashboardTap = useMemo5(
|
|
10026
10909
|
() => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input), logger) : void 0,
|
|
10027
10910
|
[built, logger]
|
|
10028
10911
|
);
|
|
@@ -10040,11 +10923,11 @@ function StatsWidget(props) {
|
|
|
10040
10923
|
const operatorUserId = cfg?.userContext?.operatorUserId;
|
|
10041
10924
|
const anonymousUserId = cfg?.userContext?.anonymousUserId;
|
|
10042
10925
|
const conversationId = controller.state.conversationId ?? void 0;
|
|
10043
|
-
|
|
10926
|
+
useEffect26(() => {
|
|
10044
10927
|
if (!logger) return;
|
|
10045
10928
|
logger.setIdentity({ operatorUserId, anonymousUserId, conversationId });
|
|
10046
10929
|
}, [logger, operatorUserId, anonymousUserId, conversationId]);
|
|
10047
|
-
|
|
10930
|
+
useEffect26(() => {
|
|
10048
10931
|
if (!logger) return;
|
|
10049
10932
|
logger.resume();
|
|
10050
10933
|
const onHide = () => {
|
|
@@ -10062,14 +10945,14 @@ function StatsWidget(props) {
|
|
|
10062
10945
|
logger.dispose();
|
|
10063
10946
|
};
|
|
10064
10947
|
}, [logger]);
|
|
10065
|
-
|
|
10948
|
+
useEffect26(() => {
|
|
10066
10949
|
if (!built || !ui.injectStyles) return;
|
|
10067
10950
|
const node = rootRef.current?.getRootNode();
|
|
10068
10951
|
if (node) injectStyles(node);
|
|
10069
10952
|
}, [built, ui.injectStyles]);
|
|
10070
10953
|
if (!built) return null;
|
|
10071
10954
|
const attrs = themeAttrs(ui);
|
|
10072
|
-
return /* @__PURE__ */
|
|
10955
|
+
return /* @__PURE__ */ jsx94(EventLogContext.Provider, { value: logger, children: /* @__PURE__ */ jsx94(HostEventsProvider, { value: hostEvents, children: /* @__PURE__ */ jsx94("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__ */ jsx94(
|
|
10073
10956
|
ChatPanel,
|
|
10074
10957
|
{
|
|
10075
10958
|
controller,
|
|
@@ -10081,9 +10964,10 @@ function StatsWidget(props) {
|
|
|
10081
10964
|
gamesResult,
|
|
10082
10965
|
leaderboardResult,
|
|
10083
10966
|
loggedIn: !!operatorUserId,
|
|
10084
|
-
conversation: built?.conversation ?? null
|
|
10967
|
+
conversation: built?.conversation ?? null,
|
|
10968
|
+
presetGrid
|
|
10085
10969
|
}
|
|
10086
|
-
) : /* @__PURE__ */
|
|
10970
|
+
) : /* @__PURE__ */ jsx94(
|
|
10087
10971
|
ChatFab,
|
|
10088
10972
|
{
|
|
10089
10973
|
label: ui.launcherLabel,
|