@sensiblestats/widget-react 0.69.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.cjs CHANGED
@@ -37,7 +37,7 @@ __export(index_exports, {
37
37
  module.exports = __toCommonJS(index_exports);
38
38
 
39
39
  // src/StatsWidget.tsx
40
- var import_react50 = require("react");
40
+ var import_react55 = require("react");
41
41
  var import_widget_sdk5 = require("@sensiblestats/widget-sdk");
42
42
 
43
43
  // src/kickoff.ts
@@ -161,6 +161,30 @@ var STRINGS = {
161
161
  gamePlay: "Play",
162
162
  gameLocked: "Locked",
163
163
  gameVoided: "Voided",
164
+ presets: {
165
+ lang: "en",
166
+ setNumbers: "Set the numbers",
167
+ buildCombo: "Build a combo",
168
+ dialName: "Set the numbers and Build a combo",
169
+ maxPriceRow: "{count} picks, max {price}",
170
+ minPriceRow: "Picks from {price} up",
171
+ comboRow: "A combo of {legs} matches priced at about {total}",
172
+ maxPriceDisplay: (count, price) => `${count} picks, max ${price.toFixed(2)}`,
173
+ maxPriceMessage: (count, price) => `Show me ${count} picks priced at ${price.toFixed(2)} or less.`,
174
+ minPriceDisplay: (count, price) => `${count} picks from ${price.toFixed(2)} up`,
175
+ minPriceMessage: (count, price) => `Show me ${count} picks priced at ${price.toFixed(2)} or more.`,
176
+ comboDisplay: (legs, total) => `A combo of ${legs} priced at about ${total.toFixed(1)}`,
177
+ comboMessage: (legs, total) => `Build me a combo of ${legs} priced at about ${total.toFixed(1)}.`,
178
+ unavailable: "Not available today.",
179
+ send: "Send",
180
+ close: "Close",
181
+ totalLabel: "Total",
182
+ matchesLabel: "Matches",
183
+ fewerMatches: "Fewer matches",
184
+ moreMatches: "More matches",
185
+ changeNumber: (value) => `${value}, tap to change`,
186
+ changeCombo: "Change total or matches"
187
+ },
164
188
  games: {
165
189
  lang: "en",
166
190
  dailyChallenge: "Daily challenge",
@@ -467,6 +491,30 @@ var STRINGS = {
467
491
  gamePlay: "Oyna",
468
492
  gameLocked: "Kilitli",
469
493
  gameVoided: "\u0130ptal edildi",
494
+ presets: {
495
+ lang: "tr",
496
+ setNumbers: "Say\u0131lar\u0131 belirle",
497
+ buildCombo: "Kombin olu\u015Ftur",
498
+ dialName: "Say\u0131lar\u0131 belirle ve Kombin olu\u015Ftur",
499
+ maxPriceRow: "{count} se\xE7im, en fazla {price}",
500
+ minPriceRow: "{price} ve \xFCzeri se\xE7imler",
501
+ comboRow: "Yakla\u015F\u0131k {total} oranl\u0131 {legs} ma\xE7l\u0131k kombin",
502
+ maxPriceDisplay: (count, price) => `${count} se\xE7im, en fazla ${price.toFixed(2)}`,
503
+ maxPriceMessage: (count, price) => `${price.toFixed(2)} veya alt\u0131 oranl\u0131 ${count} se\xE7im g\xF6ster.`,
504
+ minPriceDisplay: (count, price) => `${count} se\xE7im, ${price.toFixed(2)} ve \xFCzeri`,
505
+ minPriceMessage: (count, price) => `${price.toFixed(2)} veya \xFCzeri oranl\u0131 ${count} se\xE7im g\xF6ster.`,
506
+ comboDisplay: (legs, total) => `${legs} ma\xE7l\u0131k kombin, yakla\u015F\u0131k ${total.toFixed(1)}`,
507
+ comboMessage: (legs, total) => `Yakla\u015F\u0131k ${total.toFixed(1)} oranl\u0131 ${legs} ma\xE7l\u0131k bir kombin haz\u0131rla.`,
508
+ unavailable: "Bug\xFCn mevcut de\u011Fil.",
509
+ send: "G\xF6nder",
510
+ close: "Kapat",
511
+ totalLabel: "Toplam",
512
+ matchesLabel: "Ma\xE7 say\u0131s\u0131",
513
+ fewerMatches: "Daha az ma\xE7",
514
+ moreMatches: "Daha fazla ma\xE7",
515
+ changeNumber: (value) => `${value}, de\u011Fi\u015Ftirmek i\xE7in dokun`,
516
+ changeCombo: "Toplam\u0131 veya ma\xE7lar\u0131 de\u011Fi\u015Ftir"
517
+ },
470
518
  games: {
471
519
  lang: "tr",
472
520
  dailyChallenge: "G\xFCn\xFCn meydan okumas\u0131",
@@ -793,6 +841,7 @@ function normalizeUi(cfg) {
793
841
  gameLocked: strings.gameLocked,
794
842
  gameVoided: strings.gameVoided,
795
843
  games: strings.games,
844
+ presets: strings.presets,
796
845
  liveCard: strings.liveCard
797
846
  };
798
847
  }
@@ -1131,7 +1180,9 @@ function toComboCardVM(card) {
1131
1180
  legs,
1132
1181
  multiplication,
1133
1182
  replacedNote: str(card.replacedNote),
1134
- addToSlipEnabled: card.addToSlipEnabled === true
1183
+ addToSlipEnabled: card.addToSlipEnabled === true,
1184
+ target: finite(card.target),
1185
+ legCount: finite(card.legCount)
1135
1186
  };
1136
1187
  }
1137
1188
  function comboLegToSlipSelection(leg, conversationId) {
@@ -1205,10 +1256,10 @@ function resultToToast(result, shownOdds, ui) {
1205
1256
  }
1206
1257
 
1207
1258
  // src/useHostEvents.ts
1208
- var import_react40 = require("react");
1259
+ var import_react44 = require("react");
1209
1260
 
1210
1261
  // src/components/ChatPanel.tsx
1211
- var import_react39 = require("react");
1262
+ var import_react43 = require("react");
1212
1263
 
1213
1264
  // src/components/ChatHeader.tsx
1214
1265
  var import_react2 = require("react");
@@ -1237,6 +1288,20 @@ function KebabIcon() {
1237
1288
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "19", r: "1.6" })
1238
1289
  ] }) });
1239
1290
  }
1291
+ function SlidersIcon() {
1292
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: [
1293
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M4 6h9M17 6h3M4 12h3M11 12h9M4 18h11M19 18h1" }),
1294
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "15", cy: "6", r: "2" }),
1295
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "9", cy: "12", r: "2" }),
1296
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "17", cy: "18", r: "2" })
1297
+ ] }) });
1298
+ }
1299
+ function ComboIcon() {
1300
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinejoin: "round", children: [
1301
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3", y: "3", width: "11", height: "11", rx: "2" }),
1302
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "10", y: "10", width: "11", height: "11", rx: "2" })
1303
+ ] }) });
1304
+ }
1240
1305
  var DASHBOARD_EMOJI = {
1241
1306
  football: "\u26BD",
1242
1307
  "goal-net": "\u{1F945}",
@@ -1342,7 +1407,7 @@ function ChatHeader({
1342
1407
  }
1343
1408
 
1344
1409
  // src/components/MessageList.tsx
1345
- var import_react21 = require("react");
1410
+ var import_react22 = require("react");
1346
1411
 
1347
1412
  // src/presets.ts
1348
1413
  var CLIENT_CAPABILITIES = ["price_presets"];
@@ -2009,6 +2074,13 @@ function BettingInsightList({ insights, ui, slip, monitor }) {
2009
2074
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "ss-w-binsights", children: insights.map((it) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(InsightCard, { it, ui, slip, monitor }, it.id)) });
2010
2075
  }
2011
2076
 
2077
+ // src/presetToolsContext.ts
2078
+ var import_react12 = require("react");
2079
+ var PresetToolsContext = (0, import_react12.createContext)(null);
2080
+ function usePresetTools() {
2081
+ return (0, import_react12.useContext)(PresetToolsContext);
2082
+ }
2083
+
2012
2084
  // src/components/ComboCardList.tsx
2013
2085
  var import_jsx_runtime14 = require("react/jsx-runtime");
2014
2086
  function ComboLegRow({ leg, addToSlipEnabled, ui, slip }) {
@@ -2051,10 +2123,13 @@ function ComboLegRow({ leg, addToSlipEnabled, ui, slip }) {
2051
2123
  ] });
2052
2124
  }
2053
2125
  function ComboCardView({ vm, ui, slip }) {
2126
+ const tools = usePresetTools();
2127
+ const { target, legCount } = vm;
2054
2128
  return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "ss-w-combo", children: [
2055
2129
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("ol", { className: "ss-w-combo-legs", role: "list", children: vm.legs.map((leg) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ComboLegRow, { leg, addToSlipEnabled: vm.addToSlipEnabled, ui, slip }, leg.key)) }),
2056
2130
  /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "ss-w-combo-math", children: vm.multiplication }),
2057
- vm.replacedNote ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "ss-w-combo-note", children: vm.replacedNote }) : null
2131
+ vm.replacedNote ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("p", { className: "ss-w-combo-note", children: vm.replacedNote }) : null,
2132
+ tools && target !== void 0 && legCount !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "ss-w-combo-change", onClick: () => tools.openBuilder(target, legCount), children: tools.changeLabel }) : null
2058
2133
  ] });
2059
2134
  }
2060
2135
  function ComboCardList({ cards, ui, slip }) {
@@ -2063,7 +2138,7 @@ function ComboCardList({ cards, ui, slip }) {
2063
2138
  }
2064
2139
 
2065
2140
  // src/components/MarketOddsCardList.tsx
2066
- var import_react12 = require("react");
2141
+ var import_react13 = require("react");
2067
2142
  var import_jsx_runtime15 = require("react/jsx-runtime");
2068
2143
  function OddsRow({ vm, row, ui, slip, inSlipIds, history, suppressSlip }) {
2069
2144
  const selection = vm.addToSlipEnabled && !suppressSlip ? marketOddsRowToSlipSelection(vm, row, slip.conversationId) : null;
@@ -2092,7 +2167,7 @@ function fmtHhMm(iso, locale) {
2092
2167
  return new Date(iso).toLocaleTimeString(locale, { hour: "2-digit", minute: "2-digit", hour12: false });
2093
2168
  }
2094
2169
  function MarketOddsCard({ vm, ui, slip, monitor, inSlipIds, hideHeader, pinRef }) {
2095
- const [open, setOpen] = (0, import_react12.useState)(false);
2170
+ const [open, setOpen] = (0, import_react13.useState)(false);
2096
2171
  const kickoffFallback = vm.kickoffUtc && ui?.bettingKickoff ? ui.bettingKickoff(vm.kickoffUtc) : void 0;
2097
2172
  const kickoff = vm.kickoffLabel ?? (kickoffFallback ? `${kickoffFallback.dayLabel} ${kickoffFallback.time}` : void 0);
2098
2173
  const hasNamedTeams = !!vm.homeTeamName && !!vm.awayTeamName;
@@ -2102,9 +2177,9 @@ function MarketOddsCard({ vm, ui, slip, monitor, inSlipIds, hideHeader, pinRef }
2102
2177
  const asOfMs = live ? new Date(live.asOfUtc).getTime() : NaN;
2103
2178
  const isStale = live != null && !isClosed && (vm.awaitingLiveUpdate === true || Date.now() - asOfMs > STALE_AFTER_MS);
2104
2179
  const staleTime = isStale && live ? fmtHhMm(live.asOfUtc, ui?.locale ?? "en") : null;
2105
- const [, setStaleTick] = (0, import_react12.useState)(0);
2180
+ const [, setStaleTick] = (0, import_react13.useState)(0);
2106
2181
  const watchStale = live != null && !isClosed && !isStale;
2107
- (0, import_react12.useEffect)(() => {
2182
+ (0, import_react13.useEffect)(() => {
2108
2183
  if (!watchStale) return;
2109
2184
  const id = window.setTimeout(() => setStaleTick((n) => n + 1), Math.max(0, asOfMs + STALE_AFTER_MS - Date.now()) + 1);
2110
2185
  return () => window.clearTimeout(id);
@@ -2178,7 +2253,7 @@ function MarketOddsCardList({ cards, ui, slip, monitor, inSlipIds, hideHeader, p
2178
2253
  }
2179
2254
 
2180
2255
  // src/components/CompactOddsList.tsx
2181
- var import_react13 = require("react");
2256
+ var import_react14 = require("react");
2182
2257
 
2183
2258
  // src/compactList.ts
2184
2259
  var COMPACT_MIN_CARDS = 3;
@@ -2291,11 +2366,11 @@ function Row({ row, columns, ui, slip, monitor, rowRef }) {
2291
2366
  ] });
2292
2367
  }
2293
2368
  function CompactOddsList({ list, ui, slip, monitor }) {
2294
- const [shown, setShown] = (0, import_react13.useState)(COMPACT_PAGE);
2369
+ const [shown, setShown] = (0, import_react14.useState)(COMPACT_PAGE);
2295
2370
  const hidden = list.rowCount - shown;
2296
- const [focusIndex, setFocusIndex] = (0, import_react13.useState)(null);
2297
- const focusRef = (0, import_react13.useRef)(null);
2298
- (0, import_react13.useEffect)(() => {
2371
+ const [focusIndex, setFocusIndex] = (0, import_react14.useState)(null);
2372
+ const focusRef = (0, import_react14.useRef)(null);
2373
+ (0, import_react14.useEffect)(() => {
2299
2374
  if (focusIndex !== null) focusRef.current?.focus();
2300
2375
  }, [focusIndex]);
2301
2376
  let left = shown;
@@ -2596,7 +2671,7 @@ function FixtureCard({ card }) {
2596
2671
  }
2597
2672
 
2598
2673
  // src/components/dashboard/CountdownCard.tsx
2599
- var import_react14 = require("react");
2674
+ var import_react15 = require("react");
2600
2675
  var import_jsx_runtime22 = require("react/jsx-runtime");
2601
2676
  var DAY_MS = 864e5;
2602
2677
  function remainingSegments(kickoffUnix, nowMs, withSeconds) {
@@ -2615,12 +2690,12 @@ function remainingSegments(kickoffUnix, nowMs, withSeconds) {
2615
2690
  function CountdownCard({ card }) {
2616
2691
  const { data } = card;
2617
2692
  const unitLabel = { d: data.daysUnit, h: data.hoursUnit, m: data.minutesUnit, s: data.secondsUnit };
2618
- const [nowMs, setNowMs] = (0, import_react14.useState)(() => Date.now());
2619
- const [reduced] = (0, import_react14.useState)(
2693
+ const [nowMs, setNowMs] = (0, import_react15.useState)(() => Date.now());
2694
+ const [reduced] = (0, import_react15.useState)(
2620
2695
  () => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches
2621
2696
  );
2622
2697
  const withinDay = data.kickoffUnix * 1e3 - nowMs < DAY_MS;
2623
- (0, import_react14.useEffect)(() => {
2698
+ (0, import_react15.useEffect)(() => {
2624
2699
  if (reduced) return;
2625
2700
  const id = window.setInterval(() => setNowMs(Date.now()), withinDay ? 1e3 : 6e4);
2626
2701
  return () => window.clearInterval(id);
@@ -2628,7 +2703,7 @@ function CountdownCard({ card }) {
2628
2703
  const segments = remainingSegments(data.kickoffUnix, nowMs, withinDay && !reduced);
2629
2704
  return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "ss-w-fandash-card ss-w-fandash-countdown", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "ss-w-fandash-countdown-top", children: [
2630
2705
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "ss-w-fandash-eyebrow", children: card.title ?? "Kick-off in" }),
2631
- segments ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "ss-w-fandash-countdown-clock", children: segments.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react14.Fragment, { children: [
2706
+ segments ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "ss-w-fandash-countdown-clock", children: segments.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react15.Fragment, { children: [
2632
2707
  i > 0 ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "ss-w-fandash-countdown-colon", children: ":" }) : null,
2633
2708
  /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("span", { className: "ss-w-fandash-countdown-seg", children: [
2634
2709
  /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "ss-w-fandash-countdown-value", children: s.value }),
@@ -2639,7 +2714,7 @@ function CountdownCard({ card }) {
2639
2714
  }
2640
2715
 
2641
2716
  // src/components/dashboard/InjuriesCard.tsx
2642
- var import_react15 = require("react");
2717
+ var import_react16 = require("react");
2643
2718
  var import_jsx_runtime23 = require("react/jsx-runtime");
2644
2719
  var SHOWN = 3;
2645
2720
  function ColHead({ name, crestUrl }) {
@@ -2662,7 +2737,7 @@ function InjuryList({ rows, emptyLabel, limit }) {
2662
2737
  }
2663
2738
  function InjuriesCard({ card, ui }) {
2664
2739
  const { data } = card;
2665
- const [expanded, setExpanded] = (0, import_react15.useState)(false);
2740
+ const [expanded, setExpanded] = (0, import_react16.useState)(false);
2666
2741
  const hidden = Math.max(0, data.ours.length - SHOWN) + Math.max(0, data.theirs.length - SHOWN);
2667
2742
  const limit = expanded ? void 0 : SHOWN;
2668
2743
  const showMore = ui?.showMore ?? ((n) => `Show ${n} more`);
@@ -2768,7 +2843,7 @@ function LinksCard({ card }) {
2768
2843
  }
2769
2844
 
2770
2845
  // src/components/dashboard/PostMatchResultCard.tsx
2771
- var import_react16 = require("react");
2846
+ var import_react17 = require("react");
2772
2847
  var import_jsx_runtime29 = require("react/jsx-runtime");
2773
2848
  function initials3(name) {
2774
2849
  return name.trim().slice(0, 3).toUpperCase();
@@ -2778,7 +2853,7 @@ function PostMatchCrest({
2778
2853
  name,
2779
2854
  side
2780
2855
  }) {
2781
- const [failed, setFailed] = (0, import_react16.useState)(false);
2856
+ const [failed, setFailed] = (0, import_react17.useState)(false);
2782
2857
  const cls = `ss-w-fandash-postmatch-crest ss-w-fandash-postmatch-crest--${side}`;
2783
2858
  if (url && !failed) {
2784
2859
  return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("img", { className: cls, src: url, alt: "", "aria-hidden": "true", onError: () => setFailed(true) });
@@ -2977,10 +3052,10 @@ function RestingFormCard({ card }) {
2977
3052
  }
2978
3053
 
2979
3054
  // src/components/dashboard/LiveScoreCard.tsx
2980
- var import_react18 = require("react");
3055
+ var import_react19 = require("react");
2981
3056
 
2982
3057
  // src/useLiveClock.ts
2983
- var import_react17 = require("react");
3058
+ var import_react18 = require("react");
2984
3059
  var TICK_MS = 6e4;
2985
3060
  var MAX_LIVE_TICK_MINUTES = 15;
2986
3061
  function computeDisplayMinute(minute, clockRunning, receivedAtMs) {
@@ -2989,10 +3064,10 @@ function computeDisplayMinute(minute, clockRunning, receivedAtMs) {
2989
3064
  return minute + Math.min(Math.max(0, elapsedMinutes), MAX_LIVE_TICK_MINUTES);
2990
3065
  }
2991
3066
  function useLiveClock(minute, clockRunning, receivedAtMs) {
2992
- const [displayMinute, setDisplayMinute] = (0, import_react17.useState)(
3067
+ const [displayMinute, setDisplayMinute] = (0, import_react18.useState)(
2993
3068
  () => computeDisplayMinute(minute, clockRunning, receivedAtMs)
2994
3069
  );
2995
- (0, import_react17.useEffect)(() => {
3070
+ (0, import_react18.useEffect)(() => {
2996
3071
  setDisplayMinute(computeDisplayMinute(minute, clockRunning, receivedAtMs));
2997
3072
  if (!clockRunning) return;
2998
3073
  const id = window.setInterval(() => {
@@ -3009,7 +3084,7 @@ function initials5(name) {
3009
3084
  return name.trim().slice(0, 3).toUpperCase();
3010
3085
  }
3011
3086
  function LiveCrest({ url, name, side }) {
3012
- const [failed, setFailed] = (0, import_react18.useState)(false);
3087
+ const [failed, setFailed] = (0, import_react19.useState)(false);
3013
3088
  const cls = `ss-w-fandash-live-crest ss-w-fandash-live-crest--${side}`;
3014
3089
  if (url && !failed) {
3015
3090
  return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("img", { className: cls, src: url, alt: "", "aria-hidden": "true", onError: () => setFailed(true) });
@@ -3028,7 +3103,7 @@ function LiveScoreCard({
3028
3103
  }) {
3029
3104
  const { data } = card;
3030
3105
  const seedKey = `${data.minute}:${data.homeScore}:${data.awayScore}`;
3031
- const seedRef = (0, import_react18.useRef)(null);
3106
+ const seedRef = (0, import_react19.useRef)(null);
3032
3107
  if (!seedRef.current || seedRef.current.key !== seedKey) {
3033
3108
  seedRef.current = { key: seedKey, receivedAtMs: Date.now() };
3034
3109
  }
@@ -3068,7 +3143,7 @@ function LiveScoreCard({
3068
3143
  }
3069
3144
 
3070
3145
  // src/components/dashboard/LiveTimelineCard.tsx
3071
- var import_react19 = require("react");
3146
+ var import_react20 = require("react");
3072
3147
  var import_jsx_runtime39 = require("react/jsx-runtime");
3073
3148
  var SHOWN2 = 3;
3074
3149
  function EventIcon({ type }) {
@@ -3110,7 +3185,7 @@ function EventRow({ e }) {
3110
3185
  ] });
3111
3186
  }
3112
3187
  function LiveTimelineCard({ card, ui }) {
3113
- const [expanded, setExpanded] = (0, import_react19.useState)(false);
3188
+ const [expanded, setExpanded] = (0, import_react20.useState)(false);
3114
3189
  const events = card.data.events;
3115
3190
  if (!events || events.length === 0) return null;
3116
3191
  const sorted = [...events].sort((a, b) => b.minute - a.minute);
@@ -3135,7 +3210,7 @@ function LiveTimelineCard({ card, ui }) {
3135
3210
  }
3136
3211
 
3137
3212
  // src/components/dashboard/MomentumWave.tsx
3138
- var import_react20 = require("react");
3213
+ var import_react21 = require("react");
3139
3214
  var import_jsx_runtime40 = require("react/jsx-runtime");
3140
3215
  var WIDTH = 270;
3141
3216
  var HEIGHT = 56;
@@ -3159,7 +3234,7 @@ function MomentumWave({
3159
3234
  }) {
3160
3235
  if (points.length === 0) return null;
3161
3236
  const { area, line, lastLeftPct, lastTopPx } = buildPaths(points);
3162
- const clipId = (0, import_react20.useId)();
3237
+ const clipId = (0, import_react21.useId)();
3163
3238
  const topClipId = `${clipId}-top`;
3164
3239
  const bottomClipId = `${clipId}-bottom`;
3165
3240
  const topClass = clubIsHome ? "ss-w-fandash-live-mom-home" : "ss-w-fandash-live-mom-away";
@@ -3352,11 +3427,11 @@ function turnAnchor(messages) {
3352
3427
  }
3353
3428
  function MessageList({ controller, ui, slip, onDashboardTap, monitor }) {
3354
3429
  const { state, send } = controller;
3355
- const listRef = (0, import_react21.useRef)(null);
3356
- const endRef = (0, import_react21.useRef)(null);
3357
- const pinnedRef = (0, import_react21.useRef)(true);
3358
- const anchoredTurnRef = (0, import_react21.useRef)(null);
3359
- (0, import_react21.useEffect)(() => {
3430
+ const listRef = (0, import_react22.useRef)(null);
3431
+ const endRef = (0, import_react22.useRef)(null);
3432
+ const pinnedRef = (0, import_react22.useRef)(true);
3433
+ const anchoredTurnRef = (0, import_react22.useRef)(null);
3434
+ (0, import_react22.useEffect)(() => {
3360
3435
  const anchor = turnAnchor(state.messages);
3361
3436
  const list = listRef.current;
3362
3437
  if (anchor && anchor.turnId !== anchoredTurnRef.current) {
@@ -3413,11 +3488,15 @@ function MessageList({ controller, ui, slip, onDashboardTap, monitor }) {
3413
3488
  }
3414
3489
 
3415
3490
  // src/components/ChatInput.tsx
3416
- var import_react22 = require("react");
3491
+ var import_react23 = require("react");
3417
3492
  var import_jsx_runtime46 = require("react/jsx-runtime");
3418
- function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
3419
- const [value, setValue] = (0, import_react22.useState)("");
3493
+ var ChatInput = (0, import_react23.forwardRef)(function ChatInput2({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message", disabled = false, onType }, ref) {
3494
+ const [value, setValue] = (0, import_react23.useState)("");
3495
+ const textareaRef = (0, import_react23.useRef)(null);
3496
+ const stopRef = (0, import_react23.useRef)(null);
3497
+ const textBoxWantsFocusRef = (0, import_react23.useRef)(false);
3420
3498
  const submit = () => {
3499
+ if (disabled) return;
3421
3500
  const t = value.trim();
3422
3501
  if (!t) return;
3423
3502
  onSend(t);
@@ -3429,11 +3508,48 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
3429
3508
  submit();
3430
3509
  }
3431
3510
  };
3511
+ (0, import_react23.useImperativeHandle)(ref, () => ({
3512
+ focus() {
3513
+ const box = textareaRef.current;
3514
+ if (box && !box.disabled) {
3515
+ textBoxWantsFocusRef.current = false;
3516
+ box.focus();
3517
+ return;
3518
+ }
3519
+ textBoxWantsFocusRef.current = true;
3520
+ stopRef.current?.focus();
3521
+ }
3522
+ }), []);
3523
+ (0, import_react23.useEffect)(() => {
3524
+ if (streaming || disabled || !textBoxWantsFocusRef.current) return;
3525
+ textBoxWantsFocusRef.current = false;
3526
+ const box = textareaRef.current;
3527
+ if (!box) return;
3528
+ const active = box.getRootNode().activeElement;
3529
+ const input = box.closest(".ss-w-input");
3530
+ const isWithinComponent = input?.contains(active);
3531
+ if (!active || active === box.ownerDocument.body || isWithinComponent) box.focus();
3532
+ }, [streaming, disabled]);
3432
3533
  return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "ss-w-input", children: [
3433
- /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
3434
- streaming ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(SendIcon, {}) })
3534
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
3535
+ "textarea",
3536
+ {
3537
+ ref: textareaRef,
3538
+ className: "ss-w-textarea",
3539
+ rows: 1,
3540
+ placeholder,
3541
+ value,
3542
+ disabled: streaming || disabled,
3543
+ onChange: (e) => {
3544
+ setValue(e.target.value);
3545
+ onType?.();
3546
+ },
3547
+ onKeyDown
3548
+ }
3549
+ ),
3550
+ streaming ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { ref: stopRef, type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, disabled, onClick: onStop, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, disabled, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(SendIcon, {}) })
3435
3551
  ] });
3436
- }
3552
+ });
3437
3553
 
3438
3554
  // src/components/TabBar.tsx
3439
3555
  var import_jsx_runtime47 = require("react/jsx-runtime");
@@ -3526,10 +3642,10 @@ function MarketAccordion({ match, monitor, ui, slip }) {
3526
3642
  }
3527
3643
 
3528
3644
  // src/components/LiveFactBlock.tsx
3529
- var import_react23 = require("react");
3645
+ var import_react24 = require("react");
3530
3646
  var import_jsx_runtime49 = require("react/jsx-runtime");
3531
3647
  function LiveFactBlock({ facts, matchId, ui, slip, onAsk }) {
3532
- const [expanded, setExpanded] = (0, import_react23.useState)(false);
3648
+ const [expanded, setExpanded] = (0, import_react24.useState)(false);
3533
3649
  const [current, ...earlier] = facts;
3534
3650
  if (!current) return null;
3535
3651
  const m = current.market;
@@ -3916,7 +4032,7 @@ function TodayPanel({ discovery, ui, monitor }) {
3916
4032
  }
3917
4033
 
3918
4034
  // src/components/GamesPanel.tsx
3919
- var import_react37 = require("react");
4035
+ var import_react38 = require("react");
3920
4036
 
3921
4037
  // src/components/GamesLoginWall.tsx
3922
4038
  var import_jsx_runtime55 = require("react/jsx-runtime");
@@ -3925,7 +4041,7 @@ function GamesLoginWall({ ui }) {
3925
4041
  }
3926
4042
 
3927
4043
  // src/components/GamesHub.tsx
3928
- var import_react24 = require("react");
4044
+ var import_react25 = require("react");
3929
4045
 
3930
4046
  // src/components/games/ui/gameMeta.ts
3931
4047
  function prettifyGameId(gameId) {
@@ -4110,8 +4226,8 @@ function pickHeroGame(games) {
4110
4226
  }
4111
4227
  var tileKey = (g) => `${g.gameId}-${g.gameDay}-${g.fixtureId}`;
4112
4228
  function GamesHub({ games, myWeeklyRow, weeklyPrize, onOpen, ui }) {
4113
- const [, setTick] = (0, import_react24.useState)(0);
4114
- (0, import_react24.useEffect)(() => {
4229
+ const [, setTick] = (0, import_react25.useState)(0);
4230
+ (0, import_react25.useEffect)(() => {
4115
4231
  const id = setInterval(() => setTick((t) => t + 1), 6e4);
4116
4232
  return () => clearInterval(id);
4117
4233
  }, []);
@@ -4171,10 +4287,10 @@ function GamesHub({ games, myWeeklyRow, weeklyPrize, onOpen, ui }) {
4171
4287
  }
4172
4288
 
4173
4289
  // src/components/GameScreenHost.tsx
4174
- var import_react36 = require("react");
4290
+ var import_react37 = require("react");
4175
4291
 
4176
4292
  // src/useGamePlay.ts
4177
- var import_react25 = require("react");
4293
+ var import_react26 = require("react");
4178
4294
  function toSubmitResult(payload) {
4179
4295
  return {
4180
4296
  // 'pending' means the play is recorded but awaiting resolution (fixture games)
@@ -4186,17 +4302,17 @@ function toSubmitResult(payload) {
4186
4302
  };
4187
4303
  }
4188
4304
  function useGamePlay(conversation, { gameId, gameDay, fixtureId, locale, refresh }) {
4189
- const [result, setResult] = (0, import_react25.useState)(null);
4190
- const [submitting, setSubmitting] = (0, import_react25.useState)(false);
4191
- const [rawStatus, setRawStatus] = (0, import_react25.useState)(null);
4192
- const inFlightRef = (0, import_react25.useRef)(false);
4193
- const convRef = (0, import_react25.useRef)(conversation);
4305
+ const [result, setResult] = (0, import_react26.useState)(null);
4306
+ const [submitting, setSubmitting] = (0, import_react26.useState)(false);
4307
+ const [rawStatus, setRawStatus] = (0, import_react26.useState)(null);
4308
+ const inFlightRef = (0, import_react26.useRef)(false);
4309
+ const convRef = (0, import_react26.useRef)(conversation);
4194
4310
  convRef.current = conversation;
4195
- const optsRef = (0, import_react25.useRef)({ gameId, gameDay, fixtureId, locale, refresh });
4311
+ const optsRef = (0, import_react26.useRef)({ gameId, gameDay, fixtureId, locale, refresh });
4196
4312
  optsRef.current = { gameId, gameDay, fixtureId, locale, refresh };
4197
- const resultRef = (0, import_react25.useRef)(null);
4313
+ const resultRef = (0, import_react26.useRef)(null);
4198
4314
  resultRef.current = result;
4199
- const refetch = (0, import_react25.useCallback)(async () => {
4315
+ const refetch = (0, import_react26.useCallback)(async () => {
4200
4316
  const conv = convRef.current;
4201
4317
  if (!conv) return;
4202
4318
  const opts = optsRef.current;
@@ -4212,7 +4328,7 @@ function useGamePlay(conversation, { gameId, gameDay, fixtureId, locale, refresh
4212
4328
  } catch {
4213
4329
  }
4214
4330
  }, []);
4215
- const submit = (0, import_react25.useCallback)(async (answerJson) => {
4331
+ const submit = (0, import_react26.useCallback)(async (answerJson) => {
4216
4332
  const conv = convRef.current;
4217
4333
  if (!conv || inFlightRef.current) return;
4218
4334
  inFlightRef.current = true;
@@ -4239,14 +4355,14 @@ function useGamePlay(conversation, { gameId, gameDay, fixtureId, locale, refresh
4239
4355
  setSubmitting(false);
4240
4356
  }
4241
4357
  }, [refetch]);
4242
- const hydrate = (0, import_react25.useCallback)(() => {
4358
+ const hydrate = (0, import_react26.useCallback)(() => {
4243
4359
  void refetch();
4244
4360
  }, [refetch]);
4245
4361
  return { result, submitting, submit, hydrate, rawStatus };
4246
4362
  }
4247
4363
 
4248
4364
  // src/components/games/MysteryTigerGame.tsx
4249
- var import_react27 = require("react");
4365
+ var import_react28 = require("react");
4250
4366
 
4251
4367
  // src/components/games/nationFlags.ts
4252
4368
  var FLAGS = {
@@ -4457,7 +4573,7 @@ function isNationCode(flag) {
4457
4573
  }
4458
4574
 
4459
4575
  // src/components/games/PlayerAvatar.tsx
4460
- var import_react26 = require("react");
4576
+ var import_react27 = require("react");
4461
4577
  var import_jsx_runtime61 = require("react/jsx-runtime");
4462
4578
  function initialsOf(name) {
4463
4579
  const parts = name.trim().split(/\s+/).filter(Boolean);
@@ -4475,7 +4591,7 @@ function PlayerAvatar({
4475
4591
  imagePath,
4476
4592
  className
4477
4593
  }) {
4478
- const [failed, setFailed] = (0, import_react26.useState)(false);
4594
+ const [failed, setFailed] = (0, import_react27.useState)(false);
4479
4595
  const showImage = Boolean(imagePath) && !isPlaceholderImage(imagePath) && !failed;
4480
4596
  return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("div", { className, "aria-hidden": "true", children: showImage ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("img", { src: imagePath, alt: "", loading: "lazy", onError: () => setFailed(true) }) : /* @__PURE__ */ (0, import_jsx_runtime61.jsx)("span", { className: "ss-w-av-initials", children: initialsOf(name) }) });
4481
4597
  }
@@ -4690,8 +4806,8 @@ function MysteryTigerGame({
4690
4806
  onBack
4691
4807
  }) {
4692
4808
  const t = ui.games.mysteryTiger;
4693
- const [query, setQuery] = (0, import_react27.useState)("");
4694
- const inputId = (0, import_react27.useId)();
4809
+ const [query, setQuery] = (0, import_react28.useState)("");
4810
+ const inputId = (0, import_react28.useId)();
4695
4811
  const clientState = parseClientState(result?.clientStateJson);
4696
4812
  const guessResults = clientState?.guessResults ?? [];
4697
4813
  const alreadyGuessedIds = new Set(guessResults.map((g) => g.playerId));
@@ -4966,13 +5082,13 @@ function HigherOrLowerGame({
4966
5082
  }
4967
5083
 
4968
5084
  // src/components/games/CareerPathGame.tsx
4969
- var import_react29 = require("react");
5085
+ var import_react30 = require("react");
4970
5086
 
4971
5087
  // src/components/games/ui/CrestShield.tsx
4972
- var import_react28 = require("react");
5088
+ var import_react29 = require("react");
4973
5089
  var import_jsx_runtime72 = require("react/jsx-runtime");
4974
5090
  function CrestShield({ src, name, size = 28 }) {
4975
- const [failed, setFailed] = (0, import_react28.useState)(false);
5091
+ const [failed, setFailed] = (0, import_react29.useState)(false);
4976
5092
  if (src && !isPlaceholderImage(src) && !failed) {
4977
5093
  return /* @__PURE__ */ (0, import_jsx_runtime72.jsx)("img", { className: "ss-g-crest", src, alt: name, width: size, height: size, loading: "lazy", onError: () => setFailed(true) });
4978
5094
  }
@@ -5004,8 +5120,8 @@ function CareerPathGame({
5004
5120
  }) {
5005
5121
  const s = ui.games.careerPath;
5006
5122
  const title = gameTitle({ gameId: "career-path" }, ui.games.titles);
5007
- const [query, setQuery] = (0, import_react29.useState)("");
5008
- const inputId = (0, import_react29.useId)();
5123
+ const [query, setQuery] = (0, import_react30.useState)("");
5124
+ const inputId = (0, import_react30.useId)();
5009
5125
  const clientState = parseClientState3(result?.clientStateJson);
5010
5126
  const alreadyGuessedIds = new Set(clientState?.guessResults.map((g) => g.playerId) ?? []);
5011
5127
  const isOver = result?.alreadyPlayed === true || clientState?.solved === true || clientState?.failed === true;
@@ -5124,7 +5240,7 @@ function CareerPathGame({
5124
5240
  }
5125
5241
 
5126
5242
  // src/components/games/BeatTheGridGame.tsx
5127
- var import_react30 = __toESM(require("react"), 1);
5243
+ var import_react31 = __toESM(require("react"), 1);
5128
5244
  var import_jsx_runtime74 = require("react/jsx-runtime");
5129
5245
  function criterionIconKind(label, key) {
5130
5246
  const l = (key ?? label).toLowerCase();
@@ -5205,10 +5321,10 @@ function SearchSheet({
5205
5321
  ui
5206
5322
  }) {
5207
5323
  const s = ui.games.beatTheGrid;
5208
- const [query, setQuery] = (0, import_react30.useState)("");
5209
- const inputId = (0, import_react30.useId)();
5210
- const inputRef = (0, import_react30.useRef)(null);
5211
- (0, import_react30.useEffect)(() => {
5324
+ const [query, setQuery] = (0, import_react31.useState)("");
5325
+ const inputId = (0, import_react31.useId)();
5326
+ const inputRef = (0, import_react31.useRef)(null);
5327
+ (0, import_react31.useEffect)(() => {
5212
5328
  inputRef.current?.focus();
5213
5329
  }, [row, col]);
5214
5330
  const filtered = playerPool.filter(
@@ -5273,7 +5389,7 @@ function BeatTheGridGame({
5273
5389
  }) {
5274
5390
  const s = ui.games.beatTheGrid;
5275
5391
  const title = gameTitle({ gameId: "beat-the-grid" }, ui.games.titles);
5276
- const [activeCell, setActiveCell] = (0, import_react30.useState)(null);
5392
+ const [activeCell, setActiveCell] = (0, import_react31.useState)(null);
5277
5393
  const clientState = parseClientState4(result?.clientStateJson);
5278
5394
  const filledCells = clientState?.cells ?? [];
5279
5395
  const isLocked2 = result?.alreadyPlayed === true || clientState?.isComplete === true;
@@ -5347,7 +5463,7 @@ function BeatTheGridGame({
5347
5463
  /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { "aria-hidden": "true", children: abbr }),
5348
5464
  /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("span", { className: "ss-g-sr", children: rowLabel })
5349
5465
  ] });
5350
- return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)(import_react30.default.Fragment, { children: [
5466
+ return /* @__PURE__ */ (0, import_jsx_runtime74.jsxs)(import_react31.default.Fragment, { children: [
5351
5467
  /* @__PURE__ */ (0, import_jsx_runtime74.jsx)("div", { className: "ss-g-btg-rowhdr", "data-meta": rowMeta ? "true" : void 0, children: rowHeaderContent }),
5352
5468
  cols.map((colLabel, colIdx) => {
5353
5469
  const filled = findCellResult(filledCells, rowIdx, colIdx);
@@ -5428,7 +5544,7 @@ function BeatTheGridGame({
5428
5544
  }
5429
5545
 
5430
5546
  // src/components/games/GuessTheXiGame.tsx
5431
- var import_react31 = require("react");
5547
+ var import_react32 = require("react");
5432
5548
 
5433
5549
  // src/components/games/ui/PitchSvg.tsx
5434
5550
  var import_jsx_runtime75 = require("react/jsx-runtime");
@@ -5517,8 +5633,8 @@ function GuessTheXiGame({
5517
5633
  onBack
5518
5634
  }) {
5519
5635
  const t = ui.games.guessTheXi;
5520
- const [query, setQuery] = (0, import_react31.useState)("");
5521
- const inputId = (0, import_react31.useId)();
5636
+ const [query, setQuery] = (0, import_react32.useState)("");
5637
+ const inputId = (0, import_react32.useId)();
5522
5638
  const clientState = parseClientState5(result?.clientStateJson);
5523
5639
  const isOver = result?.alreadyPlayed === true || clientState?.status === "solved" || clientState?.status === "failed";
5524
5640
  const solved = clientState?.status === "solved";
@@ -5659,7 +5775,7 @@ function GuessTheXiGame({
5659
5775
  }
5660
5776
 
5661
5777
  // src/components/games/DailyQuizGame.tsx
5662
- var import_react32 = require("react");
5778
+ var import_react33 = require("react");
5663
5779
  var import_jsx_runtime77 = require("react/jsx-runtime");
5664
5780
  function parseClientState6(json) {
5665
5781
  if (!json) return null;
@@ -5700,8 +5816,8 @@ function DailyQuizGame({
5700
5816
  }) {
5701
5817
  const s = ui.games.dailyQuiz;
5702
5818
  const title = gameTitle({ gameId: "daily-quiz" }, ui.games.titles);
5703
- const [selections, setSelections] = (0, import_react32.useState)(/* @__PURE__ */ new Map());
5704
- const [page, setPage] = (0, import_react32.useState)(0);
5819
+ const [selections, setSelections] = (0, import_react33.useState)(/* @__PURE__ */ new Map());
5820
+ const [page, setPage] = (0, import_react33.useState)(0);
5705
5821
  const clientState = parseClientState6(result?.clientStateJson);
5706
5822
  const results = clientState?.results ?? [];
5707
5823
  const isRevealed = results.length > 0 || result?.alreadyPlayed === true;
@@ -5811,7 +5927,7 @@ function DailyQuizGame({
5811
5927
  }
5812
5928
 
5813
5929
  // src/components/games/MatchdayPredictorGame.tsx
5814
- var import_react33 = require("react");
5930
+ var import_react34 = require("react");
5815
5931
  var import_jsx_runtime78 = require("react/jsx-runtime");
5816
5932
  var SCORER_SEARCH_THRESHOLD = 9;
5817
5933
  function parseClientState7(json) {
@@ -5907,12 +6023,12 @@ function MatchdayPredictorGame({
5907
6023
  onBack
5908
6024
  }) {
5909
6025
  const t = ui.games.predictor;
5910
- const [homeScore, setHomeScore] = (0, import_react33.useState)(0);
5911
- const [awayScore, setAwayScore] = (0, import_react33.useState)(0);
5912
- const [firstScorerPlayerId, setFirstScorerPlayerId] = (0, import_react33.useState)(null);
5913
- const [lastEdited, setLastEdited] = (0, import_react33.useState)("home");
5914
- const [query, setQuery] = (0, import_react33.useState)("");
5915
- const searchId = (0, import_react33.useId)();
6026
+ const [homeScore, setHomeScore] = (0, import_react34.useState)(0);
6027
+ const [awayScore, setAwayScore] = (0, import_react34.useState)(0);
6028
+ const [firstScorerPlayerId, setFirstScorerPlayerId] = (0, import_react34.useState)(null);
6029
+ const [lastEdited, setLastEdited] = (0, import_react34.useState)("home");
6030
+ const [query, setQuery] = (0, import_react34.useState)("");
6031
+ const searchId = (0, import_react34.useId)();
5916
6032
  const clientState = parseClientState7(result?.clientStateJson);
5917
6033
  const alreadyPlayed = result?.alreadyPlayed === true;
5918
6034
  const header = /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
@@ -6079,7 +6195,7 @@ function MatchdayPredictorGame({
6079
6195
  }
6080
6196
 
6081
6197
  // src/components/games/StartingXiGame.tsx
6082
- var import_react34 = require("react");
6198
+ var import_react35 = require("react");
6083
6199
  var import_jsx_runtime79 = require("react/jsx-runtime");
6084
6200
  var POINTS_PER_STARTER = 8;
6085
6201
  var XI_SIZE = 11;
@@ -6123,10 +6239,10 @@ function StartingXiGame({
6123
6239
  lockAtUtc
6124
6240
  }) {
6125
6241
  const t = ui.games.startingXi;
6126
- const [slotPlayers, setSlotPlayers] = (0, import_react34.useState)(() => Array(XI_SIZE).fill(null));
6127
- const [active, setActive] = (0, import_react34.useState)(0);
6128
- const [query, setQuery] = (0, import_react34.useState)("");
6129
- const searchId = (0, import_react34.useId)();
6242
+ const [slotPlayers, setSlotPlayers] = (0, import_react35.useState)(() => Array(XI_SIZE).fill(null));
6243
+ const [active, setActive] = (0, import_react35.useState)(0);
6244
+ const [query, setQuery] = (0, import_react35.useState)("");
6245
+ const searchId = (0, import_react35.useId)();
6130
6246
  const clientState = parseClientState8(result?.clientStateJson);
6131
6247
  const alreadyPlayed = result?.alreadyPlayed === true;
6132
6248
  const header = /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
@@ -6265,7 +6381,7 @@ function StartingXiGame({
6265
6381
  }
6266
6382
 
6267
6383
  // src/components/games/StatLadderGame.tsx
6268
- var import_react35 = require("react");
6384
+ var import_react36 = require("react");
6269
6385
  var import_jsx_runtime80 = require("react/jsx-runtime");
6270
6386
  function ladderClue(ladder) {
6271
6387
  const mystery = ladder.find((r) => r.rank === 2);
@@ -6302,9 +6418,9 @@ function StatLadderGame({
6302
6418
  onBack
6303
6419
  }) {
6304
6420
  const t = ui.games.statLadder;
6305
- const [query, setQuery] = (0, import_react35.useState)("");
6306
- const [selectedId, setSelectedId] = (0, import_react35.useState)(null);
6307
- const searchId = (0, import_react35.useId)();
6421
+ const [query, setQuery] = (0, import_react36.useState)("");
6422
+ const [selectedId, setSelectedId] = (0, import_react36.useState)(null);
6423
+ const searchId = (0, import_react36.useId)();
6308
6424
  const clientState = parseClientState9(result?.clientStateJson);
6309
6425
  const revealed = clientState != null;
6310
6426
  const header = /* @__PURE__ */ (0, import_jsx_runtime80.jsx)(GameHeader, { title: gameTitle({ gameId: "stat-ladder" }, ui.games.titles), eyebrow: t.eyebrow, onBack, backLabel: ui.back });
@@ -6509,8 +6625,8 @@ function GameScreenHost({
6509
6625
  ui
6510
6626
  }) {
6511
6627
  const { gameId, fixtureId, gameDay = "", lockAtUtc = null } = selected;
6512
- const [configJson, setConfigJson] = (0, import_react36.useState)(null);
6513
- (0, import_react36.useEffect)(() => {
6628
+ const [configJson, setConfigJson] = (0, import_react37.useState)(null);
6629
+ (0, import_react37.useEffect)(() => {
6514
6630
  setConfigJson(null);
6515
6631
  if (!conversation) return;
6516
6632
  let cancelled = false;
@@ -6533,12 +6649,12 @@ function GameScreenHost({
6533
6649
  fixtureId: fixtureId || void 0,
6534
6650
  locale: ui.games.lang
6535
6651
  });
6536
- (0, import_react36.useEffect)(() => {
6652
+ (0, import_react37.useEffect)(() => {
6537
6653
  if (conversation) hydrate();
6538
6654
  }, [conversation, gameId]);
6539
- const hydrateRef = (0, import_react36.useRef)(hydrate);
6655
+ const hydrateRef = (0, import_react37.useRef)(hydrate);
6540
6656
  hydrateRef.current = hydrate;
6541
- (0, import_react36.useEffect)(() => {
6657
+ (0, import_react37.useEffect)(() => {
6542
6658
  if (!FIXTURE_GAME_IDS.has(gameId)) return;
6543
6659
  if (rawStatus !== "pending") return;
6544
6660
  const timerId = setInterval(() => {
@@ -6673,8 +6789,8 @@ function GamesPanel({
6673
6789
  leaderboard,
6674
6790
  refreshHub
6675
6791
  }) {
6676
- const [selected, setSelected] = (0, import_react37.useState)(null);
6677
- (0, import_react37.useEffect)(() => {
6792
+ const [selected, setSelected] = (0, import_react38.useState)(null);
6793
+ (0, import_react38.useEffect)(() => {
6678
6794
  ensureGamesFonts();
6679
6795
  }, []);
6680
6796
  if (!loggedIn) return /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(GamesLoginWall, { ui });
@@ -6705,7 +6821,7 @@ function GamesPanel({
6705
6821
  }
6706
6822
 
6707
6823
  // src/components/Leaderboard.tsx
6708
- var import_react38 = require("react");
6824
+ var import_react39 = require("react");
6709
6825
 
6710
6826
  // src/components/LeaderboardRow.tsx
6711
6827
  var import_jsx_runtime84 = require("react/jsx-runtime");
@@ -6790,7 +6906,7 @@ function Leaderboard({
6790
6906
  ui,
6791
6907
  onPlay
6792
6908
  }) {
6793
- const [segment, setSegment] = (0, import_react38.useState)("weekly");
6909
+ const [segment, setSegment] = (0, import_react39.useState)("weekly");
6794
6910
  const myRow = segment === "weekly" ? data.myWeeklyRow : data.myAllTimeRow;
6795
6911
  const offBoard = myRow !== null && myRow !== void 0 && myRow.rank === -1;
6796
6912
  const isEmpty = segment === "weekly" ? data.weeklyTop.length === 0 : data.allTimeTop.length === 0;
@@ -6869,12 +6985,440 @@ function LeaderboardPanel({ loggedIn, data, loading, onPlay, ui }) {
6869
6985
  return /* @__PURE__ */ (0, import_jsx_runtime87.jsx)("div", { className: "ss-w-mon-list", children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Leaderboard, { data: data ?? EMPTY_PAYLOAD, ui, onPlay }) });
6870
6986
  }
6871
6987
 
6872
- // src/components/ChatPanel.tsx
6988
+ // src/components/PresetSpeedDial.tsx
6989
+ var import_react40 = require("react");
6873
6990
  var import_jsx_runtime88 = require("react/jsx-runtime");
6991
+ function PresetSpeedDial({ expanded, showDot, strings, onToggle, onOpen, rootRef, buttonRef }) {
6992
+ const pillsId = (0, import_react40.useId)();
6993
+ const tabIndex = expanded ? 0 : -1;
6994
+ return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { ref: rootRef, className: "ss-w-dial", children: [
6995
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(
6996
+ "button",
6997
+ {
6998
+ ref: buttonRef,
6999
+ type: "button",
7000
+ className: "ss-w-dial-btn",
7001
+ "aria-expanded": expanded,
7002
+ "aria-controls": pillsId,
7003
+ "aria-label": strings.dialName,
7004
+ onClick: onToggle,
7005
+ children: [
7006
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "ss-w-dial-icon ss-w-dial-icon--open", children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(SlidersIcon, {}) }),
7007
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "ss-w-dial-icon ss-w-dial-icon--close", children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(CloseIcon, {}) }),
7008
+ showDot && !expanded ? /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "ss-w-dial-dot", "aria-hidden": "true" }) : null
7009
+ ]
7010
+ }
7011
+ ),
7012
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { id: pillsId, className: "ss-w-dial-pills", "data-open": expanded ? "" : void 0, children: [
7013
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("button", { type: "button", className: "ss-w-dial-pill", tabIndex, onClick: () => onOpen("numbers"), children: [
7014
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(SlidersIcon, {}),
7015
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "ss-w-dial-pill-label", children: strings.setNumbers })
7016
+ ] }),
7017
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("button", { type: "button", className: "ss-w-dial-pill", tabIndex, onClick: () => onOpen("combo"), children: [
7018
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(ComboIcon, {}),
7019
+ /* @__PURE__ */ (0, import_jsx_runtime88.jsx)("span", { className: "ss-w-dial-pill-label", children: strings.buildCombo })
7020
+ ] })
7021
+ ] })
7022
+ ] });
7023
+ }
7024
+
7025
+ // src/components/PresetTools.tsx
7026
+ var import_react41 = require("react");
7027
+
7028
+ // src/presetGrid.ts
7029
+ var DEFAULT_SINGLES_LIMITS = [1.3, 1.5, 1.7, 2];
7030
+ var DEFAULT_SINGLES_FLOORS = [1.5, 2, 2.5, 3];
7031
+ var DEFAULT_COMBO_TOTALS = [3, 5, 10, 20, 50];
7032
+ var FIXED_SINGLES_COUNT = 3;
7033
+ var MIN_COMBO_LEGS = 2;
7034
+ var MAX_COMBO_LEGS = 6;
7035
+ var START = { maxPrice: 1.5, minPrice: 2, comboTotal: 5, comboLegs: 3 };
7036
+ var finite2 = (v) => typeof v === "number" && Number.isFinite(v);
7037
+ var cents = (n) => Math.round(n * 100);
7038
+ function samePrice(a, b) {
7039
+ return cents(a) === cents(b);
7040
+ }
7041
+ function isPresetGrid(v) {
7042
+ if (!v || typeof v !== "object") return false;
7043
+ const g = v;
7044
+ return Array.isArray(g.singles) && Array.isArray(g.singlesFrom) && Array.isArray(g.combos) && finite2(g.maxLegs);
7045
+ }
7046
+ function drawable(p) {
7047
+ return !!p && finite2(p.price) && finite2(p.matchOddsId) && finite2(p.matchId) && typeof p.homeTeamName === "string" && typeof p.awayTeamName === "string";
7048
+ }
7049
+ function choices(defaults, present) {
7050
+ const out = [];
7051
+ for (const n of [...defaults, ...present]) {
7052
+ if (finite2(n) && n > 0 && !out.some((m) => samePrice(m, n))) out.push(n);
7053
+ }
7054
+ return out.sort((a, b) => a - b);
7055
+ }
7056
+ function singlesLimits(grid) {
7057
+ return choices(DEFAULT_SINGLES_LIMITS, grid.singles.map((c) => c?.price));
7058
+ }
7059
+ function singlesFloors(grid) {
7060
+ return choices(DEFAULT_SINGLES_FLOORS, grid.singlesFrom.map((c) => c?.price));
7061
+ }
7062
+ function comboTotals(grid) {
7063
+ return choices(DEFAULT_COMBO_TOTALS, grid.combos.map((c) => c?.total));
7064
+ }
7065
+ function comboLegChoices(grid) {
7066
+ const max = Math.min(MAX_COMBO_LEGS, Math.floor(grid.maxLegs));
7067
+ const out = [];
7068
+ for (let n = MIN_COMBO_LEGS; n <= max; n++) out.push(n);
7069
+ return out;
7070
+ }
7071
+ function cycle(values, current) {
7072
+ if (values.length === 0) return current;
7073
+ const i = values.findIndex((v) => samePrice(v, current));
7074
+ return values[(i + 1) % values.length] ?? current;
7075
+ }
7076
+ function startValue(values, preferred) {
7077
+ return values.find((v) => samePrice(v, preferred)) ?? values[0] ?? preferred;
7078
+ }
7079
+ function nearest(values, target) {
7080
+ const first = values[0];
7081
+ if (first === void 0) return target;
7082
+ if (!(target > 0)) return first;
7083
+ const distance = (v) => Math.abs(Math.log(v / target));
7084
+ return values.reduce((best, v) => distance(v) < distance(best) ? v : best, first);
7085
+ }
7086
+ function clampLegs(values, legs) {
7087
+ const lo = values[0];
7088
+ const hi = values[values.length - 1];
7089
+ if (lo === void 0 || hi === void 0) return legs;
7090
+ return Math.min(Math.max(Math.round(legs), lo), hi);
7091
+ }
7092
+ function findSingles(cells, price, count) {
7093
+ const cell = cells.find((c) => !!c && finite2(c.price) && samePrice(c.price, price) && c.count === count);
7094
+ return cell && Array.isArray(cell.picks) && cell.picks.length === count && cell.picks.every(drawable) ? cell : null;
7095
+ }
7096
+ function findCombo(grid, total, legs) {
7097
+ const cell = grid.combos.find((c) => !!c && finite2(c.total) && samePrice(c.total, total) && c.legs === legs);
7098
+ const combo = cell?.combo;
7099
+ const whole = !!combo && typeof combo.multiplication === "string" && combo.multiplication !== "" && Array.isArray(combo.legs) && combo.legs.length === legs && combo.legs.every(drawable);
7100
+ return cell && whole ? cell : null;
7101
+ }
7102
+ function idsOf(picks) {
7103
+ return { matchOddsIds: picks.map((p) => p.matchOddsId), matchIds: picks.map((p) => p.matchId) };
7104
+ }
7105
+ function maxPriceAsk(cell, s) {
7106
+ return {
7107
+ message: s.maxPriceMessage(cell.count, cell.price),
7108
+ displayText: s.maxPriceDisplay(cell.count, cell.price),
7109
+ preset: { kind: "max_price", price: cell.price, count: cell.count, ...idsOf(cell.picks) }
7110
+ };
7111
+ }
7112
+ function minPriceAsk(cell, s) {
7113
+ return {
7114
+ message: s.minPriceMessage(cell.count, cell.price),
7115
+ displayText: s.minPriceDisplay(cell.count, cell.price),
7116
+ preset: { kind: "min_price", price: cell.price, count: cell.count, ...idsOf(cell.picks) }
7117
+ };
7118
+ }
7119
+ function comboAsk(cell, s) {
7120
+ return {
7121
+ message: s.comboMessage(cell.legs, cell.total),
7122
+ displayText: s.comboDisplay(cell.legs, cell.total),
7123
+ preset: { kind: "combo", total: cell.total, legs: cell.legs, ...idsOf(cell.combo.legs) }
7124
+ };
7125
+ }
7126
+
7127
+ // src/components/PresetTools.tsx
7128
+ var import_jsx_runtime89 = require("react/jsx-runtime");
7129
+ var FOCUSABLE = 'button:not([disabled]), [href], input:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
7130
+ function PresetToolModal({ title, closeLabel, onClose, children, footer }) {
7131
+ const dialogRef = (0, import_react41.useRef)(null);
7132
+ const titleId = (0, import_react41.useId)();
7133
+ (0, import_react41.useEffect)(() => {
7134
+ dialogRef.current?.focus();
7135
+ }, []);
7136
+ (0, import_react41.useEffect)(() => {
7137
+ const onKey = (e) => {
7138
+ if (e.key !== "Escape") return;
7139
+ e.stopPropagation();
7140
+ e.preventDefault();
7141
+ onClose();
7142
+ };
7143
+ window.addEventListener("keydown", onKey, true);
7144
+ return () => window.removeEventListener("keydown", onKey, true);
7145
+ }, [onClose]);
7146
+ const trapTab = (e) => {
7147
+ if (e.key !== "Tab") return;
7148
+ const dialog = dialogRef.current;
7149
+ if (!dialog) return;
7150
+ const items = Array.from(dialog.querySelectorAll(FOCUSABLE));
7151
+ const first = items[0];
7152
+ const last = items[items.length - 1];
7153
+ if (!first || !last) {
7154
+ e.preventDefault();
7155
+ return;
7156
+ }
7157
+ const active = dialog.getRootNode().activeElement;
7158
+ if (e.shiftKey && (active === first || active === dialog)) {
7159
+ e.preventDefault();
7160
+ last.focus();
7161
+ } else if (!e.shiftKey && active === last) {
7162
+ e.preventDefault();
7163
+ first.focus();
7164
+ }
7165
+ };
7166
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { className: "ss-w-tool-backdrop", children: /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { ref: dialogRef, className: "ss-w-tool", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, tabIndex: -1, onKeyDown: trapTab, children: [
7167
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: "ss-w-tool-head", children: [
7168
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("h2", { id: titleId, className: "ss-w-tool-title", children: title }),
7169
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("button", { type: "button", className: "ss-w-tool-close", "aria-label": closeLabel, onClick: onClose, children: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(CloseIcon, {}) })
7170
+ ] }),
7171
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { className: "ss-w-tool-body", children }),
7172
+ footer ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { className: "ss-w-tool-foot", children: footer }) : null
7173
+ ] }) });
7174
+ }
7175
+ function fillTemplate(template, slots) {
7176
+ return template.split(/(\{[a-z]+\})/).filter((part) => part !== "").map((part, i) => {
7177
+ const key = /^\{([a-z]+)\}$/.exec(part)?.[1];
7178
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(import_react41.Fragment, { children: key !== void 0 && key in slots ? slots[key] : part }, i);
7179
+ });
7180
+ }
7181
+ function NumberButton({ value, label, onCycle }) {
7182
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("button", { type: "button", className: "ss-w-tool-num", "aria-label": label, onClick: onCycle, children: value });
7183
+ }
7184
+ function SendButton({ label, display, disabled, onClick }) {
7185
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("button", { type: "button", className: "ss-w-tool-send", "aria-label": `${label}: ${display}`, disabled, onClick, children: label });
7186
+ }
7187
+ function PickList({ picks, formatKickoff }) {
7188
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("ul", { className: "ss-w-tool-picks", role: "list", children: picks.map((p) => /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("li", { className: "ss-w-tool-pick", children: [
7189
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("span", { className: "ss-w-tool-pick-match", children: [
7190
+ p.homeTeamName,
7191
+ " \u2013 ",
7192
+ p.awayTeamName
7193
+ ] }),
7194
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("span", { className: "ss-w-tool-pick-sel", children: [
7195
+ p.marketLabel,
7196
+ ": ",
7197
+ p.selectionLabel,
7198
+ p.line ? ` ${p.line}` : ""
7199
+ ] }),
7200
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("span", { className: "ss-w-tool-pick-kick", children: formatKickoff(p.kickoffUtc) }),
7201
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("span", { className: "ss-w-tool-pick-price", children: p.price.toFixed(2) })
7202
+ ] }, p.matchOddsId)) });
7203
+ }
7204
+ function ComboPreview({ cell, formatKickoff }) {
7205
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(import_jsx_runtime89.Fragment, { children: [
7206
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(PickList, { picks: cell.combo.legs, formatKickoff }),
7207
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { className: "ss-w-tool-math", children: cell.combo.multiplication })
7208
+ ] });
7209
+ }
7210
+ function Unavailable({ text }) {
7211
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("p", { className: "ss-w-tool-unavailable", children: text });
7212
+ }
7213
+ function SetNumbersTool({ grid, strings, streaming, formatKickoff, onSend, onClose }) {
7214
+ const limits = singlesLimits(grid);
7215
+ const floors = singlesFloors(grid);
7216
+ const totals = comboTotals(grid);
7217
+ const legChoices = comboLegChoices(grid);
7218
+ const count = FIXED_SINGLES_COUNT;
7219
+ const [maxPrice, setMaxPrice] = (0, import_react41.useState)(() => startValue(limits, START.maxPrice));
7220
+ const [minPrice, setMinPrice] = (0, import_react41.useState)(() => startValue(floors, START.minPrice));
7221
+ const [total, setTotal] = (0, import_react41.useState)(() => startValue(totals, START.comboTotal));
7222
+ const [legs, setLegs] = (0, import_react41.useState)(() => startValue(legChoices, START.comboLegs));
7223
+ const [announcement, setAnnouncement] = (0, import_react41.useState)("");
7224
+ const maxCell = findSingles(grid.singles, maxPrice, count);
7225
+ const minCell = findSingles(grid.singlesFrom, minPrice, count);
7226
+ const comboCell = findCombo(grid, total, legs);
7227
+ const announce = (display, available) => setAnnouncement(available ? display : `${display}. ${strings.unavailable}`);
7228
+ const nextMax = () => {
7229
+ const v = cycle(limits, maxPrice);
7230
+ setMaxPrice(v);
7231
+ announce(strings.maxPriceDisplay(count, v), !!findSingles(grid.singles, v, count));
7232
+ };
7233
+ const nextMin = () => {
7234
+ const v = cycle(floors, minPrice);
7235
+ setMinPrice(v);
7236
+ announce(strings.minPriceDisplay(count, v), !!findSingles(grid.singlesFrom, v, count));
7237
+ };
7238
+ const nextLegs = () => {
7239
+ const v = cycle(legChoices, legs);
7240
+ setLegs(v);
7241
+ announce(strings.comboDisplay(v, total), !!findCombo(grid, total, v));
7242
+ };
7243
+ const nextTotal = () => {
7244
+ const v = cycle(totals, total);
7245
+ setTotal(v);
7246
+ announce(strings.comboDisplay(legs, v), !!findCombo(grid, v, legs));
7247
+ };
7248
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(PresetToolModal, { title: strings.setNumbers, closeLabel: strings.close, onClose, children: [
7249
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("section", { className: "ss-w-tool-row", children: [
7250
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("p", { className: "ss-w-tool-ask", children: fillTemplate(strings.maxPriceRow, {
7251
+ count: String(count),
7252
+ price: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(NumberButton, { value: maxPrice.toFixed(2), label: strings.changeNumber(maxPrice.toFixed(2)), onCycle: nextMax })
7253
+ }) }),
7254
+ maxCell ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(PickList, { picks: maxCell.picks, formatKickoff }) : /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(Unavailable, { text: strings.unavailable }),
7255
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(SendButton, { label: strings.send, display: strings.maxPriceDisplay(count, maxPrice), disabled: !maxCell || streaming, onClick: () => {
7256
+ if (maxCell) onSend(maxPriceAsk(maxCell, strings));
7257
+ } })
7258
+ ] }),
7259
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("section", { className: "ss-w-tool-row", children: [
7260
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("p", { className: "ss-w-tool-ask", children: fillTemplate(strings.minPriceRow, {
7261
+ count: String(count),
7262
+ price: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(NumberButton, { value: minPrice.toFixed(2), label: strings.changeNumber(minPrice.toFixed(2)), onCycle: nextMin })
7263
+ }) }),
7264
+ minCell ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(PickList, { picks: minCell.picks, formatKickoff }) : /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(Unavailable, { text: strings.unavailable }),
7265
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(SendButton, { label: strings.send, display: strings.minPriceDisplay(count, minPrice), disabled: !minCell || streaming, onClick: () => {
7266
+ if (minCell) onSend(minPriceAsk(minCell, strings));
7267
+ } })
7268
+ ] }),
7269
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("section", { className: "ss-w-tool-row", children: [
7270
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("p", { className: "ss-w-tool-ask", children: fillTemplate(strings.comboRow, {
7271
+ legs: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(NumberButton, { value: String(legs), label: strings.changeNumber(String(legs)), onCycle: nextLegs }),
7272
+ total: /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(NumberButton, { value: total.toFixed(1), label: strings.changeNumber(total.toFixed(1)), onCycle: nextTotal })
7273
+ }) }),
7274
+ comboCell ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(ComboPreview, { cell: comboCell, formatKickoff }) : /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(Unavailable, { text: strings.unavailable }),
7275
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(SendButton, { label: strings.send, display: strings.comboDisplay(legs, total), disabled: !comboCell || streaming, onClick: () => {
7276
+ if (comboCell) onSend(comboAsk(comboCell, strings));
7277
+ } })
7278
+ ] }),
7279
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("p", { className: "ss-w-vh", "aria-live": "polite", children: announcement })
7280
+ ] });
7281
+ }
7282
+ function BuildComboTool({ grid, strings, streaming, formatKickoff, initialTotal, initialLegs, onSend, onClose }) {
7283
+ const totals = comboTotals(grid);
7284
+ const legChoices = comboLegChoices(grid);
7285
+ const [total, setTotal] = (0, import_react41.useState)(() => initialTotal !== void 0 ? nearest(totals, initialTotal) : startValue(totals, START.comboTotal));
7286
+ const [legs, setLegs] = (0, import_react41.useState)(() => initialLegs !== void 0 ? clampLegs(legChoices, initialLegs) : startValue(legChoices, START.comboLegs));
7287
+ const [announcement, setAnnouncement] = (0, import_react41.useState)("");
7288
+ const cell = findCombo(grid, total, legs);
7289
+ const minLegs = legChoices[0] ?? legs;
7290
+ const maxLegs = legChoices[legChoices.length - 1] ?? legs;
7291
+ const display = strings.comboDisplay(legs, total);
7292
+ const choose = (nextTotal, nextLegs) => {
7293
+ setTotal(nextTotal);
7294
+ setLegs(nextLegs);
7295
+ const d = strings.comboDisplay(nextLegs, nextTotal);
7296
+ setAnnouncement(findCombo(grid, nextTotal, nextLegs) ? d : `${d}. ${strings.unavailable}`);
7297
+ };
7298
+ const footer = /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(SendButton, { label: strings.send, display, disabled: !cell || streaming, onClick: () => {
7299
+ if (cell) onSend(comboAsk(cell, strings));
7300
+ } });
7301
+ return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(PresetToolModal, { title: strings.buildCombo, closeLabel: strings.close, onClose, footer, children: [
7302
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: "ss-w-tool-section", role: "group", "aria-label": strings.totalLabel, children: [
7303
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("span", { className: "ss-w-tool-label", "aria-hidden": "true", children: strings.totalLabel }),
7304
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { className: "ss-w-tool-chips", children: totals.map((t) => /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("button", { type: "button", className: "ss-w-tool-chip", "aria-pressed": samePrice(t, total), onClick: () => choose(t, legs), children: t.toFixed(1) }, t)) })
7305
+ ] }),
7306
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: "ss-w-tool-section", role: "group", "aria-label": strings.matchesLabel, children: [
7307
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("span", { className: "ss-w-tool-label", "aria-hidden": "true", children: strings.matchesLabel }),
7308
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: "ss-w-tool-stepper", children: [
7309
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("button", { type: "button", className: "ss-w-tool-step", "aria-label": strings.fewerMatches, disabled: legs <= minLegs, onClick: () => choose(total, legs - 1), children: "\u2212" }),
7310
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("span", { className: "ss-w-tool-count", children: legs }),
7311
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("button", { type: "button", className: "ss-w-tool-step", "aria-label": strings.moreMatches, disabled: legs >= maxLegs, onClick: () => choose(total, legs + 1), children: "+" })
7312
+ ] })
7313
+ ] }),
7314
+ cell ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(ComboPreview, { cell, formatKickoff }) : /* @__PURE__ */ (0, import_jsx_runtime89.jsx)(Unavailable, { text: strings.unavailable }),
7315
+ /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("p", { className: "ss-w-vh", "aria-live": "polite", children: announcement })
7316
+ ] });
7317
+ }
7318
+
7319
+ // src/usePresetDial.ts
7320
+ var import_react42 = require("react");
7321
+ function sendInFlight(messages, isStreaming) {
7322
+ const last = messages[messages.length - 1];
7323
+ return isStreaming && !!last && last.role === "assistant" && !last.done && !last.isGreeting;
7324
+ }
7325
+ function lastUserId(messages) {
7326
+ for (let i = messages.length - 1; i >= 0; i--) {
7327
+ const m = messages[i];
7328
+ if (m && m.role === "user") return m.id;
7329
+ }
7330
+ return null;
7331
+ }
7332
+ var SCROLL_KEYS = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "PageUp", "PageDown", "Home", "End", " "]);
7333
+ function usePresetDial({ layerRef, dialRef, dialButtonRef, messages, isStreaming, activeTab }) {
7334
+ const [expanded, setExpanded] = (0, import_react42.useState)(() => !sendInFlight(messages, isStreaming));
7335
+ const fold = (0, import_react42.useCallback)(() => setExpanded(false), []);
7336
+ const toggle = (0, import_react42.useCallback)(() => setExpanded((e) => !e), []);
7337
+ const currentUserId = lastUserId(messages);
7338
+ const userIdRef = (0, import_react42.useRef)(currentUserId);
7339
+ (0, import_react42.useEffect)(() => {
7340
+ if (currentUserId === userIdRef.current) return;
7341
+ userIdRef.current = currentUserId;
7342
+ setExpanded(false);
7343
+ }, [currentUserId]);
7344
+ const tabRef = (0, import_react42.useRef)(activeTab);
7345
+ (0, import_react42.useEffect)(() => {
7346
+ if (activeTab === tabRef.current) return;
7347
+ tabRef.current = activeTab;
7348
+ setExpanded(false);
7349
+ }, [activeTab]);
7350
+ const expandedRef = (0, import_react42.useRef)(expanded);
7351
+ expandedRef.current = expanded;
7352
+ (0, import_react42.useEffect)(() => {
7353
+ const onEscape = (e) => {
7354
+ const dial = dialRef.current;
7355
+ if (e.key !== "Escape" || !dial || !e.composedPath().includes(dial)) return;
7356
+ e.stopPropagation();
7357
+ e.preventDefault();
7358
+ if (!expandedRef.current) return;
7359
+ setExpanded(false);
7360
+ dialButtonRef.current?.focus();
7361
+ };
7362
+ window.addEventListener("keydown", onEscape, true);
7363
+ return () => window.removeEventListener("keydown", onEscape, true);
7364
+ }, [dialRef, dialButtonRef]);
7365
+ (0, import_react42.useEffect)(() => {
7366
+ if (!expanded) return;
7367
+ const layer = layerRef.current;
7368
+ const inDial = (e) => {
7369
+ const dial = dialRef.current;
7370
+ return !!dial && e.composedPath().includes(dial);
7371
+ };
7372
+ const inList = (e) => e.composedPath().some((n) => n instanceof Element && n.classList.contains("ss-w-list"));
7373
+ const onPress = (e) => {
7374
+ if (!inDial(e)) setExpanded(false);
7375
+ };
7376
+ const onScrollGesture = (e) => {
7377
+ if (inList(e) && !inDial(e)) setExpanded(false);
7378
+ };
7379
+ const onKey = (e) => {
7380
+ if (SCROLL_KEYS.has(e.key) && inList(e)) setExpanded(false);
7381
+ };
7382
+ const doc = layer?.ownerDocument ?? document;
7383
+ doc.addEventListener("pointerdown", onPress, true);
7384
+ layer?.addEventListener("wheel", onScrollGesture, { passive: true });
7385
+ layer?.addEventListener("touchmove", onScrollGesture, { passive: true });
7386
+ window.addEventListener("keydown", onKey, true);
7387
+ return () => {
7388
+ doc.removeEventListener("pointerdown", onPress, true);
7389
+ layer?.removeEventListener("wheel", onScrollGesture);
7390
+ layer?.removeEventListener("touchmove", onScrollGesture);
7391
+ window.removeEventListener("keydown", onKey, true);
7392
+ };
7393
+ }, [expanded, layerRef, dialRef]);
7394
+ return { expanded, toggle, fold };
7395
+ }
7396
+
7397
+ // src/presetSeen.ts
7398
+ var KEY = "ss-w-presets-tool-opened";
7399
+ var openedThisPage = false;
7400
+ function readToolSeen() {
7401
+ if (openedThisPage) return true;
7402
+ try {
7403
+ return window.localStorage.getItem(KEY) === "1";
7404
+ } catch {
7405
+ return false;
7406
+ }
7407
+ }
7408
+ function markToolSeen() {
7409
+ openedThisPage = true;
7410
+ try {
7411
+ window.localStorage.setItem(KEY, "1");
7412
+ } catch {
7413
+ }
7414
+ }
7415
+
7416
+ // src/components/ChatPanel.tsx
7417
+ var import_jsx_runtime90 = require("react/jsx-runtime");
6874
7418
  var FULLSCREEN_QUERY = "(max-width: 639px)";
6875
7419
  function useIsModal() {
6876
- const [modal, setModal] = (0, import_react39.useState)(false);
6877
- (0, import_react39.useEffect)(() => {
7420
+ const [modal, setModal] = (0, import_react43.useState)(false);
7421
+ (0, import_react43.useEffect)(() => {
6878
7422
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
6879
7423
  const mql = window.matchMedia(FULLSCREEN_QUERY);
6880
7424
  const sync = () => setModal(mql.matches);
@@ -6884,10 +7428,11 @@ function useIsModal() {
6884
7428
  }, []);
6885
7429
  return modal;
6886
7430
  }
6887
- function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, gamesResult, leaderboardResult, loggedIn, conversation }) {
7431
+ var HIDDEN_LAYER_STYLE = { position: "absolute", inset: 0, visibility: "hidden", display: "flex", flexDirection: "column" };
7432
+ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, gamesResult, leaderboardResult, loggedIn, conversation, presetGrid }) {
6888
7433
  const { state, close, stop, send, reset } = controller;
6889
7434
  const isModal = useIsModal();
6890
- (0, import_react39.useEffect)(() => {
7435
+ (0, import_react43.useEffect)(() => {
6891
7436
  const onKey = (e) => {
6892
7437
  if (e.key === "Escape") close();
6893
7438
  };
@@ -6902,11 +7447,66 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
6902
7447
  monitor?.setActiveTab("chat");
6903
7448
  send(text, { grounding });
6904
7449
  };
6905
- (0, import_react39.useEffect)(() => {
7450
+ (0, import_react43.useEffect)(() => {
6906
7451
  if (monitor && monitor.activeTab !== "chat" && state.isStreaming) monitor.markChatActivity();
6907
7452
  }, [monitor, state.isStreaming, state.messages.length]);
6908
- return /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
6909
- /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
7453
+ const onChatTab = !monitor || monitor.activeTab === "chat";
7454
+ const layerRef = (0, import_react43.useRef)(null);
7455
+ const dialRef = (0, import_react43.useRef)(null);
7456
+ const dialButtonRef = (0, import_react43.useRef)(null);
7457
+ const inputRef = (0, import_react43.useRef)(null);
7458
+ const focusAfterCloseRef = (0, import_react43.useRef)(null);
7459
+ const [tool, setTool] = (0, import_react43.useState)(null);
7460
+ const [toolSeen, setToolSeen] = (0, import_react43.useState)(readToolSeen);
7461
+ const dial = usePresetDial({
7462
+ layerRef,
7463
+ dialRef,
7464
+ dialButtonRef,
7465
+ messages: state.messages,
7466
+ isStreaming: state.isStreaming,
7467
+ activeTab: monitor?.activeTab ?? "chat"
7468
+ });
7469
+ const { fold } = dial;
7470
+ const showDial = !!presetGrid && onChatTab && !tool;
7471
+ const openTool = (0, import_react43.useCallback)((next) => {
7472
+ fold();
7473
+ markToolSeen();
7474
+ setToolSeen(true);
7475
+ setTool(next);
7476
+ }, [fold]);
7477
+ const onPill = (0, import_react43.useCallback)((kind) => openTool(kind === "numbers" ? { kind: "numbers" } : { kind: "combo" }), [openTool]);
7478
+ const toolsValue = (0, import_react43.useMemo)(
7479
+ () => presetGrid ? { openBuilder: (total, legs) => openTool({ kind: "combo", total, legs }), changeLabel: ui.presets.changeCombo } : null,
7480
+ [presetGrid, openTool, ui.presets.changeCombo]
7481
+ );
7482
+ const closeTool = (0, import_react43.useCallback)(() => {
7483
+ focusAfterCloseRef.current = "dial";
7484
+ setTool(null);
7485
+ }, []);
7486
+ const sendAsk = (0, import_react43.useCallback)((ask) => {
7487
+ focusAfterCloseRef.current = "composer";
7488
+ setTool(null);
7489
+ monitor?.setActiveTab("chat");
7490
+ send(ask.message, { displayText: ask.displayText, preset: ask.preset });
7491
+ }, [monitor, send]);
7492
+ const formatKickoff = (0, import_react43.useCallback)((kickoffUtc) => {
7493
+ const k = ui.bettingKickoff(kickoffUtc);
7494
+ return `${k.dayLabel} ${k.time}`;
7495
+ }, [ui]);
7496
+ (0, import_react43.useEffect)(() => {
7497
+ if (tool) return;
7498
+ const target = focusAfterCloseRef.current;
7499
+ focusAfterCloseRef.current = null;
7500
+ if (target === "dial") dialButtonRef.current?.focus();
7501
+ else if (target === "composer") inputRef.current?.focus();
7502
+ }, [tool]);
7503
+ (0, import_react43.useEffect)(() => {
7504
+ if (!tool || onChatTab && presetGrid) return;
7505
+ focusAfterCloseRef.current = "composer";
7506
+ setTool(null);
7507
+ }, [tool, onChatTab, presetGrid]);
7508
+ return /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
7509
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
6910
7510
  ChatHeader,
6911
7511
  {
6912
7512
  onClose: close,
@@ -6919,7 +7519,7 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
6919
7519
  moreOptionsLabel: ui.moreOptions
6920
7520
  }
6921
7521
  ),
6922
- monitor && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
7522
+ monitor && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
6923
7523
  TabBar,
6924
7524
  {
6925
7525
  monitor,
@@ -6931,17 +7531,26 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
6931
7531
  gamesEnabled: gamesResult?.enabled ?? false
6932
7532
  }
6933
7533
  ),
6934
- /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
6935
- "div",
6936
- {
6937
- style: !monitor || monitor.activeTab === "chat" ? { display: "contents" } : { position: "absolute", inset: 0, visibility: "hidden", display: "flex", flexDirection: "column" },
6938
- children: /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(MessageList, { controller, ui, slip, onDashboardTap, monitor })
6939
- }
6940
- ),
6941
- monitor && discovery && monitor.activeTab === "today" && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(TodayPanel, { discovery, ui, monitor }),
6942
- monitor && monitor.activeTab === "following" && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(MatchesPanel, { monitor, ui, slip, onAsk: handleAsk }),
6943
- monitor && monitor.activeTab === "odds" && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(OddsPanel, { monitor, ui, slip }),
6944
- monitor && monitor.activeTab === "games" && gamesResult && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
7534
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("div", { ref: layerRef, className: "ss-w-chat-layer", "data-dial": presetGrid ? "" : void 0, style: onChatTab ? void 0 : HIDDEN_LAYER_STYLE, children: [
7535
+ /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(PresetToolsContext.Provider, { value: toolsValue, children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(MessageList, { controller, ui, slip, onDashboardTap, monitor }) }),
7536
+ showDial ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
7537
+ PresetSpeedDial,
7538
+ {
7539
+ expanded: dial.expanded,
7540
+ showDot: !toolSeen,
7541
+ strings: ui.presets,
7542
+ onToggle: dial.toggle,
7543
+ onOpen: onPill,
7544
+ rootRef: dialRef,
7545
+ buttonRef: dialButtonRef
7546
+ }
7547
+ ) : null,
7548
+ tool && presetGrid && onChatTab ? tool.kind === "numbers" ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(SetNumbersTool, { grid: presetGrid, strings: ui.presets, streaming: state.isStreaming, formatKickoff, onSend: sendAsk, onClose: closeTool }) : /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(BuildComboTool, { grid: presetGrid, strings: ui.presets, streaming: state.isStreaming, formatKickoff, initialTotal: tool.total, initialLegs: tool.legs, onSend: sendAsk, onClose: closeTool }) : null
7549
+ ] }),
7550
+ monitor && discovery && monitor.activeTab === "today" && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(TodayPanel, { discovery, ui, monitor }),
7551
+ monitor && monitor.activeTab === "following" && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(MatchesPanel, { monitor, ui, slip, onAsk: handleAsk }),
7552
+ monitor && monitor.activeTab === "odds" && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(OddsPanel, { monitor, ui, slip }),
7553
+ monitor && monitor.activeTab === "games" && gamesResult && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
6945
7554
  GamesPanel,
6946
7555
  {
6947
7556
  loggedIn: !!loggedIn,
@@ -6955,7 +7564,7 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
6955
7564
  }
6956
7565
  }
6957
7566
  ),
6958
- monitor && monitor.activeTab === "leaderboard" && leaderboardResult && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(
7567
+ monitor && monitor.activeTab === "leaderboard" && leaderboardResult && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
6959
7568
  LeaderboardPanel,
6960
7569
  {
6961
7570
  loggedIn: !!loggedIn,
@@ -6965,7 +7574,20 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
6965
7574
  ui
6966
7575
  }
6967
7576
  ),
6968
- (!monitor || monitor.activeTab !== "games" && monitor.activeTab !== "leaderboard") && /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: handleSend, onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
7577
+ (!monitor || monitor.activeTab !== "games" && monitor.activeTab !== "leaderboard") && /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(
7578
+ ChatInput,
7579
+ {
7580
+ ref: inputRef,
7581
+ placeholder: ui.placeholder,
7582
+ streaming: state.isStreaming,
7583
+ onSend: handleSend,
7584
+ onStop: stop,
7585
+ stopLabel: ui.stopResponse,
7586
+ sendLabel: ui.sendMessage,
7587
+ disabled: !!tool,
7588
+ onType: fold
7589
+ }
7590
+ )
6969
7591
  ] });
6970
7592
  }
6971
7593
 
@@ -7174,15 +7796,15 @@ function toSlipEvent(name, entry) {
7174
7796
  // src/useHostEvents.ts
7175
7797
  function useHostEvents(opts) {
7176
7798
  const { host, operatorId, currentUser, logger, isOpen, closePanel, onOpenMatch, openMatchLabel } = opts;
7177
- const [added, setAdded] = (0, import_react40.useState)({});
7178
- const [settled, setSettled] = (0, import_react40.useState)({});
7179
- const markAdded = (0, import_react40.useCallback)((matchOddsId) => {
7799
+ const [added, setAdded] = (0, import_react44.useState)({});
7800
+ const [settled, setSettled] = (0, import_react44.useState)({});
7801
+ const markAdded = (0, import_react44.useCallback)((matchOddsId) => {
7180
7802
  setAdded((prev) => prev[matchOddsId] ? prev : { ...prev, [matchOddsId]: true });
7181
7803
  }, []);
7182
- const settle = (0, import_react40.useCallback)((matchOddsId) => {
7804
+ const settle = (0, import_react44.useCallback)((matchOddsId) => {
7183
7805
  setSettled((prev) => prev[matchOddsId] ? prev : { ...prev, [matchOddsId]: true });
7184
7806
  }, []);
7185
- const clearMarks = (0, import_react40.useCallback)((ids) => {
7807
+ const clearMarks = (0, import_react44.useCallback)((ids) => {
7186
7808
  const drop = (prev) => {
7187
7809
  if (!ids.some((id) => prev[id])) return prev;
7188
7810
  const next = { ...prev };
@@ -7193,26 +7815,26 @@ function useHostEvents(opts) {
7193
7815
  setSettled(drop);
7194
7816
  }, []);
7195
7817
  const hasHost = host !== void 0;
7196
- const ledger = (0, import_react40.useMemo)(
7818
+ const ledger = (0, import_react44.useMemo)(
7197
7819
  () => hasHost ? createSlipLedger(`ss-w-slip:${operatorId}`) : null,
7198
7820
  [hasHost, operatorId]
7199
7821
  );
7200
- const currentUserRef = (0, import_react40.useRef)(currentUser);
7822
+ const currentUserRef = (0, import_react44.useRef)(currentUser);
7201
7823
  currentUserRef.current = currentUser;
7202
- const recordAdd = (0, import_react40.useCallback)((add) => {
7824
+ const recordAdd = (0, import_react44.useCallback)((add) => {
7203
7825
  ledger?.record({ id: add.selection.matchOddsId, market: add.market, ...add.pick, user: currentUserRef.current });
7204
7826
  }, [ledger]);
7205
- const slipState = (0, import_react40.useMemo)(
7827
+ const slipState = (0, import_react44.useMemo)(
7206
7828
  () => ({ added, settled, settle, recordAdd }),
7207
7829
  [added, settled, settle, recordAdd]
7208
7830
  );
7209
7831
  const hasOpenMatch = typeof onOpenMatch === "function";
7210
7832
  const tracksViews = hasHost || hasOpenMatch;
7211
- const views = (0, import_react40.useMemo)(
7833
+ const views = (0, import_react44.useMemo)(
7212
7834
  () => tracksViews ? createMatchViews(`ss-w-views:${operatorId}`, { user: currentUser }) : null,
7213
7835
  [tracksViews, operatorId, currentUser]
7214
7836
  );
7215
- const sinkRef = (0, import_react40.useRef)(() => {
7837
+ const sinkRef = (0, import_react44.useRef)(() => {
7216
7838
  });
7217
7839
  sinkRef.current = (command) => {
7218
7840
  if (command.kind === "matchOpened") {
@@ -7233,11 +7855,11 @@ function useHostEvents(opts) {
7233
7855
  logger?.log(toSlipEvent(name, entry));
7234
7856
  }
7235
7857
  };
7236
- (0, import_react40.useEffect)(() => {
7858
+ (0, import_react44.useEffect)(() => {
7237
7859
  if (!host) return;
7238
7860
  return attachHost(host, (command) => sinkRef.current(command));
7239
7861
  }, [host]);
7240
- (0, import_react40.useEffect)(() => {
7862
+ (0, import_react44.useEffect)(() => {
7241
7863
  if (!isOpen || !views) return;
7242
7864
  const held = views.markOpened();
7243
7865
  if (held.length === 0) return;
@@ -7246,7 +7868,7 @@ function useHostEvents(opts) {
7246
7868
  }
7247
7869
  void logger?.flushNow();
7248
7870
  }, [isOpen, views, logger]);
7249
- const openMatchRef = (0, import_react40.useRef)(() => {
7871
+ const openMatchRef = (0, import_react44.useRef)(() => {
7250
7872
  });
7251
7873
  openMatchRef.current = (matchId) => {
7252
7874
  if (views?.view(matchId) === "log") {
@@ -7260,7 +7882,7 @@ function useHostEvents(opts) {
7260
7882
  }
7261
7883
  if (typeof window.matchMedia === "function" && window.matchMedia(FULLSCREEN_QUERY).matches) closePanel();
7262
7884
  };
7263
- const matchLink = (0, import_react40.useMemo)(
7885
+ const matchLink = (0, import_react44.useMemo)(
7264
7886
  () => hasOpenMatch ? { open: (matchId) => openMatchRef.current(matchId), label: openMatchLabel } : null,
7265
7887
  [hasOpenMatch, openMatchLabel]
7266
7888
  );
@@ -7328,7 +7950,7 @@ function themeAttrs(ui) {
7328
7950
  }
7329
7951
 
7330
7952
  // src/useChatStream.ts
7331
- var import_react42 = require("react");
7953
+ var import_react46 = require("react");
7332
7954
  var import_widget_sdk4 = require("@sensiblestats/widget-sdk");
7333
7955
 
7334
7956
  // src/reducer.ts
@@ -7488,7 +8110,7 @@ function reducer(state, action) {
7488
8110
  }
7489
8111
 
7490
8112
  // src/useLiveDashboardPoll.ts
7491
- var import_react41 = require("react");
8113
+ var import_react45 = require("react");
7492
8114
  var POLL_INTERVAL_MS = 6e4;
7493
8115
  var LEAD_MS = 12e4;
7494
8116
  var GIVE_UP_MS = 30 * 6e4;
@@ -7530,13 +8152,13 @@ function findEarliestWakeupMs(messages) {
7530
8152
  return earliest;
7531
8153
  }
7532
8154
  function useLiveDashboardPoll({ messages, dispatch, getLiveDashboard }) {
7533
- const [, forceUpdate] = (0, import_react41.useReducer)((x) => x + 1, 0);
8155
+ const [, forceUpdate] = (0, import_react45.useReducer)((x) => x + 1, 0);
7534
8156
  const anchor = findAnchor(messages);
7535
- const anchorRef = (0, import_react41.useRef)(anchor);
8157
+ const anchorRef = (0, import_react45.useRef)(anchor);
7536
8158
  anchorRef.current = anchor;
7537
- const inFlightRef = (0, import_react41.useRef)(false);
8159
+ const inFlightRef = (0, import_react45.useRef)(false);
7538
8160
  const anchorKey = anchor ? `${anchor.messageId}:${anchor.kind}` : null;
7539
- (0, import_react41.useEffect)(() => {
8161
+ (0, import_react45.useEffect)(() => {
7540
8162
  if (!anchorKey || !anchorRef.current) return;
7541
8163
  const tick = async () => {
7542
8164
  if (inFlightRef.current) return;
@@ -7572,7 +8194,7 @@ function useLiveDashboardPoll({ messages, dispatch, getLiveDashboard }) {
7572
8194
  }, POLL_INTERVAL_MS);
7573
8195
  return () => window.clearInterval(id);
7574
8196
  }, [anchorKey, dispatch, getLiveDashboard]);
7575
- (0, import_react41.useEffect)(() => {
8197
+ (0, import_react45.useEffect)(() => {
7576
8198
  if (anchorKey !== null) return;
7577
8199
  const wakeAt = findEarliestWakeupMs(messages);
7578
8200
  if (wakeAt === null) return;
@@ -7637,7 +8259,7 @@ function seedIdCounter(messages) {
7637
8259
  return max + 1;
7638
8260
  }
7639
8261
  function useChatStream(conversation, starterTexts, greetingMessage, storageKey) {
7640
- const boot = (0, import_react42.useMemo)(() => {
8262
+ const boot = (0, import_react46.useMemo)(() => {
7641
8263
  const payload = storageKey ? readConversation(storageKey, Date.now()) : null;
7642
8264
  const restored = payload != null && payload !== "expired" && payload !== "corrupt";
7643
8265
  const base = initialState(starterTexts.map(chipFromText));
@@ -7646,27 +8268,27 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
7646
8268
  }
7647
8269
  return { state: base, restored: false, stale: payload === "expired" || payload === "corrupt" };
7648
8270
  }, []);
7649
- const [state, dispatch] = (0, import_react42.useReducer)(reducer, boot.state);
7650
- const idRef = (0, import_react42.useRef)(seedIdCounter(boot.state.messages));
7651
- const handleRef = (0, import_react42.useRef)(null);
7652
- const fallbackTimerRef = (0, import_react42.useRef)(null);
7653
- const stateRef = (0, import_react42.useRef)(state);
8271
+ const [state, dispatch] = (0, import_react46.useReducer)(reducer, boot.state);
8272
+ const idRef = (0, import_react46.useRef)(seedIdCounter(boot.state.messages));
8273
+ const handleRef = (0, import_react46.useRef)(null);
8274
+ const fallbackTimerRef = (0, import_react46.useRef)(null);
8275
+ const stateRef = (0, import_react46.useRef)(state);
7654
8276
  stateRef.current = state;
7655
- const greetedRef = (0, import_react42.useRef)(boot.restored);
7656
- const hydratedRef = (0, import_react42.useRef)(false);
7657
- const restoreSeededRef = (0, import_react42.useRef)(false);
8277
+ const greetedRef = (0, import_react46.useRef)(boot.restored);
8278
+ const hydratedRef = (0, import_react46.useRef)(false);
8279
+ const restoreSeededRef = (0, import_react46.useRef)(false);
7658
8280
  if (!restoreSeededRef.current) {
7659
8281
  restoreSeededRef.current = true;
7660
8282
  if (boot.restored && boot.state.conversationId) conversation.conversationId = boot.state.conversationId;
7661
8283
  }
7662
8284
  const nextId = () => `m${idRef.current++}`;
7663
- const clearFallbackTimer = (0, import_react42.useCallback)(() => {
8285
+ const clearFallbackTimer = (0, import_react46.useCallback)(() => {
7664
8286
  if (fallbackTimerRef.current != null) {
7665
8287
  clearTimeout(fallbackTimerRef.current);
7666
8288
  fallbackTimerRef.current = null;
7667
8289
  }
7668
8290
  }, []);
7669
- const armFallbackTimer = (0, import_react42.useCallback)((onFire) => {
8291
+ const armFallbackTimer = (0, import_react46.useCallback)((onFire) => {
7670
8292
  clearFallbackTimer();
7671
8293
  fallbackTimerRef.current = setTimeout(() => {
7672
8294
  fallbackTimerRef.current = null;
@@ -7675,7 +8297,7 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
7675
8297
  onFire();
7676
8298
  }, STREAM_FALLBACK_TIMEOUT_MS);
7677
8299
  }, [clearFallbackTimer]);
7678
- const consume = (0, import_react42.useCallback)((handle, onError) => {
8300
+ const consume = (0, import_react46.useCallback)((handle, onError) => {
7679
8301
  ;
7680
8302
  (async () => {
7681
8303
  try {
@@ -7693,7 +8315,7 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
7693
8315
  }
7694
8316
  })();
7695
8317
  }, [clearFallbackTimer]);
7696
- const run = (0, import_react42.useCallback)((input, displayText) => {
8318
+ const run = (0, import_react46.useCallback)((input, displayText) => {
7697
8319
  handleRef.current?.cancel();
7698
8320
  clearFallbackTimer();
7699
8321
  dispatch({ kind: "USER_SENT", input, userId: nextId(), assistantId: nextId(), displayText });
@@ -7705,7 +8327,7 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
7705
8327
  });
7706
8328
  consume(handle, (err) => dispatch({ kind: "STREAM_ERROR", error: err }));
7707
8329
  }, [conversation, consume, clearFallbackTimer, armFallbackTimer]);
7708
- const send = (0, import_react42.useCallback)((text, opts) => {
8330
+ const send = (0, import_react46.useCallback)((text, opts) => {
7709
8331
  const trimmed = text.trim();
7710
8332
  if (!trimmed) return;
7711
8333
  run({
@@ -7713,14 +8335,15 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
7713
8335
  actionId: opts?.actionId,
7714
8336
  actionRef: opts?.actionRef,
7715
8337
  grounding: opts?.grounding,
7716
- clientCapabilities: [...CLIENT_CAPABILITIES]
8338
+ clientCapabilities: [...CLIENT_CAPABILITIES],
8339
+ ...opts?.preset ? { preset: opts.preset } : {}
7717
8340
  }, opts?.displayText);
7718
8341
  }, [run]);
7719
- const retry = (0, import_react42.useCallback)(() => {
8342
+ const retry = (0, import_react46.useCallback)(() => {
7720
8343
  const last = stateRef.current.lastUserInput;
7721
8344
  if (last) run(last, stateRef.current.lastDisplayText ?? void 0);
7722
8345
  }, [run]);
7723
- const fireGreeting = (0, import_react42.useCallback)(() => {
8346
+ const fireGreeting = (0, import_react46.useCallback)(() => {
7724
8347
  const message = greetingMessage?.trim();
7725
8348
  if (!message) return;
7726
8349
  try {
@@ -7735,15 +8358,15 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
7735
8358
  dispatch({ kind: "STOP" });
7736
8359
  }
7737
8360
  }, [greetingMessage, conversation, consume, clearFallbackTimer, armFallbackTimer]);
7738
- (0, import_react42.useEffect)(() => {
8361
+ (0, import_react46.useEffect)(() => {
7739
8362
  if (greetedRef.current) return;
7740
8363
  greetedRef.current = true;
7741
8364
  fireGreeting();
7742
8365
  }, [fireGreeting]);
7743
- (0, import_react42.useEffect)(() => {
8366
+ (0, import_react46.useEffect)(() => {
7744
8367
  if (boot.stale && storageKey) clearConversation(storageKey);
7745
8368
  }, []);
7746
- (0, import_react42.useEffect)(() => {
8369
+ (0, import_react46.useEffect)(() => {
7747
8370
  if (!storageKey || state.isStreaming) return;
7748
8371
  if (hydratedRef.current) {
7749
8372
  hydratedRef.current = false;
@@ -7760,7 +8383,7 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
7760
8383
  if (persistable.length === 0) return;
7761
8384
  writeConversation(storageKey, state.conversationId, persistable, Date.now());
7762
8385
  }, [storageKey, state.isStreaming, state.messages, state.conversationId]);
7763
- (0, import_react42.useEffect)(() => {
8386
+ (0, import_react46.useEffect)(() => {
7764
8387
  if (!storageKey) return;
7765
8388
  const onStorage = (e) => {
7766
8389
  if (e.key !== storageKey || e.storageArea !== window.localStorage) return;
@@ -7784,25 +8407,25 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
7784
8407
  window.addEventListener("storage", onStorage);
7785
8408
  return () => window.removeEventListener("storage", onStorage);
7786
8409
  }, [storageKey, conversation, fireGreeting]);
7787
- const open = (0, import_react42.useCallback)(() => dispatch({ kind: "OPEN" }), []);
7788
- const close = (0, import_react42.useCallback)(() => {
8410
+ const open = (0, import_react46.useCallback)(() => dispatch({ kind: "OPEN" }), []);
8411
+ const close = (0, import_react46.useCallback)(() => {
7789
8412
  handleRef.current?.cancel();
7790
8413
  handleRef.current = null;
7791
8414
  clearFallbackTimer();
7792
8415
  dispatch({ kind: "CLOSE" });
7793
8416
  }, [clearFallbackTimer]);
7794
- const stop = (0, import_react42.useCallback)(() => {
8417
+ const stop = (0, import_react46.useCallback)(() => {
7795
8418
  handleRef.current?.cancel();
7796
8419
  handleRef.current = null;
7797
8420
  clearFallbackTimer();
7798
8421
  dispatch({ kind: "STOP" });
7799
8422
  }, [clearFallbackTimer]);
7800
- const toggle = (0, import_react42.useCallback)(() => {
8423
+ const toggle = (0, import_react46.useCallback)(() => {
7801
8424
  if (stateRef.current.isOpen) close();
7802
8425
  else open();
7803
8426
  }, [close, open]);
7804
- const dismissPopup = (0, import_react42.useCallback)(() => dispatch({ kind: "DISMISS_POPUP" }), []);
7805
- const reset = (0, import_react42.useCallback)(() => {
8427
+ const dismissPopup = (0, import_react46.useCallback)(() => dispatch({ kind: "DISMISS_POPUP" }), []);
8428
+ const reset = (0, import_react46.useCallback)(() => {
7806
8429
  handleRef.current?.cancel();
7807
8430
  handleRef.current = null;
7808
8431
  clearFallbackTimer();
@@ -7813,18 +8436,18 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
7813
8436
  greetedRef.current = true;
7814
8437
  fireGreeting();
7815
8438
  }, [storageKey, conversation, clearFallbackTimer, fireGreeting]);
7816
- const getLiveDashboard = (0, import_react42.useCallback)(() => conversation.getLiveDashboard(), [conversation]);
8439
+ const getLiveDashboard = (0, import_react46.useCallback)(() => conversation.getLiveDashboard(), [conversation]);
7817
8440
  useLiveDashboardPoll({ messages: state.messages, dispatch, getLiveDashboard });
7818
8441
  return { state, open, close, stop, toggle, send, retry, dismissPopup, reset, _dispatch: dispatch };
7819
8442
  }
7820
8443
 
7821
8444
  // src/useLiveMonitor.ts
7822
- var import_react44 = require("react");
8445
+ var import_react48 = require("react");
7823
8446
 
7824
8447
  // src/useTabs.ts
7825
- var import_react43 = require("react");
8448
+ var import_react47 = require("react");
7826
8449
  function useTabs(initial = "chat") {
7827
- const [activeTab, setActiveTab] = (0, import_react43.useState)(initial);
8450
+ const [activeTab, setActiveTab] = (0, import_react47.useState)(initial);
7828
8451
  return { activeTab, setActiveTab };
7829
8452
  }
7830
8453
 
@@ -7849,38 +8472,38 @@ function phaseForPin(snapshot, pin) {
7849
8472
  return snapshot.markets.find((mk) => mk.key === pin.key)?.phase;
7850
8473
  }
7851
8474
  function useLiveMonitor(opts) {
7852
- const sessionIdRef = (0, import_react44.useRef)(crypto.randomUUID());
7853
- const [pins, setPins] = (0, import_react44.useState)([]);
7854
- const [store] = (0, import_react44.useState)(() => createPinStore(opts.storageKey, sessionIdRef.current, setPins));
7855
- const [enabled, setEnabled] = (0, import_react44.useState)(true);
7856
- const [snapshot, setSnapshotState] = (0, import_react44.useState)(null);
8475
+ const sessionIdRef = (0, import_react48.useRef)(crypto.randomUUID());
8476
+ const [pins, setPins] = (0, import_react48.useState)([]);
8477
+ const [store] = (0, import_react48.useState)(() => createPinStore(opts.storageKey, sessionIdRef.current, setPins));
8478
+ const [enabled, setEnabled] = (0, import_react48.useState)(true);
8479
+ const [snapshot, setSnapshotState] = (0, import_react48.useState)(null);
7857
8480
  const { activeTab, setActiveTab: setActiveTabState } = useTabs("chat");
7858
- const [chatDot, setChatDot] = (0, import_react44.useState)(false);
7859
- const [matchesDot, setMatchesDot] = (0, import_react44.useState)(false);
7860
- const [oddsDot, setOddsDot] = (0, import_react44.useState)(false);
7861
- const [todayDot, setTodayDot] = (0, import_react44.useState)(false);
7862
- const [lastViewedPrices, setLastViewedPrices] = (0, import_react44.useState)({});
7863
- const [openMarket, setOpenMarket] = (0, import_react44.useState)({});
7864
- const [marketOdds, setMarketOdds] = (0, import_react44.useState)({});
7865
- const pinsRef = (0, import_react44.useRef)(pins);
8481
+ const [chatDot, setChatDot] = (0, import_react48.useState)(false);
8482
+ const [matchesDot, setMatchesDot] = (0, import_react48.useState)(false);
8483
+ const [oddsDot, setOddsDot] = (0, import_react48.useState)(false);
8484
+ const [todayDot, setTodayDot] = (0, import_react48.useState)(false);
8485
+ const [lastViewedPrices, setLastViewedPrices] = (0, import_react48.useState)({});
8486
+ const [openMarket, setOpenMarket] = (0, import_react48.useState)({});
8487
+ const [marketOdds, setMarketOdds] = (0, import_react48.useState)({});
8488
+ const pinsRef = (0, import_react48.useRef)(pins);
7866
8489
  pinsRef.current = pins;
7867
- const watchesRef = (0, import_react44.useRef)(opts.watches ?? []);
8490
+ const watchesRef = (0, import_react48.useRef)(opts.watches ?? []);
7868
8491
  watchesRef.current = opts.watches ?? [];
7869
- const activeTabRef = (0, import_react44.useRef)(activeTab);
8492
+ const activeTabRef = (0, import_react48.useRef)(activeTab);
7870
8493
  activeTabRef.current = activeTab;
7871
- const snapshotRef = (0, import_react44.useRef)(snapshot);
8494
+ const snapshotRef = (0, import_react48.useRef)(snapshot);
7872
8495
  snapshotRef.current = snapshot;
7873
- const lastViewedPricesRef = (0, import_react44.useRef)(lastViewedPrices);
8496
+ const lastViewedPricesRef = (0, import_react48.useRef)(lastViewedPrices);
7874
8497
  lastViewedPricesRef.current = lastViewedPrices;
7875
- const openMarketRef = (0, import_react44.useRef)(openMarket);
8498
+ const openMarketRef = (0, import_react48.useRef)(openMarket);
7876
8499
  openMarketRef.current = openMarket;
7877
- const marketOddsRef = (0, import_react44.useRef)(marketOdds);
8500
+ const marketOddsRef = (0, import_react48.useRef)(marketOdds);
7878
8501
  marketOddsRef.current = marketOdds;
7879
8502
  const hasToday = opts.hasToday ?? false;
7880
8503
  const marketKey = (matchId, dev) => `${matchId}:${dev}`;
7881
- const getMarketOddsRef = (0, import_react44.useRef)(opts.getMarketOdds);
8504
+ const getMarketOddsRef = (0, import_react48.useRef)(opts.getMarketOdds);
7882
8505
  getMarketOddsRef.current = opts.getMarketOdds;
7883
- const fetchMarket = (0, import_react44.useCallback)((matchId, dev) => {
8506
+ const fetchMarket = (0, import_react48.useCallback)((matchId, dev) => {
7884
8507
  const key = marketKey(matchId, dev);
7885
8508
  const existing = marketOddsRef.current[key];
7886
8509
  if (existing?.loading) return;
@@ -7899,7 +8522,7 @@ function useLiveMonitor(opts) {
7899
8522
  setMarketOdds((prev) => ({ ...prev, [key]: entry }));
7900
8523
  });
7901
8524
  }, []);
7902
- const toggleMarket = (0, import_react44.useCallback)((matchId, dev) => {
8525
+ const toggleMarket = (0, import_react48.useCallback)((matchId, dev) => {
7903
8526
  const key = marketKey(matchId, dev);
7904
8527
  const wasOpen = openMarketRef.current[matchId] === dev;
7905
8528
  const nextValue = wasOpen ? null : dev;
@@ -7910,10 +8533,10 @@ function useLiveMonitor(opts) {
7910
8533
  fetchMarket(matchId, dev);
7911
8534
  }
7912
8535
  }, [fetchMarket]);
7913
- const retryMarket = (0, import_react44.useCallback)((matchId, dev) => {
8536
+ const retryMarket = (0, import_react48.useCallback)((matchId, dev) => {
7914
8537
  fetchMarket(matchId, dev);
7915
8538
  }, [fetchMarket]);
7916
- const applySnapshot = (0, import_react44.useCallback)((next) => {
8539
+ const applySnapshot = (0, import_react48.useCallback)((next) => {
7917
8540
  if (next === null) {
7918
8541
  setEnabled(false);
7919
8542
  return;
@@ -7976,13 +8599,13 @@ function useLiveMonitor(opts) {
7976
8599
  snapshotRef.current = next;
7977
8600
  setSnapshotState(next);
7978
8601
  }, [store]);
7979
- const inFlightRef = (0, import_react44.useRef)(false);
7980
- const timerRef = (0, import_react44.useRef)(void 0);
7981
- const disabledRef = (0, import_react44.useRef)(false);
7982
- const mountedRef = (0, import_react44.useRef)(false);
7983
- const getLiveMonitorRef = (0, import_react44.useRef)(opts.getLiveMonitor);
8602
+ const inFlightRef = (0, import_react48.useRef)(false);
8603
+ const timerRef = (0, import_react48.useRef)(void 0);
8604
+ const disabledRef = (0, import_react48.useRef)(false);
8605
+ const mountedRef = (0, import_react48.useRef)(false);
8606
+ const getLiveMonitorRef = (0, import_react48.useRef)(opts.getLiveMonitor);
7984
8607
  getLiveMonitorRef.current = opts.getLiveMonitor;
7985
- const tick = (0, import_react44.useCallback)(async () => {
8608
+ const tick = (0, import_react48.useCallback)(async () => {
7986
8609
  const allPins = [...pinsRef.current, ...watchesRef.current];
7987
8610
  if (inFlightRef.current || disabledRef.current || allPins.length === 0) return;
7988
8611
  inFlightRef.current = true;
@@ -8006,7 +8629,7 @@ function useLiveMonitor(opts) {
8006
8629
  scheduleRef.current();
8007
8630
  }
8008
8631
  }, [applySnapshot, fetchMarket]);
8009
- const scheduleRef = (0, import_react44.useRef)(() => {
8632
+ const scheduleRef = (0, import_react48.useRef)(() => {
8010
8633
  });
8011
8634
  scheduleRef.current = () => {
8012
8635
  window.clearTimeout(timerRef.current);
@@ -8016,14 +8639,14 @@ function useLiveMonitor(opts) {
8016
8639
  const hinted = snapshotRef.current ? snapshotRef.current.nextPollSeconds * 1e3 : fallbackMs;
8017
8640
  timerRef.current = window.setTimeout(() => void tick(), Math.max(MIN_POLL_MS, hinted));
8018
8641
  };
8019
- (0, import_react44.useEffect)(() => {
8642
+ (0, import_react48.useEffect)(() => {
8020
8643
  mountedRef.current = true;
8021
8644
  return () => {
8022
8645
  mountedRef.current = false;
8023
8646
  };
8024
8647
  }, []);
8025
- const prevPinsLengthRef = (0, import_react44.useRef)(0);
8026
- (0, import_react44.useEffect)(() => {
8648
+ const prevPinsLengthRef = (0, import_react48.useRef)(0);
8649
+ (0, import_react48.useEffect)(() => {
8027
8650
  const wasEmpty = prevPinsLengthRef.current === 0 && watchesRef.current.length === 0;
8028
8651
  prevPinsLengthRef.current = pins.length;
8029
8652
  if (pins.length === 0 && watchesRef.current.length === 0) {
@@ -8032,27 +8655,27 @@ function useLiveMonitor(opts) {
8032
8655
  }
8033
8656
  if (wasEmpty) void tick();
8034
8657
  }, [pins.length, tick]);
8035
- const prevWatchesLengthRef = (0, import_react44.useRef)(0);
8658
+ const prevWatchesLengthRef = (0, import_react48.useRef)(0);
8036
8659
  const watchesLength = opts.watches?.length ?? 0;
8037
- (0, import_react44.useEffect)(() => {
8660
+ (0, import_react48.useEffect)(() => {
8038
8661
  const wasEmpty = prevWatchesLengthRef.current === 0 && pinsRef.current.length === 0;
8039
8662
  prevWatchesLengthRef.current = watchesLength;
8040
8663
  if (wasEmpty && watchesLength > 0) void tick();
8041
8664
  }, [watchesLength, tick]);
8042
- (0, import_react44.useEffect)(() => {
8665
+ (0, import_react48.useEffect)(() => {
8043
8666
  const matchPins = pins.filter((p) => p.kind === "match").length;
8044
8667
  const marketPins = pins.length - matchPins;
8045
8668
  if (activeTab === "following" && matchPins === 0) setActiveTabState(hasToday ? "today" : "chat");
8046
8669
  if (activeTab === "odds" && marketPins === 0) setActiveTabState(hasToday ? "today" : "chat");
8047
8670
  if (activeTab === "today" && !hasToday) setActiveTabState("chat");
8048
8671
  }, [pins, activeTab, hasToday]);
8049
- const prevIsOpenRef = (0, import_react44.useRef)(false);
8050
- (0, import_react44.useEffect)(() => {
8672
+ const prevIsOpenRef = (0, import_react48.useRef)(false);
8673
+ (0, import_react48.useEffect)(() => {
8051
8674
  const wasClosed = !prevIsOpenRef.current;
8052
8675
  prevIsOpenRef.current = opts.isOpen;
8053
8676
  if (wasClosed && opts.isOpen) void tick();
8054
8677
  }, [opts.isOpen, tick]);
8055
- (0, import_react44.useEffect)(() => {
8678
+ (0, import_react48.useEffect)(() => {
8056
8679
  const onVisibility = () => {
8057
8680
  if (document.visibilityState === "hidden") window.clearTimeout(timerRef.current);
8058
8681
  else void tick();
@@ -8063,7 +8686,7 @@ function useLiveMonitor(opts) {
8063
8686
  window.clearTimeout(timerRef.current);
8064
8687
  };
8065
8688
  }, [tick]);
8066
- const setActiveTab = (0, import_react44.useCallback)((tab) => {
8689
+ const setActiveTab = (0, import_react48.useCallback)((tab) => {
8067
8690
  setActiveTabState(tab);
8068
8691
  if (tab === "chat") setChatDot(false);
8069
8692
  if (tab === "today") setTodayDot(false);
@@ -8084,14 +8707,14 @@ function useLiveMonitor(opts) {
8084
8707
  void tick();
8085
8708
  }
8086
8709
  }, [tick]);
8087
- const togglePin = (0, import_react44.useCallback)((pin) => {
8710
+ const togglePin = (0, import_react48.useCallback)((pin) => {
8088
8711
  const result = store.toggle(pin);
8089
8712
  pinsRef.current = store.pins;
8090
8713
  return result;
8091
8714
  }, [store]);
8092
- const isPinned = (0, import_react44.useCallback)((key) => pinsRef.current.some((p) => p.key === key), []);
8093
- const markChatActivity = (0, import_react44.useCallback)(() => setChatDot(true), []);
8094
- const markTodayActivity = (0, import_react44.useCallback)(() => {
8715
+ const isPinned = (0, import_react48.useCallback)((key) => pinsRef.current.some((p) => p.key === key), []);
8716
+ const markChatActivity = (0, import_react48.useCallback)(() => setChatDot(true), []);
8717
+ const markTodayActivity = (0, import_react48.useCallback)(() => {
8095
8718
  if (activeTabRef.current !== "today") setTodayDot(true);
8096
8719
  }, []);
8097
8720
  return {
@@ -8118,7 +8741,7 @@ function useLiveMonitor(opts) {
8118
8741
  }
8119
8742
 
8120
8743
  // src/useMatchDiscovery.ts
8121
- var import_react45 = require("react");
8744
+ var import_react49 = require("react");
8122
8745
 
8123
8746
  // src/scheduleCache.ts
8124
8747
  var PREFIX = "ss-w-schedule:";
@@ -8206,27 +8829,27 @@ function localDateOf(now) {
8206
8829
  return `${y}-${m}-${d}`;
8207
8830
  }
8208
8831
  function useMatchDiscovery(opts) {
8209
- const [groups, setGroups] = (0, import_react45.useState)([]);
8210
- const [states, setStates] = (0, import_react45.useState)({});
8211
- const [failures, setFailures] = (0, import_react45.useState)(0);
8212
- const [expanded, setExpanded] = (0, import_react45.useState)({});
8213
- const [touched, setTouched] = (0, import_react45.useState)({});
8214
- const groupsRef = (0, import_react45.useRef)(groups);
8832
+ const [groups, setGroups] = (0, import_react49.useState)([]);
8833
+ const [states, setStates] = (0, import_react49.useState)({});
8834
+ const [failures, setFailures] = (0, import_react49.useState)(0);
8835
+ const [expanded, setExpanded] = (0, import_react49.useState)({});
8836
+ const [touched, setTouched] = (0, import_react49.useState)({});
8837
+ const groupsRef = (0, import_react49.useRef)(groups);
8215
8838
  groupsRef.current = groups;
8216
- const statesRef = (0, import_react45.useRef)(states);
8839
+ const statesRef = (0, import_react49.useRef)(states);
8217
8840
  statesRef.current = states;
8218
- const getStatesRef = (0, import_react45.useRef)(opts.getStates);
8841
+ const getStatesRef = (0, import_react49.useRef)(opts.getStates);
8219
8842
  getStatesRef.current = opts.getStates;
8220
8843
  const pinnedKey = opts.pinnedMatchIds.join(",");
8221
- const [localDate, setLocalDate] = (0, import_react45.useState)(() => localDateOf(/* @__PURE__ */ new Date()));
8222
- (0, import_react45.useEffect)(() => {
8844
+ const [localDate, setLocalDate] = (0, import_react49.useState)(() => localDateOf(/* @__PURE__ */ new Date()));
8845
+ (0, import_react49.useEffect)(() => {
8223
8846
  const id = setInterval(() => {
8224
8847
  const next = localDateOf(/* @__PURE__ */ new Date());
8225
8848
  setLocalDate((prev) => prev === next ? prev : next);
8226
8849
  }, ROLLOVER_CHECK_MS);
8227
8850
  return () => clearInterval(id);
8228
8851
  }, []);
8229
- (0, import_react45.useEffect)(() => {
8852
+ (0, import_react49.useEffect)(() => {
8230
8853
  let cancelled = false;
8231
8854
  const utcOffsetMinutes = -(/* @__PURE__ */ new Date()).getTimezoneOffset();
8232
8855
  const cached = readSchedule(opts.operatorId, localDate, Date.now());
@@ -8251,7 +8874,7 @@ function useMatchDiscovery(opts) {
8251
8874
  cancelled = true;
8252
8875
  };
8253
8876
  }, [opts.operatorId, localDate]);
8254
- (0, import_react45.useEffect)(() => {
8877
+ (0, import_react49.useEffect)(() => {
8255
8878
  setExpanded((prev) => {
8256
8879
  const next = {};
8257
8880
  for (const group of groups) {
@@ -8262,7 +8885,7 @@ function useMatchDiscovery(opts) {
8262
8885
  return next;
8263
8886
  });
8264
8887
  }, [groups, pinnedKey, touched]);
8265
- const tick = (0, import_react45.useCallback)(async () => {
8888
+ const tick = (0, import_react49.useCallback)(async () => {
8266
8889
  const ids = dueMatchIds(groupsRef.current, statesRef.current, Date.now());
8267
8890
  if (ids.length === 0) return;
8268
8891
  try {
@@ -8278,7 +8901,7 @@ function useMatchDiscovery(opts) {
8278
8901
  setFailures((n) => n + 1);
8279
8902
  }
8280
8903
  }, []);
8281
- (0, import_react45.useEffect)(() => {
8904
+ (0, import_react49.useEffect)(() => {
8282
8905
  if (!opts.isOpen) return;
8283
8906
  void tick();
8284
8907
  const id = setInterval(() => {
@@ -8287,11 +8910,11 @@ function useMatchDiscovery(opts) {
8287
8910
  return () => clearInterval(id);
8288
8911
  }, [opts.isOpen, tick, groups]);
8289
8912
  const stale = failures >= STALE_AFTER_FAILURES;
8290
- const liveCount = (0, import_react45.useMemo)(
8913
+ const liveCount = (0, import_react49.useMemo)(
8291
8914
  () => stale ? 0 : Object.values(states).filter((s) => s.phase === "live").length,
8292
8915
  [states, stale]
8293
8916
  );
8294
- const toggleGroup = (0, import_react45.useCallback)((competitionId) => {
8917
+ const toggleGroup = (0, import_react49.useCallback)((competitionId) => {
8295
8918
  setTouched((prev) => ({ ...prev, [competitionId]: true }));
8296
8919
  setExpanded((prev) => ({ ...prev, [competitionId]: !prev[competitionId] }));
8297
8920
  }, []);
@@ -8299,25 +8922,25 @@ function useMatchDiscovery(opts) {
8299
8922
  }
8300
8923
 
8301
8924
  // src/useGames.ts
8302
- var import_react46 = require("react");
8925
+ var import_react50 = require("react");
8303
8926
  var GAMES_POLL_MS = 6e4;
8304
8927
  var DEFAULT_TZ = "Europe/London";
8305
8928
  function computeGameDay(timezone) {
8306
8929
  return new Intl.DateTimeFormat("en-CA", { timeZone: timezone }).format(/* @__PURE__ */ new Date());
8307
8930
  }
8308
8931
  function useGames(conversation, { activeTab, operatorTimezone }) {
8309
- const [enabled, setEnabled] = (0, import_react46.useState)(false);
8310
- const [games, setGames] = (0, import_react46.useState)([]);
8311
- const [loading, setLoading] = (0, import_react46.useState)(false);
8312
- const [refreshCount, setRefreshCount] = (0, import_react46.useState)(0);
8313
- const inFlightRef = (0, import_react46.useRef)(false);
8314
- const hardOffRef = (0, import_react46.useRef)(false);
8315
- const conversationRef = (0, import_react46.useRef)(conversation);
8932
+ const [enabled, setEnabled] = (0, import_react50.useState)(false);
8933
+ const [games, setGames] = (0, import_react50.useState)([]);
8934
+ const [loading, setLoading] = (0, import_react50.useState)(false);
8935
+ const [refreshCount, setRefreshCount] = (0, import_react50.useState)(0);
8936
+ const inFlightRef = (0, import_react50.useRef)(false);
8937
+ const hardOffRef = (0, import_react50.useRef)(false);
8938
+ const conversationRef = (0, import_react50.useRef)(conversation);
8316
8939
  conversationRef.current = conversation;
8317
- const tzRef = (0, import_react46.useRef)(operatorTimezone ?? DEFAULT_TZ);
8940
+ const tzRef = (0, import_react50.useRef)(operatorTimezone ?? DEFAULT_TZ);
8318
8941
  tzRef.current = operatorTimezone ?? DEFAULT_TZ;
8319
8942
  const ready = conversation != null;
8320
- (0, import_react46.useEffect)(() => {
8943
+ (0, import_react50.useEffect)(() => {
8321
8944
  if (hardOffRef.current) return;
8322
8945
  let active = true;
8323
8946
  const doFetch = async () => {
@@ -8360,22 +8983,22 @@ function useGames(conversation, { activeTab, operatorTimezone }) {
8360
8983
  if (intervalId !== void 0) window.clearInterval(intervalId);
8361
8984
  };
8362
8985
  }, [activeTab, refreshCount, ready]);
8363
- const refresh = (0, import_react46.useCallback)(() => {
8986
+ const refresh = (0, import_react50.useCallback)(() => {
8364
8987
  setRefreshCount((c) => c + 1);
8365
8988
  }, []);
8366
8989
  return { enabled, games, loading, refresh };
8367
8990
  }
8368
8991
 
8369
8992
  // src/useLeaderboard.ts
8370
- var import_react47 = require("react");
8993
+ var import_react51 = require("react");
8371
8994
  function useLeaderboard(conversation, { activeTab }) {
8372
- const [data, setData] = (0, import_react47.useState)(null);
8373
- const [loading, setLoading] = (0, import_react47.useState)(false);
8374
- const [refreshCount, setRefreshCount] = (0, import_react47.useState)(0);
8375
- const inFlightRef = (0, import_react47.useRef)(false);
8376
- const conversationRef = (0, import_react47.useRef)(conversation);
8995
+ const [data, setData] = (0, import_react51.useState)(null);
8996
+ const [loading, setLoading] = (0, import_react51.useState)(false);
8997
+ const [refreshCount, setRefreshCount] = (0, import_react51.useState)(0);
8998
+ const inFlightRef = (0, import_react51.useRef)(false);
8999
+ const conversationRef = (0, import_react51.useRef)(conversation);
8377
9000
  conversationRef.current = conversation;
8378
- (0, import_react47.useEffect)(() => {
9001
+ (0, import_react51.useEffect)(() => {
8379
9002
  if (activeTab !== "leaderboard" && activeTab !== "games") return;
8380
9003
  let active = true;
8381
9004
  const doFetch = async () => {
@@ -8398,18 +9021,65 @@ function useLeaderboard(conversation, { activeTab }) {
8398
9021
  inFlightRef.current = false;
8399
9022
  };
8400
9023
  }, [activeTab, refreshCount]);
8401
- const refresh = (0, import_react47.useCallback)(() => {
9024
+ const refresh = (0, import_react51.useCallback)(() => {
8402
9025
  setRefreshCount((c) => c + 1);
8403
9026
  }, []);
8404
9027
  return { data, loading, refresh };
8405
9028
  }
8406
9029
 
9030
+ // src/usePresetGrid.ts
9031
+ var import_react52 = require("react");
9032
+ var GRID_MAX_AGE_MS = 6e5;
9033
+ function usePresetGrid({ getPricePresets, isOpen, locale, now = Date.now }) {
9034
+ const [grid, setGrid] = (0, import_react52.useState)(null);
9035
+ const keptRef = (0, import_react52.useRef)(null);
9036
+ const localeRef = (0, import_react52.useRef)(locale);
9037
+ const inFlightRef = (0, import_react52.useRef)(false);
9038
+ const mountedRef = (0, import_react52.useRef)(true);
9039
+ const getRef = (0, import_react52.useRef)(getPricePresets);
9040
+ getRef.current = getPricePresets;
9041
+ const nowRef = (0, import_react52.useRef)(now);
9042
+ nowRef.current = now;
9043
+ (0, import_react52.useEffect)(() => {
9044
+ mountedRef.current = true;
9045
+ return () => {
9046
+ mountedRef.current = false;
9047
+ };
9048
+ }, []);
9049
+ (0, import_react52.useEffect)(() => {
9050
+ if (localeRef.current !== locale) {
9051
+ localeRef.current = locale;
9052
+ keptRef.current = null;
9053
+ setGrid(null);
9054
+ }
9055
+ if (!isOpen || inFlightRef.current) return;
9056
+ const kept = keptRef.current;
9057
+ const age = kept ? nowRef.current() - Date.parse(kept.builtAtUtc) : Number.NaN;
9058
+ if (age <= GRID_MAX_AGE_MS) return;
9059
+ inFlightRef.current = true;
9060
+ Promise.resolve().then(() => getRef.current(locale)).then((body) => {
9061
+ if (body === null) {
9062
+ keptRef.current = null;
9063
+ if (mountedRef.current) setGrid(null);
9064
+ return;
9065
+ }
9066
+ if (!isPresetGrid(body)) return;
9067
+ keptRef.current = body;
9068
+ if (mountedRef.current) setGrid(body);
9069
+ }).catch(() => {
9070
+ }).finally(() => {
9071
+ inFlightRef.current = false;
9072
+ });
9073
+ }, [isOpen, locale]);
9074
+ return grid;
9075
+ }
9076
+
8407
9077
  // src/popupTelemetry.ts
8408
- var import_react48 = require("react");
9078
+ var import_react53 = require("react");
8409
9079
  function usePopupTelemetry(log, isOpen, popupText, dismissPopup) {
8410
- const shownRef = (0, import_react48.useRef)(/* @__PURE__ */ new Set());
8411
- const closedTextRef = (0, import_react48.useRef)(null);
8412
- (0, import_react48.useEffect)(() => {
9080
+ const shownRef = (0, import_react53.useRef)(/* @__PURE__ */ new Set());
9081
+ const closedTextRef = (0, import_react53.useRef)(null);
9082
+ (0, import_react53.useEffect)(() => {
8413
9083
  if (isOpen) {
8414
9084
  if (closedTextRef.current) log?.log({ name: "popup_opened" });
8415
9085
  closedTextRef.current = null;
@@ -8421,65 +9091,65 @@ function usePopupTelemetry(log, isOpen, popupText, dismissPopup) {
8421
9091
  log?.log({ name: "popup_shown" });
8422
9092
  }
8423
9093
  }, [log, isOpen, popupText]);
8424
- return (0, import_react48.useCallback)(() => {
9094
+ return (0, import_react53.useCallback)(() => {
8425
9095
  log?.log({ name: "popup_dismissed" });
8426
9096
  dismissPopup();
8427
9097
  }, [log, dismissPopup]);
8428
9098
  }
8429
9099
 
8430
9100
  // src/components/ChatFab.tsx
8431
- var import_react49 = require("react");
9101
+ var import_react54 = require("react");
8432
9102
 
8433
9103
  // src/components/GreetingBubble.tsx
8434
- var import_jsx_runtime89 = require("react/jsx-runtime");
9104
+ var import_jsx_runtime91 = require("react/jsx-runtime");
8435
9105
  function GreetingBubble({ text, onOpen, onDismiss, dismissLabel = "Dismiss", asks }) {
8436
- const body = /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("button", { type: "button", className: "ss-w-greeting-body", onClick: onOpen, children: [
8437
- /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("span", { className: "ss-w-greeting-dots", "aria-hidden": "true", children: [
8438
- /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("i", {}),
8439
- /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("i", {}),
8440
- /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("i", {})
9106
+ const body = /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)("button", { type: "button", className: "ss-w-greeting-body", onClick: onOpen, children: [
9107
+ /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)("span", { className: "ss-w-greeting-dots", "aria-hidden": "true", children: [
9108
+ /* @__PURE__ */ (0, import_jsx_runtime91.jsx)("i", {}),
9109
+ /* @__PURE__ */ (0, import_jsx_runtime91.jsx)("i", {}),
9110
+ /* @__PURE__ */ (0, import_jsx_runtime91.jsx)("i", {})
8441
9111
  ] }),
8442
- /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("span", { className: "ss-w-greeting-text", children: text })
9112
+ /* @__PURE__ */ (0, import_jsx_runtime91.jsx)("span", { className: "ss-w-greeting-text", children: text })
8443
9113
  ] });
8444
- const dismiss = onDismiss ? /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("button", { type: "button", className: "ss-w-greeting-x", "aria-label": dismissLabel, onClick: onDismiss, children: "\xD7" }) : null;
9114
+ const dismiss = onDismiss ? /* @__PURE__ */ (0, import_jsx_runtime91.jsx)("button", { type: "button", className: "ss-w-greeting-x", "aria-label": dismissLabel, onClick: onDismiss, children: "\xD7" }) : null;
8445
9115
  if (!asks || asks.length === 0) {
8446
- return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: "ss-w-greeting", role: "note", children: [
9116
+ return /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)("div", { className: "ss-w-greeting", role: "note", children: [
8447
9117
  body,
8448
9118
  dismiss
8449
9119
  ] });
8450
9120
  }
8451
- return /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: "ss-w-greeting ss-w-greeting--asks", role: "note", children: [
8452
- /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)("div", { className: "ss-w-greeting-top", children: [
9121
+ return /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)("div", { className: "ss-w-greeting ss-w-greeting--asks", role: "note", children: [
9122
+ /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)("div", { className: "ss-w-greeting-top", children: [
8453
9123
  body,
8454
9124
  dismiss
8455
9125
  ] }),
8456
- /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("div", { className: "ss-w-greeting-asks", children: asks.map((ask, i) => /* @__PURE__ */ (0, import_jsx_runtime89.jsx)("button", { type: "button", className: "ss-w-greeting-ask", onClick: ask.onTap, children: ask.label }, `${i}:${ask.label}`)) })
9126
+ /* @__PURE__ */ (0, import_jsx_runtime91.jsx)("div", { className: "ss-w-greeting-asks", children: asks.map((ask, i) => /* @__PURE__ */ (0, import_jsx_runtime91.jsx)("button", { type: "button", className: "ss-w-greeting-ask", onClick: ask.onTap, children: ask.label }, `${i}:${ask.label}`)) })
8457
9127
  ] });
8458
9128
  }
8459
9129
 
8460
9130
  // src/components/ChatFab.tsx
8461
- var import_jsx_runtime90 = require("react/jsx-runtime");
9131
+ var import_jsx_runtime92 = require("react/jsx-runtime");
8462
9132
  function ChatFab({ label, greeting, popupText, logoUrl, onOpen, onDismissPopup, dismissLabel = "Dismiss", introAllowed = false, onIntroStart, asks }) {
8463
9133
  const bubble = popupText ?? greeting;
8464
- const [intro, setIntro] = (0, import_react49.useState)(false);
8465
- const armedRef = (0, import_react49.useRef)(introAllowed);
8466
- (0, import_react49.useEffect)(() => {
9134
+ const [intro, setIntro] = (0, import_react54.useState)(false);
9135
+ const armedRef = (0, import_react54.useRef)(introAllowed);
9136
+ (0, import_react54.useEffect)(() => {
8467
9137
  if (armedRef.current && bubble) {
8468
9138
  armedRef.current = false;
8469
9139
  setIntro(true);
8470
9140
  onIntroStart?.();
8471
9141
  }
8472
9142
  }, [bubble, onIntroStart]);
8473
- return /* @__PURE__ */ (0, import_jsx_runtime90.jsxs)("div", { className: intro ? "ss-w-fab-wrap ss-w-intro" : "ss-w-fab-wrap", children: [
8474
- bubble ? /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(GreetingBubble, { text: bubble, onOpen, onDismiss: popupText ? onDismissPopup : void 0, dismissLabel, asks: popupText ? asks : void 0 }) : null,
8475
- /* @__PURE__ */ (0, import_jsx_runtime90.jsx)("button", { type: "button", className: "ss-w-fab", "aria-label": label, onClick: onOpen, children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(BrandMark, { logoUrl, className: "ss-w-fab-mark" }) })
9143
+ return /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)("div", { className: intro ? "ss-w-fab-wrap ss-w-intro" : "ss-w-fab-wrap", children: [
9144
+ bubble ? /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(GreetingBubble, { text: bubble, onOpen, onDismiss: popupText ? onDismissPopup : void 0, dismissLabel, asks: popupText ? asks : void 0 }) : null,
9145
+ /* @__PURE__ */ (0, import_jsx_runtime92.jsx)("button", { type: "button", className: "ss-w-fab", "aria-label": label, onClick: onOpen, children: /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(BrandMark, { logoUrl, className: "ss-w-fab-mark" }) })
8476
9146
  ] });
8477
9147
  }
8478
9148
 
8479
9149
  // src/components/HostEventsProvider.tsx
8480
- var import_jsx_runtime91 = require("react/jsx-runtime");
9150
+ var import_jsx_runtime93 = require("react/jsx-runtime");
8481
9151
  function HostEventsProvider({ value, children }) {
8482
- return /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(SlipStateContext.Provider, { value: value.slipState, children: /* @__PURE__ */ (0, import_jsx_runtime91.jsx)(MatchLinkContext.Provider, { value: value.matchLink, children }) });
9152
+ return /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(SlipStateContext.Provider, { value: value.slipState, children: /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(MatchLinkContext.Provider, { value: value.matchLink, children }) });
8483
9153
  }
8484
9154
 
8485
9155
  // src/styles/css.generated.ts
@@ -8916,6 +9586,71 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
8916
9586
  .ss-w-combo-leg-stat { font-size: 12px; line-height: 1.45; color: var(--ss-w-ink); background: var(--ss-w-bubble); border-radius: 8px; padding: 6px 9px; }
8917
9587
  .ss-w-combo-math { font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 14px; font-weight: 700; line-height: 1.4; color: var(--ss-w-ink); text-align: right; background: color-mix(in srgb, var(--ss-w-accent) 9%, var(--ss-w-panel)); border-radius: 8px; padding: 8px 10px; overflow-wrap: anywhere; }
8918
9588
  .ss-w-combo-note { font-size: 11px; line-height: 1.4; color: var(--ss-w-faint); }
9589
+ .ss-w-combo-change { align-self: flex-start; min-height: 36px; padding: 6px 12px; font: inherit; font-size: 12px; font-weight: 650; color: var(--ss-w-accent); background: var(--ss-w-panel); border: 1px solid var(--ss-w-accent); border-radius: 16px; cursor: pointer; }
9590
+
9591
+ /* onboarding presets stage 2: the two tools (spec Stage 2 Behaviour, boards 1c and 2c). A sheet
9592
+ over the message list, inside .ss-w-chat-layer; the body scrolls and the builder's Send sits in
9593
+ the footer, so it stays visible on a phone. Colours come from the theme tokens. */
9594
+ .ss-w-tool-backdrop { position: absolute; inset: 0; z-index: 3; display: flex; flex-direction: column; justify-content: flex-end; background: rgba(0,0,0,.35); }
9595
+ .ss-w-tool { display: flex; flex-direction: column; min-height: 0; max-height: 100%; background: var(--ss-w-panel); color: var(--ss-w-ink); border-top: 1px solid var(--ss-w-line); border-radius: 16px 16px 0 0; box-shadow: 0 -8px 24px rgba(0,0,0,.18); outline: none; }
9596
+ .ss-w-tool-head { flex: none; display: flex; align-items: center; gap: 8px; padding: 12px 14px 8px; }
9597
+ .ss-w-tool-title { flex: 1; font-size: 15px; font-weight: 700; line-height: 1.3; }
9598
+ .ss-w-tool-close { flex: none; width: 36px; height: 36px; display: grid; place-items: center; font-size: 16px; color: var(--ss-w-faint); background: var(--ss-w-bubble); border: none; border-radius: 50%; cursor: pointer; }
9599
+ .ss-w-tool-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; display: flex; flex-direction: column; gap: 12px; padding: 4px 14px 14px; }
9600
+ .ss-w-tool-body > * { flex: none; }
9601
+ .ss-w-tool-foot { flex: none; padding: 10px 14px 12px; border-top: 1px solid var(--ss-w-line); }
9602
+ .ss-w-tool-row { display: flex; flex-direction: column; gap: 8px; padding: 10px 11px; border: 1px solid var(--ss-w-line); border-left: 3px solid var(--ss-w-accent); border-radius: 12px; }
9603
+ .ss-w-tool-ask { font-size: 14px; font-weight: 650; line-height: 1.7; color: var(--ss-w-ink); overflow-wrap: anywhere; }
9604
+ .ss-w-tool-num { display: inline-block; min-width: 44px; min-height: 32px; margin: 0 2px; padding: 3px 8px; font: inherit; font-variant-numeric: tabular-nums; font-weight: 800; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 10%, var(--ss-w-panel)); border: 1px dashed color-mix(in srgb, var(--ss-w-accent) 55%, var(--ss-w-line)); border-radius: 8px; cursor: pointer; }
9605
+ .ss-w-tool-picks { list-style: none; display: flex; flex-direction: column; gap: 4px; }
9606
+ .ss-w-tool-pick { display: grid; grid-template-columns: 1fr auto; column-gap: 8px; padding: 5px 8px; font-size: 12px; line-height: 1.35; background: var(--ss-w-bubble); border-radius: 8px; }
9607
+ .ss-w-tool-pick-match { grid-column: 1; font-weight: 650; color: var(--ss-w-ink); overflow-wrap: anywhere; }
9608
+ .ss-w-tool-pick-sel { grid-column: 1; color: var(--ss-w-faint); overflow-wrap: anywhere; }
9609
+ .ss-w-tool-pick-kick { grid-column: 1; font-size: 11px; color: var(--ss-w-faint); }
9610
+ .ss-w-tool-pick-price { grid-column: 2; grid-row: 1 / span 3; align-self: center; font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 14px; font-weight: 800; color: var(--ss-w-accent); }
9611
+ .ss-w-tool-math { padding: 6px 8px; font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 700; text-align: right; color: var(--ss-w-ink); background: color-mix(in srgb, var(--ss-w-accent) 9%, var(--ss-w-panel)); border-radius: 8px; overflow-wrap: anywhere; }
9612
+ .ss-w-tool-unavailable { padding: 6px 8px; font-size: 12px; color: var(--ss-w-faint); border: 1px dashed var(--ss-w-line); border-radius: 8px; }
9613
+ .ss-w-tool-send { align-self: flex-end; min-height: 40px; padding: 8px 18px; font: inherit; font-size: 13px; font-weight: 700; color: #fff; background: var(--ss-w-accent); border: none; border-radius: 20px; cursor: pointer; }
9614
+ .ss-w-tool-send:disabled { color: var(--ss-w-faint); background: var(--ss-w-bubble); cursor: not-allowed; }
9615
+ .ss-w-tool-foot .ss-w-tool-send { width: 100%; }
9616
+ .ss-w-tool-section { display: flex; flex-direction: column; gap: 6px; }
9617
+ .ss-w-tool-label { font-size: 11.5px; font-weight: 650; color: var(--ss-w-faint); }
9618
+ .ss-w-tool-chips { display: flex; flex-wrap: wrap; gap: 6px; }
9619
+ .ss-w-tool-chip { min-width: 52px; min-height: 36px; padding: 6px 10px; font: inherit; font-variant-numeric: tabular-nums; font-weight: 700; color: var(--ss-w-ink); background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 18px; cursor: pointer; }
9620
+ .ss-w-tool-chip[aria-pressed="true"] { color: #fff; background: var(--ss-w-accent); border-color: var(--ss-w-accent); }
9621
+ .ss-w-tool-stepper { display: inline-flex; align-items: center; gap: 10px; }
9622
+ .ss-w-tool-step { width: 40px; height: 40px; display: grid; place-items: center; font: inherit; font-size: 18px; font-weight: 700; color: var(--ss-w-accent); background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 50%; cursor: pointer; }
9623
+ .ss-w-tool-step:disabled { opacity: .5; cursor: not-allowed; }
9624
+ .ss-w-tool-count { min-width: 24px; text-align: center; font-size: 16px; font-weight: 800; font-variant-numeric: tabular-nums; }
9625
+
9626
+ /* onboarding presets stage 2: the chat layer and the speed dial (spec Stage 2 Behaviour). The
9627
+ layer is the message list's positioned box (P6), so the dial sits against the list's bottom
9628
+ right and rises with the composer: right 8px puts the 44px button's centre on Send's (12px
9629
+ padding + 18px), bottom 10px is above the composer's top border. While the button shows the list
9630
+ gets 54px more bottom padding so its last message can scroll clear. */
9631
+ .ss-w-chat-layer { position: relative; flex: 1; min-height: 0; display: flex; flex-direction: column; }
9632
+ .ss-w-chat-layer[data-dial] .ss-w-list { padding-bottom: 68px; }
9633
+ .ss-w-dial { position: absolute; left: 12px; right: 8px; bottom: 10px; z-index: 2; display: flex; flex-direction: column-reverse; align-items: flex-end; gap: 8px; pointer-events: none; }
9634
+ .ss-w-dial-btn, .ss-w-dial-pill { pointer-events: auto; }
9635
+ .ss-w-dial-btn { position: relative; flex: none; width: 44px; height: 44px; display: grid; place-items: center; padding: 0; font-size: 20px; color: var(--ss-w-accent); background: var(--ss-w-panel); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 40%, var(--ss-w-line)); border-radius: 50%; box-shadow: 0 4px 14px rgba(0,0,0,.18); cursor: pointer; }
9636
+ .ss-w-dial-btn[aria-expanded="true"] { color: var(--ss-w-panel); background: var(--ss-w-accent); border-color: var(--ss-w-accent); }
9637
+ .ss-w-dial-btn:focus-visible, .ss-w-dial-pill:focus-visible { outline: 2px solid var(--ss-w-accent); outline-offset: 2px; }
9638
+ .ss-w-dial-icon { grid-area: 1 / 1; display: grid; place-items: center; transition: transform 200ms ease, opacity 200ms ease; }
9639
+ .ss-w-dial-icon--close { opacity: 0; transform: rotate(-90deg); }
9640
+ .ss-w-dial-btn[aria-expanded="true"] .ss-w-dial-icon--open { opacity: 0; transform: rotate(90deg); }
9641
+ .ss-w-dial-btn[aria-expanded="true"] .ss-w-dial-icon--close { opacity: 1; transform: none; }
9642
+ .ss-w-dial-dot { position: absolute; top: 3px; right: 3px; width: 10px; height: 10px; background: var(--ss-w-accent); border: 2px solid var(--ss-w-panel); border-radius: 50%; }
9643
+ .ss-w-dial-pills { max-width: 100%; display: flex; flex-direction: column; align-items: flex-end; gap: 8px; }
9644
+ .ss-w-dial-pill { max-width: 100%; min-height: 44px; display: inline-flex; align-items: center; gap: 8px; padding: 6px 16px 6px 12px; font: inherit; font-size: 12.5px; font-weight: 650; line-height: 1.25; text-align: left; white-space: normal; overflow-wrap: anywhere; color: var(--ss-w-ink); background: var(--ss-w-panel); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 35%, var(--ss-w-line)); border-radius: 22px; box-shadow: 0 4px 14px rgba(0,0,0,.16); cursor: pointer; opacity: 0; visibility: hidden; transform: translateY(14px) scale(.5); transform-origin: bottom right; transition: opacity 200ms ease, transform 200ms ease, visibility 0s linear 200ms; }
9645
+ .ss-w-dial-pill svg { flex: none; font-size: 18px; color: var(--ss-w-accent); }
9646
+ .ss-w-dial-pill-label { min-width: 0; }
9647
+ .ss-w-dial-pills[data-open] .ss-w-dial-pill { opacity: 1; visibility: visible; transform: none; transition: opacity 200ms ease, transform 200ms ease, visibility 0s; }
9648
+ @media (prefers-reduced-motion: reduce) {
9649
+ .ss-w-dial-pill { transform: none; transition: opacity 200ms ease, visibility 0s linear 200ms; }
9650
+ .ss-w-dial-pills[data-open] .ss-w-dial-pill { transition: opacity 200ms ease, visibility 0s; }
9651
+ .ss-w-dial-icon { transition: opacity 200ms ease; }
9652
+ .ss-w-dial-icon--close, .ss-w-dial-btn[aria-expanded="true"] .ss-w-dial-icon--open { transform: none; }
9653
+ }
8919
9654
 
8920
9655
  /* Compact price list (D16): three or more cards of one turn, one row per fixture. Team names wrap
8921
9656
  to a second line and are never cut; the time sits above the match pin, with no column of its own. */
@@ -9002,6 +9737,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
9002
9737
  .ss-w-textarea { font-size: 16px; }
9003
9738
  }
9004
9739
  .ss-w-send { width: 36px; height: 36px; flex: none; border: none; border-radius: 50%; background: var(--ss-w-accent); color: #fff; cursor: pointer; display: grid; place-items: center; font-size: 16px; }
9740
+ .ss-w-send:disabled { opacity: .45; cursor: not-allowed; }
9005
9741
  .ss-w-stop-glyph { width: 11px; height: 11px; background: currentColor; border-radius: 2px; }
9006
9742
 
9007
9743
  /* add to slip */
@@ -10041,7 +10777,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
10041
10777
  `;
10042
10778
 
10043
10779
  // src/StatsWidget.tsx
10044
- var import_jsx_runtime92 = require("react/jsx-runtime");
10780
+ var import_jsx_runtime94 = require("react/jsx-runtime");
10045
10781
  function buildClient(props) {
10046
10782
  try {
10047
10783
  if ("client" in props && props.client) {
@@ -10066,24 +10802,24 @@ function injectStyles(root) {
10066
10802
  function StatsWidget(props) {
10067
10803
  const injectedClient = "client" in props ? props.client : void 0;
10068
10804
  const cfg = props.config;
10069
- const built = (0, import_react50.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
10070
- const logger = (0, import_react50.useMemo)(
10805
+ const built = (0, import_react55.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
10806
+ const logger = (0, import_react55.useMemo)(
10071
10807
  () => built ? built.client.createEventLogger({
10072
10808
  operatorUserId: cfg?.userContext?.operatorUserId,
10073
10809
  anonymousUserId: cfg?.userContext?.anonymousUserId
10074
10810
  }) : null,
10075
10811
  [built?.client]
10076
10812
  );
10077
- const ui = (0, import_react50.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
10078
- const rootRef = (0, import_react50.useRef)(null);
10079
- const introDoneRef = (0, import_react50.useRef)(false);
10080
- const markIntroDone = (0, import_react50.useCallback)(() => {
10813
+ const ui = (0, import_react55.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
10814
+ const rootRef = (0, import_react55.useRef)(null);
10815
+ const introDoneRef = (0, import_react55.useRef)(false);
10816
+ const markIntroDone = (0, import_react55.useCallback)(() => {
10081
10817
  introDoneRef.current = true;
10082
10818
  }, []);
10083
10819
  const convStorageKey = `ss-w-conv:${cfg?.operatorId ?? ""}:${cfg?.userContext?.operatorUserId ?? "anon"}`;
10084
10820
  const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0, convStorageKey);
10085
- const [hasToday, setHasToday] = (0, import_react50.useState)(false);
10086
- const liveCardWatches = (0, import_react50.useMemo)(() => {
10821
+ const [hasToday, setHasToday] = (0, import_react55.useState)(false);
10822
+ const liveCardWatches = (0, import_react55.useMemo)(() => {
10087
10823
  const seen = /* @__PURE__ */ new Set();
10088
10824
  const out = [];
10089
10825
  for (const msg of controller.state.messages) {
@@ -10105,15 +10841,15 @@ function StatsWidget(props) {
10105
10841
  hasToday,
10106
10842
  watches: liveCardWatches
10107
10843
  });
10108
- const getSchedule = (0, import_react50.useCallback)(
10844
+ const getSchedule = (0, import_react55.useCallback)(
10109
10845
  (localDate, utcOffsetMinutes) => built ? built.conversation.getSchedule(localDate, utcOffsetMinutes) : Promise.resolve(null),
10110
10846
  [built]
10111
10847
  );
10112
- const getStates = (0, import_react50.useCallback)(
10848
+ const getStates = (0, import_react55.useCallback)(
10113
10849
  (matchIds) => built ? built.conversation.getStates(matchIds) : Promise.resolve(null),
10114
10850
  [built]
10115
10851
  );
10116
- const pinnedMatchIds = (0, import_react50.useMemo)(
10852
+ const pinnedMatchIds = (0, import_react55.useMemo)(
10117
10853
  () => monitor.pins.filter((p) => p.kind === "match").map((p) => p.matchId),
10118
10854
  [monitor.pins]
10119
10855
  );
@@ -10125,11 +10861,11 @@ function StatsWidget(props) {
10125
10861
  pinnedMatchIds
10126
10862
  });
10127
10863
  const todayHasContent = visibleGroupsOf(discovery.groups, discovery.states).length > 0;
10128
- (0, import_react50.useEffect)(() => {
10864
+ (0, import_react55.useEffect)(() => {
10129
10865
  setHasToday(todayHasContent);
10130
10866
  }, [todayHasContent]);
10131
- const prevLiveSnapshotRef = (0, import_react50.useRef)(monitor.snapshot);
10132
- (0, import_react50.useEffect)(() => {
10867
+ const prevLiveSnapshotRef = (0, import_react55.useRef)(monitor.snapshot);
10868
+ (0, import_react55.useEffect)(() => {
10133
10869
  const snap = monitor.snapshot;
10134
10870
  if (!snap || snap === prevLiveSnapshotRef.current) return;
10135
10871
  prevLiveSnapshotRef.current = snap;
@@ -10165,9 +10901,14 @@ function StatsWidget(props) {
10165
10901
  }, [monitor.snapshot]);
10166
10902
  const gamesResult = useGames(built?.conversation ?? null, { activeTab: monitor.activeTab });
10167
10903
  const leaderboardResult = useLeaderboard(built?.conversation ?? null, { activeTab: monitor.activeTab });
10904
+ const getPricePresets = (0, import_react55.useCallback)(
10905
+ (locale2) => built ? built.conversation.getPricePresets(locale2) : Promise.resolve(null),
10906
+ [built]
10907
+ );
10908
+ const presetGrid = usePresetGrid({ getPricePresets, isOpen: controller.state.isOpen, locale: ui.presets.lang });
10168
10909
  const markTodayActivity = monitor.markTodayActivity;
10169
- const prevLiveCountRef = (0, import_react50.useRef)(0);
10170
- (0, import_react50.useEffect)(() => {
10910
+ const prevLiveCountRef = (0, import_react55.useRef)(0);
10911
+ (0, import_react55.useEffect)(() => {
10171
10912
  const rose = prevLiveCountRef.current === 0 && discovery.liveCount > 0;
10172
10913
  prevLiveCountRef.current = discovery.liveCount;
10173
10914
  if (rose) markTodayActivity();
@@ -10183,7 +10924,7 @@ function StatsWidget(props) {
10183
10924
  openMatchLabel: ui.openMatch
10184
10925
  });
10185
10926
  const markAdded = hostEvents.markAdded;
10186
- const slip = (0, import_react50.useMemo)(
10927
+ const slip = (0, import_react55.useMemo)(
10187
10928
  () => {
10188
10929
  const handler = props.config?.onAddToSlip;
10189
10930
  return {
@@ -10202,7 +10943,7 @@ function StatsWidget(props) {
10202
10943
  },
10203
10944
  [props.config?.onAddToSlip, controller.state.conversationId, markAdded]
10204
10945
  );
10205
- const onDashboardTap = (0, import_react50.useMemo)(
10946
+ const onDashboardTap = (0, import_react55.useMemo)(
10206
10947
  () => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input), logger) : void 0,
10207
10948
  [built, logger]
10208
10949
  );
@@ -10220,11 +10961,11 @@ function StatsWidget(props) {
10220
10961
  const operatorUserId = cfg?.userContext?.operatorUserId;
10221
10962
  const anonymousUserId = cfg?.userContext?.anonymousUserId;
10222
10963
  const conversationId = controller.state.conversationId ?? void 0;
10223
- (0, import_react50.useEffect)(() => {
10964
+ (0, import_react55.useEffect)(() => {
10224
10965
  if (!logger) return;
10225
10966
  logger.setIdentity({ operatorUserId, anonymousUserId, conversationId });
10226
10967
  }, [logger, operatorUserId, anonymousUserId, conversationId]);
10227
- (0, import_react50.useEffect)(() => {
10968
+ (0, import_react55.useEffect)(() => {
10228
10969
  if (!logger) return;
10229
10970
  logger.resume();
10230
10971
  const onHide = () => {
@@ -10242,14 +10983,14 @@ function StatsWidget(props) {
10242
10983
  logger.dispose();
10243
10984
  };
10244
10985
  }, [logger]);
10245
- (0, import_react50.useEffect)(() => {
10986
+ (0, import_react55.useEffect)(() => {
10246
10987
  if (!built || !ui.injectStyles) return;
10247
10988
  const node = rootRef.current?.getRootNode();
10248
10989
  if (node) injectStyles(node);
10249
10990
  }, [built, ui.injectStyles]);
10250
10991
  if (!built) return null;
10251
10992
  const attrs = themeAttrs(ui);
10252
- return /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(EventLogContext.Provider, { value: logger, children: /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(HostEventsProvider, { value: hostEvents, children: /* @__PURE__ */ (0, import_jsx_runtime92.jsx)("div", { ref: rootRef, className: "ss-w-root", "data-ss-w-theme": attrs["data-ss-w-theme"], "data-ss-w-pos": attrs["data-ss-w-pos"], style: attrs.style, children: controller.state.isOpen ? /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
10993
+ return /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(EventLogContext.Provider, { value: logger, children: /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(HostEventsProvider, { value: hostEvents, children: /* @__PURE__ */ (0, import_jsx_runtime94.jsx)("div", { ref: rootRef, className: "ss-w-root", "data-ss-w-theme": attrs["data-ss-w-theme"], "data-ss-w-pos": attrs["data-ss-w-pos"], style: attrs.style, children: controller.state.isOpen ? /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
10253
10994
  ChatPanel,
10254
10995
  {
10255
10996
  controller,
@@ -10261,9 +11002,10 @@ function StatsWidget(props) {
10261
11002
  gamesResult,
10262
11003
  leaderboardResult,
10263
11004
  loggedIn: !!operatorUserId,
10264
- conversation: built?.conversation ?? null
11005
+ conversation: built?.conversation ?? null,
11006
+ presetGrid
10265
11007
  }
10266
- ) : /* @__PURE__ */ (0, import_jsx_runtime92.jsx)(
11008
+ ) : /* @__PURE__ */ (0, import_jsx_runtime94.jsx)(
10267
11009
  ChatFab,
10268
11010
  {
10269
11011
  label: ui.launcherLabel,