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