@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.cjs
CHANGED
|
@@ -37,7 +37,7 @@ __export(index_exports, {
|
|
|
37
37
|
module.exports = __toCommonJS(index_exports);
|
|
38
38
|
|
|
39
39
|
// src/StatsWidget.tsx
|
|
40
|
-
var
|
|
40
|
+
var import_react55 = require("react");
|
|
41
41
|
var import_widget_sdk5 = require("@sensiblestats/widget-sdk");
|
|
42
42
|
|
|
43
43
|
// src/kickoff.ts
|
|
@@ -72,6 +72,12 @@ var STRINGS = {
|
|
|
72
72
|
showMore: (n) => `Show ${n} more`,
|
|
73
73
|
oddsLabel: "Odds",
|
|
74
74
|
liveLabel: "Live",
|
|
75
|
+
liveCard: {
|
|
76
|
+
stale: (time) => `Prices from ${time}`,
|
|
77
|
+
suspended: "Market suspended",
|
|
78
|
+
noPrice: "No live price available",
|
|
79
|
+
fullTime: "Full time"
|
|
80
|
+
},
|
|
75
81
|
yourMarketTag: "Your market",
|
|
76
82
|
marketFloorLabel: "Market price \xB7 not a value pick",
|
|
77
83
|
addToSlip: "Add to slip",
|
|
@@ -155,6 +161,30 @@ var STRINGS = {
|
|
|
155
161
|
gamePlay: "Play",
|
|
156
162
|
gameLocked: "Locked",
|
|
157
163
|
gameVoided: "Voided",
|
|
164
|
+
presets: {
|
|
165
|
+
lang: "en",
|
|
166
|
+
setNumbers: "Set the numbers",
|
|
167
|
+
buildCombo: "Build a combo",
|
|
168
|
+
dialName: "Set the numbers and Build a combo",
|
|
169
|
+
maxPriceRow: "{count} picks, max {price}",
|
|
170
|
+
minPriceRow: "Picks from {price} up",
|
|
171
|
+
comboRow: "A combo of {legs} matches priced at about {total}",
|
|
172
|
+
maxPriceDisplay: (count, price) => `${count} picks, max ${price.toFixed(2)}`,
|
|
173
|
+
maxPriceMessage: (count, price) => `Show me ${count} picks priced at ${price.toFixed(2)} or less.`,
|
|
174
|
+
minPriceDisplay: (count, price) => `${count} picks from ${price.toFixed(2)} up`,
|
|
175
|
+
minPriceMessage: (count, price) => `Show me ${count} picks priced at ${price.toFixed(2)} or more.`,
|
|
176
|
+
comboDisplay: (legs, total) => `A combo of ${legs} priced at about ${total.toFixed(1)}`,
|
|
177
|
+
comboMessage: (legs, total) => `Build me a combo of ${legs} priced at about ${total.toFixed(1)}.`,
|
|
178
|
+
unavailable: "Not available today.",
|
|
179
|
+
send: "Send",
|
|
180
|
+
close: "Close",
|
|
181
|
+
totalLabel: "Total",
|
|
182
|
+
matchesLabel: "Matches",
|
|
183
|
+
fewerMatches: "Fewer matches",
|
|
184
|
+
moreMatches: "More matches",
|
|
185
|
+
changeNumber: (value) => `${value}, tap to change`,
|
|
186
|
+
changeCombo: "Change total or matches"
|
|
187
|
+
},
|
|
158
188
|
games: {
|
|
159
189
|
lang: "en",
|
|
160
190
|
dailyChallenge: "Daily challenge",
|
|
@@ -372,6 +402,12 @@ var STRINGS = {
|
|
|
372
402
|
showMore: (n) => `${n} tane daha g\xF6ster`,
|
|
373
403
|
oddsLabel: "Oran",
|
|
374
404
|
liveLabel: "Canl\u0131",
|
|
405
|
+
liveCard: {
|
|
406
|
+
stale: (time) => `${time} itibar\u0131yla fiyatlar`,
|
|
407
|
+
suspended: "Market ask\u0131ya al\u0131nd\u0131",
|
|
408
|
+
noPrice: "Canl\u0131 fiyat mevcut de\u011Fil",
|
|
409
|
+
fullTime: "Ma\xE7 bitti"
|
|
410
|
+
},
|
|
375
411
|
yourMarketTag: "\u0130lgilendi\u011Fin bahis",
|
|
376
412
|
marketFloorLabel: "Piyasa oran\u0131 \xB7 de\u011Fer bahsi de\u011Fil",
|
|
377
413
|
addToSlip: "Kupona ekle",
|
|
@@ -455,6 +491,30 @@ var STRINGS = {
|
|
|
455
491
|
gamePlay: "Oyna",
|
|
456
492
|
gameLocked: "Kilitli",
|
|
457
493
|
gameVoided: "\u0130ptal edildi",
|
|
494
|
+
presets: {
|
|
495
|
+
lang: "tr",
|
|
496
|
+
setNumbers: "Say\u0131lar\u0131 belirle",
|
|
497
|
+
buildCombo: "Kombin olu\u015Ftur",
|
|
498
|
+
dialName: "Say\u0131lar\u0131 belirle ve Kombin olu\u015Ftur",
|
|
499
|
+
maxPriceRow: "{count} se\xE7im, en fazla {price}",
|
|
500
|
+
minPriceRow: "{price} ve \xFCzeri se\xE7imler",
|
|
501
|
+
comboRow: "Yakla\u015F\u0131k {total} oranl\u0131 {legs} ma\xE7l\u0131k kombin",
|
|
502
|
+
maxPriceDisplay: (count, price) => `${count} se\xE7im, en fazla ${price.toFixed(2)}`,
|
|
503
|
+
maxPriceMessage: (count, price) => `${price.toFixed(2)} veya alt\u0131 oranl\u0131 ${count} se\xE7im g\xF6ster.`,
|
|
504
|
+
minPriceDisplay: (count, price) => `${count} se\xE7im, ${price.toFixed(2)} ve \xFCzeri`,
|
|
505
|
+
minPriceMessage: (count, price) => `${price.toFixed(2)} veya \xFCzeri oranl\u0131 ${count} se\xE7im g\xF6ster.`,
|
|
506
|
+
comboDisplay: (legs, total) => `${legs} ma\xE7l\u0131k kombin, yakla\u015F\u0131k ${total.toFixed(1)}`,
|
|
507
|
+
comboMessage: (legs, total) => `Yakla\u015F\u0131k ${total.toFixed(1)} oranl\u0131 ${legs} ma\xE7l\u0131k bir kombin haz\u0131rla.`,
|
|
508
|
+
unavailable: "Bug\xFCn mevcut de\u011Fil.",
|
|
509
|
+
send: "G\xF6nder",
|
|
510
|
+
close: "Kapat",
|
|
511
|
+
totalLabel: "Toplam",
|
|
512
|
+
matchesLabel: "Ma\xE7 say\u0131s\u0131",
|
|
513
|
+
fewerMatches: "Daha az ma\xE7",
|
|
514
|
+
moreMatches: "Daha fazla ma\xE7",
|
|
515
|
+
changeNumber: (value) => `${value}, de\u011Fi\u015Ftirmek i\xE7in dokun`,
|
|
516
|
+
changeCombo: "Toplam\u0131 veya ma\xE7lar\u0131 de\u011Fi\u015Ftir"
|
|
517
|
+
},
|
|
458
518
|
games: {
|
|
459
519
|
lang: "tr",
|
|
460
520
|
dailyChallenge: "G\xFCn\xFCn meydan okumas\u0131",
|
|
@@ -673,6 +733,7 @@ function normalizeUi(cfg) {
|
|
|
673
733
|
accent: cfg.accent,
|
|
674
734
|
logoUrl: cfg.logoUrl,
|
|
675
735
|
headerTitle: cfg.headerTitle,
|
|
736
|
+
locale: cfg.locale ?? "en",
|
|
676
737
|
rawBettingLabels: isEnglishLocale(cfg.locale),
|
|
677
738
|
cta: cfg.cta,
|
|
678
739
|
surface: cfg.surface,
|
|
@@ -779,7 +840,9 @@ function normalizeUi(cfg) {
|
|
|
779
840
|
gamePlay: strings.gamePlay,
|
|
780
841
|
gameLocked: strings.gameLocked,
|
|
781
842
|
gameVoided: strings.gameVoided,
|
|
782
|
-
games: strings.games
|
|
843
|
+
games: strings.games,
|
|
844
|
+
presets: strings.presets,
|
|
845
|
+
liveCard: strings.liveCard
|
|
783
846
|
};
|
|
784
847
|
}
|
|
785
848
|
function toClientOptions(cfg) {
|
|
@@ -791,121 +854,478 @@ function toUserContext(cfg) {
|
|
|
791
854
|
return cfg.userContext && Object.keys(cfg.userContext).length > 0 ? cfg.userContext : void 0;
|
|
792
855
|
}
|
|
793
856
|
|
|
794
|
-
// src/
|
|
795
|
-
var
|
|
796
|
-
var
|
|
797
|
-
var
|
|
798
|
-
var
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
if (
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
const
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
857
|
+
// src/chart.ts
|
|
858
|
+
var CHART_VIEW_W = 320;
|
|
859
|
+
var CHART_VIEW_H = 140;
|
|
860
|
+
var PLOT_X0 = 40;
|
|
861
|
+
var PLOT_X1 = 312;
|
|
862
|
+
var PLOT_Y0 = 10;
|
|
863
|
+
var PLOT_Y1 = 110;
|
|
864
|
+
var MARKER_MAX_POINTS = 8;
|
|
865
|
+
var FLAT_DOMAIN_PAD = 0.05;
|
|
866
|
+
var round2 = (n) => Math.round(n * 100) / 100;
|
|
867
|
+
function projectSeries(samples) {
|
|
868
|
+
if (samples.length < 2) return null;
|
|
869
|
+
const tMin = samples[0].atMs;
|
|
870
|
+
const tMax = samples[samples.length - 1].atMs;
|
|
871
|
+
if (tMax <= tMin) return null;
|
|
872
|
+
const prices = samples.map((s) => s.price);
|
|
873
|
+
const pMin = Math.min(...prices);
|
|
874
|
+
const pMax = Math.max(...prices);
|
|
875
|
+
let yMin;
|
|
876
|
+
let yMax;
|
|
877
|
+
if (pMax === pMin) {
|
|
878
|
+
yMin = pMin - FLAT_DOMAIN_PAD;
|
|
879
|
+
yMax = pMax + FLAT_DOMAIN_PAD;
|
|
880
|
+
} else {
|
|
881
|
+
const pad = (pMax - pMin) * 0.1;
|
|
882
|
+
yMin = pMin - pad;
|
|
883
|
+
yMax = pMax + pad;
|
|
884
|
+
}
|
|
885
|
+
const x = (atMs) => round2(PLOT_X0 + (atMs - tMin) / (tMax - tMin) * (PLOT_X1 - PLOT_X0));
|
|
886
|
+
const y = (price) => round2(PLOT_Y1 - (price - yMin) / (yMax - yMin) * (PLOT_Y1 - PLOT_Y0));
|
|
887
|
+
const projected = samples.map((s) => ({ x: x(s.atMs), y: y(s.price) }));
|
|
888
|
+
const path = projected.map((p, i) => `${i === 0 ? "M" : "L"}${p.x} ${p.y}`).join(" ");
|
|
889
|
+
return {
|
|
890
|
+
path,
|
|
891
|
+
markers: samples.length <= MARKER_MAX_POINTS ? projected : [],
|
|
892
|
+
// The real extremes, never the padded bounds: a gridline label is a price, and the padded
|
|
893
|
+
// bounds are prices nobody was ever offered. A flat series has one real extreme, not two —
|
|
894
|
+
// two identical gridlines would collide on both React key and rendered text.
|
|
895
|
+
gridlines: pMax === pMin ? [{ y: y(pMax), price: pMax }] : [
|
|
896
|
+
{ y: y(pMax), price: pMax },
|
|
897
|
+
{ y: y(pMin), price: pMin }
|
|
898
|
+
],
|
|
899
|
+
viewBox: `0 0 ${CHART_VIEW_W} ${CHART_VIEW_H}`,
|
|
900
|
+
startMs: tMin,
|
|
901
|
+
endMs: tMax
|
|
902
|
+
};
|
|
818
903
|
}
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
return
|
|
904
|
+
|
|
905
|
+
// src/model.ts
|
|
906
|
+
function str(v) {
|
|
907
|
+
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
823
908
|
}
|
|
824
|
-
function
|
|
825
|
-
|
|
826
|
-
switch (result.status) {
|
|
827
|
-
case "added":
|
|
828
|
-
case "duplicate":
|
|
829
|
-
return result.status;
|
|
830
|
-
case "price_changed":
|
|
831
|
-
return result.accepted ? "added" : "price_changed";
|
|
832
|
-
case "rejected":
|
|
833
|
-
return result.reason;
|
|
834
|
-
}
|
|
909
|
+
function num(v) {
|
|
910
|
+
return typeof v === "number" && Number.isFinite(v) ? String(v) : void 0;
|
|
835
911
|
}
|
|
836
|
-
function
|
|
837
|
-
if (!
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
case "price_changed":
|
|
844
|
-
return result.accepted ? { kind: "success", text: ui.slipPriceChanged(shownOdds, result.newOddsDecimal.toFixed(2)) } : { kind: "error", text: ui.slipPriceNotAccepted(shownOdds, result.newOddsDecimal.toFixed(2)) };
|
|
845
|
-
case "rejected":
|
|
846
|
-
if (result.reason === "login_required") return { kind: "error", text: ui.slipLoginRequired };
|
|
847
|
-
if (result.reason === "suspended") return { kind: "error", text: ui.slipSuspended };
|
|
848
|
-
return { kind: "error", text: result.message ?? ui.slipFailed };
|
|
912
|
+
function readStatCells(raw) {
|
|
913
|
+
if (!Array.isArray(raw)) return [];
|
|
914
|
+
const cells = [];
|
|
915
|
+
for (const entry of raw) {
|
|
916
|
+
const label = str(entry.label);
|
|
917
|
+
const value = num(entry.value) ?? str(entry.value);
|
|
918
|
+
if (label !== void 0 && value !== void 0) cells.push({ label, value });
|
|
849
919
|
}
|
|
920
|
+
return cells;
|
|
850
921
|
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
var SS_LOGO_DATA_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAjoAAAI6CAYAAAAuSOLAAAAAAXNSR0IArs4c6QAAAJplWElmTU0AKgAAAAgAAwESAAMAAAABAAEAAAExAAIAAAA9AAAAModpAAQAAAABAAAAcAAAAABBZG9iZSBQaG90b3Nob3AgMjYuMTAgKDIwMjUwNzMwLm0uMzE2NiAwN2IxMjM0KSAgKE1hY2ludG9zaCkAAAADoAEAAwAAAAEAAQAAoAIABAAAAAEAAAI6oAMABAAAAAEAAAI6AAAAACwGtVwAAAQwaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA2LjAuMCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgICAgICAgICB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx4bXBNTTpEb2N1bWVudElEPnhtcC5kaWQ6QzBGRTM5NjM5NEJEMTFGMEJGNjZEMjUyN0U0MzU5RUM8L3htcE1NOkRvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpJbnN0YW5jZUlEPnhtcC5paWQ6QzBGRTM5NjI5NEJEMTFGMEJGNjZEMjUyN0U0MzU5RUM8L3htcE1NOkluc3RhbmNlSUQ+CiAgICAgICAgIDx4bXBNTTpEZXJpdmVkRnJvbSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgIDxzdFJlZjppbnN0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";
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
922
|
+
function toCardVM(card) {
|
|
923
|
+
const entityType = str(card.entityType)?.toLowerCase();
|
|
924
|
+
const kind = entityType === "player" ? "player" : entityType === "team" ? "team" : "generic";
|
|
925
|
+
const scopes = Array.isArray(card.scopes) ? card.scopes.map((s) => {
|
|
926
|
+
const label = str(s.label);
|
|
927
|
+
if (!label) return null;
|
|
928
|
+
return {
|
|
929
|
+
competitionId: typeof s.competitionId === "number" ? s.competitionId : null,
|
|
930
|
+
label,
|
|
931
|
+
image: str(s.imagePath),
|
|
932
|
+
stats: readStatCells(s.stats)
|
|
933
|
+
};
|
|
934
|
+
}).filter((s) => s !== null) : [];
|
|
935
|
+
return {
|
|
936
|
+
id: String(card.entityId),
|
|
937
|
+
kind,
|
|
938
|
+
name: card.canonicalName,
|
|
939
|
+
image: str(card.imagePath) ?? str(card.imageUrl) ?? str(card.image_url) ?? str(card.logoUrl) ?? str(card.logo),
|
|
940
|
+
sub: str(card.subtitle) ?? str(card.teamName) ?? str(card.leagueName) ?? str(card.position),
|
|
941
|
+
season: str(card.seasonName),
|
|
942
|
+
scopes
|
|
943
|
+
};
|
|
868
944
|
}
|
|
869
|
-
function
|
|
870
|
-
|
|
945
|
+
function toActionVM(action) {
|
|
946
|
+
const label = str(action.actionLabel) ?? str(action.label) ?? str(action.text) ?? str(action.title) ?? "Continue";
|
|
947
|
+
const actionId = str(action.actionId) ?? str(action.id);
|
|
948
|
+
const actionRef = str(action.actionRef);
|
|
949
|
+
const message = str(action.actionValue) ?? str(action.message) ?? str(action.query) ?? label;
|
|
950
|
+
return { label, actionId, actionRef, message };
|
|
871
951
|
}
|
|
872
|
-
function
|
|
873
|
-
|
|
952
|
+
function toChipVM(intent) {
|
|
953
|
+
const text = str(intent.text) ?? str(intent.label) ?? str(intent.title);
|
|
954
|
+
return text ? { text } : null;
|
|
874
955
|
}
|
|
875
|
-
function
|
|
876
|
-
return
|
|
956
|
+
function chipFromText(text) {
|
|
957
|
+
return { text };
|
|
877
958
|
}
|
|
878
|
-
function
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "1.6" }),
|
|
882
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "19", r: "1.6" })
|
|
883
|
-
] }) });
|
|
959
|
+
function joinSelection(label, line) {
|
|
960
|
+
if (label && line && label.includes(line)) return label;
|
|
961
|
+
return [label, line].filter(Boolean).join(" ") || void 0;
|
|
884
962
|
}
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
963
|
+
function toInsightVM(insight) {
|
|
964
|
+
const market = str(insight.marketLabel) ?? str(insight.marketDeveloperName) ?? "";
|
|
965
|
+
const selectionLabel = str(insight.selectionLabel);
|
|
966
|
+
const selectionLine = str(insight.line);
|
|
967
|
+
const selection = joinSelection(selectionLabel, selectionLine);
|
|
968
|
+
const value = insight.odds?.value;
|
|
969
|
+
const odds = typeof value === "number" && Number.isFinite(value) ? value.toFixed(2) : void 0;
|
|
970
|
+
const oddsValue = insight.odds?.value;
|
|
971
|
+
const matchOddsId = typeof insight.odds?.matchOddsId === "number" ? insight.odds.matchOddsId : void 0;
|
|
972
|
+
const home = str(insight.homeTeamName);
|
|
973
|
+
const away = str(insight.awayTeamName);
|
|
974
|
+
const fixture = home && away ? `${home} vs ${away}` : void 0;
|
|
975
|
+
const competition = str(insight.competitionName);
|
|
976
|
+
const previousValue = insight.odds?.previousValue;
|
|
977
|
+
const previous = typeof previousValue === "number" && Number.isFinite(previousValue) ? previousValue : void 0;
|
|
978
|
+
const direction = insight.odds?.movementDirection === "shortened" || insight.odds?.movementDirection === "drifted" ? insight.odds.movementDirection : void 0;
|
|
979
|
+
return {
|
|
980
|
+
id: str(insight.insightId) ?? `${str(insight.ruleId) ?? "insight"}:${num(insight.matchId) ?? ""}`,
|
|
981
|
+
market,
|
|
982
|
+
selection,
|
|
983
|
+
odds,
|
|
984
|
+
// The wire payload carries bookmakerId (not a name); render a name only if one is present.
|
|
985
|
+
bookmaker: str(insight.odds?.bookmakerName),
|
|
986
|
+
isLive: insight.odds?.isLive === true,
|
|
987
|
+
phase: str(insight.phase),
|
|
988
|
+
fixture,
|
|
989
|
+
competition,
|
|
990
|
+
text: str(insight.text) ?? "",
|
|
991
|
+
disclaimer: str(insight.disclaimer),
|
|
992
|
+
matchId: typeof insight.matchId === "number" ? insight.matchId : 0,
|
|
993
|
+
matchOddsId,
|
|
994
|
+
marketDeveloperName: str(insight.marketDeveloperName),
|
|
995
|
+
selectionLabel: str(insight.selectionLabel),
|
|
996
|
+
line: str(insight.line),
|
|
997
|
+
addToSlipEnabled: insight.addToSlipEnabled === true,
|
|
998
|
+
pinEnabled: insight.pinEnabled === true,
|
|
999
|
+
isMarketFloor: insight.isMarketFloor === true,
|
|
1000
|
+
oddsDecimal: typeof oddsValue === "number" && Number.isFinite(oddsValue) ? oddsValue : void 0,
|
|
1001
|
+
quotedAtUtc: str(insight.odds?.quotedAtUtc),
|
|
1002
|
+
previousOdds: previous !== void 0 && direction !== void 0 ? previous.toFixed(2) : void 0,
|
|
1003
|
+
movementDirection: previous !== void 0 && direction !== void 0 ? direction : void 0,
|
|
1004
|
+
homeTeamName: home,
|
|
1005
|
+
awayTeamName: away,
|
|
1006
|
+
homeCrest: str(insight.homeTeamImagePath),
|
|
1007
|
+
awayCrest: str(insight.awayTeamImagePath),
|
|
1008
|
+
competitionCrest: str(insight.competitionImagePath),
|
|
1009
|
+
kickoffUtc: str(insight.kickoffUtc),
|
|
1010
|
+
kickoffLabel: str(insight.kickoffLabel)
|
|
1011
|
+
};
|
|
903
1012
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
1013
|
+
function toSlipSelection(vm, conversationId) {
|
|
1014
|
+
if (vm.matchOddsId === void 0 || vm.oddsDecimal === void 0) return null;
|
|
1015
|
+
const selection = {
|
|
1016
|
+
matchOddsId: vm.matchOddsId,
|
|
1017
|
+
matchId: vm.matchId,
|
|
1018
|
+
oddsDecimal: vm.oddsDecimal
|
|
1019
|
+
};
|
|
1020
|
+
if (vm.quotedAtUtc !== void 0) selection.quotedAtUtc = vm.quotedAtUtc;
|
|
1021
|
+
if (vm.id !== void 0) selection.insightId = vm.id;
|
|
1022
|
+
if (conversationId !== void 0) selection.conversationId = conversationId;
|
|
1023
|
+
return selection;
|
|
1024
|
+
}
|
|
1025
|
+
function toMarketOddsVM(card) {
|
|
1026
|
+
const rows = Array.isArray(card.rows) ? card.rows : [];
|
|
1027
|
+
const toRow = (r, i) => {
|
|
1028
|
+
const selection = joinSelection(str(r.selectionLabel), str(r.line)) ?? "";
|
|
1029
|
+
const price = typeof r.price === "number" && Number.isFinite(r.price) ? r.price : void 0;
|
|
1030
|
+
const previous = typeof r.previousPrice === "number" && Number.isFinite(r.previousPrice) ? r.previousPrice : void 0;
|
|
1031
|
+
const direction = r.movementDirection === "shortened" || r.movementDirection === "drifted" ? r.movementDirection : void 0;
|
|
1032
|
+
return {
|
|
1033
|
+
key: `${selection}:${str(r.line) ?? ""}:${i}`,
|
|
1034
|
+
selection,
|
|
1035
|
+
selectionLabel: str(r.selectionLabel),
|
|
1036
|
+
line: str(r.line),
|
|
1037
|
+
odds: price !== void 0 ? price.toFixed(2) : "",
|
|
1038
|
+
oddsDecimal: price,
|
|
1039
|
+
matchOddsId: typeof r.matchOddsId === "number" ? r.matchOddsId : void 0,
|
|
1040
|
+
quotedAtUtc: str(r.quotedAtUtc),
|
|
1041
|
+
isMain: r.isMainLine !== false,
|
|
1042
|
+
// === true, not !== false: a server that has not shipped the field yet leaves it undefined,
|
|
1043
|
+
// and undefined must read as open rather than hiding every price on the card.
|
|
1044
|
+
isSuspended: r.isSuspended === true,
|
|
1045
|
+
previousOdds: previous !== void 0 && direction !== void 0 ? previous.toFixed(2) : void 0,
|
|
1046
|
+
movementDirection: previous !== void 0 && direction !== void 0 ? direction : void 0
|
|
1047
|
+
};
|
|
1048
|
+
};
|
|
1049
|
+
const vms = rows.map(toRow).filter((r) => r.selection && r.odds && !r.isSuspended);
|
|
1050
|
+
const home = str(card.homeTeamName);
|
|
1051
|
+
const away = str(card.awayTeamName);
|
|
1052
|
+
const updated = rows.map((r) => str(r.quotedAtUtc)).filter((v) => !!v).sort();
|
|
1053
|
+
return {
|
|
1054
|
+
id: `${typeof card.matchId === "number" ? card.matchId : 0}:${str(card.marketDeveloperName) ?? ""}`,
|
|
1055
|
+
market: str(card.marketLabel) ?? str(card.marketDeveloperName) ?? "",
|
|
1056
|
+
fixture: home && away ? `${home} vs ${away}` : void 0,
|
|
1057
|
+
homeTeamName: home,
|
|
1058
|
+
awayTeamName: away,
|
|
1059
|
+
homeCrest: str(card.homeTeamImagePath),
|
|
1060
|
+
awayCrest: str(card.awayTeamImagePath),
|
|
1061
|
+
competitionCrest: str(card.competitionImagePath),
|
|
1062
|
+
competition: str(card.competitionName),
|
|
1063
|
+
kickoffUtc: str(card.kickoffUtc),
|
|
1064
|
+
kickoffLabel: str(card.kickoffLabel),
|
|
1065
|
+
bookmaker: rows.map((r) => str(r.bookmakerName)).find(Boolean),
|
|
1066
|
+
updatedAt: updated.length > 0 ? updated[updated.length - 1] : void 0,
|
|
1067
|
+
isLive: rows.some((r) => r.isLive === true),
|
|
1068
|
+
matchId: typeof card.matchId === "number" ? card.matchId : 0,
|
|
1069
|
+
mainRows: vms.filter((r) => r.isMain),
|
|
1070
|
+
moreRows: vms.filter((r) => !r.isMain),
|
|
1071
|
+
disclaimer: str(card.disclaimer),
|
|
1072
|
+
addToSlipEnabled: card.addToSlipEnabled === true,
|
|
1073
|
+
marketDeveloperName: str(card.marketDeveloperName),
|
|
1074
|
+
mainLine: str(card.mainLine),
|
|
1075
|
+
pinEnabled: card.pinEnabled === true,
|
|
1076
|
+
statText: card.insight && typeof card.insight === "object" ? str(card.insight.text) : void 0,
|
|
1077
|
+
tag: card.tag === "your_market" ? "your_market" : void 0,
|
|
1078
|
+
live: card.live ?? null
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
function toOddsMovementVM(card) {
|
|
1082
|
+
const raw = Array.isArray(card.points) ? card.points : [];
|
|
1083
|
+
const samples = [];
|
|
1084
|
+
for (const p of raw) {
|
|
1085
|
+
const price = typeof p.price === "number" && Number.isFinite(p.price) ? p.price : void 0;
|
|
1086
|
+
const atMs = typeof p.atUtc === "string" ? Date.parse(p.atUtc) : NaN;
|
|
1087
|
+
if (price === void 0 || Number.isNaN(atMs)) continue;
|
|
1088
|
+
samples.push({ atMs, price });
|
|
1089
|
+
}
|
|
1090
|
+
samples.sort((a, b) => a.atMs - b.atMs);
|
|
1091
|
+
const asNumber = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
1092
|
+
const opening = asNumber(card.openingPrice);
|
|
1093
|
+
const current = asNumber(card.currentPrice);
|
|
1094
|
+
const home = str(card.homeTeamName);
|
|
1095
|
+
const away = str(card.awayTeamName);
|
|
1096
|
+
const selection = joinSelection(str(card.selectionLabel), str(card.line)) ?? "";
|
|
1097
|
+
const bookmakerId = typeof card.bookmakerId === "number" ? card.bookmakerId : "";
|
|
1098
|
+
return {
|
|
1099
|
+
id: `${typeof card.matchId === "number" ? card.matchId : 0}:${str(card.marketDeveloperName) ?? ""}:${selection}:${bookmakerId}`,
|
|
1100
|
+
matchId: typeof card.matchId === "number" ? card.matchId : 0,
|
|
1101
|
+
market: str(card.marketLabel) ?? str(card.marketDeveloperName) ?? "",
|
|
1102
|
+
marketDeveloperName: str(card.marketDeveloperName),
|
|
1103
|
+
selection,
|
|
1104
|
+
selectionLabel: str(card.selectionLabel),
|
|
1105
|
+
line: str(card.line),
|
|
1106
|
+
fixture: home && away ? `${home} vs ${away}` : void 0,
|
|
1107
|
+
competition: str(card.competitionName),
|
|
1108
|
+
bookmaker: str(card.bookmakerName),
|
|
1109
|
+
opening: opening !== void 0 ? opening.toFixed(2) : "",
|
|
1110
|
+
current: current !== void 0 ? current.toFixed(2) : "",
|
|
1111
|
+
direction: card.movementDirection === "shortened" || card.movementDirection === "drifted" ? card.movementDirection : void 0,
|
|
1112
|
+
windowHours: asNumber(card.windowHours) ?? 0,
|
|
1113
|
+
excludedCount: asNumber(card.excludedSuspendedCount) ?? 0,
|
|
1114
|
+
isSuspended: card.isSuspended === true,
|
|
1115
|
+
disclaimer: str(card.disclaimer),
|
|
1116
|
+
geometry: projectSeries(samples),
|
|
1117
|
+
observationCount: samples.length,
|
|
1118
|
+
pinEnabled: card.pinEnabled === true,
|
|
1119
|
+
matchOddsId: typeof card.matchOddsId === "number" ? card.matchOddsId : void 0
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
function marketOddsRowToSlipSelection(vm, row, conversationId) {
|
|
1123
|
+
if (row.matchOddsId === void 0 || row.oddsDecimal === void 0) return null;
|
|
1124
|
+
const selection = { matchOddsId: row.matchOddsId, matchId: vm.matchId, oddsDecimal: row.oddsDecimal };
|
|
1125
|
+
if (row.quotedAtUtc !== void 0) selection.quotedAtUtc = row.quotedAtUtc;
|
|
1126
|
+
if (conversationId !== void 0) selection.conversationId = conversationId;
|
|
1127
|
+
return selection;
|
|
1128
|
+
}
|
|
1129
|
+
function finite(v) {
|
|
1130
|
+
return typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
1131
|
+
}
|
|
1132
|
+
function toComboLegVM(leg) {
|
|
1133
|
+
if (!leg || typeof leg !== "object") return null;
|
|
1134
|
+
const price = finite(leg.price);
|
|
1135
|
+
const matchOddsId = finite(leg.matchOddsId);
|
|
1136
|
+
const matchId = finite(leg.matchId);
|
|
1137
|
+
const home = str(leg.homeTeamName);
|
|
1138
|
+
const away = str(leg.awayTeamName);
|
|
1139
|
+
const selectionLabel = str(leg.selectionLabel);
|
|
1140
|
+
const insight = leg.insight && typeof leg.insight === "object" ? leg.insight : void 0;
|
|
1141
|
+
const insightId = str(insight?.insightId);
|
|
1142
|
+
const statText = str(insight?.text);
|
|
1143
|
+
if (price === void 0 || matchOddsId === void 0 || matchId === void 0 || !home || !away || !selectionLabel || !insightId || !statText) return null;
|
|
1144
|
+
const line = str(leg.line);
|
|
1145
|
+
return {
|
|
1146
|
+
key: `${matchOddsId}`,
|
|
1147
|
+
matchId,
|
|
1148
|
+
homeTeamName: home,
|
|
1149
|
+
awayTeamName: away,
|
|
1150
|
+
homeCrest: str(leg.homeTeamImagePath),
|
|
1151
|
+
awayCrest: str(leg.awayTeamImagePath),
|
|
1152
|
+
competition: str(leg.competitionName),
|
|
1153
|
+
kickoffUtc: str(leg.kickoffUtc),
|
|
1154
|
+
kickoffLabel: str(leg.kickoffLabel),
|
|
1155
|
+
market: str(leg.marketLabel) ?? str(leg.marketDeveloperName) ?? "",
|
|
1156
|
+
marketDeveloperName: str(leg.marketDeveloperName) ?? "",
|
|
1157
|
+
selection: joinSelection(selectionLabel, line) ?? selectionLabel,
|
|
1158
|
+
selectionLabel,
|
|
1159
|
+
line,
|
|
1160
|
+
odds: price.toFixed(2),
|
|
1161
|
+
oddsDecimal: price,
|
|
1162
|
+
matchOddsId,
|
|
1163
|
+
quotedAtUtc: str(leg.quotedAtUtc),
|
|
1164
|
+
insightId,
|
|
1165
|
+
statText
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
function toComboCardVM(card) {
|
|
1169
|
+
const rawLegs = Array.isArray(card?.legs) ? card.legs : [];
|
|
1170
|
+
const multiplication = str(card?.multiplication);
|
|
1171
|
+
if (rawLegs.length < 2 || !multiplication) return null;
|
|
1172
|
+
const legs = [];
|
|
1173
|
+
for (const raw of rawLegs) {
|
|
1174
|
+
const vm = toComboLegVM(raw);
|
|
1175
|
+
if (!vm) return null;
|
|
1176
|
+
legs.push(vm);
|
|
1177
|
+
}
|
|
1178
|
+
return {
|
|
1179
|
+
id: str(card.comboId) ?? legs.map((l) => l.matchOddsId).join("-"),
|
|
1180
|
+
legs,
|
|
1181
|
+
multiplication,
|
|
1182
|
+
replacedNote: str(card.replacedNote),
|
|
1183
|
+
addToSlipEnabled: card.addToSlipEnabled === true,
|
|
1184
|
+
target: finite(card.target),
|
|
1185
|
+
legCount: finite(card.legCount)
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
function comboLegToSlipSelection(leg, conversationId) {
|
|
1189
|
+
const selection = {
|
|
1190
|
+
matchOddsId: leg.matchOddsId,
|
|
1191
|
+
matchId: leg.matchId,
|
|
1192
|
+
oddsDecimal: leg.oddsDecimal,
|
|
1193
|
+
insightId: leg.insightId
|
|
1194
|
+
};
|
|
1195
|
+
if (leg.quotedAtUtc !== void 0) selection.quotedAtUtc = leg.quotedAtUtc;
|
|
1196
|
+
if (conversationId !== void 0) selection.conversationId = conversationId;
|
|
1197
|
+
return selection;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
// src/slip.ts
|
|
1201
|
+
var ADD_TO_SLIP_TIMEOUT_MS = 1e4;
|
|
1202
|
+
var MAX_SELECTION = 64;
|
|
1203
|
+
var MAX_LINE = 32;
|
|
1204
|
+
var MAX_ODDS = 1e4;
|
|
1205
|
+
function slipPickFields(selection, label, line) {
|
|
1206
|
+
const fields = {};
|
|
1207
|
+
if (Number.isSafeInteger(selection.matchId) && selection.matchId > 0) fields.matchId = selection.matchId;
|
|
1208
|
+
if (label && label.trim() && label.length <= MAX_SELECTION) fields.selection = label;
|
|
1209
|
+
if (line && line.trim() && line.length <= MAX_LINE) fields.line = line;
|
|
1210
|
+
const odds = selection.oddsDecimal;
|
|
1211
|
+
if (Number.isFinite(odds) && odds > 1 && odds <= MAX_ODDS) fields.odds = odds;
|
|
1212
|
+
return fields;
|
|
1213
|
+
}
|
|
1214
|
+
function chipSelection(market, matchId, conversationId) {
|
|
1215
|
+
if (!market || !market.addToSlipEnabled || typeof market.matchOddsId !== "number" || typeof market.price !== "number") return null;
|
|
1216
|
+
const selection = { matchOddsId: market.matchOddsId, matchId, oddsDecimal: market.price };
|
|
1217
|
+
if (conversationId !== void 0) selection.conversationId = conversationId;
|
|
1218
|
+
return selection;
|
|
1219
|
+
}
|
|
1220
|
+
function wasAdded(result) {
|
|
1221
|
+
if (!result) return true;
|
|
1222
|
+
if (result.status === "price_changed") return result.accepted;
|
|
1223
|
+
return result.status === "added";
|
|
1224
|
+
}
|
|
1225
|
+
function isTerminal(result) {
|
|
1226
|
+
if (!result) return true;
|
|
1227
|
+
if (result.status === "price_changed") return result.accepted;
|
|
1228
|
+
return result.status === "added" || result.status === "duplicate";
|
|
1229
|
+
}
|
|
1230
|
+
function slipOutcome(result) {
|
|
1231
|
+
if (!result) return "added";
|
|
1232
|
+
switch (result.status) {
|
|
1233
|
+
case "added":
|
|
1234
|
+
case "duplicate":
|
|
1235
|
+
return result.status;
|
|
1236
|
+
case "price_changed":
|
|
1237
|
+
return result.accepted ? "added" : "price_changed";
|
|
1238
|
+
case "rejected":
|
|
1239
|
+
return result.reason;
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
function resultToToast(result, shownOdds, ui) {
|
|
1243
|
+
if (!result) return { kind: "success", text: ui.slipAdded };
|
|
1244
|
+
switch (result.status) {
|
|
1245
|
+
case "added":
|
|
1246
|
+
return { kind: "success", text: ui.slipAdded };
|
|
1247
|
+
case "duplicate":
|
|
1248
|
+
return { kind: "success", text: ui.slipDuplicate };
|
|
1249
|
+
case "price_changed":
|
|
1250
|
+
return result.accepted ? { kind: "success", text: ui.slipPriceChanged(shownOdds, result.newOddsDecimal.toFixed(2)) } : { kind: "error", text: ui.slipPriceNotAccepted(shownOdds, result.newOddsDecimal.toFixed(2)) };
|
|
1251
|
+
case "rejected":
|
|
1252
|
+
if (result.reason === "login_required") return { kind: "error", text: ui.slipLoginRequired };
|
|
1253
|
+
if (result.reason === "suspended") return { kind: "error", text: ui.slipSuspended };
|
|
1254
|
+
return { kind: "error", text: result.message ?? ui.slipFailed };
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
// src/useHostEvents.ts
|
|
1259
|
+
var import_react44 = require("react");
|
|
1260
|
+
|
|
1261
|
+
// src/components/ChatPanel.tsx
|
|
1262
|
+
var import_react43 = require("react");
|
|
1263
|
+
|
|
1264
|
+
// src/components/ChatHeader.tsx
|
|
1265
|
+
var import_react2 = require("react");
|
|
1266
|
+
|
|
1267
|
+
// src/assets/logo.ts
|
|
1268
|
+
var SS_LOGO_DATA_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAjoAAAI6CAYAAAAuSOLAAAAAAXNSR0IArs4c6QAAAJplWElmTU0AKgAAAAgAAwESAAMAAAABAAEAAAExAAIAAAA9AAAAModpAAQAAAABAAAAcAAAAABBZG9iZSBQaG90b3Nob3AgMjYuMTAgKDIwMjUwNzMwLm0uMzE2NiAwN2IxMjM0KSAgKE1hY2ludG9zaCkAAAADoAEAAwAAAAEAAQAAoAIABAAAAAEAAAI6oAMABAAAAAEAAAI6AAAAACwGtVwAAAQwaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA2LjAuMCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgICAgICAgICB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgICAgICAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyI+CiAgICAgICAgIDx4bXBNTTpEb2N1bWVudElEPnhtcC5kaWQ6QzBGRTM5NjM5NEJEMTFGMEJGNjZEMjUyN0U0MzU5RUM8L3htcE1NOkRvY3VtZW50SUQ+CiAgICAgICAgIDx4bXBNTTpJbnN0YW5jZUlEPnhtcC5paWQ6QzBGRTM5NjI5NEJEMTFGMEJGNjZEMjUyN0U0MzU5RUM8L3htcE1NOkluc3RhbmNlSUQ+CiAgICAgICAgIDx4bXBNTTpEZXJpdmVkRnJvbSByZGY6cGFyc2VUeXBlPSJSZXNvdXJjZSI+CiAgICAgICAgICAgIDxzdFJlZjppbnN0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";
|
|
1269
|
+
|
|
1270
|
+
// src/components/Icons.tsx
|
|
1271
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1272
|
+
function SsMark({ className }) {
|
|
1273
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { className, src: SS_LOGO_DATA_URI, alt: "", "aria-hidden": "true" });
|
|
1274
|
+
}
|
|
1275
|
+
function SendIcon() {
|
|
1276
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M3 20l18-8L3 4v6l12 2-12 2z" }) });
|
|
1277
|
+
}
|
|
1278
|
+
function CloseIcon() {
|
|
1279
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "none", d: "M18 6L6 18M6 6l12 12", stroke: "currentColor", strokeWidth: "2" }) });
|
|
1280
|
+
}
|
|
1281
|
+
function ChevronIcon() {
|
|
1282
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
|
|
1283
|
+
}
|
|
1284
|
+
function KebabIcon() {
|
|
1285
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { fill: "currentColor", children: [
|
|
1286
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "5", r: "1.6" }),
|
|
1287
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "1.6" }),
|
|
1288
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "19", r: "1.6" })
|
|
1289
|
+
] }) });
|
|
1290
|
+
}
|
|
1291
|
+
function SlidersIcon() {
|
|
1292
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
|
|
1293
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 6h9M17 6h3M4 12h3M11 12h9M4 18h11M19 18h1" }),
|
|
1294
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "15", cy: "6", r: "2" }),
|
|
1295
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "9", cy: "12", r: "2" }),
|
|
1296
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "17", cy: "18", r: "2" })
|
|
1297
|
+
] }) });
|
|
1298
|
+
}
|
|
1299
|
+
function ComboIcon() {
|
|
1300
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinejoin: "round", children: [
|
|
1301
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3", y: "3", width: "11", height: "11", rx: "2" }),
|
|
1302
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "10", y: "10", width: "11", height: "11", rx: "2" })
|
|
1303
|
+
] }) });
|
|
1304
|
+
}
|
|
1305
|
+
var DASHBOARD_EMOJI = {
|
|
1306
|
+
football: "\u26BD",
|
|
1307
|
+
"goal-net": "\u{1F945}",
|
|
1308
|
+
btts: "\u{1F91D}",
|
|
1309
|
+
card: "\u{1F7E8}",
|
|
1310
|
+
"corner-flag": "\u{1F6A9}",
|
|
1311
|
+
player: "\u{1F464}",
|
|
1312
|
+
target: "\u{1F3AF}",
|
|
1313
|
+
trophy: "\u{1F3C6}",
|
|
1314
|
+
"chart-line": "\u{1F4C8}",
|
|
1315
|
+
"chart-bar": "\u{1F4CA}",
|
|
1316
|
+
chat: "\u{1F4AC}",
|
|
1317
|
+
fire: "\u{1F525}",
|
|
1318
|
+
sparkle: "\u2728"
|
|
1319
|
+
};
|
|
1320
|
+
function DashboardIcon({ name }) {
|
|
1321
|
+
const glyph = Object.prototype.hasOwnProperty.call(DASHBOARD_EMOJI, name) ? DASHBOARD_EMOJI[name] : DASHBOARD_EMOJI.target;
|
|
1322
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ss-w-dash-emoji", "aria-hidden": "true", children: glyph });
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// src/components/BrandMark.tsx
|
|
1326
|
+
var import_react = require("react");
|
|
1327
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1328
|
+
function BrandMark({ logoUrl, className }) {
|
|
909
1329
|
const [failed, setFailed] = (0, import_react.useState)(false);
|
|
910
1330
|
if (!logoUrl || failed) return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SsMark, { className });
|
|
911
1331
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("img", { className, src: logoUrl, alt: "", "aria-hidden": "true", onError: () => setFailed(true) });
|
|
@@ -948,551 +1368,211 @@ function ChatHeader({
|
|
|
948
1368
|
document.removeEventListener("mousedown", onPointer);
|
|
949
1369
|
document.removeEventListener("keydown", onKey);
|
|
950
1370
|
};
|
|
951
|
-
}, [menuOpen]);
|
|
952
|
-
const runNewConversation = () => {
|
|
953
|
-
setMenuOpen(false);
|
|
954
|
-
onNewConversation?.();
|
|
955
|
-
};
|
|
956
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "ss-w-header", children: [
|
|
957
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(BrandMark, { logoUrl, className: "ss-w-header-mark" }),
|
|
958
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ss-w-header-title", children: headerTitle ?? "SensibleStats" }),
|
|
959
|
-
hasMenu ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ss-w-menu-wrap", ref: wrapRef, children: [
|
|
960
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
961
|
-
"button",
|
|
962
|
-
{
|
|
963
|
-
type: "button",
|
|
964
|
-
className: "ss-w-kebab",
|
|
965
|
-
ref: kebabRef,
|
|
966
|
-
"aria-haspopup": "menu",
|
|
967
|
-
"aria-expanded": menuOpen,
|
|
968
|
-
"aria-label": moreOptionsLabel,
|
|
969
|
-
onClick: () => setMenuOpen((open) => !open),
|
|
970
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(KebabIcon, {})
|
|
971
|
-
}
|
|
972
|
-
),
|
|
973
|
-
menuOpen ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ss-w-menu", role: "menu", "aria-label": moreOptionsLabel, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
974
|
-
"button",
|
|
975
|
-
{
|
|
976
|
-
type: "button",
|
|
977
|
-
className: "ss-w-menu-item",
|
|
978
|
-
role: "menuitem",
|
|
979
|
-
ref: firstItemRef,
|
|
980
|
-
onClick: runNewConversation,
|
|
981
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: newConversationLabel })
|
|
982
|
-
}
|
|
983
|
-
) }) : null
|
|
984
|
-
] }) : null,
|
|
985
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "ss-w-close", "aria-label": closeLabel, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CloseIcon, {}) })
|
|
986
|
-
] });
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
// src/components/MessageList.tsx
|
|
990
|
-
var import_react21 = require("react");
|
|
991
|
-
|
|
992
|
-
// src/presets.ts
|
|
993
|
-
var CLIENT_CAPABILITIES = ["price_presets"];
|
|
994
|
-
var PRICE_TILE_PREFIX = "goal-preset-";
|
|
995
|
-
function isPriceTile(tile) {
|
|
996
|
-
const id = tile.signal?.entityId;
|
|
997
|
-
return typeof id === "string" && id.startsWith(PRICE_TILE_PREFIX);
|
|
998
|
-
}
|
|
999
|
-
function tileDisplayText(tile) {
|
|
1000
|
-
const subtitle = tile.subtitle?.trim();
|
|
1001
|
-
return isPriceTile(tile) && subtitle ? subtitle : tile.title;
|
|
1002
|
-
}
|
|
1003
|
-
var MAX_BUBBLE_ASKS = 2;
|
|
1004
|
-
function bubbleAsks(messages) {
|
|
1005
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1006
|
-
const m = messages[i];
|
|
1007
|
-
if (!m || m.role !== "assistant" || !m.isGreeting) continue;
|
|
1008
|
-
const dashboard = m.greetingDashboard;
|
|
1009
|
-
if (!dashboard) return [];
|
|
1010
|
-
const asks = [];
|
|
1011
|
-
dashboard.tiles.forEach((tile, index) => {
|
|
1012
|
-
const label = tile.subtitle?.trim();
|
|
1013
|
-
if (asks.length < MAX_BUBBLE_ASKS && isPriceTile(tile) && label) asks.push({ tile, index, position: asks.length, layout: dashboard.layout, label });
|
|
1014
|
-
});
|
|
1015
|
-
return asks;
|
|
1016
|
-
}
|
|
1017
|
-
return [];
|
|
1018
|
-
}
|
|
1019
|
-
function runBubbleAsk(ask, deps) {
|
|
1020
|
-
deps.log?.log({ name: "popup_ask_tap", outcome: String(ask.position) });
|
|
1021
|
-
deps.open();
|
|
1022
|
-
deps.setActiveTab("chat");
|
|
1023
|
-
const displayText = tileDisplayText(ask.tile);
|
|
1024
|
-
deps.send(ask.tile.actionValue, ask.tile.actionRef ? { actionRef: ask.tile.actionRef, displayText } : { displayText });
|
|
1025
|
-
deps.onDashboardTap?.(ask.tile, ask.index, ask.layout);
|
|
1026
|
-
}
|
|
1027
|
-
|
|
1028
|
-
// src/components/Markdown.tsx
|
|
1029
|
-
var import_react_markdown = __toESM(require("react-markdown"), 1);
|
|
1030
|
-
var import_remark_gfm = __toESM(require("remark-gfm"), 1);
|
|
1031
|
-
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1032
|
-
var components = {
|
|
1033
|
-
a: ({ children, href }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "ss-w-md-link", children }),
|
|
1034
|
-
table: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "ss-w-table-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("table", { className: "ss-w-md-table", children }) }),
|
|
1035
|
-
ul: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "ss-w-md-ul", children }),
|
|
1036
|
-
ol: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ol", { className: "ss-w-md-ol", children }),
|
|
1037
|
-
code: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("code", { className: "ss-w-md-code", children })
|
|
1038
|
-
};
|
|
1039
|
-
function Markdown({ text }) {
|
|
1040
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "ss-w-md", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default], components, children: text }) });
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
// src/components/EntityCardList.tsx
|
|
1044
|
-
var import_react3 = require("react");
|
|
1045
|
-
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1046
|
-
var MAX_VISIBLE = 3;
|
|
1047
|
-
var RECORD_RE = /^(\d+)-(\d+)-(\d+)$/;
|
|
1048
|
-
function initials(name) {
|
|
1049
|
-
return name.split(/\s+/).slice(0, 2).map((w) => w[0] ?? "").join("").toUpperCase();
|
|
1050
|
-
}
|
|
1051
|
-
function shortSeason(season) {
|
|
1052
|
-
return season.replace(/\d{4}/g, (year) => year.slice(2));
|
|
1053
|
-
}
|
|
1054
|
-
function Crest({ image, label }) {
|
|
1055
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1056
|
-
"span",
|
|
1057
|
-
{
|
|
1058
|
-
className: "ss-w-ecard-crest",
|
|
1059
|
-
style: { backgroundImage: `url(${image})` },
|
|
1060
|
-
...label ? { role: "img", title: label, "aria-label": label } : { "aria-hidden": "true" }
|
|
1061
|
-
}
|
|
1062
|
-
);
|
|
1063
|
-
}
|
|
1064
|
-
function FormBar({ w, d, l }) {
|
|
1065
|
-
if (w + d + l === 0) return null;
|
|
1066
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "ss-w-form", "aria-hidden": "true", children: [
|
|
1067
|
-
w > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-form-w", style: { flexGrow: w } }) : null,
|
|
1068
|
-
d > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-form-d", style: { flexGrow: d } }) : null,
|
|
1069
|
-
l > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-form-l", style: { flexGrow: l } }) : null
|
|
1070
|
-
] });
|
|
1071
|
-
}
|
|
1072
|
-
function StatCellView({ s }) {
|
|
1073
|
-
const m = RECORD_RE.exec(s.value);
|
|
1074
|
-
if (m) {
|
|
1075
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-w-ecard-cell", children: [
|
|
1076
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-ecard-cval", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FormBar, { w: Number(m[1]), d: Number(m[2]), l: Number(m[3]) }) }),
|
|
1077
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-ecard-clabel", children: s.value })
|
|
1078
|
-
] });
|
|
1079
|
-
}
|
|
1080
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-w-ecard-cell", children: [
|
|
1081
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-ecard-cval", children: s.value }),
|
|
1082
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-ecard-clabel", children: s.label })
|
|
1083
|
-
] });
|
|
1084
|
-
}
|
|
1085
|
-
function StatStrip({ stats }) {
|
|
1086
|
-
if (stats.length === 0) return null;
|
|
1087
|
-
const cols = Math.min(stats.length, 4);
|
|
1088
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ss-w-ecard-strip", style: { gridTemplateColumns: `repeat(${cols}, 1fr)` }, children: stats.map((s) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StatCellView, { s }, s.label + s.value)) });
|
|
1089
|
-
}
|
|
1090
|
-
function CardHeader({ c, showScopeCrest }) {
|
|
1091
|
-
const onlyScope = c.scopes.length === 1 ? c.scopes[0] : null;
|
|
1092
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-w-ecard-hd", children: [
|
|
1093
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1094
|
-
"span",
|
|
1095
|
-
{
|
|
1096
|
-
className: c.kind === "team" ? "ss-w-thumb ss-w-team" : "ss-w-thumb",
|
|
1097
|
-
style: c.image ? { backgroundImage: `url(${c.image})` } : void 0,
|
|
1098
|
-
children: c.image ? "" : initials(c.name)
|
|
1099
|
-
}
|
|
1100
|
-
),
|
|
1101
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "ss-w-ecard-idtext", children: [
|
|
1102
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-nm", children: c.name }),
|
|
1103
|
-
c.sub ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-sub", children: c.sub }) : null
|
|
1104
|
-
] }),
|
|
1105
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "ss-w-ecard-meta", children: [
|
|
1106
|
-
showScopeCrest && onlyScope?.image ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Crest, { image: onlyScope.image, label: onlyScope.label }) : null,
|
|
1107
|
-
c.season ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-ecard-season", children: shortSeason(c.season) }) : null
|
|
1108
|
-
] })
|
|
1109
|
-
] });
|
|
1110
|
-
}
|
|
1111
|
-
function ScopeTabs({ scopes, active, onSelect }) {
|
|
1112
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ss-w-ecard-scopes", role: "tablist", children: scopes.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1113
|
-
"button",
|
|
1114
|
-
{
|
|
1115
|
-
type: "button",
|
|
1116
|
-
role: "tab",
|
|
1117
|
-
className: "ss-w-ecard-scope",
|
|
1118
|
-
"aria-selected": i === active,
|
|
1119
|
-
"aria-label": s.label,
|
|
1120
|
-
title: s.label,
|
|
1121
|
-
onClick: () => onSelect(i),
|
|
1122
|
-
children: s.image ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Crest, { image: s.image }) : s.label
|
|
1123
|
-
},
|
|
1124
|
-
String(s.competitionId ?? "all")
|
|
1125
|
-
)) });
|
|
1126
|
-
}
|
|
1127
|
-
function EntityCardItem({ c }) {
|
|
1128
|
-
const [active, setActive] = (0, import_react3.useState)(0);
|
|
1129
|
-
const hasBody = c.scopes.length > 0;
|
|
1130
|
-
if (!hasBody) {
|
|
1131
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ss-w-ecard ss-w-ecard-bare", "data-kind": c.kind, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CardHeader, { c, showScopeCrest: false }) });
|
|
1132
|
-
}
|
|
1133
|
-
const multi = c.scopes.length > 1;
|
|
1134
|
-
const scope = c.scopes[multi ? c.scopes[active] ? active : 0 : 0];
|
|
1135
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-w-ecard", "data-kind": c.kind, children: [
|
|
1136
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CardHeader, { c, showScopeCrest: !multi }),
|
|
1137
|
-
multi ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ScopeTabs, { scopes: c.scopes, active, onSelect: setActive }) : null,
|
|
1138
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StatStrip, { stats: scope.stats })
|
|
1139
|
-
] });
|
|
1140
|
-
}
|
|
1141
|
-
function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMoreLabel = (n) => `Show ${n} more` }) {
|
|
1142
|
-
const [expanded, setExpanded] = (0, import_react3.useState)(false);
|
|
1143
|
-
if (cards.length === 0) return null;
|
|
1144
|
-
const visible = expanded ? cards : cards.slice(0, MAX_VISIBLE);
|
|
1145
|
-
const overflow = cards.length - MAX_VISIBLE;
|
|
1146
|
-
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-w-cards", children: [
|
|
1147
|
-
visible.map((c) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EntityCardItem, { c }, c.id)),
|
|
1148
|
-
overflow > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ss-w-cards-more", disabled, onClick: () => setExpanded((v) => !v), children: expanded ? showLessLabel : showMoreLabel(overflow) }) : null
|
|
1149
|
-
] });
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
|
-
// src/components/AddToSlipButton.tsx
|
|
1153
|
-
var import_react7 = require("react");
|
|
1154
|
-
var import_widget_sdk = require("@sensiblestats/widget-sdk");
|
|
1155
|
-
|
|
1156
|
-
// src/chart.ts
|
|
1157
|
-
var CHART_VIEW_W = 320;
|
|
1158
|
-
var CHART_VIEW_H = 140;
|
|
1159
|
-
var PLOT_X0 = 40;
|
|
1160
|
-
var PLOT_X1 = 312;
|
|
1161
|
-
var PLOT_Y0 = 10;
|
|
1162
|
-
var PLOT_Y1 = 110;
|
|
1163
|
-
var MARKER_MAX_POINTS = 8;
|
|
1164
|
-
var FLAT_DOMAIN_PAD = 0.05;
|
|
1165
|
-
var round2 = (n) => Math.round(n * 100) / 100;
|
|
1166
|
-
function projectSeries(samples) {
|
|
1167
|
-
if (samples.length < 2) return null;
|
|
1168
|
-
const tMin = samples[0].atMs;
|
|
1169
|
-
const tMax = samples[samples.length - 1].atMs;
|
|
1170
|
-
if (tMax <= tMin) return null;
|
|
1171
|
-
const prices = samples.map((s) => s.price);
|
|
1172
|
-
const pMin = Math.min(...prices);
|
|
1173
|
-
const pMax = Math.max(...prices);
|
|
1174
|
-
let yMin;
|
|
1175
|
-
let yMax;
|
|
1176
|
-
if (pMax === pMin) {
|
|
1177
|
-
yMin = pMin - FLAT_DOMAIN_PAD;
|
|
1178
|
-
yMax = pMax + FLAT_DOMAIN_PAD;
|
|
1179
|
-
} else {
|
|
1180
|
-
const pad = (pMax - pMin) * 0.1;
|
|
1181
|
-
yMin = pMin - pad;
|
|
1182
|
-
yMax = pMax + pad;
|
|
1183
|
-
}
|
|
1184
|
-
const x = (atMs) => round2(PLOT_X0 + (atMs - tMin) / (tMax - tMin) * (PLOT_X1 - PLOT_X0));
|
|
1185
|
-
const y = (price) => round2(PLOT_Y1 - (price - yMin) / (yMax - yMin) * (PLOT_Y1 - PLOT_Y0));
|
|
1186
|
-
const projected = samples.map((s) => ({ x: x(s.atMs), y: y(s.price) }));
|
|
1187
|
-
const path = projected.map((p, i) => `${i === 0 ? "M" : "L"}${p.x} ${p.y}`).join(" ");
|
|
1188
|
-
return {
|
|
1189
|
-
path,
|
|
1190
|
-
markers: samples.length <= MARKER_MAX_POINTS ? projected : [],
|
|
1191
|
-
// The real extremes, never the padded bounds: a gridline label is a price, and the padded
|
|
1192
|
-
// bounds are prices nobody was ever offered. A flat series has one real extreme, not two —
|
|
1193
|
-
// two identical gridlines would collide on both React key and rendered text.
|
|
1194
|
-
gridlines: pMax === pMin ? [{ y: y(pMax), price: pMax }] : [
|
|
1195
|
-
{ y: y(pMax), price: pMax },
|
|
1196
|
-
{ y: y(pMin), price: pMin }
|
|
1197
|
-
],
|
|
1198
|
-
viewBox: `0 0 ${CHART_VIEW_W} ${CHART_VIEW_H}`,
|
|
1199
|
-
startMs: tMin,
|
|
1200
|
-
endMs: tMax
|
|
1371
|
+
}, [menuOpen]);
|
|
1372
|
+
const runNewConversation = () => {
|
|
1373
|
+
setMenuOpen(false);
|
|
1374
|
+
onNewConversation?.();
|
|
1201
1375
|
};
|
|
1376
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("header", { className: "ss-w-header", children: [
|
|
1377
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(BrandMark, { logoUrl, className: "ss-w-header-mark" }),
|
|
1378
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "ss-w-header-title", children: headerTitle ?? "SensibleStats" }),
|
|
1379
|
+
hasMenu ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ss-w-menu-wrap", ref: wrapRef, children: [
|
|
1380
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1381
|
+
"button",
|
|
1382
|
+
{
|
|
1383
|
+
type: "button",
|
|
1384
|
+
className: "ss-w-kebab",
|
|
1385
|
+
ref: kebabRef,
|
|
1386
|
+
"aria-haspopup": "menu",
|
|
1387
|
+
"aria-expanded": menuOpen,
|
|
1388
|
+
"aria-label": moreOptionsLabel,
|
|
1389
|
+
onClick: () => setMenuOpen((open) => !open),
|
|
1390
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(KebabIcon, {})
|
|
1391
|
+
}
|
|
1392
|
+
),
|
|
1393
|
+
menuOpen ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ss-w-menu", role: "menu", "aria-label": moreOptionsLabel, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1394
|
+
"button",
|
|
1395
|
+
{
|
|
1396
|
+
type: "button",
|
|
1397
|
+
className: "ss-w-menu-item",
|
|
1398
|
+
role: "menuitem",
|
|
1399
|
+
ref: firstItemRef,
|
|
1400
|
+
onClick: runNewConversation,
|
|
1401
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: newConversationLabel })
|
|
1402
|
+
}
|
|
1403
|
+
) }) : null
|
|
1404
|
+
] }) : null,
|
|
1405
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: "ss-w-close", "aria-label": closeLabel, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CloseIcon, {}) })
|
|
1406
|
+
] });
|
|
1202
1407
|
}
|
|
1203
1408
|
|
|
1204
|
-
// src/
|
|
1205
|
-
|
|
1206
|
-
|
|
1409
|
+
// src/components/MessageList.tsx
|
|
1410
|
+
var import_react22 = require("react");
|
|
1411
|
+
|
|
1412
|
+
// src/presets.ts
|
|
1413
|
+
var CLIENT_CAPABILITIES = ["price_presets"];
|
|
1414
|
+
var PRICE_TILE_PREFIX = "goal-preset-";
|
|
1415
|
+
function isPriceTile(tile) {
|
|
1416
|
+
const id = tile.signal?.entityId;
|
|
1417
|
+
return typeof id === "string" && id.startsWith(PRICE_TILE_PREFIX);
|
|
1207
1418
|
}
|
|
1208
|
-
function
|
|
1209
|
-
|
|
1419
|
+
function tileDisplayText(tile) {
|
|
1420
|
+
const subtitle = tile.subtitle?.trim();
|
|
1421
|
+
return isPriceTile(tile) && subtitle ? subtitle : tile.title;
|
|
1210
1422
|
}
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
const
|
|
1217
|
-
if (
|
|
1423
|
+
var MAX_BUBBLE_ASKS = 2;
|
|
1424
|
+
function bubbleAsks(messages) {
|
|
1425
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1426
|
+
const m = messages[i];
|
|
1427
|
+
if (!m || m.role !== "assistant" || !m.isGreeting) continue;
|
|
1428
|
+
const dashboard = m.greetingDashboard;
|
|
1429
|
+
if (!dashboard) return [];
|
|
1430
|
+
const asks = [];
|
|
1431
|
+
dashboard.tiles.forEach((tile, index) => {
|
|
1432
|
+
const label = tile.subtitle?.trim();
|
|
1433
|
+
if (asks.length < MAX_BUBBLE_ASKS && isPriceTile(tile) && label) asks.push({ tile, index, position: asks.length, layout: dashboard.layout, label });
|
|
1434
|
+
});
|
|
1435
|
+
return asks;
|
|
1218
1436
|
}
|
|
1219
|
-
return
|
|
1220
|
-
}
|
|
1221
|
-
function toCardVM(card) {
|
|
1222
|
-
const entityType = str(card.entityType)?.toLowerCase();
|
|
1223
|
-
const kind = entityType === "player" ? "player" : entityType === "team" ? "team" : "generic";
|
|
1224
|
-
const scopes = Array.isArray(card.scopes) ? card.scopes.map((s) => {
|
|
1225
|
-
const label = str(s.label);
|
|
1226
|
-
if (!label) return null;
|
|
1227
|
-
return {
|
|
1228
|
-
competitionId: typeof s.competitionId === "number" ? s.competitionId : null,
|
|
1229
|
-
label,
|
|
1230
|
-
image: str(s.imagePath),
|
|
1231
|
-
stats: readStatCells(s.stats)
|
|
1232
|
-
};
|
|
1233
|
-
}).filter((s) => s !== null) : [];
|
|
1234
|
-
return {
|
|
1235
|
-
id: String(card.entityId),
|
|
1236
|
-
kind,
|
|
1237
|
-
name: card.canonicalName,
|
|
1238
|
-
image: str(card.imagePath) ?? str(card.imageUrl) ?? str(card.image_url) ?? str(card.logoUrl) ?? str(card.logo),
|
|
1239
|
-
sub: str(card.subtitle) ?? str(card.teamName) ?? str(card.leagueName) ?? str(card.position),
|
|
1240
|
-
season: str(card.seasonName),
|
|
1241
|
-
scopes
|
|
1242
|
-
};
|
|
1243
|
-
}
|
|
1244
|
-
function toActionVM(action) {
|
|
1245
|
-
const label = str(action.actionLabel) ?? str(action.label) ?? str(action.text) ?? str(action.title) ?? "Continue";
|
|
1246
|
-
const actionId = str(action.actionId) ?? str(action.id);
|
|
1247
|
-
const actionRef = str(action.actionRef);
|
|
1248
|
-
const message = str(action.actionValue) ?? str(action.message) ?? str(action.query) ?? label;
|
|
1249
|
-
return { label, actionId, actionRef, message };
|
|
1250
|
-
}
|
|
1251
|
-
function toChipVM(intent) {
|
|
1252
|
-
const text = str(intent.text) ?? str(intent.label) ?? str(intent.title);
|
|
1253
|
-
return text ? { text } : null;
|
|
1254
|
-
}
|
|
1255
|
-
function chipFromText(text) {
|
|
1256
|
-
return { text };
|
|
1257
|
-
}
|
|
1258
|
-
function joinSelection(label, line) {
|
|
1259
|
-
if (label && line && label.includes(line)) return label;
|
|
1260
|
-
return [label, line].filter(Boolean).join(" ") || void 0;
|
|
1437
|
+
return [];
|
|
1261
1438
|
}
|
|
1262
|
-
function
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
const
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
const oddsValue = insight.odds?.value;
|
|
1270
|
-
const matchOddsId = typeof insight.odds?.matchOddsId === "number" ? insight.odds.matchOddsId : void 0;
|
|
1271
|
-
const home = str(insight.homeTeamName);
|
|
1272
|
-
const away = str(insight.awayTeamName);
|
|
1273
|
-
const fixture = home && away ? `${home} vs ${away}` : void 0;
|
|
1274
|
-
const competition = str(insight.competitionName);
|
|
1275
|
-
const previousValue = insight.odds?.previousValue;
|
|
1276
|
-
const previous = typeof previousValue === "number" && Number.isFinite(previousValue) ? previousValue : void 0;
|
|
1277
|
-
const direction = insight.odds?.movementDirection === "shortened" || insight.odds?.movementDirection === "drifted" ? insight.odds.movementDirection : void 0;
|
|
1278
|
-
return {
|
|
1279
|
-
id: str(insight.insightId) ?? `${str(insight.ruleId) ?? "insight"}:${num(insight.matchId) ?? ""}`,
|
|
1280
|
-
market,
|
|
1281
|
-
selection,
|
|
1282
|
-
odds,
|
|
1283
|
-
// The wire payload carries bookmakerId (not a name); render a name only if one is present.
|
|
1284
|
-
bookmaker: str(insight.odds?.bookmakerName),
|
|
1285
|
-
isLive: insight.odds?.isLive === true,
|
|
1286
|
-
phase: str(insight.phase),
|
|
1287
|
-
fixture,
|
|
1288
|
-
competition,
|
|
1289
|
-
text: str(insight.text) ?? "",
|
|
1290
|
-
disclaimer: str(insight.disclaimer),
|
|
1291
|
-
matchId: typeof insight.matchId === "number" ? insight.matchId : 0,
|
|
1292
|
-
matchOddsId,
|
|
1293
|
-
marketDeveloperName: str(insight.marketDeveloperName),
|
|
1294
|
-
selectionLabel: str(insight.selectionLabel),
|
|
1295
|
-
line: str(insight.line),
|
|
1296
|
-
addToSlipEnabled: insight.addToSlipEnabled === true,
|
|
1297
|
-
pinEnabled: insight.pinEnabled === true,
|
|
1298
|
-
isMarketFloor: insight.isMarketFloor === true,
|
|
1299
|
-
oddsDecimal: typeof oddsValue === "number" && Number.isFinite(oddsValue) ? oddsValue : void 0,
|
|
1300
|
-
quotedAtUtc: str(insight.odds?.quotedAtUtc),
|
|
1301
|
-
previousOdds: previous !== void 0 && direction !== void 0 ? previous.toFixed(2) : void 0,
|
|
1302
|
-
movementDirection: previous !== void 0 && direction !== void 0 ? direction : void 0,
|
|
1303
|
-
homeTeamName: home,
|
|
1304
|
-
awayTeamName: away,
|
|
1305
|
-
homeCrest: str(insight.homeTeamImagePath),
|
|
1306
|
-
awayCrest: str(insight.awayTeamImagePath),
|
|
1307
|
-
competitionCrest: str(insight.competitionImagePath),
|
|
1308
|
-
kickoffUtc: str(insight.kickoffUtc),
|
|
1309
|
-
kickoffLabel: str(insight.kickoffLabel)
|
|
1310
|
-
};
|
|
1439
|
+
function runBubbleAsk(ask, deps) {
|
|
1440
|
+
deps.log?.log({ name: "popup_ask_tap", outcome: String(ask.position) });
|
|
1441
|
+
deps.open();
|
|
1442
|
+
deps.setActiveTab("chat");
|
|
1443
|
+
const displayText = tileDisplayText(ask.tile);
|
|
1444
|
+
deps.send(ask.tile.actionValue, ask.tile.actionRef ? { actionRef: ask.tile.actionRef, displayText } : { displayText });
|
|
1445
|
+
deps.onDashboardTap?.(ask.tile, ask.index, ask.layout);
|
|
1311
1446
|
}
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
}
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1447
|
+
|
|
1448
|
+
// src/components/Markdown.tsx
|
|
1449
|
+
var import_react_markdown = __toESM(require("react-markdown"), 1);
|
|
1450
|
+
var import_remark_gfm = __toESM(require("remark-gfm"), 1);
|
|
1451
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1452
|
+
var components = {
|
|
1453
|
+
a: ({ children, href }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "ss-w-md-link", children }),
|
|
1454
|
+
table: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "ss-w-table-wrap", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("table", { className: "ss-w-md-table", children }) }),
|
|
1455
|
+
ul: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "ss-w-md-ul", children }),
|
|
1456
|
+
ol: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ol", { className: "ss-w-md-ol", children }),
|
|
1457
|
+
code: ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("code", { className: "ss-w-md-code", children })
|
|
1458
|
+
};
|
|
1459
|
+
function Markdown({ text }) {
|
|
1460
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "ss-w-md", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react_markdown.default, { remarkPlugins: [import_remark_gfm.default], components, children: text }) });
|
|
1323
1461
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
key: `${selection}:${str(r.line) ?? ""}:${i}`,
|
|
1333
|
-
selection,
|
|
1334
|
-
selectionLabel: str(r.selectionLabel),
|
|
1335
|
-
line: str(r.line),
|
|
1336
|
-
odds: price !== void 0 ? price.toFixed(2) : "",
|
|
1337
|
-
oddsDecimal: price,
|
|
1338
|
-
matchOddsId: typeof r.matchOddsId === "number" ? r.matchOddsId : void 0,
|
|
1339
|
-
quotedAtUtc: str(r.quotedAtUtc),
|
|
1340
|
-
isMain: r.isMainLine !== false,
|
|
1341
|
-
// === true, not !== false: a server that has not shipped the field yet leaves it undefined,
|
|
1342
|
-
// and undefined must read as open rather than hiding every price on the card.
|
|
1343
|
-
isSuspended: r.isSuspended === true,
|
|
1344
|
-
previousOdds: previous !== void 0 && direction !== void 0 ? previous.toFixed(2) : void 0,
|
|
1345
|
-
movementDirection: previous !== void 0 && direction !== void 0 ? direction : void 0
|
|
1346
|
-
};
|
|
1347
|
-
};
|
|
1348
|
-
const vms = rows.map(toRow).filter((r) => r.selection && r.odds && !r.isSuspended);
|
|
1349
|
-
const home = str(card.homeTeamName);
|
|
1350
|
-
const away = str(card.awayTeamName);
|
|
1351
|
-
const updated = rows.map((r) => str(r.quotedAtUtc)).filter((v) => !!v).sort();
|
|
1352
|
-
return {
|
|
1353
|
-
id: `${typeof card.matchId === "number" ? card.matchId : 0}:${str(card.marketDeveloperName) ?? ""}`,
|
|
1354
|
-
market: str(card.marketLabel) ?? str(card.marketDeveloperName) ?? "",
|
|
1355
|
-
fixture: home && away ? `${home} vs ${away}` : void 0,
|
|
1356
|
-
homeTeamName: home,
|
|
1357
|
-
awayTeamName: away,
|
|
1358
|
-
homeCrest: str(card.homeTeamImagePath),
|
|
1359
|
-
awayCrest: str(card.awayTeamImagePath),
|
|
1360
|
-
competitionCrest: str(card.competitionImagePath),
|
|
1361
|
-
competition: str(card.competitionName),
|
|
1362
|
-
kickoffUtc: str(card.kickoffUtc),
|
|
1363
|
-
kickoffLabel: str(card.kickoffLabel),
|
|
1364
|
-
bookmaker: rows.map((r) => str(r.bookmakerName)).find(Boolean),
|
|
1365
|
-
updatedAt: updated.length > 0 ? updated[updated.length - 1] : void 0,
|
|
1366
|
-
isLive: rows.some((r) => r.isLive === true),
|
|
1367
|
-
matchId: typeof card.matchId === "number" ? card.matchId : 0,
|
|
1368
|
-
mainRows: vms.filter((r) => r.isMain),
|
|
1369
|
-
moreRows: vms.filter((r) => !r.isMain),
|
|
1370
|
-
disclaimer: str(card.disclaimer),
|
|
1371
|
-
addToSlipEnabled: card.addToSlipEnabled === true,
|
|
1372
|
-
marketDeveloperName: str(card.marketDeveloperName),
|
|
1373
|
-
mainLine: str(card.mainLine),
|
|
1374
|
-
pinEnabled: card.pinEnabled === true,
|
|
1375
|
-
statText: card.insight && typeof card.insight === "object" ? str(card.insight.text) : void 0,
|
|
1376
|
-
tag: card.tag === "your_market" ? "your_market" : void 0
|
|
1377
|
-
};
|
|
1462
|
+
|
|
1463
|
+
// src/components/EntityCardList.tsx
|
|
1464
|
+
var import_react3 = require("react");
|
|
1465
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1466
|
+
var MAX_VISIBLE = 3;
|
|
1467
|
+
var RECORD_RE = /^(\d+)-(\d+)-(\d+)$/;
|
|
1468
|
+
function initials(name) {
|
|
1469
|
+
return name.split(/\s+/).slice(0, 2).map((w) => w[0] ?? "").join("").toUpperCase();
|
|
1378
1470
|
}
|
|
1379
|
-
function
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1471
|
+
function shortSeason(season) {
|
|
1472
|
+
return season.replace(/\d{4}/g, (year) => year.slice(2));
|
|
1473
|
+
}
|
|
1474
|
+
function Crest({ image, label }) {
|
|
1475
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1476
|
+
"span",
|
|
1477
|
+
{
|
|
1478
|
+
className: "ss-w-ecard-crest",
|
|
1479
|
+
style: { backgroundImage: `url(${image})` },
|
|
1480
|
+
...label ? { role: "img", title: label, "aria-label": label } : { "aria-hidden": "true" }
|
|
1481
|
+
}
|
|
1482
|
+
);
|
|
1483
|
+
}
|
|
1484
|
+
function FormBar({ w, d, l }) {
|
|
1485
|
+
if (w + d + l === 0) return null;
|
|
1486
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "ss-w-form", "aria-hidden": "true", children: [
|
|
1487
|
+
w > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-form-w", style: { flexGrow: w } }) : null,
|
|
1488
|
+
d > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-form-d", style: { flexGrow: d } }) : null,
|
|
1489
|
+
l > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-form-l", style: { flexGrow: l } }) : null
|
|
1490
|
+
] });
|
|
1491
|
+
}
|
|
1492
|
+
function StatCellView({ s }) {
|
|
1493
|
+
const m = RECORD_RE.exec(s.value);
|
|
1494
|
+
if (m) {
|
|
1495
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-w-ecard-cell", children: [
|
|
1496
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-ecard-cval", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(FormBar, { w: Number(m[1]), d: Number(m[2]), l: Number(m[3]) }) }),
|
|
1497
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-ecard-clabel", children: s.value })
|
|
1498
|
+
] });
|
|
1387
1499
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
const home = str(card.homeTeamName);
|
|
1393
|
-
const away = str(card.awayTeamName);
|
|
1394
|
-
const selection = joinSelection(str(card.selectionLabel), str(card.line)) ?? "";
|
|
1395
|
-
const bookmakerId = typeof card.bookmakerId === "number" ? card.bookmakerId : "";
|
|
1396
|
-
return {
|
|
1397
|
-
id: `${typeof card.matchId === "number" ? card.matchId : 0}:${str(card.marketDeveloperName) ?? ""}:${selection}:${bookmakerId}`,
|
|
1398
|
-
matchId: typeof card.matchId === "number" ? card.matchId : 0,
|
|
1399
|
-
market: str(card.marketLabel) ?? str(card.marketDeveloperName) ?? "",
|
|
1400
|
-
marketDeveloperName: str(card.marketDeveloperName),
|
|
1401
|
-
selection,
|
|
1402
|
-
selectionLabel: str(card.selectionLabel),
|
|
1403
|
-
line: str(card.line),
|
|
1404
|
-
fixture: home && away ? `${home} vs ${away}` : void 0,
|
|
1405
|
-
competition: str(card.competitionName),
|
|
1406
|
-
bookmaker: str(card.bookmakerName),
|
|
1407
|
-
opening: opening !== void 0 ? opening.toFixed(2) : "",
|
|
1408
|
-
current: current !== void 0 ? current.toFixed(2) : "",
|
|
1409
|
-
direction: card.movementDirection === "shortened" || card.movementDirection === "drifted" ? card.movementDirection : void 0,
|
|
1410
|
-
windowHours: asNumber(card.windowHours) ?? 0,
|
|
1411
|
-
excludedCount: asNumber(card.excludedSuspendedCount) ?? 0,
|
|
1412
|
-
isSuspended: card.isSuspended === true,
|
|
1413
|
-
disclaimer: str(card.disclaimer),
|
|
1414
|
-
geometry: projectSeries(samples),
|
|
1415
|
-
observationCount: samples.length,
|
|
1416
|
-
pinEnabled: card.pinEnabled === true,
|
|
1417
|
-
matchOddsId: typeof card.matchOddsId === "number" ? card.matchOddsId : void 0
|
|
1418
|
-
};
|
|
1500
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-w-ecard-cell", children: [
|
|
1501
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-ecard-cval", children: s.value }),
|
|
1502
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-ecard-clabel", children: s.label })
|
|
1503
|
+
] });
|
|
1419
1504
|
}
|
|
1420
|
-
function
|
|
1421
|
-
if (
|
|
1422
|
-
const
|
|
1423
|
-
|
|
1424
|
-
if (conversationId !== void 0) selection.conversationId = conversationId;
|
|
1425
|
-
return selection;
|
|
1505
|
+
function StatStrip({ stats }) {
|
|
1506
|
+
if (stats.length === 0) return null;
|
|
1507
|
+
const cols = Math.min(stats.length, 4);
|
|
1508
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ss-w-ecard-strip", style: { gridTemplateColumns: `repeat(${cols}, 1fr)` }, children: stats.map((s) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StatCellView, { s }, s.label + s.value)) });
|
|
1426
1509
|
}
|
|
1427
|
-
function
|
|
1428
|
-
|
|
1510
|
+
function CardHeader({ c, showScopeCrest }) {
|
|
1511
|
+
const onlyScope = c.scopes.length === 1 ? c.scopes[0] : null;
|
|
1512
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-w-ecard-hd", children: [
|
|
1513
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1514
|
+
"span",
|
|
1515
|
+
{
|
|
1516
|
+
className: c.kind === "team" ? "ss-w-thumb ss-w-team" : "ss-w-thumb",
|
|
1517
|
+
style: c.image ? { backgroundImage: `url(${c.image})` } : void 0,
|
|
1518
|
+
children: c.image ? "" : initials(c.name)
|
|
1519
|
+
}
|
|
1520
|
+
),
|
|
1521
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "ss-w-ecard-idtext", children: [
|
|
1522
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-nm", children: c.name }),
|
|
1523
|
+
c.sub ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-sub", children: c.sub }) : null
|
|
1524
|
+
] }),
|
|
1525
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "ss-w-ecard-meta", children: [
|
|
1526
|
+
showScopeCrest && onlyScope?.image ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Crest, { image: onlyScope.image, label: onlyScope.label }) : null,
|
|
1527
|
+
c.season ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "ss-w-ecard-season", children: shortSeason(c.season) }) : null
|
|
1528
|
+
] })
|
|
1529
|
+
] });
|
|
1429
1530
|
}
|
|
1430
|
-
function
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
matchId,
|
|
1446
|
-
homeTeamName: home,
|
|
1447
|
-
awayTeamName: away,
|
|
1448
|
-
homeCrest: str(leg.homeTeamImagePath),
|
|
1449
|
-
awayCrest: str(leg.awayTeamImagePath),
|
|
1450
|
-
competition: str(leg.competitionName),
|
|
1451
|
-
kickoffUtc: str(leg.kickoffUtc),
|
|
1452
|
-
kickoffLabel: str(leg.kickoffLabel),
|
|
1453
|
-
market: str(leg.marketLabel) ?? str(leg.marketDeveloperName) ?? "",
|
|
1454
|
-
marketDeveloperName: str(leg.marketDeveloperName) ?? "",
|
|
1455
|
-
selection: joinSelection(selectionLabel, line) ?? selectionLabel,
|
|
1456
|
-
selectionLabel,
|
|
1457
|
-
line,
|
|
1458
|
-
odds: price.toFixed(2),
|
|
1459
|
-
oddsDecimal: price,
|
|
1460
|
-
matchOddsId,
|
|
1461
|
-
quotedAtUtc: str(leg.quotedAtUtc),
|
|
1462
|
-
insightId,
|
|
1463
|
-
statText
|
|
1464
|
-
};
|
|
1531
|
+
function ScopeTabs({ scopes, active, onSelect }) {
|
|
1532
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ss-w-ecard-scopes", role: "tablist", children: scopes.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1533
|
+
"button",
|
|
1534
|
+
{
|
|
1535
|
+
type: "button",
|
|
1536
|
+
role: "tab",
|
|
1537
|
+
className: "ss-w-ecard-scope",
|
|
1538
|
+
"aria-selected": i === active,
|
|
1539
|
+
"aria-label": s.label,
|
|
1540
|
+
title: s.label,
|
|
1541
|
+
onClick: () => onSelect(i),
|
|
1542
|
+
children: s.image ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Crest, { image: s.image }) : s.label
|
|
1543
|
+
},
|
|
1544
|
+
String(s.competitionId ?? "all")
|
|
1545
|
+
)) });
|
|
1465
1546
|
}
|
|
1466
|
-
function
|
|
1467
|
-
const
|
|
1468
|
-
const
|
|
1469
|
-
if (
|
|
1470
|
-
|
|
1471
|
-
for (const raw of rawLegs) {
|
|
1472
|
-
const vm = toComboLegVM(raw);
|
|
1473
|
-
if (!vm) return null;
|
|
1474
|
-
legs.push(vm);
|
|
1547
|
+
function EntityCardItem({ c }) {
|
|
1548
|
+
const [active, setActive] = (0, import_react3.useState)(0);
|
|
1549
|
+
const hasBody = c.scopes.length > 0;
|
|
1550
|
+
if (!hasBody) {
|
|
1551
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ss-w-ecard ss-w-ecard-bare", "data-kind": c.kind, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CardHeader, { c, showScopeCrest: false }) });
|
|
1475
1552
|
}
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
};
|
|
1553
|
+
const multi = c.scopes.length > 1;
|
|
1554
|
+
const scope = c.scopes[multi ? c.scopes[active] ? active : 0 : 0];
|
|
1555
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-w-ecard", "data-kind": c.kind, children: [
|
|
1556
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CardHeader, { c, showScopeCrest: !multi }),
|
|
1557
|
+
multi ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ScopeTabs, { scopes: c.scopes, active, onSelect: setActive }) : null,
|
|
1558
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StatStrip, { stats: scope.stats })
|
|
1559
|
+
] });
|
|
1483
1560
|
}
|
|
1484
|
-
function
|
|
1485
|
-
const
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
return selection;
|
|
1561
|
+
function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMoreLabel = (n) => `Show ${n} more` }) {
|
|
1562
|
+
const [expanded, setExpanded] = (0, import_react3.useState)(false);
|
|
1563
|
+
if (cards.length === 0) return null;
|
|
1564
|
+
const visible = expanded ? cards : cards.slice(0, MAX_VISIBLE);
|
|
1565
|
+
const overflow = cards.length - MAX_VISIBLE;
|
|
1566
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "ss-w-cards", children: [
|
|
1567
|
+
visible.map((c) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(EntityCardItem, { c }, c.id)),
|
|
1568
|
+
overflow > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { type: "button", className: "ss-w-cards-more", disabled, onClick: () => setExpanded((v) => !v), children: expanded ? showLessLabel : showMoreLabel(overflow) }) : null
|
|
1569
|
+
] });
|
|
1494
1570
|
}
|
|
1495
1571
|
|
|
1572
|
+
// src/components/AddToSlipButton.tsx
|
|
1573
|
+
var import_react7 = require("react");
|
|
1574
|
+
var import_widget_sdk = require("@sensiblestats/widget-sdk");
|
|
1575
|
+
|
|
1496
1576
|
// src/eventLogContext.ts
|
|
1497
1577
|
var import_react4 = require("react");
|
|
1498
1578
|
var EventLogContext = (0, import_react4.createContext)(null);
|
|
@@ -1994,6 +2074,13 @@ function BettingInsightList({ insights, ui, slip, monitor }) {
|
|
|
1994
2074
|
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "ss-w-binsights", children: insights.map((it) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(InsightCard, { it, ui, slip, monitor }, it.id)) });
|
|
1995
2075
|
}
|
|
1996
2076
|
|
|
2077
|
+
// src/presetToolsContext.ts
|
|
2078
|
+
var import_react12 = require("react");
|
|
2079
|
+
var PresetToolsContext = (0, import_react12.createContext)(null);
|
|
2080
|
+
function usePresetTools() {
|
|
2081
|
+
return (0, import_react12.useContext)(PresetToolsContext);
|
|
2082
|
+
}
|
|
2083
|
+
|
|
1997
2084
|
// src/components/ComboCardList.tsx
|
|
1998
2085
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1999
2086
|
function ComboLegRow({ leg, addToSlipEnabled, ui, slip }) {
|
|
@@ -2036,10 +2123,13 @@ function ComboLegRow({ leg, addToSlipEnabled, ui, slip }) {
|
|
|
2036
2123
|
] });
|
|
2037
2124
|
}
|
|
2038
2125
|
function ComboCardView({ vm, ui, slip }) {
|
|
2126
|
+
const tools = usePresetTools();
|
|
2127
|
+
const { target, legCount } = vm;
|
|
2039
2128
|
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "ss-w-combo", children: [
|
|
2040
2129
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ol", { className: "ss-w-combo-legs", role: "list", children: vm.legs.map((leg) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ComboLegRow, { leg, addToSlipEnabled: vm.addToSlipEnabled, ui, slip }, leg.key)) }),
|
|
2041
2130
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "ss-w-combo-math", children: vm.multiplication }),
|
|
2042
|
-
vm.replacedNote ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "ss-w-combo-note", children: vm.replacedNote }) : null
|
|
2131
|
+
vm.replacedNote ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "ss-w-combo-note", children: vm.replacedNote }) : null,
|
|
2132
|
+
tools && target !== void 0 && legCount !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "ss-w-combo-change", onClick: () => tools.openBuilder(target, legCount), children: tools.changeLabel }) : null
|
|
2043
2133
|
] });
|
|
2044
2134
|
}
|
|
2045
2135
|
function ComboCardList({ cards, ui, slip }) {
|
|
@@ -2048,10 +2138,10 @@ function ComboCardList({ cards, ui, slip }) {
|
|
|
2048
2138
|
}
|
|
2049
2139
|
|
|
2050
2140
|
// src/components/MarketOddsCardList.tsx
|
|
2051
|
-
var
|
|
2141
|
+
var import_react13 = require("react");
|
|
2052
2142
|
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2053
|
-
function OddsRow({ vm, row, ui, slip, inSlipIds, history }) {
|
|
2054
|
-
const selection = vm.addToSlipEnabled ? marketOddsRowToSlipSelection(vm, row, slip.conversationId) : null;
|
|
2143
|
+
function OddsRow({ vm, row, ui, slip, inSlipIds, history, suppressSlip }) {
|
|
2144
|
+
const selection = vm.addToSlipEnabled && !suppressSlip ? marketOddsRowToSlipSelection(vm, row, slip.conversationId) : null;
|
|
2055
2145
|
const inSlip = !!inSlipIds && row.matchOddsId != null && !!inSlipIds[row.matchOddsId];
|
|
2056
2146
|
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ss-w-mo-row", children: [
|
|
2057
2147
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "ss-w-mo-sel", children: row.selection }),
|
|
@@ -2069,17 +2159,34 @@ function OddsRow({ vm, row, ui, slip, inSlipIds, history }) {
|
|
|
2069
2159
|
"\u2713 ",
|
|
2070
2160
|
ui.inSlipLabel
|
|
2071
2161
|
] }) : null,
|
|
2072
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SlipButton, { selection, shownOdds: row.odds, marketDeveloperName: vm.marketDeveloperName, selectionLabel: row.selectionLabel, line: row.line, slip, ui })
|
|
2162
|
+
!suppressSlip ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SlipButton, { selection, shownOdds: row.odds, marketDeveloperName: vm.marketDeveloperName, selectionLabel: row.selectionLabel, line: row.line, slip, ui }) : null
|
|
2073
2163
|
] });
|
|
2074
2164
|
}
|
|
2165
|
+
var STALE_AFTER_MS = 12e4;
|
|
2166
|
+
function fmtHhMm(iso, locale) {
|
|
2167
|
+
return new Date(iso).toLocaleTimeString(locale, { hour: "2-digit", minute: "2-digit", hour12: false });
|
|
2168
|
+
}
|
|
2075
2169
|
function MarketOddsCard({ vm, ui, slip, monitor, inSlipIds, hideHeader, pinRef }) {
|
|
2076
|
-
const [open, setOpen] = (0,
|
|
2170
|
+
const [open, setOpen] = (0, import_react13.useState)(false);
|
|
2077
2171
|
const kickoffFallback = vm.kickoffUtc && ui?.bettingKickoff ? ui.bettingKickoff(vm.kickoffUtc) : void 0;
|
|
2078
2172
|
const kickoff = vm.kickoffLabel ?? (kickoffFallback ? `${kickoffFallback.dayLabel} ${kickoffFallback.time}` : void 0);
|
|
2079
2173
|
const hasNamedTeams = !!vm.homeTeamName && !!vm.awayTeamName;
|
|
2080
|
-
const
|
|
2174
|
+
const live = vm.live ?? null;
|
|
2175
|
+
const closedReason = live?.closedReason ?? null;
|
|
2176
|
+
const isClosed = closedReason !== null;
|
|
2177
|
+
const asOfMs = live ? new Date(live.asOfUtc).getTime() : NaN;
|
|
2178
|
+
const isStale = live != null && !isClosed && (vm.awaitingLiveUpdate === true || Date.now() - asOfMs > STALE_AFTER_MS);
|
|
2179
|
+
const staleTime = isStale && live ? fmtHhMm(live.asOfUtc, ui?.locale ?? "en") : null;
|
|
2180
|
+
const [, setStaleTick] = (0, import_react13.useState)(0);
|
|
2181
|
+
const watchStale = live != null && !isClosed && !isStale;
|
|
2182
|
+
(0, import_react13.useEffect)(() => {
|
|
2183
|
+
if (!watchStale) return;
|
|
2184
|
+
const id = window.setTimeout(() => setStaleTick((n) => n + 1), Math.max(0, asOfMs + STALE_AFTER_MS - Date.now()) + 1);
|
|
2185
|
+
return () => window.clearTimeout(id);
|
|
2186
|
+
}, [watchStale, asOfMs]);
|
|
2187
|
+
const showPin = !!monitor && vm.pinEnabled && !live;
|
|
2081
2188
|
const history = showPin && !pinRef && !hideHeader ? monitor : void 0;
|
|
2082
|
-
const showHeader = !!vm.competition || vm.isLive || !!kickoff || showPin;
|
|
2189
|
+
const showHeader = !!vm.competition || vm.isLive || !!kickoff || showPin || !!live;
|
|
2083
2190
|
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: hideHeader ? "ss-w-mocard ss-w-mocard--bare" : "ss-w-mocard", children: [
|
|
2084
2191
|
!hideHeader && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
2085
2192
|
showHeader ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ss-w-bi-header", children: [
|
|
@@ -2088,7 +2195,14 @@ function MarketOddsCard({ vm, ui, slip, monitor, inSlipIds, hideHeader, pinRef }
|
|
|
2088
2195
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "ss-w-bi-comp", children: vm.competition })
|
|
2089
2196
|
] }) : null }),
|
|
2090
2197
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "ss-w-bi-header-right", children: [
|
|
2091
|
-
|
|
2198
|
+
live && !isClosed ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
2199
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "ss-w-bi-score", children: [
|
|
2200
|
+
live.homeScore,
|
|
2201
|
+
"\u2013",
|
|
2202
|
+
live.awayScore
|
|
2203
|
+
] }),
|
|
2204
|
+
live.minuteLabel ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "ss-w-bi-min", children: live.minuteLabel }) : null
|
|
2205
|
+
] }) : !live && vm.isLive ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { className: "ss-w-bi-live", children: [
|
|
2092
2206
|
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "ss-w-bi-live-dot", "aria-hidden": "true" }),
|
|
2093
2207
|
ui?.liveLabel ?? "Live"
|
|
2094
2208
|
] }) : null,
|
|
@@ -2118,14 +2232,17 @@ function MarketOddsCard({ vm, ui, slip, monitor, inSlipIds, hideHeader, pinRef }
|
|
|
2118
2232
|
] }) : null
|
|
2119
2233
|
] }),
|
|
2120
2234
|
vm.statText && !hideHeader && !pinRef ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "ss-w-mo-stat", children: vm.statText }) : null,
|
|
2121
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.
|
|
2122
|
-
|
|
2123
|
-
|
|
2235
|
+
isClosed ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "ss-w-mo-closed", children: closedReason === "suspended" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: ui?.liveCard.suspended ?? "Market suspended" }) : closedReason === "no_live_price" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: ui?.liveCard.noPrice ?? "No live price available" }) : closedReason === "full_time" ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: ui?.liveCard.fullTime ?? "Full time" }) : null }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
2236
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ss-w-mo-rows", children: [
|
|
2237
|
+
vm.mainRows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(OddsRow, { vm, row, ui, slip, inSlipIds, history, suppressSlip: isStale }, row.key)),
|
|
2238
|
+
open ? vm.moreRows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(OddsRow, { vm, row, ui, slip, inSlipIds, history, suppressSlip: isStale }, row.key)) : null
|
|
2239
|
+
] }),
|
|
2240
|
+
vm.moreRows.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("button", { type: "button", className: "ss-w-mo-more", "aria-expanded": open, onClick: () => setOpen((v) => !v), children: [
|
|
2241
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: open ? ui?.hideLines ?? "Hide lines" : `${ui?.moreLines ?? "More lines"} (${vm.moreRows.length})` }),
|
|
2242
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: `ss-w-mo-more-chev${open ? " ss-w-mo-more-chev-up" : ""}`, "aria-hidden": "true" })
|
|
2243
|
+
] }) : null,
|
|
2244
|
+
staleTime ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "ss-w-mo-stale", children: ui?.liveCard.stale(staleTime) ?? `Prices from ${staleTime}` }) : null
|
|
2124
2245
|
] }),
|
|
2125
|
-
vm.moreRows.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("button", { type: "button", className: "ss-w-mo-more", "aria-expanded": open, onClick: () => setOpen((v) => !v), children: [
|
|
2126
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: open ? ui?.hideLines ?? "Hide lines" : `${ui?.moreLines ?? "More lines"} (${vm.moreRows.length})` }),
|
|
2127
|
-
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: `ss-w-mo-more-chev${open ? " ss-w-mo-more-chev-up" : ""}`, "aria-hidden": "true" })
|
|
2128
|
-
] }) : null,
|
|
2129
2246
|
vm.bookmaker ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "ss-w-mo-foot", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: vm.bookmaker }) }) : null,
|
|
2130
2247
|
vm.disclaimer ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "ss-w-bi-disc", children: vm.disclaimer }) : null
|
|
2131
2248
|
] });
|
|
@@ -2136,7 +2253,7 @@ function MarketOddsCardList({ cards, ui, slip, monitor, inSlipIds, hideHeader, p
|
|
|
2136
2253
|
}
|
|
2137
2254
|
|
|
2138
2255
|
// src/components/CompactOddsList.tsx
|
|
2139
|
-
var
|
|
2256
|
+
var import_react14 = require("react");
|
|
2140
2257
|
|
|
2141
2258
|
// src/compactList.ts
|
|
2142
2259
|
var COMPACT_MIN_CARDS = 3;
|
|
@@ -2249,11 +2366,11 @@ function Row({ row, columns, ui, slip, monitor, rowRef }) {
|
|
|
2249
2366
|
] });
|
|
2250
2367
|
}
|
|
2251
2368
|
function CompactOddsList({ list, ui, slip, monitor }) {
|
|
2252
|
-
const [shown, setShown] = (0,
|
|
2369
|
+
const [shown, setShown] = (0, import_react14.useState)(COMPACT_PAGE);
|
|
2253
2370
|
const hidden = list.rowCount - shown;
|
|
2254
|
-
const [focusIndex, setFocusIndex] = (0,
|
|
2255
|
-
const focusRef = (0,
|
|
2256
|
-
(0,
|
|
2371
|
+
const [focusIndex, setFocusIndex] = (0, import_react14.useState)(null);
|
|
2372
|
+
const focusRef = (0, import_react14.useRef)(null);
|
|
2373
|
+
(0, import_react14.useEffect)(() => {
|
|
2257
2374
|
if (focusIndex !== null) focusRef.current?.focus();
|
|
2258
2375
|
}, [focusIndex]);
|
|
2259
2376
|
let left = shown;
|
|
@@ -2554,7 +2671,7 @@ function FixtureCard({ card }) {
|
|
|
2554
2671
|
}
|
|
2555
2672
|
|
|
2556
2673
|
// src/components/dashboard/CountdownCard.tsx
|
|
2557
|
-
var
|
|
2674
|
+
var import_react15 = require("react");
|
|
2558
2675
|
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
2559
2676
|
var DAY_MS = 864e5;
|
|
2560
2677
|
function remainingSegments(kickoffUnix, nowMs, withSeconds) {
|
|
@@ -2573,12 +2690,12 @@ function remainingSegments(kickoffUnix, nowMs, withSeconds) {
|
|
|
2573
2690
|
function CountdownCard({ card }) {
|
|
2574
2691
|
const { data } = card;
|
|
2575
2692
|
const unitLabel = { d: data.daysUnit, h: data.hoursUnit, m: data.minutesUnit, s: data.secondsUnit };
|
|
2576
|
-
const [nowMs, setNowMs] = (0,
|
|
2577
|
-
const [reduced] = (0,
|
|
2693
|
+
const [nowMs, setNowMs] = (0, import_react15.useState)(() => Date.now());
|
|
2694
|
+
const [reduced] = (0, import_react15.useState)(
|
|
2578
2695
|
() => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
2579
2696
|
);
|
|
2580
2697
|
const withinDay = data.kickoffUnix * 1e3 - nowMs < DAY_MS;
|
|
2581
|
-
(0,
|
|
2698
|
+
(0, import_react15.useEffect)(() => {
|
|
2582
2699
|
if (reduced) return;
|
|
2583
2700
|
const id = window.setInterval(() => setNowMs(Date.now()), withinDay ? 1e3 : 6e4);
|
|
2584
2701
|
return () => window.clearInterval(id);
|
|
@@ -2586,7 +2703,7 @@ function CountdownCard({ card }) {
|
|
|
2586
2703
|
const segments = remainingSegments(data.kickoffUnix, nowMs, withinDay && !reduced);
|
|
2587
2704
|
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "ss-w-fandash-card ss-w-fandash-countdown", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "ss-w-fandash-countdown-top", children: [
|
|
2588
2705
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title ?? "Kick-off in" }),
|
|
2589
|
-
segments ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "ss-w-fandash-countdown-clock", children: segments.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
2706
|
+
segments ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "ss-w-fandash-countdown-clock", children: segments.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react15.Fragment, { children: [
|
|
2590
2707
|
i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "ss-w-fandash-countdown-colon", children: ":" }) : null,
|
|
2591
2708
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "ss-w-fandash-countdown-seg", children: [
|
|
2592
2709
|
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "ss-w-fandash-countdown-value", children: s.value }),
|
|
@@ -2597,7 +2714,7 @@ function CountdownCard({ card }) {
|
|
|
2597
2714
|
}
|
|
2598
2715
|
|
|
2599
2716
|
// src/components/dashboard/InjuriesCard.tsx
|
|
2600
|
-
var
|
|
2717
|
+
var import_react16 = require("react");
|
|
2601
2718
|
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
2602
2719
|
var SHOWN = 3;
|
|
2603
2720
|
function ColHead({ name, crestUrl }) {
|
|
@@ -2620,7 +2737,7 @@ function InjuryList({ rows, emptyLabel, limit }) {
|
|
|
2620
2737
|
}
|
|
2621
2738
|
function InjuriesCard({ card, ui }) {
|
|
2622
2739
|
const { data } = card;
|
|
2623
|
-
const [expanded, setExpanded] = (0,
|
|
2740
|
+
const [expanded, setExpanded] = (0, import_react16.useState)(false);
|
|
2624
2741
|
const hidden = Math.max(0, data.ours.length - SHOWN) + Math.max(0, data.theirs.length - SHOWN);
|
|
2625
2742
|
const limit = expanded ? void 0 : SHOWN;
|
|
2626
2743
|
const showMore = ui?.showMore ?? ((n) => `Show ${n} more`);
|
|
@@ -2726,7 +2843,7 @@ function LinksCard({ card }) {
|
|
|
2726
2843
|
}
|
|
2727
2844
|
|
|
2728
2845
|
// src/components/dashboard/PostMatchResultCard.tsx
|
|
2729
|
-
var
|
|
2846
|
+
var import_react17 = require("react");
|
|
2730
2847
|
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
2731
2848
|
function initials3(name) {
|
|
2732
2849
|
return name.trim().slice(0, 3).toUpperCase();
|
|
@@ -2736,7 +2853,7 @@ function PostMatchCrest({
|
|
|
2736
2853
|
name,
|
|
2737
2854
|
side
|
|
2738
2855
|
}) {
|
|
2739
|
-
const [failed, setFailed] = (0,
|
|
2856
|
+
const [failed, setFailed] = (0, import_react17.useState)(false);
|
|
2740
2857
|
const cls = `ss-w-fandash-postmatch-crest ss-w-fandash-postmatch-crest--${side}`;
|
|
2741
2858
|
if (url && !failed) {
|
|
2742
2859
|
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("img", { className: cls, src: url, alt: "", "aria-hidden": "true", onError: () => setFailed(true) });
|
|
@@ -2935,10 +3052,10 @@ function RestingFormCard({ card }) {
|
|
|
2935
3052
|
}
|
|
2936
3053
|
|
|
2937
3054
|
// src/components/dashboard/LiveScoreCard.tsx
|
|
2938
|
-
var
|
|
3055
|
+
var import_react19 = require("react");
|
|
2939
3056
|
|
|
2940
3057
|
// src/useLiveClock.ts
|
|
2941
|
-
var
|
|
3058
|
+
var import_react18 = require("react");
|
|
2942
3059
|
var TICK_MS = 6e4;
|
|
2943
3060
|
var MAX_LIVE_TICK_MINUTES = 15;
|
|
2944
3061
|
function computeDisplayMinute(minute, clockRunning, receivedAtMs) {
|
|
@@ -2947,10 +3064,10 @@ function computeDisplayMinute(minute, clockRunning, receivedAtMs) {
|
|
|
2947
3064
|
return minute + Math.min(Math.max(0, elapsedMinutes), MAX_LIVE_TICK_MINUTES);
|
|
2948
3065
|
}
|
|
2949
3066
|
function useLiveClock(minute, clockRunning, receivedAtMs) {
|
|
2950
|
-
const [displayMinute, setDisplayMinute] = (0,
|
|
3067
|
+
const [displayMinute, setDisplayMinute] = (0, import_react18.useState)(
|
|
2951
3068
|
() => computeDisplayMinute(minute, clockRunning, receivedAtMs)
|
|
2952
3069
|
);
|
|
2953
|
-
(0,
|
|
3070
|
+
(0, import_react18.useEffect)(() => {
|
|
2954
3071
|
setDisplayMinute(computeDisplayMinute(minute, clockRunning, receivedAtMs));
|
|
2955
3072
|
if (!clockRunning) return;
|
|
2956
3073
|
const id = window.setInterval(() => {
|
|
@@ -2967,7 +3084,7 @@ function initials5(name) {
|
|
|
2967
3084
|
return name.trim().slice(0, 3).toUpperCase();
|
|
2968
3085
|
}
|
|
2969
3086
|
function LiveCrest({ url, name, side }) {
|
|
2970
|
-
const [failed, setFailed] = (0,
|
|
3087
|
+
const [failed, setFailed] = (0, import_react19.useState)(false);
|
|
2971
3088
|
const cls = `ss-w-fandash-live-crest ss-w-fandash-live-crest--${side}`;
|
|
2972
3089
|
if (url && !failed) {
|
|
2973
3090
|
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("img", { className: cls, src: url, alt: "", "aria-hidden": "true", onError: () => setFailed(true) });
|
|
@@ -2986,7 +3103,7 @@ function LiveScoreCard({
|
|
|
2986
3103
|
}) {
|
|
2987
3104
|
const { data } = card;
|
|
2988
3105
|
const seedKey = `${data.minute}:${data.homeScore}:${data.awayScore}`;
|
|
2989
|
-
const seedRef = (0,
|
|
3106
|
+
const seedRef = (0, import_react19.useRef)(null);
|
|
2990
3107
|
if (!seedRef.current || seedRef.current.key !== seedKey) {
|
|
2991
3108
|
seedRef.current = { key: seedKey, receivedAtMs: Date.now() };
|
|
2992
3109
|
}
|
|
@@ -3026,7 +3143,7 @@ function LiveScoreCard({
|
|
|
3026
3143
|
}
|
|
3027
3144
|
|
|
3028
3145
|
// src/components/dashboard/LiveTimelineCard.tsx
|
|
3029
|
-
var
|
|
3146
|
+
var import_react20 = require("react");
|
|
3030
3147
|
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
3031
3148
|
var SHOWN2 = 3;
|
|
3032
3149
|
function EventIcon({ type }) {
|
|
@@ -3068,7 +3185,7 @@ function EventRow({ e }) {
|
|
|
3068
3185
|
] });
|
|
3069
3186
|
}
|
|
3070
3187
|
function LiveTimelineCard({ card, ui }) {
|
|
3071
|
-
const [expanded, setExpanded] = (0,
|
|
3188
|
+
const [expanded, setExpanded] = (0, import_react20.useState)(false);
|
|
3072
3189
|
const events = card.data.events;
|
|
3073
3190
|
if (!events || events.length === 0) return null;
|
|
3074
3191
|
const sorted = [...events].sort((a, b) => b.minute - a.minute);
|
|
@@ -3093,7 +3210,7 @@ function LiveTimelineCard({ card, ui }) {
|
|
|
3093
3210
|
}
|
|
3094
3211
|
|
|
3095
3212
|
// src/components/dashboard/MomentumWave.tsx
|
|
3096
|
-
var
|
|
3213
|
+
var import_react21 = require("react");
|
|
3097
3214
|
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
3098
3215
|
var WIDTH = 270;
|
|
3099
3216
|
var HEIGHT = 56;
|
|
@@ -3117,7 +3234,7 @@ function MomentumWave({
|
|
|
3117
3234
|
}) {
|
|
3118
3235
|
if (points.length === 0) return null;
|
|
3119
3236
|
const { area, line, lastLeftPct, lastTopPx } = buildPaths(points);
|
|
3120
|
-
const clipId = (0,
|
|
3237
|
+
const clipId = (0, import_react21.useId)();
|
|
3121
3238
|
const topClipId = `${clipId}-top`;
|
|
3122
3239
|
const bottomClipId = `${clipId}-bottom`;
|
|
3123
3240
|
const topClass = clubIsHome ? "ss-w-fandash-live-mom-home" : "ss-w-fandash-live-mom-away";
|
|
@@ -3310,11 +3427,11 @@ function turnAnchor(messages) {
|
|
|
3310
3427
|
}
|
|
3311
3428
|
function MessageList({ controller, ui, slip, onDashboardTap, monitor }) {
|
|
3312
3429
|
const { state, send } = controller;
|
|
3313
|
-
const listRef = (0,
|
|
3314
|
-
const endRef = (0,
|
|
3315
|
-
const pinnedRef = (0,
|
|
3316
|
-
const anchoredTurnRef = (0,
|
|
3317
|
-
(0,
|
|
3430
|
+
const listRef = (0, import_react22.useRef)(null);
|
|
3431
|
+
const endRef = (0, import_react22.useRef)(null);
|
|
3432
|
+
const pinnedRef = (0, import_react22.useRef)(true);
|
|
3433
|
+
const anchoredTurnRef = (0, import_react22.useRef)(null);
|
|
3434
|
+
(0, import_react22.useEffect)(() => {
|
|
3318
3435
|
const anchor = turnAnchor(state.messages);
|
|
3319
3436
|
const list = listRef.current;
|
|
3320
3437
|
if (anchor && anchor.turnId !== anchoredTurnRef.current) {
|
|
@@ -3371,11 +3488,15 @@ function MessageList({ controller, ui, slip, onDashboardTap, monitor }) {
|
|
|
3371
3488
|
}
|
|
3372
3489
|
|
|
3373
3490
|
// src/components/ChatInput.tsx
|
|
3374
|
-
var
|
|
3491
|
+
var import_react23 = require("react");
|
|
3375
3492
|
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
3376
|
-
|
|
3377
|
-
const [value, setValue] = (0,
|
|
3493
|
+
var ChatInput = (0, import_react23.forwardRef)(function ChatInput2({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message", disabled = false, onType }, ref) {
|
|
3494
|
+
const [value, setValue] = (0, import_react23.useState)("");
|
|
3495
|
+
const textareaRef = (0, import_react23.useRef)(null);
|
|
3496
|
+
const stopRef = (0, import_react23.useRef)(null);
|
|
3497
|
+
const textBoxWantsFocusRef = (0, import_react23.useRef)(false);
|
|
3378
3498
|
const submit = () => {
|
|
3499
|
+
if (disabled) return;
|
|
3379
3500
|
const t = value.trim();
|
|
3380
3501
|
if (!t) return;
|
|
3381
3502
|
onSend(t);
|
|
@@ -3387,11 +3508,48 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
|
|
|
3387
3508
|
submit();
|
|
3388
3509
|
}
|
|
3389
3510
|
};
|
|
3511
|
+
(0, import_react23.useImperativeHandle)(ref, () => ({
|
|
3512
|
+
focus() {
|
|
3513
|
+
const box = textareaRef.current;
|
|
3514
|
+
if (box && !box.disabled) {
|
|
3515
|
+
textBoxWantsFocusRef.current = false;
|
|
3516
|
+
box.focus();
|
|
3517
|
+
return;
|
|
3518
|
+
}
|
|
3519
|
+
textBoxWantsFocusRef.current = true;
|
|
3520
|
+
stopRef.current?.focus();
|
|
3521
|
+
}
|
|
3522
|
+
}), []);
|
|
3523
|
+
(0, import_react23.useEffect)(() => {
|
|
3524
|
+
if (streaming || disabled || !textBoxWantsFocusRef.current) return;
|
|
3525
|
+
textBoxWantsFocusRef.current = false;
|
|
3526
|
+
const box = textareaRef.current;
|
|
3527
|
+
if (!box) return;
|
|
3528
|
+
const active = box.getRootNode().activeElement;
|
|
3529
|
+
const input = box.closest(".ss-w-input");
|
|
3530
|
+
const isWithinComponent = input?.contains(active);
|
|
3531
|
+
if (!active || active === box.ownerDocument.body || isWithinComponent) box.focus();
|
|
3532
|
+
}, [streaming, disabled]);
|
|
3390
3533
|
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "ss-w-input", children: [
|
|
3391
|
-
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3392
|
-
|
|
3534
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3535
|
+
"textarea",
|
|
3536
|
+
{
|
|
3537
|
+
ref: textareaRef,
|
|
3538
|
+
className: "ss-w-textarea",
|
|
3539
|
+
rows: 1,
|
|
3540
|
+
placeholder,
|
|
3541
|
+
value,
|
|
3542
|
+
disabled: streaming || disabled,
|
|
3543
|
+
onChange: (e) => {
|
|
3544
|
+
setValue(e.target.value);
|
|
3545
|
+
onType?.();
|
|
3546
|
+
},
|
|
3547
|
+
onKeyDown
|
|
3548
|
+
}
|
|
3549
|
+
),
|
|
3550
|
+
streaming ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { ref: stopRef, type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, disabled, onClick: onStop, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, disabled, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(SendIcon, {}) })
|
|
3393
3551
|
] });
|
|
3394
|
-
}
|
|
3552
|
+
});
|
|
3395
3553
|
|
|
3396
3554
|
// src/components/TabBar.tsx
|
|
3397
3555
|
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
@@ -3484,10 +3642,10 @@ function MarketAccordion({ match, monitor, ui, slip }) {
|
|
|
3484
3642
|
}
|
|
3485
3643
|
|
|
3486
3644
|
// src/components/LiveFactBlock.tsx
|
|
3487
|
-
var
|
|
3645
|
+
var import_react24 = require("react");
|
|
3488
3646
|
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
3489
3647
|
function LiveFactBlock({ facts, matchId, ui, slip, onAsk }) {
|
|
3490
|
-
const [expanded, setExpanded] = (0,
|
|
3648
|
+
const [expanded, setExpanded] = (0, import_react24.useState)(false);
|
|
3491
3649
|
const [current, ...earlier] = facts;
|
|
3492
3650
|
if (!current) return null;
|
|
3493
3651
|
const m = current.market;
|
|
@@ -3874,7 +4032,7 @@ function TodayPanel({ discovery, ui, monitor }) {
|
|
|
3874
4032
|
}
|
|
3875
4033
|
|
|
3876
4034
|
// src/components/GamesPanel.tsx
|
|
3877
|
-
var
|
|
4035
|
+
var import_react38 = require("react");
|
|
3878
4036
|
|
|
3879
4037
|
// src/components/GamesLoginWall.tsx
|
|
3880
4038
|
var import_jsx_runtime55 = require("react/jsx-runtime");
|
|
@@ -3883,7 +4041,7 @@ function GamesLoginWall({ ui }) {
|
|
|
3883
4041
|
}
|
|
3884
4042
|
|
|
3885
4043
|
// src/components/GamesHub.tsx
|
|
3886
|
-
var
|
|
4044
|
+
var import_react25 = require("react");
|
|
3887
4045
|
|
|
3888
4046
|
// src/components/games/ui/gameMeta.ts
|
|
3889
4047
|
function prettifyGameId(gameId) {
|
|
@@ -4068,8 +4226,8 @@ function pickHeroGame(games) {
|
|
|
4068
4226
|
}
|
|
4069
4227
|
var tileKey = (g) => `${g.gameId}-${g.gameDay}-${g.fixtureId}`;
|
|
4070
4228
|
function GamesHub({ games, myWeeklyRow, weeklyPrize, onOpen, ui }) {
|
|
4071
|
-
const [, setTick] = (0,
|
|
4072
|
-
(0,
|
|
4229
|
+
const [, setTick] = (0, import_react25.useState)(0);
|
|
4230
|
+
(0, import_react25.useEffect)(() => {
|
|
4073
4231
|
const id = setInterval(() => setTick((t) => t + 1), 6e4);
|
|
4074
4232
|
return () => clearInterval(id);
|
|
4075
4233
|
}, []);
|
|
@@ -4129,10 +4287,10 @@ function GamesHub({ games, myWeeklyRow, weeklyPrize, onOpen, ui }) {
|
|
|
4129
4287
|
}
|
|
4130
4288
|
|
|
4131
4289
|
// src/components/GameScreenHost.tsx
|
|
4132
|
-
var
|
|
4290
|
+
var import_react37 = require("react");
|
|
4133
4291
|
|
|
4134
4292
|
// src/useGamePlay.ts
|
|
4135
|
-
var
|
|
4293
|
+
var import_react26 = require("react");
|
|
4136
4294
|
function toSubmitResult(payload) {
|
|
4137
4295
|
return {
|
|
4138
4296
|
// 'pending' means the play is recorded but awaiting resolution (fixture games)
|
|
@@ -4144,17 +4302,17 @@ function toSubmitResult(payload) {
|
|
|
4144
4302
|
};
|
|
4145
4303
|
}
|
|
4146
4304
|
function useGamePlay(conversation, { gameId, gameDay, fixtureId, locale, refresh }) {
|
|
4147
|
-
const [result, setResult] = (0,
|
|
4148
|
-
const [submitting, setSubmitting] = (0,
|
|
4149
|
-
const [rawStatus, setRawStatus] = (0,
|
|
4150
|
-
const inFlightRef = (0,
|
|
4151
|
-
const convRef = (0,
|
|
4305
|
+
const [result, setResult] = (0, import_react26.useState)(null);
|
|
4306
|
+
const [submitting, setSubmitting] = (0, import_react26.useState)(false);
|
|
4307
|
+
const [rawStatus, setRawStatus] = (0, import_react26.useState)(null);
|
|
4308
|
+
const inFlightRef = (0, import_react26.useRef)(false);
|
|
4309
|
+
const convRef = (0, import_react26.useRef)(conversation);
|
|
4152
4310
|
convRef.current = conversation;
|
|
4153
|
-
const optsRef = (0,
|
|
4311
|
+
const optsRef = (0, import_react26.useRef)({ gameId, gameDay, fixtureId, locale, refresh });
|
|
4154
4312
|
optsRef.current = { gameId, gameDay, fixtureId, locale, refresh };
|
|
4155
|
-
const resultRef = (0,
|
|
4313
|
+
const resultRef = (0, import_react26.useRef)(null);
|
|
4156
4314
|
resultRef.current = result;
|
|
4157
|
-
const refetch = (0,
|
|
4315
|
+
const refetch = (0, import_react26.useCallback)(async () => {
|
|
4158
4316
|
const conv = convRef.current;
|
|
4159
4317
|
if (!conv) return;
|
|
4160
4318
|
const opts = optsRef.current;
|
|
@@ -4170,7 +4328,7 @@ function useGamePlay(conversation, { gameId, gameDay, fixtureId, locale, refresh
|
|
|
4170
4328
|
} catch {
|
|
4171
4329
|
}
|
|
4172
4330
|
}, []);
|
|
4173
|
-
const submit = (0,
|
|
4331
|
+
const submit = (0, import_react26.useCallback)(async (answerJson) => {
|
|
4174
4332
|
const conv = convRef.current;
|
|
4175
4333
|
if (!conv || inFlightRef.current) return;
|
|
4176
4334
|
inFlightRef.current = true;
|
|
@@ -4197,14 +4355,14 @@ function useGamePlay(conversation, { gameId, gameDay, fixtureId, locale, refresh
|
|
|
4197
4355
|
setSubmitting(false);
|
|
4198
4356
|
}
|
|
4199
4357
|
}, [refetch]);
|
|
4200
|
-
const hydrate = (0,
|
|
4358
|
+
const hydrate = (0, import_react26.useCallback)(() => {
|
|
4201
4359
|
void refetch();
|
|
4202
4360
|
}, [refetch]);
|
|
4203
4361
|
return { result, submitting, submit, hydrate, rawStatus };
|
|
4204
4362
|
}
|
|
4205
4363
|
|
|
4206
4364
|
// src/components/games/MysteryTigerGame.tsx
|
|
4207
|
-
var
|
|
4365
|
+
var import_react28 = require("react");
|
|
4208
4366
|
|
|
4209
4367
|
// src/components/games/nationFlags.ts
|
|
4210
4368
|
var FLAGS = {
|
|
@@ -4415,7 +4573,7 @@ function isNationCode(flag) {
|
|
|
4415
4573
|
}
|
|
4416
4574
|
|
|
4417
4575
|
// src/components/games/PlayerAvatar.tsx
|
|
4418
|
-
var
|
|
4576
|
+
var import_react27 = require("react");
|
|
4419
4577
|
var import_jsx_runtime61 = require("react/jsx-runtime");
|
|
4420
4578
|
function initialsOf(name) {
|
|
4421
4579
|
const parts = name.trim().split(/\s+/).filter(Boolean);
|
|
@@ -4433,7 +4591,7 @@ function PlayerAvatar({
|
|
|
4433
4591
|
imagePath,
|
|
4434
4592
|
className
|
|
4435
4593
|
}) {
|
|
4436
|
-
const [failed, setFailed] = (0,
|
|
4594
|
+
const [failed, setFailed] = (0, import_react27.useState)(false);
|
|
4437
4595
|
const showImage = Boolean(imagePath) && !isPlaceholderImage(imagePath) && !failed;
|
|
4438
4596
|
return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className, "aria-hidden": "true", children: showImage ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("img", { src: imagePath, alt: "", loading: "lazy", onError: () => setFailed(true) }) : /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "ss-w-av-initials", children: initialsOf(name) }) });
|
|
4439
4597
|
}
|
|
@@ -4648,8 +4806,8 @@ function MysteryTigerGame({
|
|
|
4648
4806
|
onBack
|
|
4649
4807
|
}) {
|
|
4650
4808
|
const t = ui.games.mysteryTiger;
|
|
4651
|
-
const [query, setQuery] = (0,
|
|
4652
|
-
const inputId = (0,
|
|
4809
|
+
const [query, setQuery] = (0, import_react28.useState)("");
|
|
4810
|
+
const inputId = (0, import_react28.useId)();
|
|
4653
4811
|
const clientState = parseClientState(result?.clientStateJson);
|
|
4654
4812
|
const guessResults = clientState?.guessResults ?? [];
|
|
4655
4813
|
const alreadyGuessedIds = new Set(guessResults.map((g) => g.playerId));
|
|
@@ -4924,13 +5082,13 @@ function HigherOrLowerGame({
|
|
|
4924
5082
|
}
|
|
4925
5083
|
|
|
4926
5084
|
// src/components/games/CareerPathGame.tsx
|
|
4927
|
-
var
|
|
5085
|
+
var import_react30 = require("react");
|
|
4928
5086
|
|
|
4929
5087
|
// src/components/games/ui/CrestShield.tsx
|
|
4930
|
-
var
|
|
5088
|
+
var import_react29 = require("react");
|
|
4931
5089
|
var import_jsx_runtime72 = require("react/jsx-runtime");
|
|
4932
5090
|
function CrestShield({ src, name, size = 28 }) {
|
|
4933
|
-
const [failed, setFailed] = (0,
|
|
5091
|
+
const [failed, setFailed] = (0, import_react29.useState)(false);
|
|
4934
5092
|
if (src && !isPlaceholderImage(src) && !failed) {
|
|
4935
5093
|
return /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("img", { className: "ss-g-crest", src, alt: name, width: size, height: size, loading: "lazy", onError: () => setFailed(true) });
|
|
4936
5094
|
}
|
|
@@ -4962,8 +5120,8 @@ function CareerPathGame({
|
|
|
4962
5120
|
}) {
|
|
4963
5121
|
const s = ui.games.careerPath;
|
|
4964
5122
|
const title = gameTitle({ gameId: "career-path" }, ui.games.titles);
|
|
4965
|
-
const [query, setQuery] = (0,
|
|
4966
|
-
const inputId = (0,
|
|
5123
|
+
const [query, setQuery] = (0, import_react30.useState)("");
|
|
5124
|
+
const inputId = (0, import_react30.useId)();
|
|
4967
5125
|
const clientState = parseClientState3(result?.clientStateJson);
|
|
4968
5126
|
const alreadyGuessedIds = new Set(clientState?.guessResults.map((g) => g.playerId) ?? []);
|
|
4969
5127
|
const isOver = result?.alreadyPlayed === true || clientState?.solved === true || clientState?.failed === true;
|
|
@@ -5082,7 +5240,7 @@ function CareerPathGame({
|
|
|
5082
5240
|
}
|
|
5083
5241
|
|
|
5084
5242
|
// src/components/games/BeatTheGridGame.tsx
|
|
5085
|
-
var
|
|
5243
|
+
var import_react31 = __toESM(require("react"), 1);
|
|
5086
5244
|
var import_jsx_runtime74 = require("react/jsx-runtime");
|
|
5087
5245
|
function criterionIconKind(label, key) {
|
|
5088
5246
|
const l = (key ?? label).toLowerCase();
|
|
@@ -5163,10 +5321,10 @@ function SearchSheet({
|
|
|
5163
5321
|
ui
|
|
5164
5322
|
}) {
|
|
5165
5323
|
const s = ui.games.beatTheGrid;
|
|
5166
|
-
const [query, setQuery] = (0,
|
|
5167
|
-
const inputId = (0,
|
|
5168
|
-
const inputRef = (0,
|
|
5169
|
-
(0,
|
|
5324
|
+
const [query, setQuery] = (0, import_react31.useState)("");
|
|
5325
|
+
const inputId = (0, import_react31.useId)();
|
|
5326
|
+
const inputRef = (0, import_react31.useRef)(null);
|
|
5327
|
+
(0, import_react31.useEffect)(() => {
|
|
5170
5328
|
inputRef.current?.focus();
|
|
5171
5329
|
}, [row, col]);
|
|
5172
5330
|
const filtered = playerPool.filter(
|
|
@@ -5231,7 +5389,7 @@ function BeatTheGridGame({
|
|
|
5231
5389
|
}) {
|
|
5232
5390
|
const s = ui.games.beatTheGrid;
|
|
5233
5391
|
const title = gameTitle({ gameId: "beat-the-grid" }, ui.games.titles);
|
|
5234
|
-
const [activeCell, setActiveCell] = (0,
|
|
5392
|
+
const [activeCell, setActiveCell] = (0, import_react31.useState)(null);
|
|
5235
5393
|
const clientState = parseClientState4(result?.clientStateJson);
|
|
5236
5394
|
const filledCells = clientState?.cells ?? [];
|
|
5237
5395
|
const isLocked2 = result?.alreadyPlayed === true || clientState?.isComplete === true;
|
|
@@ -5305,7 +5463,7 @@ function BeatTheGridGame({
|
|
|
5305
5463
|
/* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { "aria-hidden": "true", children: abbr }),
|
|
5306
5464
|
/* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "ss-g-sr", children: rowLabel })
|
|
5307
5465
|
] });
|
|
5308
|
-
return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)(
|
|
5466
|
+
return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)(import_react31.default.Fragment, { children: [
|
|
5309
5467
|
/* @__PURE__ */ (0, import_jsx_runtime74.jsx)("div", { className: "ss-g-btg-rowhdr", "data-meta": rowMeta ? "true" : void 0, children: rowHeaderContent }),
|
|
5310
5468
|
cols.map((colLabel, colIdx) => {
|
|
5311
5469
|
const filled = findCellResult(filledCells, rowIdx, colIdx);
|
|
@@ -5386,7 +5544,7 @@ function BeatTheGridGame({
|
|
|
5386
5544
|
}
|
|
5387
5545
|
|
|
5388
5546
|
// src/components/games/GuessTheXiGame.tsx
|
|
5389
|
-
var
|
|
5547
|
+
var import_react32 = require("react");
|
|
5390
5548
|
|
|
5391
5549
|
// src/components/games/ui/PitchSvg.tsx
|
|
5392
5550
|
var import_jsx_runtime75 = require("react/jsx-runtime");
|
|
@@ -5475,8 +5633,8 @@ function GuessTheXiGame({
|
|
|
5475
5633
|
onBack
|
|
5476
5634
|
}) {
|
|
5477
5635
|
const t = ui.games.guessTheXi;
|
|
5478
|
-
const [query, setQuery] = (0,
|
|
5479
|
-
const inputId = (0,
|
|
5636
|
+
const [query, setQuery] = (0, import_react32.useState)("");
|
|
5637
|
+
const inputId = (0, import_react32.useId)();
|
|
5480
5638
|
const clientState = parseClientState5(result?.clientStateJson);
|
|
5481
5639
|
const isOver = result?.alreadyPlayed === true || clientState?.status === "solved" || clientState?.status === "failed";
|
|
5482
5640
|
const solved = clientState?.status === "solved";
|
|
@@ -5617,7 +5775,7 @@ function GuessTheXiGame({
|
|
|
5617
5775
|
}
|
|
5618
5776
|
|
|
5619
5777
|
// src/components/games/DailyQuizGame.tsx
|
|
5620
|
-
var
|
|
5778
|
+
var import_react33 = require("react");
|
|
5621
5779
|
var import_jsx_runtime77 = require("react/jsx-runtime");
|
|
5622
5780
|
function parseClientState6(json) {
|
|
5623
5781
|
if (!json) return null;
|
|
@@ -5658,8 +5816,8 @@ function DailyQuizGame({
|
|
|
5658
5816
|
}) {
|
|
5659
5817
|
const s = ui.games.dailyQuiz;
|
|
5660
5818
|
const title = gameTitle({ gameId: "daily-quiz" }, ui.games.titles);
|
|
5661
|
-
const [selections, setSelections] = (0,
|
|
5662
|
-
const [page, setPage] = (0,
|
|
5819
|
+
const [selections, setSelections] = (0, import_react33.useState)(/* @__PURE__ */ new Map());
|
|
5820
|
+
const [page, setPage] = (0, import_react33.useState)(0);
|
|
5663
5821
|
const clientState = parseClientState6(result?.clientStateJson);
|
|
5664
5822
|
const results = clientState?.results ?? [];
|
|
5665
5823
|
const isRevealed = results.length > 0 || result?.alreadyPlayed === true;
|
|
@@ -5769,7 +5927,7 @@ function DailyQuizGame({
|
|
|
5769
5927
|
}
|
|
5770
5928
|
|
|
5771
5929
|
// src/components/games/MatchdayPredictorGame.tsx
|
|
5772
|
-
var
|
|
5930
|
+
var import_react34 = require("react");
|
|
5773
5931
|
var import_jsx_runtime78 = require("react/jsx-runtime");
|
|
5774
5932
|
var SCORER_SEARCH_THRESHOLD = 9;
|
|
5775
5933
|
function parseClientState7(json) {
|
|
@@ -5865,12 +6023,12 @@ function MatchdayPredictorGame({
|
|
|
5865
6023
|
onBack
|
|
5866
6024
|
}) {
|
|
5867
6025
|
const t = ui.games.predictor;
|
|
5868
|
-
const [homeScore, setHomeScore] = (0,
|
|
5869
|
-
const [awayScore, setAwayScore] = (0,
|
|
5870
|
-
const [firstScorerPlayerId, setFirstScorerPlayerId] = (0,
|
|
5871
|
-
const [lastEdited, setLastEdited] = (0,
|
|
5872
|
-
const [query, setQuery] = (0,
|
|
5873
|
-
const searchId = (0,
|
|
6026
|
+
const [homeScore, setHomeScore] = (0, import_react34.useState)(0);
|
|
6027
|
+
const [awayScore, setAwayScore] = (0, import_react34.useState)(0);
|
|
6028
|
+
const [firstScorerPlayerId, setFirstScorerPlayerId] = (0, import_react34.useState)(null);
|
|
6029
|
+
const [lastEdited, setLastEdited] = (0, import_react34.useState)("home");
|
|
6030
|
+
const [query, setQuery] = (0, import_react34.useState)("");
|
|
6031
|
+
const searchId = (0, import_react34.useId)();
|
|
5874
6032
|
const clientState = parseClientState7(result?.clientStateJson);
|
|
5875
6033
|
const alreadyPlayed = result?.alreadyPlayed === true;
|
|
5876
6034
|
const header = /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
|
|
@@ -6037,7 +6195,7 @@ function MatchdayPredictorGame({
|
|
|
6037
6195
|
}
|
|
6038
6196
|
|
|
6039
6197
|
// src/components/games/StartingXiGame.tsx
|
|
6040
|
-
var
|
|
6198
|
+
var import_react35 = require("react");
|
|
6041
6199
|
var import_jsx_runtime79 = require("react/jsx-runtime");
|
|
6042
6200
|
var POINTS_PER_STARTER = 8;
|
|
6043
6201
|
var XI_SIZE = 11;
|
|
@@ -6081,10 +6239,10 @@ function StartingXiGame({
|
|
|
6081
6239
|
lockAtUtc
|
|
6082
6240
|
}) {
|
|
6083
6241
|
const t = ui.games.startingXi;
|
|
6084
|
-
const [slotPlayers, setSlotPlayers] = (0,
|
|
6085
|
-
const [active, setActive] = (0,
|
|
6086
|
-
const [query, setQuery] = (0,
|
|
6087
|
-
const searchId = (0,
|
|
6242
|
+
const [slotPlayers, setSlotPlayers] = (0, import_react35.useState)(() => Array(XI_SIZE).fill(null));
|
|
6243
|
+
const [active, setActive] = (0, import_react35.useState)(0);
|
|
6244
|
+
const [query, setQuery] = (0, import_react35.useState)("");
|
|
6245
|
+
const searchId = (0, import_react35.useId)();
|
|
6088
6246
|
const clientState = parseClientState8(result?.clientStateJson);
|
|
6089
6247
|
const alreadyPlayed = result?.alreadyPlayed === true;
|
|
6090
6248
|
const header = /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
@@ -6223,7 +6381,7 @@ function StartingXiGame({
|
|
|
6223
6381
|
}
|
|
6224
6382
|
|
|
6225
6383
|
// src/components/games/StatLadderGame.tsx
|
|
6226
|
-
var
|
|
6384
|
+
var import_react36 = require("react");
|
|
6227
6385
|
var import_jsx_runtime80 = require("react/jsx-runtime");
|
|
6228
6386
|
function ladderClue(ladder) {
|
|
6229
6387
|
const mystery = ladder.find((r) => r.rank === 2);
|
|
@@ -6260,9 +6418,9 @@ function StatLadderGame({
|
|
|
6260
6418
|
onBack
|
|
6261
6419
|
}) {
|
|
6262
6420
|
const t = ui.games.statLadder;
|
|
6263
|
-
const [query, setQuery] = (0,
|
|
6264
|
-
const [selectedId, setSelectedId] = (0,
|
|
6265
|
-
const searchId = (0,
|
|
6421
|
+
const [query, setQuery] = (0, import_react36.useState)("");
|
|
6422
|
+
const [selectedId, setSelectedId] = (0, import_react36.useState)(null);
|
|
6423
|
+
const searchId = (0, import_react36.useId)();
|
|
6266
6424
|
const clientState = parseClientState9(result?.clientStateJson);
|
|
6267
6425
|
const revealed = clientState != null;
|
|
6268
6426
|
const header = /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(GameHeader, { title: gameTitle({ gameId: "stat-ladder" }, ui.games.titles), eyebrow: t.eyebrow, onBack, backLabel: ui.back });
|
|
@@ -6467,8 +6625,8 @@ function GameScreenHost({
|
|
|
6467
6625
|
ui
|
|
6468
6626
|
}) {
|
|
6469
6627
|
const { gameId, fixtureId, gameDay = "", lockAtUtc = null } = selected;
|
|
6470
|
-
const [configJson, setConfigJson] = (0,
|
|
6471
|
-
(0,
|
|
6628
|
+
const [configJson, setConfigJson] = (0, import_react37.useState)(null);
|
|
6629
|
+
(0, import_react37.useEffect)(() => {
|
|
6472
6630
|
setConfigJson(null);
|
|
6473
6631
|
if (!conversation) return;
|
|
6474
6632
|
let cancelled = false;
|
|
@@ -6491,12 +6649,12 @@ function GameScreenHost({
|
|
|
6491
6649
|
fixtureId: fixtureId || void 0,
|
|
6492
6650
|
locale: ui.games.lang
|
|
6493
6651
|
});
|
|
6494
|
-
(0,
|
|
6652
|
+
(0, import_react37.useEffect)(() => {
|
|
6495
6653
|
if (conversation) hydrate();
|
|
6496
6654
|
}, [conversation, gameId]);
|
|
6497
|
-
const hydrateRef = (0,
|
|
6655
|
+
const hydrateRef = (0, import_react37.useRef)(hydrate);
|
|
6498
6656
|
hydrateRef.current = hydrate;
|
|
6499
|
-
(0,
|
|
6657
|
+
(0, import_react37.useEffect)(() => {
|
|
6500
6658
|
if (!FIXTURE_GAME_IDS.has(gameId)) return;
|
|
6501
6659
|
if (rawStatus !== "pending") return;
|
|
6502
6660
|
const timerId = setInterval(() => {
|
|
@@ -6631,8 +6789,8 @@ function GamesPanel({
|
|
|
6631
6789
|
leaderboard,
|
|
6632
6790
|
refreshHub
|
|
6633
6791
|
}) {
|
|
6634
|
-
const [selected, setSelected] = (0,
|
|
6635
|
-
(0,
|
|
6792
|
+
const [selected, setSelected] = (0, import_react38.useState)(null);
|
|
6793
|
+
(0, import_react38.useEffect)(() => {
|
|
6636
6794
|
ensureGamesFonts();
|
|
6637
6795
|
}, []);
|
|
6638
6796
|
if (!loggedIn) return /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(GamesLoginWall, { ui });
|
|
@@ -6663,7 +6821,7 @@ function GamesPanel({
|
|
|
6663
6821
|
}
|
|
6664
6822
|
|
|
6665
6823
|
// src/components/Leaderboard.tsx
|
|
6666
|
-
var
|
|
6824
|
+
var import_react39 = require("react");
|
|
6667
6825
|
|
|
6668
6826
|
// src/components/LeaderboardRow.tsx
|
|
6669
6827
|
var import_jsx_runtime84 = require("react/jsx-runtime");
|
|
@@ -6748,7 +6906,7 @@ function Leaderboard({
|
|
|
6748
6906
|
ui,
|
|
6749
6907
|
onPlay
|
|
6750
6908
|
}) {
|
|
6751
|
-
const [segment, setSegment] = (0,
|
|
6909
|
+
const [segment, setSegment] = (0, import_react39.useState)("weekly");
|
|
6752
6910
|
const myRow = segment === "weekly" ? data.myWeeklyRow : data.myAllTimeRow;
|
|
6753
6911
|
const offBoard = myRow !== null && myRow !== void 0 && myRow.rank === -1;
|
|
6754
6912
|
const isEmpty = segment === "weekly" ? data.weeklyTop.length === 0 : data.allTimeTop.length === 0;
|
|
@@ -6827,12 +6985,440 @@ function LeaderboardPanel({ loggedIn, data, loading, onPlay, ui }) {
|
|
|
6827
6985
|
return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "ss-w-mon-list", children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Leaderboard, { data: data ?? EMPTY_PAYLOAD, ui, onPlay }) });
|
|
6828
6986
|
}
|
|
6829
6987
|
|
|
6830
|
-
// src/components/
|
|
6988
|
+
// src/components/PresetSpeedDial.tsx
|
|
6989
|
+
var import_react40 = require("react");
|
|
6831
6990
|
var import_jsx_runtime88 = require("react/jsx-runtime");
|
|
6991
|
+
function PresetSpeedDial({ expanded, showDot, strings, onToggle, onOpen, rootRef, buttonRef }) {
|
|
6992
|
+
const pillsId = (0, import_react40.useId)();
|
|
6993
|
+
const tabIndex = expanded ? 0 : -1;
|
|
6994
|
+
return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { ref: rootRef, className: "ss-w-dial", children: [
|
|
6995
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(
|
|
6996
|
+
"button",
|
|
6997
|
+
{
|
|
6998
|
+
ref: buttonRef,
|
|
6999
|
+
type: "button",
|
|
7000
|
+
className: "ss-w-dial-btn",
|
|
7001
|
+
"aria-expanded": expanded,
|
|
7002
|
+
"aria-controls": pillsId,
|
|
7003
|
+
"aria-label": strings.dialName,
|
|
7004
|
+
onClick: onToggle,
|
|
7005
|
+
children: [
|
|
7006
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "ss-w-dial-icon ss-w-dial-icon--open", children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(SlidersIcon, {}) }),
|
|
7007
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "ss-w-dial-icon ss-w-dial-icon--close", children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(CloseIcon, {}) }),
|
|
7008
|
+
showDot && !expanded ? /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "ss-w-dial-dot", "aria-hidden": "true" }) : null
|
|
7009
|
+
]
|
|
7010
|
+
}
|
|
7011
|
+
),
|
|
7012
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { id: pillsId, className: "ss-w-dial-pills", "data-open": expanded ? "" : void 0, children: [
|
|
7013
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("button", { type: "button", className: "ss-w-dial-pill", tabIndex, onClick: () => onOpen("numbers"), children: [
|
|
7014
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(SlidersIcon, {}),
|
|
7015
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "ss-w-dial-pill-label", children: strings.setNumbers })
|
|
7016
|
+
] }),
|
|
7017
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("button", { type: "button", className: "ss-w-dial-pill", tabIndex, onClick: () => onOpen("combo"), children: [
|
|
7018
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(ComboIcon, {}),
|
|
7019
|
+
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "ss-w-dial-pill-label", children: strings.buildCombo })
|
|
7020
|
+
] })
|
|
7021
|
+
] })
|
|
7022
|
+
] });
|
|
7023
|
+
}
|
|
7024
|
+
|
|
7025
|
+
// src/components/PresetTools.tsx
|
|
7026
|
+
var import_react41 = require("react");
|
|
7027
|
+
|
|
7028
|
+
// src/presetGrid.ts
|
|
7029
|
+
var DEFAULT_SINGLES_LIMITS = [1.3, 1.5, 1.7, 2];
|
|
7030
|
+
var DEFAULT_SINGLES_FLOORS = [1.5, 2, 2.5, 3];
|
|
7031
|
+
var DEFAULT_COMBO_TOTALS = [3, 5, 10, 20, 50];
|
|
7032
|
+
var FIXED_SINGLES_COUNT = 3;
|
|
7033
|
+
var MIN_COMBO_LEGS = 2;
|
|
7034
|
+
var MAX_COMBO_LEGS = 6;
|
|
7035
|
+
var START = { maxPrice: 1.5, minPrice: 2, comboTotal: 5, comboLegs: 3 };
|
|
7036
|
+
var finite2 = (v) => typeof v === "number" && Number.isFinite(v);
|
|
7037
|
+
var cents = (n) => Math.round(n * 100);
|
|
7038
|
+
function samePrice(a, b) {
|
|
7039
|
+
return cents(a) === cents(b);
|
|
7040
|
+
}
|
|
7041
|
+
function isPresetGrid(v) {
|
|
7042
|
+
if (!v || typeof v !== "object") return false;
|
|
7043
|
+
const g = v;
|
|
7044
|
+
return Array.isArray(g.singles) && Array.isArray(g.singlesFrom) && Array.isArray(g.combos) && finite2(g.maxLegs);
|
|
7045
|
+
}
|
|
7046
|
+
function drawable(p) {
|
|
7047
|
+
return !!p && finite2(p.price) && finite2(p.matchOddsId) && finite2(p.matchId) && typeof p.homeTeamName === "string" && typeof p.awayTeamName === "string";
|
|
7048
|
+
}
|
|
7049
|
+
function choices(defaults, present) {
|
|
7050
|
+
const out = [];
|
|
7051
|
+
for (const n of [...defaults, ...present]) {
|
|
7052
|
+
if (finite2(n) && n > 0 && !out.some((m) => samePrice(m, n))) out.push(n);
|
|
7053
|
+
}
|
|
7054
|
+
return out.sort((a, b) => a - b);
|
|
7055
|
+
}
|
|
7056
|
+
function singlesLimits(grid) {
|
|
7057
|
+
return choices(DEFAULT_SINGLES_LIMITS, grid.singles.map((c) => c?.price));
|
|
7058
|
+
}
|
|
7059
|
+
function singlesFloors(grid) {
|
|
7060
|
+
return choices(DEFAULT_SINGLES_FLOORS, grid.singlesFrom.map((c) => c?.price));
|
|
7061
|
+
}
|
|
7062
|
+
function comboTotals(grid) {
|
|
7063
|
+
return choices(DEFAULT_COMBO_TOTALS, grid.combos.map((c) => c?.total));
|
|
7064
|
+
}
|
|
7065
|
+
function comboLegChoices(grid) {
|
|
7066
|
+
const max = Math.min(MAX_COMBO_LEGS, Math.floor(grid.maxLegs));
|
|
7067
|
+
const out = [];
|
|
7068
|
+
for (let n = MIN_COMBO_LEGS; n <= max; n++) out.push(n);
|
|
7069
|
+
return out;
|
|
7070
|
+
}
|
|
7071
|
+
function cycle(values, current) {
|
|
7072
|
+
if (values.length === 0) return current;
|
|
7073
|
+
const i = values.findIndex((v) => samePrice(v, current));
|
|
7074
|
+
return values[(i + 1) % values.length] ?? current;
|
|
7075
|
+
}
|
|
7076
|
+
function startValue(values, preferred) {
|
|
7077
|
+
return values.find((v) => samePrice(v, preferred)) ?? values[0] ?? preferred;
|
|
7078
|
+
}
|
|
7079
|
+
function nearest(values, target) {
|
|
7080
|
+
const first = values[0];
|
|
7081
|
+
if (first === void 0) return target;
|
|
7082
|
+
if (!(target > 0)) return first;
|
|
7083
|
+
const distance = (v) => Math.abs(Math.log(v / target));
|
|
7084
|
+
return values.reduce((best, v) => distance(v) < distance(best) ? v : best, first);
|
|
7085
|
+
}
|
|
7086
|
+
function clampLegs(values, legs) {
|
|
7087
|
+
const lo = values[0];
|
|
7088
|
+
const hi = values[values.length - 1];
|
|
7089
|
+
if (lo === void 0 || hi === void 0) return legs;
|
|
7090
|
+
return Math.min(Math.max(Math.round(legs), lo), hi);
|
|
7091
|
+
}
|
|
7092
|
+
function findSingles(cells, price, count) {
|
|
7093
|
+
const cell = cells.find((c) => !!c && finite2(c.price) && samePrice(c.price, price) && c.count === count);
|
|
7094
|
+
return cell && Array.isArray(cell.picks) && cell.picks.length === count && cell.picks.every(drawable) ? cell : null;
|
|
7095
|
+
}
|
|
7096
|
+
function findCombo(grid, total, legs) {
|
|
7097
|
+
const cell = grid.combos.find((c) => !!c && finite2(c.total) && samePrice(c.total, total) && c.legs === legs);
|
|
7098
|
+
const combo = cell?.combo;
|
|
7099
|
+
const whole = !!combo && typeof combo.multiplication === "string" && combo.multiplication !== "" && Array.isArray(combo.legs) && combo.legs.length === legs && combo.legs.every(drawable);
|
|
7100
|
+
return cell && whole ? cell : null;
|
|
7101
|
+
}
|
|
7102
|
+
function idsOf(picks) {
|
|
7103
|
+
return { matchOddsIds: picks.map((p) => p.matchOddsId), matchIds: picks.map((p) => p.matchId) };
|
|
7104
|
+
}
|
|
7105
|
+
function maxPriceAsk(cell, s) {
|
|
7106
|
+
return {
|
|
7107
|
+
message: s.maxPriceMessage(cell.count, cell.price),
|
|
7108
|
+
displayText: s.maxPriceDisplay(cell.count, cell.price),
|
|
7109
|
+
preset: { kind: "max_price", price: cell.price, count: cell.count, ...idsOf(cell.picks) }
|
|
7110
|
+
};
|
|
7111
|
+
}
|
|
7112
|
+
function minPriceAsk(cell, s) {
|
|
7113
|
+
return {
|
|
7114
|
+
message: s.minPriceMessage(cell.count, cell.price),
|
|
7115
|
+
displayText: s.minPriceDisplay(cell.count, cell.price),
|
|
7116
|
+
preset: { kind: "min_price", price: cell.price, count: cell.count, ...idsOf(cell.picks) }
|
|
7117
|
+
};
|
|
7118
|
+
}
|
|
7119
|
+
function comboAsk(cell, s) {
|
|
7120
|
+
return {
|
|
7121
|
+
message: s.comboMessage(cell.legs, cell.total),
|
|
7122
|
+
displayText: s.comboDisplay(cell.legs, cell.total),
|
|
7123
|
+
preset: { kind: "combo", total: cell.total, legs: cell.legs, ...idsOf(cell.combo.legs) }
|
|
7124
|
+
};
|
|
7125
|
+
}
|
|
7126
|
+
|
|
7127
|
+
// src/components/PresetTools.tsx
|
|
7128
|
+
var import_jsx_runtime89 = require("react/jsx-runtime");
|
|
7129
|
+
var FOCUSABLE = 'button:not([disabled]), [href], input:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
7130
|
+
function PresetToolModal({ title, closeLabel, onClose, children, footer }) {
|
|
7131
|
+
const dialogRef = (0, import_react41.useRef)(null);
|
|
7132
|
+
const titleId = (0, import_react41.useId)();
|
|
7133
|
+
(0, import_react41.useEffect)(() => {
|
|
7134
|
+
dialogRef.current?.focus();
|
|
7135
|
+
}, []);
|
|
7136
|
+
(0, import_react41.useEffect)(() => {
|
|
7137
|
+
const onKey = (e) => {
|
|
7138
|
+
if (e.key !== "Escape") return;
|
|
7139
|
+
e.stopPropagation();
|
|
7140
|
+
e.preventDefault();
|
|
7141
|
+
onClose();
|
|
7142
|
+
};
|
|
7143
|
+
window.addEventListener("keydown", onKey, true);
|
|
7144
|
+
return () => window.removeEventListener("keydown", onKey, true);
|
|
7145
|
+
}, [onClose]);
|
|
7146
|
+
const trapTab = (e) => {
|
|
7147
|
+
if (e.key !== "Tab") return;
|
|
7148
|
+
const dialog = dialogRef.current;
|
|
7149
|
+
if (!dialog) return;
|
|
7150
|
+
const items = Array.from(dialog.querySelectorAll(FOCUSABLE));
|
|
7151
|
+
const first = items[0];
|
|
7152
|
+
const last = items[items.length - 1];
|
|
7153
|
+
if (!first || !last) {
|
|
7154
|
+
e.preventDefault();
|
|
7155
|
+
return;
|
|
7156
|
+
}
|
|
7157
|
+
const active = dialog.getRootNode().activeElement;
|
|
7158
|
+
if (e.shiftKey && (active === first || active === dialog)) {
|
|
7159
|
+
e.preventDefault();
|
|
7160
|
+
last.focus();
|
|
7161
|
+
} else if (!e.shiftKey && active === last) {
|
|
7162
|
+
e.preventDefault();
|
|
7163
|
+
first.focus();
|
|
7164
|
+
}
|
|
7165
|
+
};
|
|
7166
|
+
return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { className: "ss-w-tool-backdrop", children: /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { ref: dialogRef, className: "ss-w-tool", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, tabIndex: -1, onKeyDown: trapTab, children: [
|
|
7167
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: "ss-w-tool-head", children: [
|
|
7168
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("h2", { id: titleId, className: "ss-w-tool-title", children: title }),
|
|
7169
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("button", { type: "button", className: "ss-w-tool-close", "aria-label": closeLabel, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(CloseIcon, {}) })
|
|
7170
|
+
] }),
|
|
7171
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { className: "ss-w-tool-body", children }),
|
|
7172
|
+
footer ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { className: "ss-w-tool-foot", children: footer }) : null
|
|
7173
|
+
] }) });
|
|
7174
|
+
}
|
|
7175
|
+
function fillTemplate(template, slots) {
|
|
7176
|
+
return template.split(/(\{[a-z]+\})/).filter((part) => part !== "").map((part, i) => {
|
|
7177
|
+
const key = /^\{([a-z]+)\}$/.exec(part)?.[1];
|
|
7178
|
+
return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(import_react41.Fragment, { children: key !== void 0 && key in slots ? slots[key] : part }, i);
|
|
7179
|
+
});
|
|
7180
|
+
}
|
|
7181
|
+
function NumberButton({ value, label, onCycle }) {
|
|
7182
|
+
return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("button", { type: "button", className: "ss-w-tool-num", "aria-label": label, onClick: onCycle, children: value });
|
|
7183
|
+
}
|
|
7184
|
+
function SendButton({ label, display, disabled, onClick }) {
|
|
7185
|
+
return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("button", { type: "button", className: "ss-w-tool-send", "aria-label": `${label}: ${display}`, disabled, onClick, children: label });
|
|
7186
|
+
}
|
|
7187
|
+
function PickList({ picks, formatKickoff }) {
|
|
7188
|
+
return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("ul", { className: "ss-w-tool-picks", role: "list", children: picks.map((p) => /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("li", { className: "ss-w-tool-pick", children: [
|
|
7189
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("span", { className: "ss-w-tool-pick-match", children: [
|
|
7190
|
+
p.homeTeamName,
|
|
7191
|
+
" \u2013 ",
|
|
7192
|
+
p.awayTeamName
|
|
7193
|
+
] }),
|
|
7194
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("span", { className: "ss-w-tool-pick-sel", children: [
|
|
7195
|
+
p.marketLabel,
|
|
7196
|
+
": ",
|
|
7197
|
+
p.selectionLabel,
|
|
7198
|
+
p.line ? ` ${p.line}` : ""
|
|
7199
|
+
] }),
|
|
7200
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("span", { className: "ss-w-tool-pick-kick", children: formatKickoff(p.kickoffUtc) }),
|
|
7201
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("span", { className: "ss-w-tool-pick-price", children: p.price.toFixed(2) })
|
|
7202
|
+
] }, p.matchOddsId)) });
|
|
7203
|
+
}
|
|
7204
|
+
function ComboPreview({ cell, formatKickoff }) {
|
|
7205
|
+
return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(import_jsx_runtime89.Fragment, { children: [
|
|
7206
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)(PickList, { picks: cell.combo.legs, formatKickoff }),
|
|
7207
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { className: "ss-w-tool-math", children: cell.combo.multiplication })
|
|
7208
|
+
] });
|
|
7209
|
+
}
|
|
7210
|
+
function Unavailable({ text }) {
|
|
7211
|
+
return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("p", { className: "ss-w-tool-unavailable", children: text });
|
|
7212
|
+
}
|
|
7213
|
+
function SetNumbersTool({ grid, strings, streaming, formatKickoff, onSend, onClose }) {
|
|
7214
|
+
const limits = singlesLimits(grid);
|
|
7215
|
+
const floors = singlesFloors(grid);
|
|
7216
|
+
const totals = comboTotals(grid);
|
|
7217
|
+
const legChoices = comboLegChoices(grid);
|
|
7218
|
+
const count = FIXED_SINGLES_COUNT;
|
|
7219
|
+
const [maxPrice, setMaxPrice] = (0, import_react41.useState)(() => startValue(limits, START.maxPrice));
|
|
7220
|
+
const [minPrice, setMinPrice] = (0, import_react41.useState)(() => startValue(floors, START.minPrice));
|
|
7221
|
+
const [total, setTotal] = (0, import_react41.useState)(() => startValue(totals, START.comboTotal));
|
|
7222
|
+
const [legs, setLegs] = (0, import_react41.useState)(() => startValue(legChoices, START.comboLegs));
|
|
7223
|
+
const [announcement, setAnnouncement] = (0, import_react41.useState)("");
|
|
7224
|
+
const maxCell = findSingles(grid.singles, maxPrice, count);
|
|
7225
|
+
const minCell = findSingles(grid.singlesFrom, minPrice, count);
|
|
7226
|
+
const comboCell = findCombo(grid, total, legs);
|
|
7227
|
+
const announce = (display, available) => setAnnouncement(available ? display : `${display}. ${strings.unavailable}`);
|
|
7228
|
+
const nextMax = () => {
|
|
7229
|
+
const v = cycle(limits, maxPrice);
|
|
7230
|
+
setMaxPrice(v);
|
|
7231
|
+
announce(strings.maxPriceDisplay(count, v), !!findSingles(grid.singles, v, count));
|
|
7232
|
+
};
|
|
7233
|
+
const nextMin = () => {
|
|
7234
|
+
const v = cycle(floors, minPrice);
|
|
7235
|
+
setMinPrice(v);
|
|
7236
|
+
announce(strings.minPriceDisplay(count, v), !!findSingles(grid.singlesFrom, v, count));
|
|
7237
|
+
};
|
|
7238
|
+
const nextLegs = () => {
|
|
7239
|
+
const v = cycle(legChoices, legs);
|
|
7240
|
+
setLegs(v);
|
|
7241
|
+
announce(strings.comboDisplay(v, total), !!findCombo(grid, total, v));
|
|
7242
|
+
};
|
|
7243
|
+
const nextTotal = () => {
|
|
7244
|
+
const v = cycle(totals, total);
|
|
7245
|
+
setTotal(v);
|
|
7246
|
+
announce(strings.comboDisplay(legs, v), !!findCombo(grid, v, legs));
|
|
7247
|
+
};
|
|
7248
|
+
return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(PresetToolModal, { title: strings.setNumbers, closeLabel: strings.close, onClose, children: [
|
|
7249
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("section", { className: "ss-w-tool-row", children: [
|
|
7250
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("p", { className: "ss-w-tool-ask", children: fillTemplate(strings.maxPriceRow, {
|
|
7251
|
+
count: String(count),
|
|
7252
|
+
price: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(NumberButton, { value: maxPrice.toFixed(2), label: strings.changeNumber(maxPrice.toFixed(2)), onCycle: nextMax })
|
|
7253
|
+
}) }),
|
|
7254
|
+
maxCell ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(PickList, { picks: maxCell.picks, formatKickoff }) : /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(Unavailable, { text: strings.unavailable }),
|
|
7255
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)(SendButton, { label: strings.send, display: strings.maxPriceDisplay(count, maxPrice), disabled: !maxCell || streaming, onClick: () => {
|
|
7256
|
+
if (maxCell) onSend(maxPriceAsk(maxCell, strings));
|
|
7257
|
+
} })
|
|
7258
|
+
] }),
|
|
7259
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("section", { className: "ss-w-tool-row", children: [
|
|
7260
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("p", { className: "ss-w-tool-ask", children: fillTemplate(strings.minPriceRow, {
|
|
7261
|
+
count: String(count),
|
|
7262
|
+
price: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(NumberButton, { value: minPrice.toFixed(2), label: strings.changeNumber(minPrice.toFixed(2)), onCycle: nextMin })
|
|
7263
|
+
}) }),
|
|
7264
|
+
minCell ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(PickList, { picks: minCell.picks, formatKickoff }) : /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(Unavailable, { text: strings.unavailable }),
|
|
7265
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)(SendButton, { label: strings.send, display: strings.minPriceDisplay(count, minPrice), disabled: !minCell || streaming, onClick: () => {
|
|
7266
|
+
if (minCell) onSend(minPriceAsk(minCell, strings));
|
|
7267
|
+
} })
|
|
7268
|
+
] }),
|
|
7269
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("section", { className: "ss-w-tool-row", children: [
|
|
7270
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("p", { className: "ss-w-tool-ask", children: fillTemplate(strings.comboRow, {
|
|
7271
|
+
legs: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(NumberButton, { value: String(legs), label: strings.changeNumber(String(legs)), onCycle: nextLegs }),
|
|
7272
|
+
total: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(NumberButton, { value: total.toFixed(1), label: strings.changeNumber(total.toFixed(1)), onCycle: nextTotal })
|
|
7273
|
+
}) }),
|
|
7274
|
+
comboCell ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(ComboPreview, { cell: comboCell, formatKickoff }) : /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(Unavailable, { text: strings.unavailable }),
|
|
7275
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)(SendButton, { label: strings.send, display: strings.comboDisplay(legs, total), disabled: !comboCell || streaming, onClick: () => {
|
|
7276
|
+
if (comboCell) onSend(comboAsk(comboCell, strings));
|
|
7277
|
+
} })
|
|
7278
|
+
] }),
|
|
7279
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("p", { className: "ss-w-vh", "aria-live": "polite", children: announcement })
|
|
7280
|
+
] });
|
|
7281
|
+
}
|
|
7282
|
+
function BuildComboTool({ grid, strings, streaming, formatKickoff, initialTotal, initialLegs, onSend, onClose }) {
|
|
7283
|
+
const totals = comboTotals(grid);
|
|
7284
|
+
const legChoices = comboLegChoices(grid);
|
|
7285
|
+
const [total, setTotal] = (0, import_react41.useState)(() => initialTotal !== void 0 ? nearest(totals, initialTotal) : startValue(totals, START.comboTotal));
|
|
7286
|
+
const [legs, setLegs] = (0, import_react41.useState)(() => initialLegs !== void 0 ? clampLegs(legChoices, initialLegs) : startValue(legChoices, START.comboLegs));
|
|
7287
|
+
const [announcement, setAnnouncement] = (0, import_react41.useState)("");
|
|
7288
|
+
const cell = findCombo(grid, total, legs);
|
|
7289
|
+
const minLegs = legChoices[0] ?? legs;
|
|
7290
|
+
const maxLegs = legChoices[legChoices.length - 1] ?? legs;
|
|
7291
|
+
const display = strings.comboDisplay(legs, total);
|
|
7292
|
+
const choose = (nextTotal, nextLegs) => {
|
|
7293
|
+
setTotal(nextTotal);
|
|
7294
|
+
setLegs(nextLegs);
|
|
7295
|
+
const d = strings.comboDisplay(nextLegs, nextTotal);
|
|
7296
|
+
setAnnouncement(findCombo(grid, nextTotal, nextLegs) ? d : `${d}. ${strings.unavailable}`);
|
|
7297
|
+
};
|
|
7298
|
+
const footer = /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(SendButton, { label: strings.send, display, disabled: !cell || streaming, onClick: () => {
|
|
7299
|
+
if (cell) onSend(comboAsk(cell, strings));
|
|
7300
|
+
} });
|
|
7301
|
+
return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(PresetToolModal, { title: strings.buildCombo, closeLabel: strings.close, onClose, footer, children: [
|
|
7302
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: "ss-w-tool-section", role: "group", "aria-label": strings.totalLabel, children: [
|
|
7303
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("span", { className: "ss-w-tool-label", "aria-hidden": "true", children: strings.totalLabel }),
|
|
7304
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { className: "ss-w-tool-chips", children: totals.map((t) => /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("button", { type: "button", className: "ss-w-tool-chip", "aria-pressed": samePrice(t, total), onClick: () => choose(t, legs), children: t.toFixed(1) }, t)) })
|
|
7305
|
+
] }),
|
|
7306
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: "ss-w-tool-section", role: "group", "aria-label": strings.matchesLabel, children: [
|
|
7307
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("span", { className: "ss-w-tool-label", "aria-hidden": "true", children: strings.matchesLabel }),
|
|
7308
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: "ss-w-tool-stepper", children: [
|
|
7309
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("button", { type: "button", className: "ss-w-tool-step", "aria-label": strings.fewerMatches, disabled: legs <= minLegs, onClick: () => choose(total, legs - 1), children: "\u2212" }),
|
|
7310
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("span", { className: "ss-w-tool-count", children: legs }),
|
|
7311
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("button", { type: "button", className: "ss-w-tool-step", "aria-label": strings.moreMatches, disabled: legs >= maxLegs, onClick: () => choose(total, legs + 1), children: "+" })
|
|
7312
|
+
] })
|
|
7313
|
+
] }),
|
|
7314
|
+
cell ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(ComboPreview, { cell, formatKickoff }) : /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(Unavailable, { text: strings.unavailable }),
|
|
7315
|
+
/* @__PURE__ */ (0, import_jsx_runtime89.jsx)("p", { className: "ss-w-vh", "aria-live": "polite", children: announcement })
|
|
7316
|
+
] });
|
|
7317
|
+
}
|
|
7318
|
+
|
|
7319
|
+
// src/usePresetDial.ts
|
|
7320
|
+
var import_react42 = require("react");
|
|
7321
|
+
function sendInFlight(messages, isStreaming) {
|
|
7322
|
+
const last = messages[messages.length - 1];
|
|
7323
|
+
return isStreaming && !!last && last.role === "assistant" && !last.done && !last.isGreeting;
|
|
7324
|
+
}
|
|
7325
|
+
function lastUserId(messages) {
|
|
7326
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
7327
|
+
const m = messages[i];
|
|
7328
|
+
if (m && m.role === "user") return m.id;
|
|
7329
|
+
}
|
|
7330
|
+
return null;
|
|
7331
|
+
}
|
|
7332
|
+
var SCROLL_KEYS = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End", " "]);
|
|
7333
|
+
function usePresetDial({ layerRef, dialRef, dialButtonRef, messages, isStreaming, activeTab }) {
|
|
7334
|
+
const [expanded, setExpanded] = (0, import_react42.useState)(() => !sendInFlight(messages, isStreaming));
|
|
7335
|
+
const fold = (0, import_react42.useCallback)(() => setExpanded(false), []);
|
|
7336
|
+
const toggle = (0, import_react42.useCallback)(() => setExpanded((e) => !e), []);
|
|
7337
|
+
const currentUserId = lastUserId(messages);
|
|
7338
|
+
const userIdRef = (0, import_react42.useRef)(currentUserId);
|
|
7339
|
+
(0, import_react42.useEffect)(() => {
|
|
7340
|
+
if (currentUserId === userIdRef.current) return;
|
|
7341
|
+
userIdRef.current = currentUserId;
|
|
7342
|
+
setExpanded(false);
|
|
7343
|
+
}, [currentUserId]);
|
|
7344
|
+
const tabRef = (0, import_react42.useRef)(activeTab);
|
|
7345
|
+
(0, import_react42.useEffect)(() => {
|
|
7346
|
+
if (activeTab === tabRef.current) return;
|
|
7347
|
+
tabRef.current = activeTab;
|
|
7348
|
+
setExpanded(false);
|
|
7349
|
+
}, [activeTab]);
|
|
7350
|
+
const expandedRef = (0, import_react42.useRef)(expanded);
|
|
7351
|
+
expandedRef.current = expanded;
|
|
7352
|
+
(0, import_react42.useEffect)(() => {
|
|
7353
|
+
const onEscape = (e) => {
|
|
7354
|
+
const dial = dialRef.current;
|
|
7355
|
+
if (e.key !== "Escape" || !dial || !e.composedPath().includes(dial)) return;
|
|
7356
|
+
e.stopPropagation();
|
|
7357
|
+
e.preventDefault();
|
|
7358
|
+
if (!expandedRef.current) return;
|
|
7359
|
+
setExpanded(false);
|
|
7360
|
+
dialButtonRef.current?.focus();
|
|
7361
|
+
};
|
|
7362
|
+
window.addEventListener("keydown", onEscape, true);
|
|
7363
|
+
return () => window.removeEventListener("keydown", onEscape, true);
|
|
7364
|
+
}, [dialRef, dialButtonRef]);
|
|
7365
|
+
(0, import_react42.useEffect)(() => {
|
|
7366
|
+
if (!expanded) return;
|
|
7367
|
+
const layer = layerRef.current;
|
|
7368
|
+
const inDial = (e) => {
|
|
7369
|
+
const dial = dialRef.current;
|
|
7370
|
+
return !!dial && e.composedPath().includes(dial);
|
|
7371
|
+
};
|
|
7372
|
+
const inList = (e) => e.composedPath().some((n) => n instanceof Element && n.classList.contains("ss-w-list"));
|
|
7373
|
+
const onPress = (e) => {
|
|
7374
|
+
if (!inDial(e)) setExpanded(false);
|
|
7375
|
+
};
|
|
7376
|
+
const onScrollGesture = (e) => {
|
|
7377
|
+
if (inList(e) && !inDial(e)) setExpanded(false);
|
|
7378
|
+
};
|
|
7379
|
+
const onKey = (e) => {
|
|
7380
|
+
if (SCROLL_KEYS.has(e.key) && inList(e)) setExpanded(false);
|
|
7381
|
+
};
|
|
7382
|
+
const doc = layer?.ownerDocument ?? document;
|
|
7383
|
+
doc.addEventListener("pointerdown", onPress, true);
|
|
7384
|
+
layer?.addEventListener("wheel", onScrollGesture, { passive: true });
|
|
7385
|
+
layer?.addEventListener("touchmove", onScrollGesture, { passive: true });
|
|
7386
|
+
window.addEventListener("keydown", onKey, true);
|
|
7387
|
+
return () => {
|
|
7388
|
+
doc.removeEventListener("pointerdown", onPress, true);
|
|
7389
|
+
layer?.removeEventListener("wheel", onScrollGesture);
|
|
7390
|
+
layer?.removeEventListener("touchmove", onScrollGesture);
|
|
7391
|
+
window.removeEventListener("keydown", onKey, true);
|
|
7392
|
+
};
|
|
7393
|
+
}, [expanded, layerRef, dialRef]);
|
|
7394
|
+
return { expanded, toggle, fold };
|
|
7395
|
+
}
|
|
7396
|
+
|
|
7397
|
+
// src/presetSeen.ts
|
|
7398
|
+
var KEY = "ss-w-presets-tool-opened";
|
|
7399
|
+
var openedThisPage = false;
|
|
7400
|
+
function readToolSeen() {
|
|
7401
|
+
if (openedThisPage) return true;
|
|
7402
|
+
try {
|
|
7403
|
+
return window.localStorage.getItem(KEY) === "1";
|
|
7404
|
+
} catch {
|
|
7405
|
+
return false;
|
|
7406
|
+
}
|
|
7407
|
+
}
|
|
7408
|
+
function markToolSeen() {
|
|
7409
|
+
openedThisPage = true;
|
|
7410
|
+
try {
|
|
7411
|
+
window.localStorage.setItem(KEY, "1");
|
|
7412
|
+
} catch {
|
|
7413
|
+
}
|
|
7414
|
+
}
|
|
7415
|
+
|
|
7416
|
+
// src/components/ChatPanel.tsx
|
|
7417
|
+
var import_jsx_runtime90 = require("react/jsx-runtime");
|
|
6832
7418
|
var FULLSCREEN_QUERY = "(max-width: 639px)";
|
|
6833
7419
|
function useIsModal() {
|
|
6834
|
-
const [modal, setModal] = (0,
|
|
6835
|
-
(0,
|
|
7420
|
+
const [modal, setModal] = (0, import_react43.useState)(false);
|
|
7421
|
+
(0, import_react43.useEffect)(() => {
|
|
6836
7422
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
6837
7423
|
const mql = window.matchMedia(FULLSCREEN_QUERY);
|
|
6838
7424
|
const sync = () => setModal(mql.matches);
|
|
@@ -6842,10 +7428,11 @@ function useIsModal() {
|
|
|
6842
7428
|
}, []);
|
|
6843
7429
|
return modal;
|
|
6844
7430
|
}
|
|
6845
|
-
|
|
7431
|
+
var HIDDEN_LAYER_STYLE = { position: "absolute", inset: 0, visibility: "hidden", display: "flex", flexDirection: "column" };
|
|
7432
|
+
function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, gamesResult, leaderboardResult, loggedIn, conversation, presetGrid }) {
|
|
6846
7433
|
const { state, close, stop, send, reset } = controller;
|
|
6847
7434
|
const isModal = useIsModal();
|
|
6848
|
-
(0,
|
|
7435
|
+
(0, import_react43.useEffect)(() => {
|
|
6849
7436
|
const onKey = (e) => {
|
|
6850
7437
|
if (e.key === "Escape") close();
|
|
6851
7438
|
};
|
|
@@ -6860,11 +7447,66 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
|
|
|
6860
7447
|
monitor?.setActiveTab("chat");
|
|
6861
7448
|
send(text, { grounding });
|
|
6862
7449
|
};
|
|
6863
|
-
(0,
|
|
7450
|
+
(0, import_react43.useEffect)(() => {
|
|
6864
7451
|
if (monitor && monitor.activeTab !== "chat" && state.isStreaming) monitor.markChatActivity();
|
|
6865
7452
|
}, [monitor, state.isStreaming, state.messages.length]);
|
|
6866
|
-
|
|
6867
|
-
|
|
7453
|
+
const onChatTab = !monitor || monitor.activeTab === "chat";
|
|
7454
|
+
const layerRef = (0, import_react43.useRef)(null);
|
|
7455
|
+
const dialRef = (0, import_react43.useRef)(null);
|
|
7456
|
+
const dialButtonRef = (0, import_react43.useRef)(null);
|
|
7457
|
+
const inputRef = (0, import_react43.useRef)(null);
|
|
7458
|
+
const focusAfterCloseRef = (0, import_react43.useRef)(null);
|
|
7459
|
+
const [tool, setTool] = (0, import_react43.useState)(null);
|
|
7460
|
+
const [toolSeen, setToolSeen] = (0, import_react43.useState)(readToolSeen);
|
|
7461
|
+
const dial = usePresetDial({
|
|
7462
|
+
layerRef,
|
|
7463
|
+
dialRef,
|
|
7464
|
+
dialButtonRef,
|
|
7465
|
+
messages: state.messages,
|
|
7466
|
+
isStreaming: state.isStreaming,
|
|
7467
|
+
activeTab: monitor?.activeTab ?? "chat"
|
|
7468
|
+
});
|
|
7469
|
+
const { fold } = dial;
|
|
7470
|
+
const showDial = !!presetGrid && onChatTab && !tool;
|
|
7471
|
+
const openTool = (0, import_react43.useCallback)((next) => {
|
|
7472
|
+
fold();
|
|
7473
|
+
markToolSeen();
|
|
7474
|
+
setToolSeen(true);
|
|
7475
|
+
setTool(next);
|
|
7476
|
+
}, [fold]);
|
|
7477
|
+
const onPill = (0, import_react43.useCallback)((kind) => openTool(kind === "numbers" ? { kind: "numbers" } : { kind: "combo" }), [openTool]);
|
|
7478
|
+
const toolsValue = (0, import_react43.useMemo)(
|
|
7479
|
+
() => presetGrid ? { openBuilder: (total, legs) => openTool({ kind: "combo", total, legs }), changeLabel: ui.presets.changeCombo } : null,
|
|
7480
|
+
[presetGrid, openTool, ui.presets.changeCombo]
|
|
7481
|
+
);
|
|
7482
|
+
const closeTool = (0, import_react43.useCallback)(() => {
|
|
7483
|
+
focusAfterCloseRef.current = "dial";
|
|
7484
|
+
setTool(null);
|
|
7485
|
+
}, []);
|
|
7486
|
+
const sendAsk = (0, import_react43.useCallback)((ask) => {
|
|
7487
|
+
focusAfterCloseRef.current = "composer";
|
|
7488
|
+
setTool(null);
|
|
7489
|
+
monitor?.setActiveTab("chat");
|
|
7490
|
+
send(ask.message, { displayText: ask.displayText, preset: ask.preset });
|
|
7491
|
+
}, [monitor, send]);
|
|
7492
|
+
const formatKickoff = (0, import_react43.useCallback)((kickoffUtc) => {
|
|
7493
|
+
const k = ui.bettingKickoff(kickoffUtc);
|
|
7494
|
+
return `${k.dayLabel} ${k.time}`;
|
|
7495
|
+
}, [ui]);
|
|
7496
|
+
(0, import_react43.useEffect)(() => {
|
|
7497
|
+
if (tool) return;
|
|
7498
|
+
const target = focusAfterCloseRef.current;
|
|
7499
|
+
focusAfterCloseRef.current = null;
|
|
7500
|
+
if (target === "dial") dialButtonRef.current?.focus();
|
|
7501
|
+
else if (target === "composer") inputRef.current?.focus();
|
|
7502
|
+
}, [tool]);
|
|
7503
|
+
(0, import_react43.useEffect)(() => {
|
|
7504
|
+
if (!tool || onChatTab && presetGrid) return;
|
|
7505
|
+
focusAfterCloseRef.current = "composer";
|
|
7506
|
+
setTool(null);
|
|
7507
|
+
}, [tool, onChatTab, presetGrid]);
|
|
7508
|
+
return /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
|
|
7509
|
+
/* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
|
|
6868
7510
|
ChatHeader,
|
|
6869
7511
|
{
|
|
6870
7512
|
onClose: close,
|
|
@@ -6877,7 +7519,7 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
|
|
|
6877
7519
|
moreOptionsLabel: ui.moreOptions
|
|
6878
7520
|
}
|
|
6879
7521
|
),
|
|
6880
|
-
monitor && /* @__PURE__ */ (0,
|
|
7522
|
+
monitor && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
|
|
6881
7523
|
TabBar,
|
|
6882
7524
|
{
|
|
6883
7525
|
monitor,
|
|
@@ -6889,17 +7531,26 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
|
|
|
6889
7531
|
gamesEnabled: gamesResult?.enabled ?? false
|
|
6890
7532
|
}
|
|
6891
7533
|
),
|
|
6892
|
-
/* @__PURE__ */ (0,
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
7534
|
+
/* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("div", { ref: layerRef, className: "ss-w-chat-layer", "data-dial": presetGrid ? "" : void 0, style: onChatTab ? void 0 : HIDDEN_LAYER_STYLE, children: [
|
|
7535
|
+
/* @__PURE__ */ (0, import_jsx_runtime90.jsx)(PresetToolsContext.Provider, { value: toolsValue, children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(MessageList, { controller, ui, slip, onDashboardTap, monitor }) }),
|
|
7536
|
+
showDial ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
|
|
7537
|
+
PresetSpeedDial,
|
|
7538
|
+
{
|
|
7539
|
+
expanded: dial.expanded,
|
|
7540
|
+
showDot: !toolSeen,
|
|
7541
|
+
strings: ui.presets,
|
|
7542
|
+
onToggle: dial.toggle,
|
|
7543
|
+
onOpen: onPill,
|
|
7544
|
+
rootRef: dialRef,
|
|
7545
|
+
buttonRef: dialButtonRef
|
|
7546
|
+
}
|
|
7547
|
+
) : null,
|
|
7548
|
+
tool && presetGrid && onChatTab ? tool.kind === "numbers" ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(SetNumbersTool, { grid: presetGrid, strings: ui.presets, streaming: state.isStreaming, formatKickoff, onSend: sendAsk, onClose: closeTool }) : /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(BuildComboTool, { grid: presetGrid, strings: ui.presets, streaming: state.isStreaming, formatKickoff, initialTotal: tool.total, initialLegs: tool.legs, onSend: sendAsk, onClose: closeTool }) : null
|
|
7549
|
+
] }),
|
|
7550
|
+
monitor && discovery && monitor.activeTab === "today" && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(TodayPanel, { discovery, ui, monitor }),
|
|
7551
|
+
monitor && monitor.activeTab === "following" && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(MatchesPanel, { monitor, ui, slip, onAsk: handleAsk }),
|
|
7552
|
+
monitor && monitor.activeTab === "odds" && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(OddsPanel, { monitor, ui, slip }),
|
|
7553
|
+
monitor && monitor.activeTab === "games" && gamesResult && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
|
|
6903
7554
|
GamesPanel,
|
|
6904
7555
|
{
|
|
6905
7556
|
loggedIn: !!loggedIn,
|
|
@@ -6913,7 +7564,7 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
|
|
|
6913
7564
|
}
|
|
6914
7565
|
}
|
|
6915
7566
|
),
|
|
6916
|
-
monitor && monitor.activeTab === "leaderboard" && leaderboardResult && /* @__PURE__ */ (0,
|
|
7567
|
+
monitor && monitor.activeTab === "leaderboard" && leaderboardResult && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
|
|
6917
7568
|
LeaderboardPanel,
|
|
6918
7569
|
{
|
|
6919
7570
|
loggedIn: !!loggedIn,
|
|
@@ -6923,7 +7574,20 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
|
|
|
6923
7574
|
ui
|
|
6924
7575
|
}
|
|
6925
7576
|
),
|
|
6926
|
-
(!monitor || monitor.activeTab !== "games" && monitor.activeTab !== "leaderboard") && /* @__PURE__ */ (0,
|
|
7577
|
+
(!monitor || monitor.activeTab !== "games" && monitor.activeTab !== "leaderboard") && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
|
|
7578
|
+
ChatInput,
|
|
7579
|
+
{
|
|
7580
|
+
ref: inputRef,
|
|
7581
|
+
placeholder: ui.placeholder,
|
|
7582
|
+
streaming: state.isStreaming,
|
|
7583
|
+
onSend: handleSend,
|
|
7584
|
+
onStop: stop,
|
|
7585
|
+
stopLabel: ui.stopResponse,
|
|
7586
|
+
sendLabel: ui.sendMessage,
|
|
7587
|
+
disabled: !!tool,
|
|
7588
|
+
onType: fold
|
|
7589
|
+
}
|
|
7590
|
+
)
|
|
6927
7591
|
] });
|
|
6928
7592
|
}
|
|
6929
7593
|
|
|
@@ -7132,15 +7796,15 @@ function toSlipEvent(name, entry) {
|
|
|
7132
7796
|
// src/useHostEvents.ts
|
|
7133
7797
|
function useHostEvents(opts) {
|
|
7134
7798
|
const { host, operatorId, currentUser, logger, isOpen, closePanel, onOpenMatch, openMatchLabel } = opts;
|
|
7135
|
-
const [added, setAdded] = (0,
|
|
7136
|
-
const [settled, setSettled] = (0,
|
|
7137
|
-
const markAdded = (0,
|
|
7799
|
+
const [added, setAdded] = (0, import_react44.useState)({});
|
|
7800
|
+
const [settled, setSettled] = (0, import_react44.useState)({});
|
|
7801
|
+
const markAdded = (0, import_react44.useCallback)((matchOddsId) => {
|
|
7138
7802
|
setAdded((prev) => prev[matchOddsId] ? prev : { ...prev, [matchOddsId]: true });
|
|
7139
7803
|
}, []);
|
|
7140
|
-
const settle = (0,
|
|
7804
|
+
const settle = (0, import_react44.useCallback)((matchOddsId) => {
|
|
7141
7805
|
setSettled((prev) => prev[matchOddsId] ? prev : { ...prev, [matchOddsId]: true });
|
|
7142
7806
|
}, []);
|
|
7143
|
-
const clearMarks = (0,
|
|
7807
|
+
const clearMarks = (0, import_react44.useCallback)((ids) => {
|
|
7144
7808
|
const drop = (prev) => {
|
|
7145
7809
|
if (!ids.some((id) => prev[id])) return prev;
|
|
7146
7810
|
const next = { ...prev };
|
|
@@ -7151,26 +7815,26 @@ function useHostEvents(opts) {
|
|
|
7151
7815
|
setSettled(drop);
|
|
7152
7816
|
}, []);
|
|
7153
7817
|
const hasHost = host !== void 0;
|
|
7154
|
-
const ledger = (0,
|
|
7818
|
+
const ledger = (0, import_react44.useMemo)(
|
|
7155
7819
|
() => hasHost ? createSlipLedger(`ss-w-slip:${operatorId}`) : null,
|
|
7156
7820
|
[hasHost, operatorId]
|
|
7157
7821
|
);
|
|
7158
|
-
const currentUserRef = (0,
|
|
7822
|
+
const currentUserRef = (0, import_react44.useRef)(currentUser);
|
|
7159
7823
|
currentUserRef.current = currentUser;
|
|
7160
|
-
const recordAdd = (0,
|
|
7824
|
+
const recordAdd = (0, import_react44.useCallback)((add) => {
|
|
7161
7825
|
ledger?.record({ id: add.selection.matchOddsId, market: add.market, ...add.pick, user: currentUserRef.current });
|
|
7162
7826
|
}, [ledger]);
|
|
7163
|
-
const slipState = (0,
|
|
7827
|
+
const slipState = (0, import_react44.useMemo)(
|
|
7164
7828
|
() => ({ added, settled, settle, recordAdd }),
|
|
7165
7829
|
[added, settled, settle, recordAdd]
|
|
7166
7830
|
);
|
|
7167
7831
|
const hasOpenMatch = typeof onOpenMatch === "function";
|
|
7168
7832
|
const tracksViews = hasHost || hasOpenMatch;
|
|
7169
|
-
const views = (0,
|
|
7833
|
+
const views = (0, import_react44.useMemo)(
|
|
7170
7834
|
() => tracksViews ? createMatchViews(`ss-w-views:${operatorId}`, { user: currentUser }) : null,
|
|
7171
7835
|
[tracksViews, operatorId, currentUser]
|
|
7172
7836
|
);
|
|
7173
|
-
const sinkRef = (0,
|
|
7837
|
+
const sinkRef = (0, import_react44.useRef)(() => {
|
|
7174
7838
|
});
|
|
7175
7839
|
sinkRef.current = (command) => {
|
|
7176
7840
|
if (command.kind === "matchOpened") {
|
|
@@ -7191,11 +7855,11 @@ function useHostEvents(opts) {
|
|
|
7191
7855
|
logger?.log(toSlipEvent(name, entry));
|
|
7192
7856
|
}
|
|
7193
7857
|
};
|
|
7194
|
-
(0,
|
|
7858
|
+
(0, import_react44.useEffect)(() => {
|
|
7195
7859
|
if (!host) return;
|
|
7196
7860
|
return attachHost(host, (command) => sinkRef.current(command));
|
|
7197
7861
|
}, [host]);
|
|
7198
|
-
(0,
|
|
7862
|
+
(0, import_react44.useEffect)(() => {
|
|
7199
7863
|
if (!isOpen || !views) return;
|
|
7200
7864
|
const held = views.markOpened();
|
|
7201
7865
|
if (held.length === 0) return;
|
|
@@ -7204,7 +7868,7 @@ function useHostEvents(opts) {
|
|
|
7204
7868
|
}
|
|
7205
7869
|
void logger?.flushNow();
|
|
7206
7870
|
}, [isOpen, views, logger]);
|
|
7207
|
-
const openMatchRef = (0,
|
|
7871
|
+
const openMatchRef = (0, import_react44.useRef)(() => {
|
|
7208
7872
|
});
|
|
7209
7873
|
openMatchRef.current = (matchId) => {
|
|
7210
7874
|
if (views?.view(matchId) === "log") {
|
|
@@ -7218,7 +7882,7 @@ function useHostEvents(opts) {
|
|
|
7218
7882
|
}
|
|
7219
7883
|
if (typeof window.matchMedia === "function" && window.matchMedia(FULLSCREEN_QUERY).matches) closePanel();
|
|
7220
7884
|
};
|
|
7221
|
-
const matchLink = (0,
|
|
7885
|
+
const matchLink = (0, import_react44.useMemo)(
|
|
7222
7886
|
() => hasOpenMatch ? { open: (matchId) => openMatchRef.current(matchId), label: openMatchLabel } : null,
|
|
7223
7887
|
[hasOpenMatch, openMatchLabel]
|
|
7224
7888
|
);
|
|
@@ -7286,12 +7950,27 @@ function themeAttrs(ui) {
|
|
|
7286
7950
|
}
|
|
7287
7951
|
|
|
7288
7952
|
// src/useChatStream.ts
|
|
7289
|
-
var
|
|
7953
|
+
var import_react46 = require("react");
|
|
7290
7954
|
var import_widget_sdk4 = require("@sensiblestats/widget-sdk");
|
|
7291
7955
|
|
|
7292
7956
|
// src/reducer.ts
|
|
7293
7957
|
var import_widget_sdk3 = require("@sensiblestats/widget-sdk");
|
|
7294
7958
|
var POPUP_MAX = 160;
|
|
7959
|
+
function markAwaitingLiveUpdate(messages, held = []) {
|
|
7960
|
+
if (!messages.some((m) => m.role === "assistant" && m.marketOdds.some((vm) => vm.live != null))) return messages;
|
|
7961
|
+
const heldFlags = /* @__PURE__ */ new Map();
|
|
7962
|
+
for (const m of held) {
|
|
7963
|
+
if (m.role !== "assistant") continue;
|
|
7964
|
+
for (const vm of m.marketOdds) if (vm.live != null) heldFlags.set(`${m.id}|${vm.id}`, vm.awaitingLiveUpdate === true);
|
|
7965
|
+
}
|
|
7966
|
+
return messages.map((m) => {
|
|
7967
|
+
if (m.role !== "assistant" || !m.marketOdds.some((vm) => vm.live != null)) return m;
|
|
7968
|
+
return {
|
|
7969
|
+
...m,
|
|
7970
|
+
marketOdds: m.marketOdds.map((vm) => vm.live != null ? { ...vm, awaitingLiveUpdate: heldFlags.get(`${m.id}|${vm.id}`) ?? true } : vm)
|
|
7971
|
+
};
|
|
7972
|
+
});
|
|
7973
|
+
}
|
|
7295
7974
|
function initialState(starters) {
|
|
7296
7975
|
return { isOpen: false, messages: [], isStreaming: false, status: null, actionsLoading: false, starters, configuredStarters: starters, error: null, lastUserInput: null, lastDisplayText: null, popupText: null, conversationId: null };
|
|
7297
7976
|
}
|
|
@@ -7383,7 +8062,7 @@ function reducer(state, action) {
|
|
|
7383
8062
|
case "RESTORE":
|
|
7384
8063
|
return {
|
|
7385
8064
|
...state,
|
|
7386
|
-
messages: action.messages,
|
|
8065
|
+
messages: markAwaitingLiveUpdate(action.messages, state.messages),
|
|
7387
8066
|
conversationId: action.conversationId,
|
|
7388
8067
|
isStreaming: false,
|
|
7389
8068
|
status: null,
|
|
@@ -7397,6 +8076,22 @@ function reducer(state, action) {
|
|
|
7397
8076
|
return { ...state, error: action.error, isStreaming: false, status: null, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, done: true })) };
|
|
7398
8077
|
case "STOP":
|
|
7399
8078
|
return { ...state, isStreaming: false, status: null, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, done: true })) };
|
|
8079
|
+
case "UPDATE_LIVE_CARD": {
|
|
8080
|
+
const { matchId, live, mainRows, moreRows, addToSlipEnabled } = action;
|
|
8081
|
+
return {
|
|
8082
|
+
...state,
|
|
8083
|
+
messages: state.messages.map((m) => {
|
|
8084
|
+
if (m.role !== "assistant") return m;
|
|
8085
|
+
if (!m.marketOdds.some((vm) => vm.matchId === matchId && vm.live != null)) return m;
|
|
8086
|
+
return {
|
|
8087
|
+
...m,
|
|
8088
|
+
marketOdds: m.marketOdds.map(
|
|
8089
|
+
(vm) => vm.matchId === matchId && vm.live != null ? { ...vm, live, mainRows, moreRows, addToSlipEnabled, awaitingLiveUpdate: false } : vm
|
|
8090
|
+
)
|
|
8091
|
+
};
|
|
8092
|
+
})
|
|
8093
|
+
};
|
|
8094
|
+
}
|
|
7400
8095
|
case "LIVE_REFRESH":
|
|
7401
8096
|
return {
|
|
7402
8097
|
...state,
|
|
@@ -7415,7 +8110,7 @@ function reducer(state, action) {
|
|
|
7415
8110
|
}
|
|
7416
8111
|
|
|
7417
8112
|
// src/useLiveDashboardPoll.ts
|
|
7418
|
-
var
|
|
8113
|
+
var import_react45 = require("react");
|
|
7419
8114
|
var POLL_INTERVAL_MS = 6e4;
|
|
7420
8115
|
var LEAD_MS = 12e4;
|
|
7421
8116
|
var GIVE_UP_MS = 30 * 6e4;
|
|
@@ -7457,13 +8152,13 @@ function findEarliestWakeupMs(messages) {
|
|
|
7457
8152
|
return earliest;
|
|
7458
8153
|
}
|
|
7459
8154
|
function useLiveDashboardPoll({ messages, dispatch, getLiveDashboard }) {
|
|
7460
|
-
const [, forceUpdate] = (0,
|
|
8155
|
+
const [, forceUpdate] = (0, import_react45.useReducer)((x) => x + 1, 0);
|
|
7461
8156
|
const anchor = findAnchor(messages);
|
|
7462
|
-
const anchorRef = (0,
|
|
8157
|
+
const anchorRef = (0, import_react45.useRef)(anchor);
|
|
7463
8158
|
anchorRef.current = anchor;
|
|
7464
|
-
const inFlightRef = (0,
|
|
8159
|
+
const inFlightRef = (0, import_react45.useRef)(false);
|
|
7465
8160
|
const anchorKey = anchor ? `${anchor.messageId}:${anchor.kind}` : null;
|
|
7466
|
-
(0,
|
|
8161
|
+
(0, import_react45.useEffect)(() => {
|
|
7467
8162
|
if (!anchorKey || !anchorRef.current) return;
|
|
7468
8163
|
const tick = async () => {
|
|
7469
8164
|
if (inFlightRef.current) return;
|
|
@@ -7499,7 +8194,7 @@ function useLiveDashboardPoll({ messages, dispatch, getLiveDashboard }) {
|
|
|
7499
8194
|
}, POLL_INTERVAL_MS);
|
|
7500
8195
|
return () => window.clearInterval(id);
|
|
7501
8196
|
}, [anchorKey, dispatch, getLiveDashboard]);
|
|
7502
|
-
(0,
|
|
8197
|
+
(0, import_react45.useEffect)(() => {
|
|
7503
8198
|
if (anchorKey !== null) return;
|
|
7504
8199
|
const wakeAt = findEarliestWakeupMs(messages);
|
|
7505
8200
|
if (wakeAt === null) return;
|
|
@@ -7564,36 +8259,36 @@ function seedIdCounter(messages) {
|
|
|
7564
8259
|
return max + 1;
|
|
7565
8260
|
}
|
|
7566
8261
|
function useChatStream(conversation, starterTexts, greetingMessage, storageKey) {
|
|
7567
|
-
const boot = (0,
|
|
8262
|
+
const boot = (0, import_react46.useMemo)(() => {
|
|
7568
8263
|
const payload = storageKey ? readConversation(storageKey, Date.now()) : null;
|
|
7569
8264
|
const restored = payload != null && payload !== "expired" && payload !== "corrupt";
|
|
7570
8265
|
const base = initialState(starterTexts.map(chipFromText));
|
|
7571
8266
|
if (restored) {
|
|
7572
|
-
return { state: { ...base, messages: payload.messages, conversationId: payload.conversationId, starters: [] }, restored: true, stale: false };
|
|
8267
|
+
return { state: { ...base, messages: markAwaitingLiveUpdate(payload.messages), conversationId: payload.conversationId, starters: [] }, restored: true, stale: false };
|
|
7573
8268
|
}
|
|
7574
8269
|
return { state: base, restored: false, stale: payload === "expired" || payload === "corrupt" };
|
|
7575
8270
|
}, []);
|
|
7576
|
-
const [state, dispatch] = (0,
|
|
7577
|
-
const idRef = (0,
|
|
7578
|
-
const handleRef = (0,
|
|
7579
|
-
const fallbackTimerRef = (0,
|
|
7580
|
-
const stateRef = (0,
|
|
8271
|
+
const [state, dispatch] = (0, import_react46.useReducer)(reducer, boot.state);
|
|
8272
|
+
const idRef = (0, import_react46.useRef)(seedIdCounter(boot.state.messages));
|
|
8273
|
+
const handleRef = (0, import_react46.useRef)(null);
|
|
8274
|
+
const fallbackTimerRef = (0, import_react46.useRef)(null);
|
|
8275
|
+
const stateRef = (0, import_react46.useRef)(state);
|
|
7581
8276
|
stateRef.current = state;
|
|
7582
|
-
const greetedRef = (0,
|
|
7583
|
-
const hydratedRef = (0,
|
|
7584
|
-
const restoreSeededRef = (0,
|
|
8277
|
+
const greetedRef = (0, import_react46.useRef)(boot.restored);
|
|
8278
|
+
const hydratedRef = (0, import_react46.useRef)(false);
|
|
8279
|
+
const restoreSeededRef = (0, import_react46.useRef)(false);
|
|
7585
8280
|
if (!restoreSeededRef.current) {
|
|
7586
8281
|
restoreSeededRef.current = true;
|
|
7587
8282
|
if (boot.restored && boot.state.conversationId) conversation.conversationId = boot.state.conversationId;
|
|
7588
8283
|
}
|
|
7589
8284
|
const nextId = () => `m${idRef.current++}`;
|
|
7590
|
-
const clearFallbackTimer = (0,
|
|
8285
|
+
const clearFallbackTimer = (0, import_react46.useCallback)(() => {
|
|
7591
8286
|
if (fallbackTimerRef.current != null) {
|
|
7592
8287
|
clearTimeout(fallbackTimerRef.current);
|
|
7593
8288
|
fallbackTimerRef.current = null;
|
|
7594
8289
|
}
|
|
7595
8290
|
}, []);
|
|
7596
|
-
const armFallbackTimer = (0,
|
|
8291
|
+
const armFallbackTimer = (0, import_react46.useCallback)((onFire) => {
|
|
7597
8292
|
clearFallbackTimer();
|
|
7598
8293
|
fallbackTimerRef.current = setTimeout(() => {
|
|
7599
8294
|
fallbackTimerRef.current = null;
|
|
@@ -7602,7 +8297,7 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7602
8297
|
onFire();
|
|
7603
8298
|
}, STREAM_FALLBACK_TIMEOUT_MS);
|
|
7604
8299
|
}, [clearFallbackTimer]);
|
|
7605
|
-
const consume = (0,
|
|
8300
|
+
const consume = (0, import_react46.useCallback)((handle, onError) => {
|
|
7606
8301
|
;
|
|
7607
8302
|
(async () => {
|
|
7608
8303
|
try {
|
|
@@ -7620,7 +8315,7 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7620
8315
|
}
|
|
7621
8316
|
})();
|
|
7622
8317
|
}, [clearFallbackTimer]);
|
|
7623
|
-
const run = (0,
|
|
8318
|
+
const run = (0, import_react46.useCallback)((input, displayText) => {
|
|
7624
8319
|
handleRef.current?.cancel();
|
|
7625
8320
|
clearFallbackTimer();
|
|
7626
8321
|
dispatch({ kind: "USER_SENT", input, userId: nextId(), assistantId: nextId(), displayText });
|
|
@@ -7632,7 +8327,7 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7632
8327
|
});
|
|
7633
8328
|
consume(handle, (err) => dispatch({ kind: "STREAM_ERROR", error: err }));
|
|
7634
8329
|
}, [conversation, consume, clearFallbackTimer, armFallbackTimer]);
|
|
7635
|
-
const send = (0,
|
|
8330
|
+
const send = (0, import_react46.useCallback)((text, opts) => {
|
|
7636
8331
|
const trimmed = text.trim();
|
|
7637
8332
|
if (!trimmed) return;
|
|
7638
8333
|
run({
|
|
@@ -7640,14 +8335,15 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7640
8335
|
actionId: opts?.actionId,
|
|
7641
8336
|
actionRef: opts?.actionRef,
|
|
7642
8337
|
grounding: opts?.grounding,
|
|
7643
|
-
clientCapabilities: [...CLIENT_CAPABILITIES]
|
|
8338
|
+
clientCapabilities: [...CLIENT_CAPABILITIES],
|
|
8339
|
+
...opts?.preset ? { preset: opts.preset } : {}
|
|
7644
8340
|
}, opts?.displayText);
|
|
7645
8341
|
}, [run]);
|
|
7646
|
-
const retry = (0,
|
|
8342
|
+
const retry = (0, import_react46.useCallback)(() => {
|
|
7647
8343
|
const last = stateRef.current.lastUserInput;
|
|
7648
8344
|
if (last) run(last, stateRef.current.lastDisplayText ?? void 0);
|
|
7649
8345
|
}, [run]);
|
|
7650
|
-
const fireGreeting = (0,
|
|
8346
|
+
const fireGreeting = (0, import_react46.useCallback)(() => {
|
|
7651
8347
|
const message = greetingMessage?.trim();
|
|
7652
8348
|
if (!message) return;
|
|
7653
8349
|
try {
|
|
@@ -7662,15 +8358,15 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7662
8358
|
dispatch({ kind: "STOP" });
|
|
7663
8359
|
}
|
|
7664
8360
|
}, [greetingMessage, conversation, consume, clearFallbackTimer, armFallbackTimer]);
|
|
7665
|
-
(0,
|
|
8361
|
+
(0, import_react46.useEffect)(() => {
|
|
7666
8362
|
if (greetedRef.current) return;
|
|
7667
8363
|
greetedRef.current = true;
|
|
7668
8364
|
fireGreeting();
|
|
7669
8365
|
}, [fireGreeting]);
|
|
7670
|
-
(0,
|
|
8366
|
+
(0, import_react46.useEffect)(() => {
|
|
7671
8367
|
if (boot.stale && storageKey) clearConversation(storageKey);
|
|
7672
8368
|
}, []);
|
|
7673
|
-
(0,
|
|
8369
|
+
(0, import_react46.useEffect)(() => {
|
|
7674
8370
|
if (!storageKey || state.isStreaming) return;
|
|
7675
8371
|
if (hydratedRef.current) {
|
|
7676
8372
|
hydratedRef.current = false;
|
|
@@ -7687,7 +8383,7 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7687
8383
|
if (persistable.length === 0) return;
|
|
7688
8384
|
writeConversation(storageKey, state.conversationId, persistable, Date.now());
|
|
7689
8385
|
}, [storageKey, state.isStreaming, state.messages, state.conversationId]);
|
|
7690
|
-
(0,
|
|
8386
|
+
(0, import_react46.useEffect)(() => {
|
|
7691
8387
|
if (!storageKey) return;
|
|
7692
8388
|
const onStorage = (e) => {
|
|
7693
8389
|
if (e.key !== storageKey || e.storageArea !== window.localStorage) return;
|
|
@@ -7711,25 +8407,25 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7711
8407
|
window.addEventListener("storage", onStorage);
|
|
7712
8408
|
return () => window.removeEventListener("storage", onStorage);
|
|
7713
8409
|
}, [storageKey, conversation, fireGreeting]);
|
|
7714
|
-
const open = (0,
|
|
7715
|
-
const close = (0,
|
|
8410
|
+
const open = (0, import_react46.useCallback)(() => dispatch({ kind: "OPEN" }), []);
|
|
8411
|
+
const close = (0, import_react46.useCallback)(() => {
|
|
7716
8412
|
handleRef.current?.cancel();
|
|
7717
8413
|
handleRef.current = null;
|
|
7718
8414
|
clearFallbackTimer();
|
|
7719
8415
|
dispatch({ kind: "CLOSE" });
|
|
7720
8416
|
}, [clearFallbackTimer]);
|
|
7721
|
-
const stop = (0,
|
|
8417
|
+
const stop = (0, import_react46.useCallback)(() => {
|
|
7722
8418
|
handleRef.current?.cancel();
|
|
7723
8419
|
handleRef.current = null;
|
|
7724
8420
|
clearFallbackTimer();
|
|
7725
8421
|
dispatch({ kind: "STOP" });
|
|
7726
8422
|
}, [clearFallbackTimer]);
|
|
7727
|
-
const toggle = (0,
|
|
8423
|
+
const toggle = (0, import_react46.useCallback)(() => {
|
|
7728
8424
|
if (stateRef.current.isOpen) close();
|
|
7729
8425
|
else open();
|
|
7730
8426
|
}, [close, open]);
|
|
7731
|
-
const dismissPopup = (0,
|
|
7732
|
-
const reset = (0,
|
|
8427
|
+
const dismissPopup = (0, import_react46.useCallback)(() => dispatch({ kind: "DISMISS_POPUP" }), []);
|
|
8428
|
+
const reset = (0, import_react46.useCallback)(() => {
|
|
7733
8429
|
handleRef.current?.cancel();
|
|
7734
8430
|
handleRef.current = null;
|
|
7735
8431
|
clearFallbackTimer();
|
|
@@ -7740,18 +8436,18 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
7740
8436
|
greetedRef.current = true;
|
|
7741
8437
|
fireGreeting();
|
|
7742
8438
|
}, [storageKey, conversation, clearFallbackTimer, fireGreeting]);
|
|
7743
|
-
const getLiveDashboard = (0,
|
|
8439
|
+
const getLiveDashboard = (0, import_react46.useCallback)(() => conversation.getLiveDashboard(), [conversation]);
|
|
7744
8440
|
useLiveDashboardPoll({ messages: state.messages, dispatch, getLiveDashboard });
|
|
7745
|
-
return { state, open, close, stop, toggle, send, retry, dismissPopup, reset };
|
|
8441
|
+
return { state, open, close, stop, toggle, send, retry, dismissPopup, reset, _dispatch: dispatch };
|
|
7746
8442
|
}
|
|
7747
8443
|
|
|
7748
8444
|
// src/useLiveMonitor.ts
|
|
7749
|
-
var
|
|
8445
|
+
var import_react48 = require("react");
|
|
7750
8446
|
|
|
7751
8447
|
// src/useTabs.ts
|
|
7752
|
-
var
|
|
8448
|
+
var import_react47 = require("react");
|
|
7753
8449
|
function useTabs(initial = "chat") {
|
|
7754
|
-
const [activeTab, setActiveTab] = (0,
|
|
8450
|
+
const [activeTab, setActiveTab] = (0, import_react47.useState)(initial);
|
|
7755
8451
|
return { activeTab, setActiveTab };
|
|
7756
8452
|
}
|
|
7757
8453
|
|
|
@@ -7759,6 +8455,7 @@ function useTabs(initial = "chat") {
|
|
|
7759
8455
|
var GOAL_OR_RED = /* @__PURE__ */ new Set(["goal", "red"]);
|
|
7760
8456
|
var MIN_POLL_MS = 5e3;
|
|
7761
8457
|
var DEFAULT_POLL_MS = 6e4;
|
|
8458
|
+
var LIVE_WATCH_POLL_MS = 12e3;
|
|
7762
8459
|
function eventIdentity(e) {
|
|
7763
8460
|
return `${e.type}:${e.side}:${e.minuteLabel}:${e.player ?? ""}`;
|
|
7764
8461
|
}
|
|
@@ -7775,36 +8472,38 @@ function phaseForPin(snapshot, pin) {
|
|
|
7775
8472
|
return snapshot.markets.find((mk) => mk.key === pin.key)?.phase;
|
|
7776
8473
|
}
|
|
7777
8474
|
function useLiveMonitor(opts) {
|
|
7778
|
-
const sessionIdRef = (0,
|
|
7779
|
-
const [pins, setPins] = (0,
|
|
7780
|
-
const [store] = (0,
|
|
7781
|
-
const [enabled, setEnabled] = (0,
|
|
7782
|
-
const [snapshot, setSnapshotState] = (0,
|
|
8475
|
+
const sessionIdRef = (0, import_react48.useRef)(crypto.randomUUID());
|
|
8476
|
+
const [pins, setPins] = (0, import_react48.useState)([]);
|
|
8477
|
+
const [store] = (0, import_react48.useState)(() => createPinStore(opts.storageKey, sessionIdRef.current, setPins));
|
|
8478
|
+
const [enabled, setEnabled] = (0, import_react48.useState)(true);
|
|
8479
|
+
const [snapshot, setSnapshotState] = (0, import_react48.useState)(null);
|
|
7783
8480
|
const { activeTab, setActiveTab: setActiveTabState } = useTabs("chat");
|
|
7784
|
-
const [chatDot, setChatDot] = (0,
|
|
7785
|
-
const [matchesDot, setMatchesDot] = (0,
|
|
7786
|
-
const [oddsDot, setOddsDot] = (0,
|
|
7787
|
-
const [todayDot, setTodayDot] = (0,
|
|
7788
|
-
const [lastViewedPrices, setLastViewedPrices] = (0,
|
|
7789
|
-
const [openMarket, setOpenMarket] = (0,
|
|
7790
|
-
const [marketOdds, setMarketOdds] = (0,
|
|
7791
|
-
const pinsRef = (0,
|
|
8481
|
+
const [chatDot, setChatDot] = (0, import_react48.useState)(false);
|
|
8482
|
+
const [matchesDot, setMatchesDot] = (0, import_react48.useState)(false);
|
|
8483
|
+
const [oddsDot, setOddsDot] = (0, import_react48.useState)(false);
|
|
8484
|
+
const [todayDot, setTodayDot] = (0, import_react48.useState)(false);
|
|
8485
|
+
const [lastViewedPrices, setLastViewedPrices] = (0, import_react48.useState)({});
|
|
8486
|
+
const [openMarket, setOpenMarket] = (0, import_react48.useState)({});
|
|
8487
|
+
const [marketOdds, setMarketOdds] = (0, import_react48.useState)({});
|
|
8488
|
+
const pinsRef = (0, import_react48.useRef)(pins);
|
|
7792
8489
|
pinsRef.current = pins;
|
|
7793
|
-
const
|
|
8490
|
+
const watchesRef = (0, import_react48.useRef)(opts.watches ?? []);
|
|
8491
|
+
watchesRef.current = opts.watches ?? [];
|
|
8492
|
+
const activeTabRef = (0, import_react48.useRef)(activeTab);
|
|
7794
8493
|
activeTabRef.current = activeTab;
|
|
7795
|
-
const snapshotRef = (0,
|
|
8494
|
+
const snapshotRef = (0, import_react48.useRef)(snapshot);
|
|
7796
8495
|
snapshotRef.current = snapshot;
|
|
7797
|
-
const lastViewedPricesRef = (0,
|
|
8496
|
+
const lastViewedPricesRef = (0, import_react48.useRef)(lastViewedPrices);
|
|
7798
8497
|
lastViewedPricesRef.current = lastViewedPrices;
|
|
7799
|
-
const openMarketRef = (0,
|
|
8498
|
+
const openMarketRef = (0, import_react48.useRef)(openMarket);
|
|
7800
8499
|
openMarketRef.current = openMarket;
|
|
7801
|
-
const marketOddsRef = (0,
|
|
8500
|
+
const marketOddsRef = (0, import_react48.useRef)(marketOdds);
|
|
7802
8501
|
marketOddsRef.current = marketOdds;
|
|
7803
8502
|
const hasToday = opts.hasToday ?? false;
|
|
7804
8503
|
const marketKey = (matchId, dev) => `${matchId}:${dev}`;
|
|
7805
|
-
const getMarketOddsRef = (0,
|
|
8504
|
+
const getMarketOddsRef = (0, import_react48.useRef)(opts.getMarketOdds);
|
|
7806
8505
|
getMarketOddsRef.current = opts.getMarketOdds;
|
|
7807
|
-
const fetchMarket = (0,
|
|
8506
|
+
const fetchMarket = (0, import_react48.useCallback)((matchId, dev) => {
|
|
7808
8507
|
const key = marketKey(matchId, dev);
|
|
7809
8508
|
const existing = marketOddsRef.current[key];
|
|
7810
8509
|
if (existing?.loading) return;
|
|
@@ -7823,7 +8522,7 @@ function useLiveMonitor(opts) {
|
|
|
7823
8522
|
setMarketOdds((prev) => ({ ...prev, [key]: entry }));
|
|
7824
8523
|
});
|
|
7825
8524
|
}, []);
|
|
7826
|
-
const toggleMarket = (0,
|
|
8525
|
+
const toggleMarket = (0, import_react48.useCallback)((matchId, dev) => {
|
|
7827
8526
|
const key = marketKey(matchId, dev);
|
|
7828
8527
|
const wasOpen = openMarketRef.current[matchId] === dev;
|
|
7829
8528
|
const nextValue = wasOpen ? null : dev;
|
|
@@ -7834,10 +8533,10 @@ function useLiveMonitor(opts) {
|
|
|
7834
8533
|
fetchMarket(matchId, dev);
|
|
7835
8534
|
}
|
|
7836
8535
|
}, [fetchMarket]);
|
|
7837
|
-
const retryMarket = (0,
|
|
8536
|
+
const retryMarket = (0, import_react48.useCallback)((matchId, dev) => {
|
|
7838
8537
|
fetchMarket(matchId, dev);
|
|
7839
8538
|
}, [fetchMarket]);
|
|
7840
|
-
const applySnapshot = (0,
|
|
8539
|
+
const applySnapshot = (0, import_react48.useCallback)((next) => {
|
|
7841
8540
|
if (next === null) {
|
|
7842
8541
|
setEnabled(false);
|
|
7843
8542
|
return;
|
|
@@ -7900,17 +8599,18 @@ function useLiveMonitor(opts) {
|
|
|
7900
8599
|
snapshotRef.current = next;
|
|
7901
8600
|
setSnapshotState(next);
|
|
7902
8601
|
}, [store]);
|
|
7903
|
-
const inFlightRef = (0,
|
|
7904
|
-
const timerRef = (0,
|
|
7905
|
-
const disabledRef = (0,
|
|
7906
|
-
const mountedRef = (0,
|
|
7907
|
-
const getLiveMonitorRef = (0,
|
|
8602
|
+
const inFlightRef = (0, import_react48.useRef)(false);
|
|
8603
|
+
const timerRef = (0, import_react48.useRef)(void 0);
|
|
8604
|
+
const disabledRef = (0, import_react48.useRef)(false);
|
|
8605
|
+
const mountedRef = (0, import_react48.useRef)(false);
|
|
8606
|
+
const getLiveMonitorRef = (0, import_react48.useRef)(opts.getLiveMonitor);
|
|
7908
8607
|
getLiveMonitorRef.current = opts.getLiveMonitor;
|
|
7909
|
-
const tick = (0,
|
|
7910
|
-
|
|
8608
|
+
const tick = (0, import_react48.useCallback)(async () => {
|
|
8609
|
+
const allPins = [...pinsRef.current, ...watchesRef.current];
|
|
8610
|
+
if (inFlightRef.current || disabledRef.current || allPins.length === 0) return;
|
|
7911
8611
|
inFlightRef.current = true;
|
|
7912
8612
|
try {
|
|
7913
|
-
const snap = await getLiveMonitorRef.current(
|
|
8613
|
+
const snap = await getLiveMonitorRef.current(allPins);
|
|
7914
8614
|
if (!mountedRef.current) return;
|
|
7915
8615
|
if (snap === null) {
|
|
7916
8616
|
disabledRef.current = true;
|
|
@@ -7929,44 +8629,53 @@ function useLiveMonitor(opts) {
|
|
|
7929
8629
|
scheduleRef.current();
|
|
7930
8630
|
}
|
|
7931
8631
|
}, [applySnapshot, fetchMarket]);
|
|
7932
|
-
const scheduleRef = (0,
|
|
8632
|
+
const scheduleRef = (0, import_react48.useRef)(() => {
|
|
7933
8633
|
});
|
|
7934
8634
|
scheduleRef.current = () => {
|
|
7935
8635
|
window.clearTimeout(timerRef.current);
|
|
7936
|
-
|
|
7937
|
-
|
|
8636
|
+
const hasWork = pinsRef.current.length > 0 || watchesRef.current.length > 0;
|
|
8637
|
+
if (!mountedRef.current || disabledRef.current || !hasWork || document.visibilityState === "hidden") return;
|
|
8638
|
+
const fallbackMs = watchesRef.current.length > 0 ? LIVE_WATCH_POLL_MS : DEFAULT_POLL_MS;
|
|
8639
|
+
const hinted = snapshotRef.current ? snapshotRef.current.nextPollSeconds * 1e3 : fallbackMs;
|
|
7938
8640
|
timerRef.current = window.setTimeout(() => void tick(), Math.max(MIN_POLL_MS, hinted));
|
|
7939
8641
|
};
|
|
7940
|
-
(0,
|
|
8642
|
+
(0, import_react48.useEffect)(() => {
|
|
7941
8643
|
mountedRef.current = true;
|
|
7942
8644
|
return () => {
|
|
7943
8645
|
mountedRef.current = false;
|
|
7944
8646
|
};
|
|
7945
8647
|
}, []);
|
|
7946
|
-
const prevPinsLengthRef = (0,
|
|
7947
|
-
(0,
|
|
7948
|
-
const wasEmpty = prevPinsLengthRef.current === 0;
|
|
8648
|
+
const prevPinsLengthRef = (0, import_react48.useRef)(0);
|
|
8649
|
+
(0, import_react48.useEffect)(() => {
|
|
8650
|
+
const wasEmpty = prevPinsLengthRef.current === 0 && watchesRef.current.length === 0;
|
|
7949
8651
|
prevPinsLengthRef.current = pins.length;
|
|
7950
|
-
if (pins.length === 0) {
|
|
8652
|
+
if (pins.length === 0 && watchesRef.current.length === 0) {
|
|
7951
8653
|
window.clearTimeout(timerRef.current);
|
|
7952
8654
|
return;
|
|
7953
8655
|
}
|
|
7954
8656
|
if (wasEmpty) void tick();
|
|
7955
8657
|
}, [pins.length, tick]);
|
|
7956
|
-
(0,
|
|
8658
|
+
const prevWatchesLengthRef = (0, import_react48.useRef)(0);
|
|
8659
|
+
const watchesLength = opts.watches?.length ?? 0;
|
|
8660
|
+
(0, import_react48.useEffect)(() => {
|
|
8661
|
+
const wasEmpty = prevWatchesLengthRef.current === 0 && pinsRef.current.length === 0;
|
|
8662
|
+
prevWatchesLengthRef.current = watchesLength;
|
|
8663
|
+
if (wasEmpty && watchesLength > 0) void tick();
|
|
8664
|
+
}, [watchesLength, tick]);
|
|
8665
|
+
(0, import_react48.useEffect)(() => {
|
|
7957
8666
|
const matchPins = pins.filter((p) => p.kind === "match").length;
|
|
7958
8667
|
const marketPins = pins.length - matchPins;
|
|
7959
8668
|
if (activeTab === "following" && matchPins === 0) setActiveTabState(hasToday ? "today" : "chat");
|
|
7960
8669
|
if (activeTab === "odds" && marketPins === 0) setActiveTabState(hasToday ? "today" : "chat");
|
|
7961
8670
|
if (activeTab === "today" && !hasToday) setActiveTabState("chat");
|
|
7962
8671
|
}, [pins, activeTab, hasToday]);
|
|
7963
|
-
const prevIsOpenRef = (0,
|
|
7964
|
-
(0,
|
|
8672
|
+
const prevIsOpenRef = (0, import_react48.useRef)(false);
|
|
8673
|
+
(0, import_react48.useEffect)(() => {
|
|
7965
8674
|
const wasClosed = !prevIsOpenRef.current;
|
|
7966
8675
|
prevIsOpenRef.current = opts.isOpen;
|
|
7967
8676
|
if (wasClosed && opts.isOpen) void tick();
|
|
7968
8677
|
}, [opts.isOpen, tick]);
|
|
7969
|
-
(0,
|
|
8678
|
+
(0, import_react48.useEffect)(() => {
|
|
7970
8679
|
const onVisibility = () => {
|
|
7971
8680
|
if (document.visibilityState === "hidden") window.clearTimeout(timerRef.current);
|
|
7972
8681
|
else void tick();
|
|
@@ -7977,7 +8686,7 @@ function useLiveMonitor(opts) {
|
|
|
7977
8686
|
window.clearTimeout(timerRef.current);
|
|
7978
8687
|
};
|
|
7979
8688
|
}, [tick]);
|
|
7980
|
-
const setActiveTab = (0,
|
|
8689
|
+
const setActiveTab = (0, import_react48.useCallback)((tab) => {
|
|
7981
8690
|
setActiveTabState(tab);
|
|
7982
8691
|
if (tab === "chat") setChatDot(false);
|
|
7983
8692
|
if (tab === "today") setTodayDot(false);
|
|
@@ -7998,14 +8707,14 @@ function useLiveMonitor(opts) {
|
|
|
7998
8707
|
void tick();
|
|
7999
8708
|
}
|
|
8000
8709
|
}, [tick]);
|
|
8001
|
-
const togglePin = (0,
|
|
8710
|
+
const togglePin = (0, import_react48.useCallback)((pin) => {
|
|
8002
8711
|
const result = store.toggle(pin);
|
|
8003
8712
|
pinsRef.current = store.pins;
|
|
8004
8713
|
return result;
|
|
8005
8714
|
}, [store]);
|
|
8006
|
-
const isPinned = (0,
|
|
8007
|
-
const markChatActivity = (0,
|
|
8008
|
-
const markTodayActivity = (0,
|
|
8715
|
+
const isPinned = (0, import_react48.useCallback)((key) => pinsRef.current.some((p) => p.key === key), []);
|
|
8716
|
+
const markChatActivity = (0, import_react48.useCallback)(() => setChatDot(true), []);
|
|
8717
|
+
const markTodayActivity = (0, import_react48.useCallback)(() => {
|
|
8009
8718
|
if (activeTabRef.current !== "today") setTodayDot(true);
|
|
8010
8719
|
}, []);
|
|
8011
8720
|
return {
|
|
@@ -8032,7 +8741,7 @@ function useLiveMonitor(opts) {
|
|
|
8032
8741
|
}
|
|
8033
8742
|
|
|
8034
8743
|
// src/useMatchDiscovery.ts
|
|
8035
|
-
var
|
|
8744
|
+
var import_react49 = require("react");
|
|
8036
8745
|
|
|
8037
8746
|
// src/scheduleCache.ts
|
|
8038
8747
|
var PREFIX = "ss-w-schedule:";
|
|
@@ -8120,27 +8829,27 @@ function localDateOf(now) {
|
|
|
8120
8829
|
return `${y}-${m}-${d}`;
|
|
8121
8830
|
}
|
|
8122
8831
|
function useMatchDiscovery(opts) {
|
|
8123
|
-
const [groups, setGroups] = (0,
|
|
8124
|
-
const [states, setStates] = (0,
|
|
8125
|
-
const [failures, setFailures] = (0,
|
|
8126
|
-
const [expanded, setExpanded] = (0,
|
|
8127
|
-
const [touched, setTouched] = (0,
|
|
8128
|
-
const groupsRef = (0,
|
|
8832
|
+
const [groups, setGroups] = (0, import_react49.useState)([]);
|
|
8833
|
+
const [states, setStates] = (0, import_react49.useState)({});
|
|
8834
|
+
const [failures, setFailures] = (0, import_react49.useState)(0);
|
|
8835
|
+
const [expanded, setExpanded] = (0, import_react49.useState)({});
|
|
8836
|
+
const [touched, setTouched] = (0, import_react49.useState)({});
|
|
8837
|
+
const groupsRef = (0, import_react49.useRef)(groups);
|
|
8129
8838
|
groupsRef.current = groups;
|
|
8130
|
-
const statesRef = (0,
|
|
8839
|
+
const statesRef = (0, import_react49.useRef)(states);
|
|
8131
8840
|
statesRef.current = states;
|
|
8132
|
-
const getStatesRef = (0,
|
|
8841
|
+
const getStatesRef = (0, import_react49.useRef)(opts.getStates);
|
|
8133
8842
|
getStatesRef.current = opts.getStates;
|
|
8134
8843
|
const pinnedKey = opts.pinnedMatchIds.join(",");
|
|
8135
|
-
const [localDate, setLocalDate] = (0,
|
|
8136
|
-
(0,
|
|
8844
|
+
const [localDate, setLocalDate] = (0, import_react49.useState)(() => localDateOf(/* @__PURE__ */ new Date()));
|
|
8845
|
+
(0, import_react49.useEffect)(() => {
|
|
8137
8846
|
const id = setInterval(() => {
|
|
8138
8847
|
const next = localDateOf(/* @__PURE__ */ new Date());
|
|
8139
8848
|
setLocalDate((prev) => prev === next ? prev : next);
|
|
8140
8849
|
}, ROLLOVER_CHECK_MS);
|
|
8141
8850
|
return () => clearInterval(id);
|
|
8142
8851
|
}, []);
|
|
8143
|
-
(0,
|
|
8852
|
+
(0, import_react49.useEffect)(() => {
|
|
8144
8853
|
let cancelled = false;
|
|
8145
8854
|
const utcOffsetMinutes = -(/* @__PURE__ */ new Date()).getTimezoneOffset();
|
|
8146
8855
|
const cached = readSchedule(opts.operatorId, localDate, Date.now());
|
|
@@ -8165,7 +8874,7 @@ function useMatchDiscovery(opts) {
|
|
|
8165
8874
|
cancelled = true;
|
|
8166
8875
|
};
|
|
8167
8876
|
}, [opts.operatorId, localDate]);
|
|
8168
|
-
(0,
|
|
8877
|
+
(0, import_react49.useEffect)(() => {
|
|
8169
8878
|
setExpanded((prev) => {
|
|
8170
8879
|
const next = {};
|
|
8171
8880
|
for (const group of groups) {
|
|
@@ -8176,7 +8885,7 @@ function useMatchDiscovery(opts) {
|
|
|
8176
8885
|
return next;
|
|
8177
8886
|
});
|
|
8178
8887
|
}, [groups, pinnedKey, touched]);
|
|
8179
|
-
const tick = (0,
|
|
8888
|
+
const tick = (0, import_react49.useCallback)(async () => {
|
|
8180
8889
|
const ids = dueMatchIds(groupsRef.current, statesRef.current, Date.now());
|
|
8181
8890
|
if (ids.length === 0) return;
|
|
8182
8891
|
try {
|
|
@@ -8192,7 +8901,7 @@ function useMatchDiscovery(opts) {
|
|
|
8192
8901
|
setFailures((n) => n + 1);
|
|
8193
8902
|
}
|
|
8194
8903
|
}, []);
|
|
8195
|
-
(0,
|
|
8904
|
+
(0, import_react49.useEffect)(() => {
|
|
8196
8905
|
if (!opts.isOpen) return;
|
|
8197
8906
|
void tick();
|
|
8198
8907
|
const id = setInterval(() => {
|
|
@@ -8201,11 +8910,11 @@ function useMatchDiscovery(opts) {
|
|
|
8201
8910
|
return () => clearInterval(id);
|
|
8202
8911
|
}, [opts.isOpen, tick, groups]);
|
|
8203
8912
|
const stale = failures >= STALE_AFTER_FAILURES;
|
|
8204
|
-
const liveCount = (0,
|
|
8913
|
+
const liveCount = (0, import_react49.useMemo)(
|
|
8205
8914
|
() => stale ? 0 : Object.values(states).filter((s) => s.phase === "live").length,
|
|
8206
8915
|
[states, stale]
|
|
8207
8916
|
);
|
|
8208
|
-
const toggleGroup = (0,
|
|
8917
|
+
const toggleGroup = (0, import_react49.useCallback)((competitionId) => {
|
|
8209
8918
|
setTouched((prev) => ({ ...prev, [competitionId]: true }));
|
|
8210
8919
|
setExpanded((prev) => ({ ...prev, [competitionId]: !prev[competitionId] }));
|
|
8211
8920
|
}, []);
|
|
@@ -8213,25 +8922,25 @@ function useMatchDiscovery(opts) {
|
|
|
8213
8922
|
}
|
|
8214
8923
|
|
|
8215
8924
|
// src/useGames.ts
|
|
8216
|
-
var
|
|
8925
|
+
var import_react50 = require("react");
|
|
8217
8926
|
var GAMES_POLL_MS = 6e4;
|
|
8218
8927
|
var DEFAULT_TZ = "Europe/London";
|
|
8219
8928
|
function computeGameDay(timezone) {
|
|
8220
8929
|
return new Intl.DateTimeFormat("en-CA", { timeZone: timezone }).format(/* @__PURE__ */ new Date());
|
|
8221
8930
|
}
|
|
8222
8931
|
function useGames(conversation, { activeTab, operatorTimezone }) {
|
|
8223
|
-
const [enabled, setEnabled] = (0,
|
|
8224
|
-
const [games, setGames] = (0,
|
|
8225
|
-
const [loading, setLoading] = (0,
|
|
8226
|
-
const [refreshCount, setRefreshCount] = (0,
|
|
8227
|
-
const inFlightRef = (0,
|
|
8228
|
-
const hardOffRef = (0,
|
|
8229
|
-
const conversationRef = (0,
|
|
8932
|
+
const [enabled, setEnabled] = (0, import_react50.useState)(false);
|
|
8933
|
+
const [games, setGames] = (0, import_react50.useState)([]);
|
|
8934
|
+
const [loading, setLoading] = (0, import_react50.useState)(false);
|
|
8935
|
+
const [refreshCount, setRefreshCount] = (0, import_react50.useState)(0);
|
|
8936
|
+
const inFlightRef = (0, import_react50.useRef)(false);
|
|
8937
|
+
const hardOffRef = (0, import_react50.useRef)(false);
|
|
8938
|
+
const conversationRef = (0, import_react50.useRef)(conversation);
|
|
8230
8939
|
conversationRef.current = conversation;
|
|
8231
|
-
const tzRef = (0,
|
|
8940
|
+
const tzRef = (0, import_react50.useRef)(operatorTimezone ?? DEFAULT_TZ);
|
|
8232
8941
|
tzRef.current = operatorTimezone ?? DEFAULT_TZ;
|
|
8233
8942
|
const ready = conversation != null;
|
|
8234
|
-
(0,
|
|
8943
|
+
(0, import_react50.useEffect)(() => {
|
|
8235
8944
|
if (hardOffRef.current) return;
|
|
8236
8945
|
let active = true;
|
|
8237
8946
|
const doFetch = async () => {
|
|
@@ -8274,22 +8983,22 @@ function useGames(conversation, { activeTab, operatorTimezone }) {
|
|
|
8274
8983
|
if (intervalId !== void 0) window.clearInterval(intervalId);
|
|
8275
8984
|
};
|
|
8276
8985
|
}, [activeTab, refreshCount, ready]);
|
|
8277
|
-
const refresh = (0,
|
|
8986
|
+
const refresh = (0, import_react50.useCallback)(() => {
|
|
8278
8987
|
setRefreshCount((c) => c + 1);
|
|
8279
8988
|
}, []);
|
|
8280
8989
|
return { enabled, games, loading, refresh };
|
|
8281
8990
|
}
|
|
8282
8991
|
|
|
8283
8992
|
// src/useLeaderboard.ts
|
|
8284
|
-
var
|
|
8993
|
+
var import_react51 = require("react");
|
|
8285
8994
|
function useLeaderboard(conversation, { activeTab }) {
|
|
8286
|
-
const [data, setData] = (0,
|
|
8287
|
-
const [loading, setLoading] = (0,
|
|
8288
|
-
const [refreshCount, setRefreshCount] = (0,
|
|
8289
|
-
const inFlightRef = (0,
|
|
8290
|
-
const conversationRef = (0,
|
|
8995
|
+
const [data, setData] = (0, import_react51.useState)(null);
|
|
8996
|
+
const [loading, setLoading] = (0, import_react51.useState)(false);
|
|
8997
|
+
const [refreshCount, setRefreshCount] = (0, import_react51.useState)(0);
|
|
8998
|
+
const inFlightRef = (0, import_react51.useRef)(false);
|
|
8999
|
+
const conversationRef = (0, import_react51.useRef)(conversation);
|
|
8291
9000
|
conversationRef.current = conversation;
|
|
8292
|
-
(0,
|
|
9001
|
+
(0, import_react51.useEffect)(() => {
|
|
8293
9002
|
if (activeTab !== "leaderboard" && activeTab !== "games") return;
|
|
8294
9003
|
let active = true;
|
|
8295
9004
|
const doFetch = async () => {
|
|
@@ -8312,18 +9021,65 @@ function useLeaderboard(conversation, { activeTab }) {
|
|
|
8312
9021
|
inFlightRef.current = false;
|
|
8313
9022
|
};
|
|
8314
9023
|
}, [activeTab, refreshCount]);
|
|
8315
|
-
const refresh = (0,
|
|
9024
|
+
const refresh = (0, import_react51.useCallback)(() => {
|
|
8316
9025
|
setRefreshCount((c) => c + 1);
|
|
8317
9026
|
}, []);
|
|
8318
9027
|
return { data, loading, refresh };
|
|
8319
9028
|
}
|
|
8320
9029
|
|
|
9030
|
+
// src/usePresetGrid.ts
|
|
9031
|
+
var import_react52 = require("react");
|
|
9032
|
+
var GRID_MAX_AGE_MS = 6e5;
|
|
9033
|
+
function usePresetGrid({ getPricePresets, isOpen, locale, now = Date.now }) {
|
|
9034
|
+
const [grid, setGrid] = (0, import_react52.useState)(null);
|
|
9035
|
+
const keptRef = (0, import_react52.useRef)(null);
|
|
9036
|
+
const localeRef = (0, import_react52.useRef)(locale);
|
|
9037
|
+
const inFlightRef = (0, import_react52.useRef)(false);
|
|
9038
|
+
const mountedRef = (0, import_react52.useRef)(true);
|
|
9039
|
+
const getRef = (0, import_react52.useRef)(getPricePresets);
|
|
9040
|
+
getRef.current = getPricePresets;
|
|
9041
|
+
const nowRef = (0, import_react52.useRef)(now);
|
|
9042
|
+
nowRef.current = now;
|
|
9043
|
+
(0, import_react52.useEffect)(() => {
|
|
9044
|
+
mountedRef.current = true;
|
|
9045
|
+
return () => {
|
|
9046
|
+
mountedRef.current = false;
|
|
9047
|
+
};
|
|
9048
|
+
}, []);
|
|
9049
|
+
(0, import_react52.useEffect)(() => {
|
|
9050
|
+
if (localeRef.current !== locale) {
|
|
9051
|
+
localeRef.current = locale;
|
|
9052
|
+
keptRef.current = null;
|
|
9053
|
+
setGrid(null);
|
|
9054
|
+
}
|
|
9055
|
+
if (!isOpen || inFlightRef.current) return;
|
|
9056
|
+
const kept = keptRef.current;
|
|
9057
|
+
const age = kept ? nowRef.current() - Date.parse(kept.builtAtUtc) : Number.NaN;
|
|
9058
|
+
if (age <= GRID_MAX_AGE_MS) return;
|
|
9059
|
+
inFlightRef.current = true;
|
|
9060
|
+
Promise.resolve().then(() => getRef.current(locale)).then((body) => {
|
|
9061
|
+
if (body === null) {
|
|
9062
|
+
keptRef.current = null;
|
|
9063
|
+
if (mountedRef.current) setGrid(null);
|
|
9064
|
+
return;
|
|
9065
|
+
}
|
|
9066
|
+
if (!isPresetGrid(body)) return;
|
|
9067
|
+
keptRef.current = body;
|
|
9068
|
+
if (mountedRef.current) setGrid(body);
|
|
9069
|
+
}).catch(() => {
|
|
9070
|
+
}).finally(() => {
|
|
9071
|
+
inFlightRef.current = false;
|
|
9072
|
+
});
|
|
9073
|
+
}, [isOpen, locale]);
|
|
9074
|
+
return grid;
|
|
9075
|
+
}
|
|
9076
|
+
|
|
8321
9077
|
// src/popupTelemetry.ts
|
|
8322
|
-
var
|
|
9078
|
+
var import_react53 = require("react");
|
|
8323
9079
|
function usePopupTelemetry(log, isOpen, popupText, dismissPopup) {
|
|
8324
|
-
const shownRef = (0,
|
|
8325
|
-
const closedTextRef = (0,
|
|
8326
|
-
(0,
|
|
9080
|
+
const shownRef = (0, import_react53.useRef)(/* @__PURE__ */ new Set());
|
|
9081
|
+
const closedTextRef = (0, import_react53.useRef)(null);
|
|
9082
|
+
(0, import_react53.useEffect)(() => {
|
|
8327
9083
|
if (isOpen) {
|
|
8328
9084
|
if (closedTextRef.current) log?.log({ name: "popup_opened" });
|
|
8329
9085
|
closedTextRef.current = null;
|
|
@@ -8335,65 +9091,65 @@ function usePopupTelemetry(log, isOpen, popupText, dismissPopup) {
|
|
|
8335
9091
|
log?.log({ name: "popup_shown" });
|
|
8336
9092
|
}
|
|
8337
9093
|
}, [log, isOpen, popupText]);
|
|
8338
|
-
return (0,
|
|
9094
|
+
return (0, import_react53.useCallback)(() => {
|
|
8339
9095
|
log?.log({ name: "popup_dismissed" });
|
|
8340
9096
|
dismissPopup();
|
|
8341
9097
|
}, [log, dismissPopup]);
|
|
8342
9098
|
}
|
|
8343
9099
|
|
|
8344
9100
|
// src/components/ChatFab.tsx
|
|
8345
|
-
var
|
|
9101
|
+
var import_react54 = require("react");
|
|
8346
9102
|
|
|
8347
9103
|
// src/components/GreetingBubble.tsx
|
|
8348
|
-
var
|
|
9104
|
+
var import_jsx_runtime91 = require("react/jsx-runtime");
|
|
8349
9105
|
function GreetingBubble({ text, onOpen, onDismiss, dismissLabel = "Dismiss", asks }) {
|
|
8350
|
-
const body = /* @__PURE__ */ (0,
|
|
8351
|
-
/* @__PURE__ */ (0,
|
|
8352
|
-
/* @__PURE__ */ (0,
|
|
8353
|
-
/* @__PURE__ */ (0,
|
|
8354
|
-
/* @__PURE__ */ (0,
|
|
9106
|
+
const body = /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)("button", { type: "button", className: "ss-w-greeting-body", onClick: onOpen, children: [
|
|
9107
|
+
/* @__PURE__ */ (0, import_jsx_runtime91.jsxs)("span", { className: "ss-w-greeting-dots", "aria-hidden": "true", children: [
|
|
9108
|
+
/* @__PURE__ */ (0, import_jsx_runtime91.jsx)("i", {}),
|
|
9109
|
+
/* @__PURE__ */ (0, import_jsx_runtime91.jsx)("i", {}),
|
|
9110
|
+
/* @__PURE__ */ (0, import_jsx_runtime91.jsx)("i", {})
|
|
8355
9111
|
] }),
|
|
8356
|
-
/* @__PURE__ */ (0,
|
|
9112
|
+
/* @__PURE__ */ (0, import_jsx_runtime91.jsx)("span", { className: "ss-w-greeting-text", children: text })
|
|
8357
9113
|
] });
|
|
8358
|
-
const dismiss = onDismiss ? /* @__PURE__ */ (0,
|
|
9114
|
+
const dismiss = onDismiss ? /* @__PURE__ */ (0, import_jsx_runtime91.jsx)("button", { type: "button", className: "ss-w-greeting-x", "aria-label": dismissLabel, onClick: onDismiss, children: "\xD7" }) : null;
|
|
8359
9115
|
if (!asks || asks.length === 0) {
|
|
8360
|
-
return /* @__PURE__ */ (0,
|
|
9116
|
+
return /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)("div", { className: "ss-w-greeting", role: "note", children: [
|
|
8361
9117
|
body,
|
|
8362
9118
|
dismiss
|
|
8363
9119
|
] });
|
|
8364
9120
|
}
|
|
8365
|
-
return /* @__PURE__ */ (0,
|
|
8366
|
-
/* @__PURE__ */ (0,
|
|
9121
|
+
return /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)("div", { className: "ss-w-greeting ss-w-greeting--asks", role: "note", children: [
|
|
9122
|
+
/* @__PURE__ */ (0, import_jsx_runtime91.jsxs)("div", { className: "ss-w-greeting-top", children: [
|
|
8367
9123
|
body,
|
|
8368
9124
|
dismiss
|
|
8369
9125
|
] }),
|
|
8370
|
-
/* @__PURE__ */ (0,
|
|
9126
|
+
/* @__PURE__ */ (0, import_jsx_runtime91.jsx)("div", { className: "ss-w-greeting-asks", children: asks.map((ask, i) => /* @__PURE__ */ (0, import_jsx_runtime91.jsx)("button", { type: "button", className: "ss-w-greeting-ask", onClick: ask.onTap, children: ask.label }, `${i}:${ask.label}`)) })
|
|
8371
9127
|
] });
|
|
8372
9128
|
}
|
|
8373
9129
|
|
|
8374
9130
|
// src/components/ChatFab.tsx
|
|
8375
|
-
var
|
|
9131
|
+
var import_jsx_runtime92 = require("react/jsx-runtime");
|
|
8376
9132
|
function ChatFab({ label, greeting, popupText, logoUrl, onOpen, onDismissPopup, dismissLabel = "Dismiss", introAllowed = false, onIntroStart, asks }) {
|
|
8377
9133
|
const bubble = popupText ?? greeting;
|
|
8378
|
-
const [intro, setIntro] = (0,
|
|
8379
|
-
const armedRef = (0,
|
|
8380
|
-
(0,
|
|
9134
|
+
const [intro, setIntro] = (0, import_react54.useState)(false);
|
|
9135
|
+
const armedRef = (0, import_react54.useRef)(introAllowed);
|
|
9136
|
+
(0, import_react54.useEffect)(() => {
|
|
8381
9137
|
if (armedRef.current && bubble) {
|
|
8382
9138
|
armedRef.current = false;
|
|
8383
9139
|
setIntro(true);
|
|
8384
9140
|
onIntroStart?.();
|
|
8385
9141
|
}
|
|
8386
9142
|
}, [bubble, onIntroStart]);
|
|
8387
|
-
return /* @__PURE__ */ (0,
|
|
8388
|
-
bubble ? /* @__PURE__ */ (0,
|
|
8389
|
-
/* @__PURE__ */ (0,
|
|
9143
|
+
return /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)("div", { className: intro ? "ss-w-fab-wrap ss-w-intro" : "ss-w-fab-wrap", children: [
|
|
9144
|
+
bubble ? /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(GreetingBubble, { text: bubble, onOpen, onDismiss: popupText ? onDismissPopup : void 0, dismissLabel, asks: popupText ? asks : void 0 }) : null,
|
|
9145
|
+
/* @__PURE__ */ (0, import_jsx_runtime92.jsx)("button", { type: "button", className: "ss-w-fab", "aria-label": label, onClick: onOpen, children: /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(BrandMark, { logoUrl, className: "ss-w-fab-mark" }) })
|
|
8390
9146
|
] });
|
|
8391
9147
|
}
|
|
8392
9148
|
|
|
8393
9149
|
// src/components/HostEventsProvider.tsx
|
|
8394
|
-
var
|
|
9150
|
+
var import_jsx_runtime93 = require("react/jsx-runtime");
|
|
8395
9151
|
function HostEventsProvider({ value, children }) {
|
|
8396
|
-
return /* @__PURE__ */ (0,
|
|
9152
|
+
return /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(SlipStateContext.Provider, { value: value.slipState, children: /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(MatchLinkContext.Provider, { value: value.matchLink, children }) });
|
|
8397
9153
|
}
|
|
8398
9154
|
|
|
8399
9155
|
// src/styles/css.generated.ts
|
|
@@ -8789,6 +9545,12 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
8789
9545
|
.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; }
|
|
8790
9546
|
.ss-w-mo-more-chev-up { transform: translateY(1px) rotate(-135deg); }
|
|
8791
9547
|
.ss-w-mo-rows { display: flex; flex-direction: column; gap: 4px; }
|
|
9548
|
+
/* Stage 4: live card score+minute in header (R18). */
|
|
9549
|
+
.ss-w-bi-score { font-size: 13px; font-weight: 700; color: var(--ss-w-ink); white-space: nowrap; }
|
|
9550
|
+
.ss-w-bi-min { font-size: 11px; font-weight: 600; color: var(--ss-w-faint); white-space: nowrap; }
|
|
9551
|
+
/* D12: closed state message row; D13: stale-price notice. */
|
|
9552
|
+
.ss-w-mo-closed { padding: 6px 0; font-size: 12.5px; color: var(--ss-w-faint); }
|
|
9553
|
+
.ss-w-mo-stale { margin: 0; font-size: 11px; color: var(--ss-w-faint); }
|
|
8792
9554
|
/* The card's stat, in the Live tab accordion's insight style. No row below it is marked (R17). */
|
|
8793
9555
|
.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; }
|
|
8794
9556
|
.ss-w-mo-row { display: flex; align-items: center; gap: 10px; padding: 4px 0; border-bottom: 1px dashed var(--ss-w-line); }
|
|
@@ -8824,6 +9586,71 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
8824
9586
|
.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; }
|
|
8825
9587
|
.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; }
|
|
8826
9588
|
.ss-w-combo-note { font-size: 11px; line-height: 1.4; color: var(--ss-w-faint); }
|
|
9589
|
+
.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; }
|
|
9590
|
+
|
|
9591
|
+
/* onboarding presets stage 2: the two tools (spec Stage 2 Behaviour, boards 1c and 2c). A sheet
|
|
9592
|
+
over the message list, inside .ss-w-chat-layer; the body scrolls and the builder's Send sits in
|
|
9593
|
+
the footer, so it stays visible on a phone. Colours come from the theme tokens. */
|
|
9594
|
+
.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); }
|
|
9595
|
+
.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; }
|
|
9596
|
+
.ss-w-tool-head { flex: none; display: flex; align-items: center; gap: 8px; padding: 12px 14px 8px; }
|
|
9597
|
+
.ss-w-tool-title { flex: 1; font-size: 15px; font-weight: 700; line-height: 1.3; }
|
|
9598
|
+
.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; }
|
|
9599
|
+
.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; }
|
|
9600
|
+
.ss-w-tool-body > * { flex: none; }
|
|
9601
|
+
.ss-w-tool-foot { flex: none; padding: 10px 14px 12px; border-top: 1px solid var(--ss-w-line); }
|
|
9602
|
+
.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; }
|
|
9603
|
+
.ss-w-tool-ask { font-size: 14px; font-weight: 650; line-height: 1.7; color: var(--ss-w-ink); overflow-wrap: anywhere; }
|
|
9604
|
+
.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; }
|
|
9605
|
+
.ss-w-tool-picks { list-style: none; display: flex; flex-direction: column; gap: 4px; }
|
|
9606
|
+
.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; }
|
|
9607
|
+
.ss-w-tool-pick-match { grid-column: 1; font-weight: 650; color: var(--ss-w-ink); overflow-wrap: anywhere; }
|
|
9608
|
+
.ss-w-tool-pick-sel { grid-column: 1; color: var(--ss-w-faint); overflow-wrap: anywhere; }
|
|
9609
|
+
.ss-w-tool-pick-kick { grid-column: 1; font-size: 11px; color: var(--ss-w-faint); }
|
|
9610
|
+
.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); }
|
|
9611
|
+
.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; }
|
|
9612
|
+
.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; }
|
|
9613
|
+
.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; }
|
|
9614
|
+
.ss-w-tool-send:disabled { color: var(--ss-w-faint); background: var(--ss-w-bubble); cursor: not-allowed; }
|
|
9615
|
+
.ss-w-tool-foot .ss-w-tool-send { width: 100%; }
|
|
9616
|
+
.ss-w-tool-section { display: flex; flex-direction: column; gap: 6px; }
|
|
9617
|
+
.ss-w-tool-label { font-size: 11.5px; font-weight: 650; color: var(--ss-w-faint); }
|
|
9618
|
+
.ss-w-tool-chips { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
9619
|
+
.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; }
|
|
9620
|
+
.ss-w-tool-chip[aria-pressed="true"] { color: #fff; background: var(--ss-w-accent); border-color: var(--ss-w-accent); }
|
|
9621
|
+
.ss-w-tool-stepper { display: inline-flex; align-items: center; gap: 10px; }
|
|
9622
|
+
.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; }
|
|
9623
|
+
.ss-w-tool-step:disabled { opacity: .5; cursor: not-allowed; }
|
|
9624
|
+
.ss-w-tool-count { min-width: 24px; text-align: center; font-size: 16px; font-weight: 800; font-variant-numeric: tabular-nums; }
|
|
9625
|
+
|
|
9626
|
+
/* onboarding presets stage 2: the chat layer and the speed dial (spec Stage 2 Behaviour). The
|
|
9627
|
+
layer is the message list's positioned box (P6), so the dial sits against the list's bottom
|
|
9628
|
+
right and rises with the composer: right 8px puts the 44px button's centre on Send's (12px
|
|
9629
|
+
padding + 18px), bottom 10px is above the composer's top border. While the button shows the list
|
|
9630
|
+
gets 54px more bottom padding so its last message can scroll clear. */
|
|
9631
|
+
.ss-w-chat-layer { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; }
|
|
9632
|
+
.ss-w-chat-layer[data-dial] .ss-w-list { padding-bottom: 68px; }
|
|
9633
|
+
.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; }
|
|
9634
|
+
.ss-w-dial-btn, .ss-w-dial-pill { pointer-events: auto; }
|
|
9635
|
+
.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; }
|
|
9636
|
+
.ss-w-dial-btn[aria-expanded="true"] { color: var(--ss-w-panel); background: var(--ss-w-accent); border-color: var(--ss-w-accent); }
|
|
9637
|
+
.ss-w-dial-btn:focus-visible, .ss-w-dial-pill:focus-visible { outline: 2px solid var(--ss-w-accent); outline-offset: 2px; }
|
|
9638
|
+
.ss-w-dial-icon { grid-area: 1 / 1; display: grid; place-items: center; transition: transform 200ms ease, opacity 200ms ease; }
|
|
9639
|
+
.ss-w-dial-icon--close { opacity: 0; transform: rotate(-90deg); }
|
|
9640
|
+
.ss-w-dial-btn[aria-expanded="true"] .ss-w-dial-icon--open { opacity: 0; transform: rotate(90deg); }
|
|
9641
|
+
.ss-w-dial-btn[aria-expanded="true"] .ss-w-dial-icon--close { opacity: 1; transform: none; }
|
|
9642
|
+
.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%; }
|
|
9643
|
+
.ss-w-dial-pills { max-width: 100%; display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }
|
|
9644
|
+
.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; }
|
|
9645
|
+
.ss-w-dial-pill svg { flex: none; font-size: 18px; color: var(--ss-w-accent); }
|
|
9646
|
+
.ss-w-dial-pill-label { min-width: 0; }
|
|
9647
|
+
.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; }
|
|
9648
|
+
@media (prefers-reduced-motion: reduce) {
|
|
9649
|
+
.ss-w-dial-pill { transform: none; transition: opacity 200ms ease, visibility 0s linear 200ms; }
|
|
9650
|
+
.ss-w-dial-pills[data-open] .ss-w-dial-pill { transition: opacity 200ms ease, visibility 0s; }
|
|
9651
|
+
.ss-w-dial-icon { transition: opacity 200ms ease; }
|
|
9652
|
+
.ss-w-dial-icon--close, .ss-w-dial-btn[aria-expanded="true"] .ss-w-dial-icon--open { transform: none; }
|
|
9653
|
+
}
|
|
8827
9654
|
|
|
8828
9655
|
/* Compact price list (D16): three or more cards of one turn, one row per fixture. Team names wrap
|
|
8829
9656
|
to a second line and are never cut; the time sits above the match pin, with no column of its own. */
|
|
@@ -8910,6 +9737,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
8910
9737
|
.ss-w-textarea { font-size: 16px; }
|
|
8911
9738
|
}
|
|
8912
9739
|
.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; }
|
|
9740
|
+
.ss-w-send:disabled { opacity: .45; cursor: not-allowed; }
|
|
8913
9741
|
.ss-w-stop-glyph { width: 11px; height: 11px; background: currentColor; border-radius: 2px; }
|
|
8914
9742
|
|
|
8915
9743
|
/* add to slip */
|
|
@@ -9949,7 +10777,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
9949
10777
|
`;
|
|
9950
10778
|
|
|
9951
10779
|
// src/StatsWidget.tsx
|
|
9952
|
-
var
|
|
10780
|
+
var import_jsx_runtime94 = require("react/jsx-runtime");
|
|
9953
10781
|
function buildClient(props) {
|
|
9954
10782
|
try {
|
|
9955
10783
|
if ("client" in props && props.client) {
|
|
@@ -9974,39 +10802,54 @@ function injectStyles(root) {
|
|
|
9974
10802
|
function StatsWidget(props) {
|
|
9975
10803
|
const injectedClient = "client" in props ? props.client : void 0;
|
|
9976
10804
|
const cfg = props.config;
|
|
9977
|
-
const built = (0,
|
|
9978
|
-
const logger = (0,
|
|
10805
|
+
const built = (0, import_react55.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
|
|
10806
|
+
const logger = (0, import_react55.useMemo)(
|
|
9979
10807
|
() => built ? built.client.createEventLogger({
|
|
9980
10808
|
operatorUserId: cfg?.userContext?.operatorUserId,
|
|
9981
10809
|
anonymousUserId: cfg?.userContext?.anonymousUserId
|
|
9982
10810
|
}) : null,
|
|
9983
10811
|
[built?.client]
|
|
9984
10812
|
);
|
|
9985
|
-
const ui = (0,
|
|
9986
|
-
const rootRef = (0,
|
|
9987
|
-
const introDoneRef = (0,
|
|
9988
|
-
const markIntroDone = (0,
|
|
10813
|
+
const ui = (0, import_react55.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
|
|
10814
|
+
const rootRef = (0, import_react55.useRef)(null);
|
|
10815
|
+
const introDoneRef = (0, import_react55.useRef)(false);
|
|
10816
|
+
const markIntroDone = (0, import_react55.useCallback)(() => {
|
|
9989
10817
|
introDoneRef.current = true;
|
|
9990
10818
|
}, []);
|
|
9991
10819
|
const convStorageKey = `ss-w-conv:${cfg?.operatorId ?? ""}:${cfg?.userContext?.operatorUserId ?? "anon"}`;
|
|
9992
10820
|
const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0, convStorageKey);
|
|
9993
|
-
const [hasToday, setHasToday] = (0,
|
|
10821
|
+
const [hasToday, setHasToday] = (0, import_react55.useState)(false);
|
|
10822
|
+
const liveCardWatches = (0, import_react55.useMemo)(() => {
|
|
10823
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10824
|
+
const out = [];
|
|
10825
|
+
for (const msg of controller.state.messages) {
|
|
10826
|
+
if (msg.role !== "assistant") continue;
|
|
10827
|
+
for (const vm of msg.marketOdds) {
|
|
10828
|
+
if (vm.live == null || seen.has(vm.matchId)) continue;
|
|
10829
|
+
seen.add(vm.matchId);
|
|
10830
|
+
out.push({ key: `live:card:${vm.matchId}`, kind: "market", matchId: vm.matchId, marketDeveloperName: vm.marketDeveloperName ?? "FULLTIME_RESULT" });
|
|
10831
|
+
}
|
|
10832
|
+
}
|
|
10833
|
+
return out;
|
|
10834
|
+
}, [controller.state.messages]);
|
|
10835
|
+
const locale = cfg?.locale;
|
|
9994
10836
|
const monitor = useLiveMonitor({
|
|
9995
|
-
getLiveMonitor: (pins) => built ? built.conversation.getLiveMonitor(pins) : Promise.resolve(null),
|
|
10837
|
+
getLiveMonitor: (pins) => built ? built.conversation.getLiveMonitor(pins, locale) : Promise.resolve(null),
|
|
9996
10838
|
getMarketOdds: (matchId, dev) => built ? built.conversation.getMarketOdds(matchId, dev) : Promise.resolve(null),
|
|
9997
10839
|
storageKey: `ss-w-pins:${cfg?.operatorId ?? ""}:${cfg?.userContext?.operatorUserId ?? "anon"}`,
|
|
9998
10840
|
isOpen: controller.state.isOpen,
|
|
9999
|
-
hasToday
|
|
10841
|
+
hasToday,
|
|
10842
|
+
watches: liveCardWatches
|
|
10000
10843
|
});
|
|
10001
|
-
const getSchedule = (0,
|
|
10844
|
+
const getSchedule = (0, import_react55.useCallback)(
|
|
10002
10845
|
(localDate, utcOffsetMinutes) => built ? built.conversation.getSchedule(localDate, utcOffsetMinutes) : Promise.resolve(null),
|
|
10003
10846
|
[built]
|
|
10004
10847
|
);
|
|
10005
|
-
const getStates = (0,
|
|
10848
|
+
const getStates = (0, import_react55.useCallback)(
|
|
10006
10849
|
(matchIds) => built ? built.conversation.getStates(matchIds) : Promise.resolve(null),
|
|
10007
10850
|
[built]
|
|
10008
10851
|
);
|
|
10009
|
-
const pinnedMatchIds = (0,
|
|
10852
|
+
const pinnedMatchIds = (0, import_react55.useMemo)(
|
|
10010
10853
|
() => monitor.pins.filter((p) => p.kind === "match").map((p) => p.matchId),
|
|
10011
10854
|
[monitor.pins]
|
|
10012
10855
|
);
|
|
@@ -10018,14 +10861,54 @@ function StatsWidget(props) {
|
|
|
10018
10861
|
pinnedMatchIds
|
|
10019
10862
|
});
|
|
10020
10863
|
const todayHasContent = visibleGroupsOf(discovery.groups, discovery.states).length > 0;
|
|
10021
|
-
(0,
|
|
10864
|
+
(0, import_react55.useEffect)(() => {
|
|
10022
10865
|
setHasToday(todayHasContent);
|
|
10023
10866
|
}, [todayHasContent]);
|
|
10867
|
+
const prevLiveSnapshotRef = (0, import_react55.useRef)(monitor.snapshot);
|
|
10868
|
+
(0, import_react55.useEffect)(() => {
|
|
10869
|
+
const snap = monitor.snapshot;
|
|
10870
|
+
if (!snap || snap === prevLiveSnapshotRef.current) return;
|
|
10871
|
+
prevLiveSnapshotRef.current = snap;
|
|
10872
|
+
for (const market of snap.markets) {
|
|
10873
|
+
if (!market.key.startsWith("live:card:")) continue;
|
|
10874
|
+
const matchId = parseInt(market.key.slice("live:card:".length), 10);
|
|
10875
|
+
if (!Number.isFinite(matchId)) continue;
|
|
10876
|
+
let mainRows = [];
|
|
10877
|
+
let moreRows = [];
|
|
10878
|
+
let addToSlipEnabled = false;
|
|
10879
|
+
if (market.card) {
|
|
10880
|
+
const updated = toMarketOddsVM(market.card);
|
|
10881
|
+
mainRows = updated.mainRows;
|
|
10882
|
+
moreRows = updated.moreRows;
|
|
10883
|
+
addToSlipEnabled = updated.addToSlipEnabled;
|
|
10884
|
+
}
|
|
10885
|
+
let closedReason = null;
|
|
10886
|
+
if (market.phase === "fullTime" || market.phase === "postponed") closedReason = "full_time";
|
|
10887
|
+
else if (market.isSuspended) closedReason = "suspended";
|
|
10888
|
+
else if (!market.card) closedReason = "no_live_price";
|
|
10889
|
+
const live = {
|
|
10890
|
+
phase: market.phase,
|
|
10891
|
+
homeScore: market.homeScore ?? null,
|
|
10892
|
+
awayScore: market.awayScore ?? null,
|
|
10893
|
+
minuteLabel: market.minuteLabel ?? null,
|
|
10894
|
+
isSuspended: market.isSuspended,
|
|
10895
|
+
isSettled: market.isSettled,
|
|
10896
|
+
asOfUtc: market.asOfUtc,
|
|
10897
|
+
closedReason
|
|
10898
|
+
};
|
|
10899
|
+
controller._dispatch?.({ kind: "UPDATE_LIVE_CARD", matchId, live, mainRows, moreRows, addToSlipEnabled });
|
|
10900
|
+
}
|
|
10901
|
+
}, [monitor.snapshot]);
|
|
10024
10902
|
const gamesResult = useGames(built?.conversation ?? null, { activeTab: monitor.activeTab });
|
|
10025
10903
|
const leaderboardResult = useLeaderboard(built?.conversation ?? null, { activeTab: monitor.activeTab });
|
|
10904
|
+
const getPricePresets = (0, import_react55.useCallback)(
|
|
10905
|
+
(locale2) => built ? built.conversation.getPricePresets(locale2) : Promise.resolve(null),
|
|
10906
|
+
[built]
|
|
10907
|
+
);
|
|
10908
|
+
const presetGrid = usePresetGrid({ getPricePresets, isOpen: controller.state.isOpen, locale: ui.presets.lang });
|
|
10026
10909
|
const markTodayActivity = monitor.markTodayActivity;
|
|
10027
|
-
const prevLiveCountRef = (0,
|
|
10028
|
-
(0,
|
|
10910
|
+
const prevLiveCountRef = (0, import_react55.useRef)(0);
|
|
10911
|
+
(0, import_react55.useEffect)(() => {
|
|
10029
10912
|
const rose = prevLiveCountRef.current === 0 && discovery.liveCount > 0;
|
|
10030
10913
|
prevLiveCountRef.current = discovery.liveCount;
|
|
10031
10914
|
if (rose) markTodayActivity();
|
|
@@ -10041,7 +10924,7 @@ function StatsWidget(props) {
|
|
|
10041
10924
|
openMatchLabel: ui.openMatch
|
|
10042
10925
|
});
|
|
10043
10926
|
const markAdded = hostEvents.markAdded;
|
|
10044
|
-
const slip = (0,
|
|
10927
|
+
const slip = (0, import_react55.useMemo)(
|
|
10045
10928
|
() => {
|
|
10046
10929
|
const handler = props.config?.onAddToSlip;
|
|
10047
10930
|
return {
|
|
@@ -10060,7 +10943,7 @@ function StatsWidget(props) {
|
|
|
10060
10943
|
},
|
|
10061
10944
|
[props.config?.onAddToSlip, controller.state.conversationId, markAdded]
|
|
10062
10945
|
);
|
|
10063
|
-
const onDashboardTap = (0,
|
|
10946
|
+
const onDashboardTap = (0, import_react55.useMemo)(
|
|
10064
10947
|
() => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input), logger) : void 0,
|
|
10065
10948
|
[built, logger]
|
|
10066
10949
|
);
|
|
@@ -10078,11 +10961,11 @@ function StatsWidget(props) {
|
|
|
10078
10961
|
const operatorUserId = cfg?.userContext?.operatorUserId;
|
|
10079
10962
|
const anonymousUserId = cfg?.userContext?.anonymousUserId;
|
|
10080
10963
|
const conversationId = controller.state.conversationId ?? void 0;
|
|
10081
|
-
(0,
|
|
10964
|
+
(0, import_react55.useEffect)(() => {
|
|
10082
10965
|
if (!logger) return;
|
|
10083
10966
|
logger.setIdentity({ operatorUserId, anonymousUserId, conversationId });
|
|
10084
10967
|
}, [logger, operatorUserId, anonymousUserId, conversationId]);
|
|
10085
|
-
(0,
|
|
10968
|
+
(0, import_react55.useEffect)(() => {
|
|
10086
10969
|
if (!logger) return;
|
|
10087
10970
|
logger.resume();
|
|
10088
10971
|
const onHide = () => {
|
|
@@ -10100,14 +10983,14 @@ function StatsWidget(props) {
|
|
|
10100
10983
|
logger.dispose();
|
|
10101
10984
|
};
|
|
10102
10985
|
}, [logger]);
|
|
10103
|
-
(0,
|
|
10986
|
+
(0, import_react55.useEffect)(() => {
|
|
10104
10987
|
if (!built || !ui.injectStyles) return;
|
|
10105
10988
|
const node = rootRef.current?.getRootNode();
|
|
10106
10989
|
if (node) injectStyles(node);
|
|
10107
10990
|
}, [built, ui.injectStyles]);
|
|
10108
10991
|
if (!built) return null;
|
|
10109
10992
|
const attrs = themeAttrs(ui);
|
|
10110
|
-
return /* @__PURE__ */ (0,
|
|
10993
|
+
return /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(EventLogContext.Provider, { value: logger, children: /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(HostEventsProvider, { value: hostEvents, children: /* @__PURE__ */ (0, import_jsx_runtime94.jsx)("div", { ref: rootRef, className: "ss-w-root", "data-ss-w-theme": attrs["data-ss-w-theme"], "data-ss-w-pos": attrs["data-ss-w-pos"], style: attrs.style, children: controller.state.isOpen ? /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
|
|
10111
10994
|
ChatPanel,
|
|
10112
10995
|
{
|
|
10113
10996
|
controller,
|
|
@@ -10119,9 +11002,10 @@ function StatsWidget(props) {
|
|
|
10119
11002
|
gamesResult,
|
|
10120
11003
|
leaderboardResult,
|
|
10121
11004
|
loggedIn: !!operatorUserId,
|
|
10122
|
-
conversation: built?.conversation ?? null
|
|
11005
|
+
conversation: built?.conversation ?? null,
|
|
11006
|
+
presetGrid
|
|
10123
11007
|
}
|
|
10124
|
-
) : /* @__PURE__ */ (0,
|
|
11008
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
|
|
10125
11009
|
ChatFab,
|
|
10126
11010
|
{
|
|
10127
11011
|
label: ui.launcherLabel,
|