@sensiblestats/widget-react 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @sensiblestats/widget-react
2
2
 
3
+ ## 0.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 8af5152: Render the new market_odds card: hero main-line rows, collapsible ladder with per-row add-to-slip, bookmaker/updated footer. Older servers simply never emit the event; older widgets ignore it.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [8af5152]
12
+ - @sensiblestats/widget-sdk@0.7.0
13
+
14
+ ## 0.7.2
15
+
16
+ ### Patch Changes
17
+
18
+ - Redesign the betting insight card for clarity. The market/selection is now the visual hero, the odds token is unmistakable (legible `ODDS` label + larger value, boxed and tinted). Stats and scope chips are collapsed by default behind a `Show stats` toggle — the reason-to-bet prose carries the justification, and the numbers are one tap away. When expanded, all stat rows show at once (the old three-row `Show N more` cap is removed) and the `This League / All comps / H2H` scopes render as a segmented control. Add to slip stays visible whether stats are open or closed. Adds `showStats` / `hideStats` UI strings (en + tr) to `UiConfig`.
19
+
3
20
  ## 0.7.1
4
21
 
5
22
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -36,7 +36,7 @@ __export(index_exports, {
36
36
  module.exports = __toCommonJS(index_exports);
37
37
 
38
38
  // src/StatsWidget.tsx
39
- var import_react9 = require("react");
39
+ var import_react10 = require("react");
40
40
  var import_widget_sdk4 = require("@sensiblestats/widget-sdk");
41
41
 
42
42
  // src/i18n.ts
@@ -53,9 +53,15 @@ var STRINGS = {
53
53
  retry: "Retry",
54
54
  showLess: "Show less",
55
55
  showMore: (n) => `Show ${n} more`,
56
+ showStats: "Show stats",
57
+ hideStats: "Hide stats",
56
58
  oddsLabel: "Odds",
57
59
  liveLabel: "Live",
58
60
  addToSlip: "Add to slip",
61
+ marketPrices: "Market prices",
62
+ moreLines: "More lines",
63
+ hideLines: "Hide lines",
64
+ updatedLabel: "updated",
59
65
  inSlip: "In slip",
60
66
  slipAdded: (n) => n === void 0 ? "Added to slip" : `Added to slip (${n} selection${n === 1 ? "" : "s"})`,
61
67
  slipDuplicate: "Already in your slip",
@@ -77,9 +83,15 @@ var STRINGS = {
77
83
  retry: "Tekrar dene",
78
84
  showLess: "Daha az g\xF6ster",
79
85
  showMore: (n) => `${n} tane daha g\xF6ster`,
86
+ showStats: "\u0130statistikleri g\xF6ster",
87
+ hideStats: "\u0130statistikleri gizle",
80
88
  oddsLabel: "Oran",
81
89
  liveLabel: "Canl\u0131",
82
90
  addToSlip: "Kupona ekle",
91
+ marketPrices: "Piyasa fiyatlar\u0131",
92
+ moreLines: "Di\u011Fer \xE7izgiler",
93
+ hideLines: "\xC7izgileri gizle",
94
+ updatedLabel: "g\xFCncellendi",
83
95
  inSlip: "Kuponda",
84
96
  slipAdded: (n) => n === void 0 ? "Kupona eklendi" : `Kupona eklendi (${n} se\xE7im)`,
85
97
  slipDuplicate: "Bu se\xE7im kuponunuzda zaten var",
@@ -118,8 +130,14 @@ function normalizeUi(cfg) {
118
130
  retry: strings.retry,
119
131
  showLess: strings.showLess,
120
132
  showMore: strings.showMore,
133
+ showStats: strings.showStats,
134
+ hideStats: strings.hideStats,
121
135
  oddsLabel: strings.oddsLabel,
122
136
  liveLabel: strings.liveLabel,
137
+ marketPrices: strings.marketPrices,
138
+ moreLines: strings.moreLines,
139
+ hideLines: strings.hideLines,
140
+ updatedLabel: strings.updatedLabel,
123
141
  addToSlip: strings.addToSlip,
124
142
  inSlip: strings.inSlip,
125
143
  slipAdded: strings.slipAdded,
@@ -249,6 +267,47 @@ function toSlipSelection(vm, conversationId) {
249
267
  if (conversationId !== void 0) selection.conversationId = conversationId;
250
268
  return selection;
251
269
  }
270
+ function toMarketOddsVM(card) {
271
+ const rows = Array.isArray(card.rows) ? card.rows : [];
272
+ const toRow = (r, i) => {
273
+ const selection = [str(r.selectionLabel), str(r.line)].filter(Boolean).join(" ");
274
+ const price = typeof r.price === "number" && Number.isFinite(r.price) ? r.price : void 0;
275
+ return {
276
+ key: `${selection}:${str(r.line) ?? ""}:${i}`,
277
+ selection,
278
+ odds: price !== void 0 ? price.toFixed(2) : "",
279
+ oddsDecimal: price,
280
+ matchOddsId: typeof r.matchOddsId === "number" ? r.matchOddsId : void 0,
281
+ quotedAtUtc: str(r.quotedAtUtc),
282
+ isMain: r.isMainLine !== false
283
+ };
284
+ };
285
+ const vms = rows.map(toRow).filter((r) => r.selection && r.odds);
286
+ const home = str(card.homeTeamName);
287
+ const away = str(card.awayTeamName);
288
+ const updated = rows.map((r) => str(r.quotedAtUtc)).filter((v) => !!v).sort();
289
+ return {
290
+ id: `${typeof card.matchId === "number" ? card.matchId : 0}:${str(card.marketDeveloperName) ?? ""}`,
291
+ market: str(card.marketLabel) ?? str(card.marketDeveloperName) ?? "",
292
+ fixture: home && away ? `${home} vs ${away}` : void 0,
293
+ competition: str(card.competitionName),
294
+ bookmaker: rows.map((r) => str(r.bookmakerName)).find(Boolean),
295
+ updatedAt: updated.length > 0 ? updated[updated.length - 1] : void 0,
296
+ isLive: rows.some((r) => r.isLive === true),
297
+ matchId: typeof card.matchId === "number" ? card.matchId : 0,
298
+ mainRows: vms.filter((r) => r.isMain),
299
+ moreRows: vms.filter((r) => !r.isMain),
300
+ disclaimer: str(card.disclaimer),
301
+ addToSlipEnabled: card.addToSlipEnabled === true
302
+ };
303
+ }
304
+ function marketOddsRowToSlipSelection(vm, row, conversationId) {
305
+ if (row.matchOddsId === void 0 || row.oddsDecimal === void 0) return null;
306
+ const selection = { matchOddsId: row.matchOddsId, matchId: vm.matchId, oddsDecimal: row.oddsDecimal };
307
+ if (row.quotedAtUtc !== void 0) selection.quotedAtUtc = row.quotedAtUtc;
308
+ if (conversationId !== void 0) selection.conversationId = conversationId;
309
+ return selection;
310
+ }
252
311
 
253
312
  // src/reducer.ts
254
313
  var POPUP_MAX = 160;
@@ -280,6 +339,8 @@ function applyEvent(state, event) {
280
339
  return { ...state, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, actions: [...t.actions, toActionVM(event.data.action)] })) };
281
340
  case "betting_insight":
282
341
  return { ...state, messages: mapActive(state, (t) => ({ ...t, insights: [...t.insights, toInsightVM(event.data.insight)] })) };
342
+ case "market_odds":
343
+ return { ...state, messages: mapActive(state, (t) => ({ ...t, marketOdds: [...t.marketOdds, toMarketOddsVM(event.data.card)] })) };
283
344
  case "intent_chip": {
284
345
  const chip = toChipVM(event.data.intent);
285
346
  return chip ? { ...state, starters: [...state.starters, chip] } : state;
@@ -315,11 +376,11 @@ function reducer(state, action) {
315
376
  return { ...state, isOpen: false, isStreaming: false, status: null, actionsLoading: false };
316
377
  case "USER_SENT": {
317
378
  const user = { role: "user", id: action.userId, text: action.displayText ?? action.input.message };
318
- const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], done: false };
379
+ const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], marketOdds: [], done: false };
319
380
  return { ...state, messages: [...state.messages, user, assistant], isStreaming: true, status: null, actionsLoading: false, starters: [], error: null, lastUserInput: action.input, lastDisplayText: action.displayText ?? null };
320
381
  }
321
382
  case "GREETING_SENT": {
322
- const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], done: false, isGreeting: true };
383
+ const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], marketOdds: [], done: false, isGreeting: true };
323
384
  return { ...state, messages: [...state.messages, assistant], isStreaming: true, status: null, error: null };
324
385
  }
325
386
  case "DISMISS_POPUP":
@@ -441,7 +502,7 @@ function ChatFab({ label, greeting, popupText, onOpen, onDismissPopup, dismissLa
441
502
  }
442
503
 
443
504
  // src/components/ChatPanel.tsx
444
- var import_react8 = require("react");
505
+ var import_react9 = require("react");
445
506
 
446
507
  // src/components/ChatHeader.tsx
447
508
  var import_jsx_runtime4 = require("react/jsx-runtime");
@@ -454,7 +515,7 @@ function ChatHeader({ onClose, closeLabel = "Close chat" }) {
454
515
  }
455
516
 
456
517
  // src/components/MessageList.tsx
457
- var import_react6 = require("react");
518
+ var import_react7 = require("react");
458
519
 
459
520
  // src/components/Markdown.tsx
460
521
  var import_react_markdown = __toESM(require("react-markdown"), 1);
@@ -564,13 +625,12 @@ function withTimeout(value) {
564
625
  new Promise((_, reject) => setTimeout(() => reject(new Error("add-to-slip timed out")), ADD_TO_SLIP_TIMEOUT_MS))
565
626
  ]);
566
627
  }
567
- function AddToSlipButton({ it, slip, ui }) {
628
+ function SlipButton({ selection, shownOdds, marketName, slip, ui }) {
568
629
  const [pending, setPending] = (0, import_react4.useState)(false);
569
630
  const [settled, setSettled] = (0, import_react4.useState)(false);
570
631
  const [toast, setToast] = (0, import_react4.useState)(null);
571
632
  const dismiss = (0, import_react4.useCallback)(() => setToast(null), []);
572
633
  const handler = slip.onAddToSlip;
573
- const selection = it.addToSlipEnabled ? toSlipSelection(it, slip.conversationId) : null;
574
634
  if (!ui || !handler || !selection) return null;
575
635
  const onClick = async () => {
576
636
  setPending(true);
@@ -578,11 +638,11 @@ function AddToSlipButton({ it, slip, ui }) {
578
638
  const raw = await withTimeout(Promise.resolve(handler(selection)));
579
639
  if (raw != null && !(0, import_widget_sdk3.isAddToSlipResult)(raw)) throw new Error("malformed add-to-slip result");
580
640
  const result = raw ?? void 0;
581
- setToast(resultToToast(result, it.odds ?? selection.oddsDecimal.toFixed(2), ui));
641
+ setToast(resultToToast(result, shownOdds || selection.oddsDecimal.toFixed(2), ui));
582
642
  setSettled(isTerminal(result));
583
643
  if (wasAdded(result) && slip.onMarketClicked) {
584
644
  try {
585
- slip.onMarketClicked(it.market);
645
+ slip.onMarketClicked(marketName);
586
646
  } catch {
587
647
  }
588
648
  }
@@ -606,29 +666,47 @@ function AddToSlipButton({ it, slip, ui }) {
606
666
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Toast, { toast, onDismiss: dismiss })
607
667
  ] });
608
668
  }
669
+ function AddToSlipButton({ it, slip, ui }) {
670
+ const selection = it.addToSlipEnabled ? toSlipSelection(it, slip.conversationId) : null;
671
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SlipButton, { selection, shownOdds: it.odds ?? "", marketName: it.market, slip, ui });
672
+ }
609
673
 
610
674
  // src/components/BettingInsightList.tsx
611
675
  var import_jsx_runtime9 = require("react/jsx-runtime");
612
- var MAX_ROWS = 3;
613
- function StatRows({ stats, ui }) {
614
- const [expanded, setExpanded] = (0, import_react5.useState)(false);
615
- const shown = expanded ? stats : stats.slice(0, MAX_ROWS);
616
- const overflow = stats.length - MAX_ROWS;
617
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ss-w-bi-rows", children: [
618
- shown.map((s) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ss-w-bi-row", children: [
619
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-row-lbl", children: s.label }),
620
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-row-dots", "aria-hidden": "true" }),
621
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-row-num", children: s.value })
622
- ] }, `${s.label}:${s.value}`)),
623
- overflow > 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "ss-w-bi-more", onClick: () => setExpanded((v) => !v), children: expanded ? ui?.showLess ?? "Show less" : (ui?.showMore ?? ((n) => `Show ${n} more`))(overflow) }) : null
624
- ] });
676
+ function StatRows({ stats }) {
677
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "ss-w-bi-rows", children: stats.map((s) => /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ss-w-bi-row", children: [
678
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-row-lbl", children: s.label }),
679
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "ss-w-bi-row-num", children: s.value })
680
+ ] }, `${s.label}:${s.value}`)) });
625
681
  }
626
- function ScopedStats({ scopes, ui }) {
682
+ function ScopedStats({ scopes }) {
627
683
  const [active, setActive] = (0, import_react5.useState)(0);
628
684
  const current = scopes[active] ?? scopes[0] ?? { key: "", label: "", stats: [] };
629
685
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ss-w-bi-scoped", children: [
630
686
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "ss-w-bi-scopes", role: "group", children: scopes.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("button", { type: "button", className: "ss-w-bi-chip", "aria-pressed": i === active, onClick: () => setActive(i), children: s.label }, s.key)) }),
631
- /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatRows, { stats: current.stats, ui }, current.key)
687
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatRows, { stats: current.stats }, current.key)
688
+ ] });
689
+ }
690
+ function StatsDisclosure({ it, ui }) {
691
+ const [open, setOpen] = (0, import_react5.useState)(false);
692
+ const hasScopes = !!it.scopes && it.scopes.length >= 2;
693
+ const hasStats = hasScopes || it.stats.length > 0;
694
+ if (!hasStats) return null;
695
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "ss-w-bi-stats", children: [
696
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
697
+ "button",
698
+ {
699
+ type: "button",
700
+ className: "ss-w-bi-toggle",
701
+ "aria-expanded": open,
702
+ onClick: () => setOpen((v) => !v),
703
+ children: [
704
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: open ? ui?.hideStats ?? "Hide stats" : ui?.showStats ?? "Show stats" }),
705
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: `ss-w-bi-chev${open ? " ss-w-bi-chev-up" : ""}`, "aria-hidden": "true" })
706
+ ]
707
+ }
708
+ ),
709
+ open ? hasScopes ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ScopedStats, { scopes: it.scopes }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatRows, { stats: it.stats }) : null
632
710
  ] });
633
711
  }
634
712
  function InsightCard({ it, ui, slip }) {
@@ -654,7 +732,7 @@ function InsightCard({ it, ui, slip }) {
654
732
  ] }) : null
655
733
  ] }),
656
734
  it.text ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "ss-w-bi-text", children: it.text }) : null,
657
- it.scopes && it.scopes.length >= 2 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ScopedStats, { scopes: it.scopes, ui }) : it.stats.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatRows, { stats: it.stats, ui }) : null,
735
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StatsDisclosure, { it, ui }),
658
736
  it.disclaimer ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "ss-w-bi-disc", children: it.disclaimer }) : null,
659
737
  /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(AddToSlipButton, { it, slip, ui })
660
738
  ] });
@@ -664,38 +742,88 @@ function BettingInsightList({ insights, ui, slip }) {
664
742
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "ss-w-binsights", children: insights.map((it) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(InsightCard, { it, ui, slip }, it.id)) });
665
743
  }
666
744
 
667
- // src/components/ActionButtons.tsx
745
+ // src/components/MarketOddsCardList.tsx
746
+ var import_react6 = require("react");
668
747
  var import_jsx_runtime10 = require("react/jsx-runtime");
748
+ function OddsRow({ vm, row, ui, slip }) {
749
+ const selection = vm.addToSlipEnabled ? marketOddsRowToSlipSelection(vm, row, slip.conversationId) : null;
750
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mo-row", children: [
751
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-mo-sel", children: row.selection }),
752
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-mo-price", children: row.odds }),
753
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SlipButton, { selection, shownOdds: row.odds, marketName: vm.market, slip, ui })
754
+ ] });
755
+ }
756
+ function formatUpdated(iso) {
757
+ if (!iso) return void 0;
758
+ const date = new Date(iso);
759
+ if (Number.isNaN(date.getTime())) return void 0;
760
+ return date.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit" });
761
+ }
762
+ function MarketOddsCard({ vm, ui, slip }) {
763
+ const [open, setOpen] = (0, import_react6.useState)(false);
764
+ const updated = formatUpdated(vm.updatedAt);
765
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mocard", children: [
766
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mo-head", children: [
767
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-mo-market", children: vm.market }),
768
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-mo-tag", children: ui?.marketPrices ?? "Market prices" })
769
+ ] }),
770
+ vm.fixture || vm.competition ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mo-fixture", children: [
771
+ vm.fixture ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: vm.fixture }) : null,
772
+ vm.competition ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-mo-comp", children: vm.competition }) : null
773
+ ] }) : null,
774
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mo-rows", children: [
775
+ vm.mainRows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(OddsRow, { vm, row, ui, slip }, row.key)),
776
+ open ? vm.moreRows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(OddsRow, { vm, row, ui, slip }, row.key)) : null
777
+ ] }),
778
+ vm.moreRows.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("button", { type: "button", className: "ss-w-bi-toggle", "aria-expanded": open, onClick: () => setOpen((v) => !v), children: [
779
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: open ? ui?.hideLines ?? "Hide lines" : `${ui?.moreLines ?? "More lines"} (${vm.moreRows.length})` }),
780
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: `ss-w-bi-chev${open ? " ss-w-bi-chev-up" : ""}`, "aria-hidden": "true" })
781
+ ] }) : null,
782
+ vm.bookmaker || updated ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mo-foot", children: [
783
+ vm.bookmaker ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: vm.bookmaker }) : null,
784
+ updated ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: `${ui?.updatedLabel ?? "updated"} ${updated}` }) : null
785
+ ] }) : null,
786
+ vm.disclaimer ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ss-w-bi-disc", children: vm.disclaimer }) : null
787
+ ] });
788
+ }
789
+ function MarketOddsCardList({ cards, ui, slip }) {
790
+ if (!cards || cards.length === 0) return null;
791
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "ss-w-mocards", children: cards.map((vm) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MarketOddsCard, { vm, ui, slip }, vm.id)) });
792
+ }
793
+
794
+ // src/components/ActionButtons.tsx
795
+ var import_jsx_runtime11 = require("react/jsx-runtime");
669
796
  function ActionButtons({ actions, loading, disabled, onAct }) {
670
797
  if (actions.length === 0 && !loading) return null;
671
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-actions", children: [
672
- actions.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("button", { type: "button", className: "ss-w-abtn", disabled, onClick: () => onAct(a), children: a.label }, `${a.label}-${i}`)),
673
- actions.length === 0 && loading ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
674
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-abtn-shimmer" }),
675
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-abtn-shimmer" })
798
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ss-w-actions", children: [
799
+ actions.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "ss-w-abtn", disabled, onClick: () => onAct(a), children: a.label }, `${a.label}-${i}`)),
800
+ actions.length === 0 && loading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
801
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-abtn-shimmer" }),
802
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-abtn-shimmer" })
676
803
  ] }) : null
677
804
  ] });
678
805
  }
679
806
 
680
807
  // src/components/ChatMessage.tsx
681
- var import_jsx_runtime11 = require("react/jsx-runtime");
808
+ var import_jsx_runtime12 = require("react/jsx-runtime");
682
809
  function ChatMessage({ message, disabled, onAct, ui, slip }) {
683
810
  if (message.role === "user") {
684
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "ss-w-bubble", children: message.text }) });
811
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "ss-w-bubble", children: message.text }) });
685
812
  }
686
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ss-w-msg ss-w-bot", children: [
687
- message.text ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Markdown, { text: message.text }) : null,
688
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
689
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(BettingInsightList, { insights: message.insights, ui, slip }),
690
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
813
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "ss-w-msg ss-w-bot", children: [
814
+ message.text ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Markdown, { text: message.text }) : null,
815
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
816
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(BettingInsightList, { insights: message.insights, ui, slip }),
817
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
818
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
691
819
  ] });
692
820
  }
693
821
 
694
822
  // src/components/IntentChips.tsx
695
- var import_jsx_runtime12 = require("react/jsx-runtime");
823
+ var import_jsx_runtime13 = require("react/jsx-runtime");
696
824
  function IntentChips({ chips, onPick }) {
697
825
  if (chips.length === 0) return null;
698
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
826
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
699
827
  }
700
828
 
701
829
  // src/scroll.ts
@@ -705,17 +833,17 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
705
833
  }
706
834
 
707
835
  // src/components/MessageList.tsx
708
- var import_jsx_runtime13 = require("react/jsx-runtime");
836
+ var import_jsx_runtime14 = require("react/jsx-runtime");
709
837
  function MessageList({ controller, ui, slip }) {
710
838
  const { state, send } = controller;
711
- const listRef = (0, import_react6.useRef)(null);
712
- const endRef = (0, import_react6.useRef)(null);
713
- const pinnedRef = (0, import_react6.useRef)(true);
714
- (0, import_react6.useEffect)(() => {
839
+ const listRef = (0, import_react7.useRef)(null);
840
+ const endRef = (0, import_react7.useRef)(null);
841
+ const pinnedRef = (0, import_react7.useRef)(true);
842
+ (0, import_react7.useEffect)(() => {
715
843
  if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
716
844
  }, [state.messages, state.status]);
717
845
  const empty = state.messages.length === 0;
718
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
846
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
719
847
  "div",
720
848
  {
721
849
  className: "ss-w-list",
@@ -725,7 +853,7 @@ function MessageList({ controller, ui, slip }) {
725
853
  if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
726
854
  },
727
855
  children: [
728
- state.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
856
+ state.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
729
857
  ChatMessage,
730
858
  {
731
859
  message: m,
@@ -736,25 +864,25 @@ function MessageList({ controller, ui, slip }) {
736
864
  },
737
865
  m.id
738
866
  )),
739
- state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ActionButtons, { actions: [], loading: true, onAct: () => {
867
+ state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ActionButtons, { actions: [], loading: true, onAct: () => {
740
868
  } }) : null,
741
- state.status ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "ss-w-status", children: state.status }) : null,
742
- state.error ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "ss-w-error", role: "alert", children: [
743
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
744
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
869
+ state.status ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "ss-w-status", children: state.status }) : null,
870
+ state.error ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "ss-w-error", role: "alert", children: [
871
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
872
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
745
873
  ] }) : null,
746
- empty ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
747
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { ref: endRef })
874
+ empty ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
875
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref: endRef })
748
876
  ]
749
877
  }
750
878
  );
751
879
  }
752
880
 
753
881
  // src/components/ChatInput.tsx
754
- var import_react7 = require("react");
755
- var import_jsx_runtime14 = require("react/jsx-runtime");
882
+ var import_react8 = require("react");
883
+ var import_jsx_runtime15 = require("react/jsx-runtime");
756
884
  function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
757
- const [value, setValue] = (0, import_react7.useState)("");
885
+ const [value, setValue] = (0, import_react8.useState)("");
758
886
  const submit = () => {
759
887
  const t = value.trim();
760
888
  if (!t) return;
@@ -767,18 +895,18 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
767
895
  submit();
768
896
  }
769
897
  };
770
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "ss-w-input", children: [
771
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
772
- streaming ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SendIcon, {}) })
898
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ss-w-input", children: [
899
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
900
+ streaming ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(SendIcon, {}) })
773
901
  ] });
774
902
  }
775
903
 
776
904
  // src/components/ChatPanel.tsx
777
- var import_jsx_runtime15 = require("react/jsx-runtime");
905
+ var import_jsx_runtime16 = require("react/jsx-runtime");
778
906
  var FULLSCREEN_QUERY = "(max-width: 639px)";
779
907
  function useIsModal() {
780
- const [modal, setModal] = (0, import_react8.useState)(false);
781
- (0, import_react8.useEffect)(() => {
908
+ const [modal, setModal] = (0, import_react9.useState)(false);
909
+ (0, import_react9.useEffect)(() => {
782
910
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
783
911
  const mql = window.matchMedia(FULLSCREEN_QUERY);
784
912
  const sync = () => setModal(mql.matches);
@@ -791,17 +919,17 @@ function useIsModal() {
791
919
  function ChatPanel({ controller, ui, slip }) {
792
920
  const { state, close, stop, send } = controller;
793
921
  const isModal = useIsModal();
794
- (0, import_react8.useEffect)(() => {
922
+ (0, import_react9.useEffect)(() => {
795
923
  const onKey = (e) => {
796
924
  if (e.key === "Escape") close();
797
925
  };
798
926
  window.addEventListener("keydown", onKey);
799
927
  return () => window.removeEventListener("keydown", onKey);
800
928
  }, [close]);
801
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
802
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
803
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(MessageList, { controller, ui, slip }),
804
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
929
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
930
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
931
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MessageList, { controller, ui, slip }),
932
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
805
933
  ] });
806
934
  }
807
935
 
@@ -913,27 +1041,45 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
913
1041
  .ss-w-bi-live { font-size: 8.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: #fff; background: #c0392b; border-radius: 4px; padding: 1px 5px; display: inline-flex; align-items: center; gap: 4px; flex: none; }
914
1042
  .ss-w-bi-live-dot { width: 5px; height: 5px; border-radius: 50%; background: #fff; }
915
1043
  .ss-w-bi-head { display: flex; align-items: flex-start; gap: 10px; }
916
- .ss-w-bi-mkt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
917
- .ss-w-bi-market { font-size: 13px; font-weight: 660; line-height: 1.3; color: var(--ss-w-ink); }
918
- .ss-w-bi-sel { font-size: 11px; line-height: 1.3; color: var(--ss-w-faint); }
919
- .ss-w-bi-odds { margin-left: auto; flex: none; display: flex; flex-direction: column; align-items: center; justify-content: center; background: color-mix(in srgb, var(--ss-w-accent) 12%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 24%, var(--ss-w-line)); border-radius: 8px; padding: 3px 9px; min-width: 56px; }
920
- .ss-w-bi-odds-cap { font-size: 8px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--ss-w-faint); }
921
- .ss-w-bi-odds b { font-size: 15px; font-family: ui-monospace, monospace; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); line-height: 1.15; }
1044
+ .ss-w-bi-mkt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
1045
+ .ss-w-bi-market { font-size: 15px; font-weight: 700; line-height: 1.25; color: var(--ss-w-ink); }
1046
+ .ss-w-bi-sel { font-size: 12px; line-height: 1.3; color: var(--ss-w-faint); }
1047
+ .ss-w-bi-odds { margin-left: auto; flex: none; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 1px; background: color-mix(in srgb, var(--ss-w-accent) 12%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 30%, var(--ss-w-line)); border-radius: 10px; padding: 4px 11px; min-width: 62px; }
1048
+ .ss-w-bi-odds-cap { font-size: 9px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--ss-w-faint); }
1049
+ .ss-w-bi-odds b { font-size: 19px; font-family: ui-monospace, monospace; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); line-height: 1.1; }
922
1050
  .ss-w-bi-book { font-size: 9.5px; color: var(--ss-w-faint); }
923
- .ss-w-bi-text { font-size: 12px; color: var(--ss-w-ink); }
1051
+ .ss-w-bi-text { font-size: 12px; line-height: 1.45; color: var(--ss-w-ink); }
1052
+ .ss-w-bi-stats { display: flex; flex-direction: column; gap: 8px; }
1053
+ .ss-w-bi-toggle { align-self: flex-start; display: inline-flex; align-items: center; gap: 5px; background: none; border: none; color: var(--ss-w-accent); font: inherit; font-size: 11.5px; font-weight: 600; padding: 2px 0; cursor: pointer; }
1054
+ .ss-w-bi-toggle:hover { text-decoration: underline; }
1055
+ .ss-w-bi-chev { width: 7px; height: 7px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: translateY(-2px) rotate(45deg); transition: transform .15s ease; }
1056
+ .ss-w-bi-chev-up { transform: translateY(1px) rotate(-135deg); }
924
1057
  .ss-w-bi-rows { display: flex; flex-direction: column; }
925
- .ss-w-bi-row { display: flex; align-items: baseline; gap: 8px; padding: 4px 0; border-top: 1px solid var(--ss-w-line); }
1058
+ .ss-w-bi-row { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; padding: 5px 0; border-top: 1px solid var(--ss-w-line); }
926
1059
  .ss-w-bi-row:first-child { border-top: none; }
927
- .ss-w-bi-row-lbl { font-size: 11px; color: var(--ss-w-faint); min-width: 0; }
928
- .ss-w-bi-row-dots { flex: 1; border-bottom: 1px dotted var(--ss-w-line); transform: translateY(-3px); min-width: 12px; }
929
- .ss-w-bi-row-num { font-family: ui-monospace, monospace; font-weight: 700; font-variant-numeric: tabular-nums; font-size: 12.5px; flex: none; color: var(--ss-w-ink); }
930
- .ss-w-bi-more { align-self: flex-start; background: none; border: none; color: var(--ss-w-accent); font: inherit; font-size: 11px; font-weight: 560; padding: 2px 0; cursor: pointer; }
931
- .ss-w-bi-more:hover { text-decoration: underline; }
1060
+ .ss-w-bi-row-lbl { font-size: 11.5px; color: var(--ss-w-faint); min-width: 0; }
1061
+ .ss-w-bi-row-num { font-family: ui-monospace, monospace; font-weight: 700; font-variant-numeric: tabular-nums; font-size: 13px; flex: none; color: var(--ss-w-ink); }
932
1062
  .ss-w-bi-disc { font-size: 10px; font-style: italic; color: var(--ss-w-faint); }
933
- .ss-w-bi-scoped { display: flex; flex-direction: column; gap: 6px; }
934
- .ss-w-bi-scopes { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; }
935
- .ss-w-bi-chip { font-size: 11px; font-weight: 550; border: 1px solid var(--ss-w-line); border-radius: 14px; padding: 3px 10px; color: var(--ss-w-ink); background: var(--ss-w-bubble); cursor: pointer; }
936
- .ss-w-bi-chip[aria-pressed="true"] { color: #fff; background: var(--ss-w-accent); border-color: var(--ss-w-accent); }
1063
+ .ss-w-bi-scoped { display: flex; flex-direction: column; gap: 8px; }
1064
+ .ss-w-bi-scopes { display: inline-flex; align-items: center; gap: 2px; flex-wrap: wrap; align-self: flex-start; background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 9px; padding: 2px; }
1065
+ .ss-w-bi-chip { font-size: 11px; font-weight: 600; border: none; border-radius: 7px; padding: 4px 10px; color: var(--ss-w-faint); background: none; cursor: pointer; }
1066
+ .ss-w-bi-chip[aria-pressed="true"] { color: var(--ss-w-ink); background: var(--ss-w-panel); box-shadow: 0 1px 2px rgba(0,0,0,.08); }
1067
+
1068
+ /* market odds card */
1069
+ .ss-w-mocards { display: flex; flex-direction: column; gap: 7px; }
1070
+ .ss-w-mocard { background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; border-left: 3px solid var(--ss-w-accent); padding: 9px 11px; display: flex; flex-direction: column; gap: 6px; }
1071
+ .ss-w-mo-head { display: flex; align-items: center; gap: 8px; }
1072
+ .ss-w-mo-market { font-size: 13px; font-weight: 700; color: var(--ss-w-ink); }
1073
+ .ss-w-mo-tag { margin-left: auto; flex: none; font-size: 9px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--ss-w-faint); border: 1px solid var(--ss-w-line); border-radius: 8px; padding: 2px 7px; }
1074
+ .ss-w-mo-fixture { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--ss-w-faint); min-width: 0; }
1075
+ .ss-w-mo-comp { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1076
+ .ss-w-mo-rows { display: flex; flex-direction: column; gap: 4px; }
1077
+ .ss-w-mo-row { display: flex; align-items: center; gap: 10px; padding: 4px 0; border-bottom: 1px dashed var(--ss-w-line); }
1078
+ .ss-w-mo-row:last-child { border-bottom: none; }
1079
+ .ss-w-mo-sel { font-size: 12.5px; font-weight: 600; color: var(--ss-w-ink); }
1080
+ .ss-w-mo-price { margin-left: auto; font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 14px; font-weight: 700; color: var(--ss-w-accent); }
1081
+ .ss-w-mo-row .ss-w-slip { margin: 0; }
1082
+ .ss-w-mo-foot { display: flex; align-items: center; gap: 8px; font-size: 10.5px; color: var(--ss-w-faint); }
937
1083
 
938
1084
  /* action buttons */
939
1085
  .ss-w-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
@@ -972,7 +1118,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
972
1118
  `;
973
1119
 
974
1120
  // src/StatsWidget.tsx
975
- var import_jsx_runtime16 = require("react/jsx-runtime");
1121
+ var import_jsx_runtime17 = require("react/jsx-runtime");
976
1122
  function buildClient(props) {
977
1123
  try {
978
1124
  if ("client" in props && props.client) {
@@ -997,11 +1143,11 @@ function injectStyles(root) {
997
1143
  function StatsWidget(props) {
998
1144
  const injectedClient = "client" in props ? props.client : void 0;
999
1145
  const cfg = props.config;
1000
- const built = (0, import_react9.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
1001
- const ui = (0, import_react9.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
1002
- const rootRef = (0, import_react9.useRef)(null);
1146
+ const built = (0, import_react10.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
1147
+ const ui = (0, import_react10.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
1148
+ const rootRef = (0, import_react10.useRef)(null);
1003
1149
  const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0);
1004
- const slip = (0, import_react9.useMemo)(
1150
+ const slip = (0, import_react10.useMemo)(
1005
1151
  () => ({
1006
1152
  onAddToSlip: props.config?.onAddToSlip,
1007
1153
  conversationId: controller.state.conversationId ?? void 0,
@@ -1014,14 +1160,14 @@ function StatsWidget(props) {
1014
1160
  }),
1015
1161
  [props.config?.onAddToSlip, controller.state.conversationId, built]
1016
1162
  );
1017
- (0, import_react9.useEffect)(() => {
1163
+ (0, import_react10.useEffect)(() => {
1018
1164
  if (!built || !ui.injectStyles) return;
1019
1165
  const node = rootRef.current?.getRootNode();
1020
1166
  if (node) injectStyles(node);
1021
1167
  }, [built, ui.injectStyles]);
1022
1168
  if (!built) return null;
1023
1169
  const attrs = themeAttrs(ui);
1024
- return /* @__PURE__ */ (0, import_jsx_runtime16.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_runtime16.jsx)(ChatPanel, { controller, ui, slip }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1170
+ return /* @__PURE__ */ (0, import_jsx_runtime17.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_runtime17.jsx)(ChatPanel, { controller, ui, slip }) : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
1025
1171
  ChatFab,
1026
1172
  {
1027
1173
  label: ui.launcherLabel,