@sensiblestats/widget-react 0.7.2 → 0.9.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,31 @@
1
1
  # @sensiblestats/widget-react
2
2
 
3
+ ## 0.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Entity cards now show the season and per-competition scope chips. `EntityCard`
8
+ drops the flat `stats` field in favor of `seasonName` + `scopes` (`All` aggregate
9
+ plus one scope per competition, each with an optional logo); the widget renders a
10
+ season label and, for 2+ competitions, a chip row that switches the stat set
11
+ client-side. `widget-embed` re-bundles `widget-react`, so it is republished too.
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies
16
+ - @sensiblestats/widget-sdk@0.8.0
17
+
18
+ ## 0.8.0
19
+
20
+ ### Minor Changes
21
+
22
+ - 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.
23
+
24
+ ### Patch Changes
25
+
26
+ - Updated dependencies [8af5152]
27
+ - @sensiblestats/widget-sdk@0.7.0
28
+
3
29
  ## 0.7.2
4
30
 
5
31
  ### 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
@@ -58,6 +58,10 @@ var STRINGS = {
58
58
  oddsLabel: "Odds",
59
59
  liveLabel: "Live",
60
60
  addToSlip: "Add to slip",
61
+ marketPrices: "Market prices",
62
+ moreLines: "More lines",
63
+ hideLines: "Hide lines",
64
+ updatedLabel: "updated",
61
65
  inSlip: "In slip",
62
66
  slipAdded: (n) => n === void 0 ? "Added to slip" : `Added to slip (${n} selection${n === 1 ? "" : "s"})`,
63
67
  slipDuplicate: "Already in your slip",
@@ -84,6 +88,10 @@ var STRINGS = {
84
88
  oddsLabel: "Oran",
85
89
  liveLabel: "Canl\u0131",
86
90
  addToSlip: "Kupona ekle",
91
+ marketPrices: "Piyasa fiyatlar\u0131",
92
+ moreLines: "Di\u011Fer \xE7izgiler",
93
+ hideLines: "\xC7izgileri gizle",
94
+ updatedLabel: "g\xFCncellendi",
87
95
  inSlip: "Kuponda",
88
96
  slipAdded: (n) => n === void 0 ? "Kupona eklendi" : `Kupona eklendi (${n} se\xE7im)`,
89
97
  slipDuplicate: "Bu se\xE7im kuponunuzda zaten var",
@@ -126,6 +134,10 @@ function normalizeUi(cfg) {
126
134
  hideStats: strings.hideStats,
127
135
  oddsLabel: strings.oddsLabel,
128
136
  liveLabel: strings.liveLabel,
137
+ marketPrices: strings.marketPrices,
138
+ moreLines: strings.moreLines,
139
+ hideLines: strings.hideLines,
140
+ updatedLabel: strings.updatedLabel,
129
141
  addToSlip: strings.addToSlip,
130
142
  inSlip: strings.inSlip,
131
143
  slipAdded: strings.slipAdded,
@@ -181,19 +193,27 @@ function readStatCells(raw) {
181
193
  }
182
194
  return cells;
183
195
  }
184
- function readStats(card) {
185
- return readStatCells(card.stats);
186
- }
187
196
  function toCardVM(card) {
188
197
  const entityType = str(card.entityType)?.toLowerCase();
189
198
  const kind = entityType === "player" ? "player" : entityType === "team" ? "team" : "generic";
199
+ const scopes = Array.isArray(card.scopes) ? card.scopes.map((s) => {
200
+ const label = str(s.label);
201
+ if (!label) return null;
202
+ return {
203
+ competitionId: typeof s.competitionId === "number" ? s.competitionId : null,
204
+ label,
205
+ image: str(s.imagePath),
206
+ stats: readStatCells(s.stats)
207
+ };
208
+ }).filter((s) => s !== null) : [];
190
209
  return {
191
210
  id: String(card.entityId),
192
211
  kind,
193
212
  name: card.canonicalName,
194
213
  image: str(card.imagePath) ?? str(card.imageUrl) ?? str(card.image_url) ?? str(card.logoUrl) ?? str(card.logo),
195
214
  sub: str(card.subtitle) ?? str(card.teamName) ?? str(card.leagueName) ?? str(card.position),
196
- stats: readStats(card)
215
+ season: str(card.seasonName),
216
+ scopes
197
217
  };
198
218
  }
199
219
  function toActionVM(action) {
@@ -255,6 +275,47 @@ function toSlipSelection(vm, conversationId) {
255
275
  if (conversationId !== void 0) selection.conversationId = conversationId;
256
276
  return selection;
257
277
  }
278
+ function toMarketOddsVM(card) {
279
+ const rows = Array.isArray(card.rows) ? card.rows : [];
280
+ const toRow = (r, i) => {
281
+ const selection = [str(r.selectionLabel), str(r.line)].filter(Boolean).join(" ");
282
+ const price = typeof r.price === "number" && Number.isFinite(r.price) ? r.price : void 0;
283
+ return {
284
+ key: `${selection}:${str(r.line) ?? ""}:${i}`,
285
+ selection,
286
+ odds: price !== void 0 ? price.toFixed(2) : "",
287
+ oddsDecimal: price,
288
+ matchOddsId: typeof r.matchOddsId === "number" ? r.matchOddsId : void 0,
289
+ quotedAtUtc: str(r.quotedAtUtc),
290
+ isMain: r.isMainLine !== false
291
+ };
292
+ };
293
+ const vms = rows.map(toRow).filter((r) => r.selection && r.odds);
294
+ const home = str(card.homeTeamName);
295
+ const away = str(card.awayTeamName);
296
+ const updated = rows.map((r) => str(r.quotedAtUtc)).filter((v) => !!v).sort();
297
+ return {
298
+ id: `${typeof card.matchId === "number" ? card.matchId : 0}:${str(card.marketDeveloperName) ?? ""}`,
299
+ market: str(card.marketLabel) ?? str(card.marketDeveloperName) ?? "",
300
+ fixture: home && away ? `${home} vs ${away}` : void 0,
301
+ competition: str(card.competitionName),
302
+ bookmaker: rows.map((r) => str(r.bookmakerName)).find(Boolean),
303
+ updatedAt: updated.length > 0 ? updated[updated.length - 1] : void 0,
304
+ isLive: rows.some((r) => r.isLive === true),
305
+ matchId: typeof card.matchId === "number" ? card.matchId : 0,
306
+ mainRows: vms.filter((r) => r.isMain),
307
+ moreRows: vms.filter((r) => !r.isMain),
308
+ disclaimer: str(card.disclaimer),
309
+ addToSlipEnabled: card.addToSlipEnabled === true
310
+ };
311
+ }
312
+ function marketOddsRowToSlipSelection(vm, row, conversationId) {
313
+ if (row.matchOddsId === void 0 || row.oddsDecimal === void 0) return null;
314
+ const selection = { matchOddsId: row.matchOddsId, matchId: vm.matchId, oddsDecimal: row.oddsDecimal };
315
+ if (row.quotedAtUtc !== void 0) selection.quotedAtUtc = row.quotedAtUtc;
316
+ if (conversationId !== void 0) selection.conversationId = conversationId;
317
+ return selection;
318
+ }
258
319
 
259
320
  // src/reducer.ts
260
321
  var POPUP_MAX = 160;
@@ -286,6 +347,8 @@ function applyEvent(state, event) {
286
347
  return { ...state, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, actions: [...t.actions, toActionVM(event.data.action)] })) };
287
348
  case "betting_insight":
288
349
  return { ...state, messages: mapActive(state, (t) => ({ ...t, insights: [...t.insights, toInsightVM(event.data.insight)] })) };
350
+ case "market_odds":
351
+ return { ...state, messages: mapActive(state, (t) => ({ ...t, marketOdds: [...t.marketOdds, toMarketOddsVM(event.data.card)] })) };
289
352
  case "intent_chip": {
290
353
  const chip = toChipVM(event.data.intent);
291
354
  return chip ? { ...state, starters: [...state.starters, chip] } : state;
@@ -321,11 +384,11 @@ function reducer(state, action) {
321
384
  return { ...state, isOpen: false, isStreaming: false, status: null, actionsLoading: false };
322
385
  case "USER_SENT": {
323
386
  const user = { role: "user", id: action.userId, text: action.displayText ?? action.input.message };
324
- const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], done: false };
387
+ const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], marketOdds: [], done: false };
325
388
  return { ...state, messages: [...state.messages, user, assistant], isStreaming: true, status: null, actionsLoading: false, starters: [], error: null, lastUserInput: action.input, lastDisplayText: action.displayText ?? null };
326
389
  }
327
390
  case "GREETING_SENT": {
328
- const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], done: false, isGreeting: true };
391
+ const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], marketOdds: [], done: false, isGreeting: true };
329
392
  return { ...state, messages: [...state.messages, assistant], isStreaming: true, status: null, error: null };
330
393
  }
331
394
  case "DISMISS_POPUP":
@@ -447,7 +510,7 @@ function ChatFab({ label, greeting, popupText, onOpen, onDismissPopup, dismissLa
447
510
  }
448
511
 
449
512
  // src/components/ChatPanel.tsx
450
- var import_react8 = require("react");
513
+ var import_react9 = require("react");
451
514
 
452
515
  // src/components/ChatHeader.tsx
453
516
  var import_jsx_runtime4 = require("react/jsx-runtime");
@@ -460,7 +523,7 @@ function ChatHeader({ onClose, closeLabel = "Close chat" }) {
460
523
  }
461
524
 
462
525
  // src/components/MessageList.tsx
463
- var import_react6 = require("react");
526
+ var import_react7 = require("react");
464
527
 
465
528
  // src/components/Markdown.tsx
466
529
  var import_react_markdown = __toESM(require("react-markdown"), 1);
@@ -484,13 +547,53 @@ var MAX_VISIBLE = 3;
484
547
  function initials(name) {
485
548
  return name.split(/\s+/).slice(0, 2).map((w) => w[0] ?? "").join("").toUpperCase();
486
549
  }
487
- function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMoreLabel = (n) => `Show ${n} more` }) {
488
- const [expanded, setExpanded] = (0, import_react2.useState)(false);
489
- if (cards.length === 0) return null;
490
- const visible = expanded ? cards : cards.slice(0, MAX_VISIBLE);
491
- const overflow = cards.length - MAX_VISIBLE;
492
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-cards", children: [
493
- visible.map((c) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-ecard", "data-kind": c.kind, children: [
550
+ function Crest({ scope }) {
551
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-ecard-crest", style: { backgroundImage: `url(${scope.image})` }, "aria-hidden": "true" });
552
+ }
553
+ function StatRow({ stats }) {
554
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ss-w-ecard-stats", children: stats.map((s) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ss-w-stat", children: [
555
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("b", { children: s.value }),
556
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: s.label })
557
+ ] }, s.label)) });
558
+ }
559
+ function ScopeChips({ scopes }) {
560
+ const [active, setActive] = (0, import_react2.useState)(0);
561
+ const current = scopes[active] ?? scopes[0];
562
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
563
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "ss-w-ecard-scopes", role: "group", children: scopes.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
564
+ "button",
565
+ {
566
+ type: "button",
567
+ className: "ss-w-ecard-chip",
568
+ "aria-pressed": i === active,
569
+ "aria-label": s.label,
570
+ title: s.label,
571
+ onClick: () => setActive(i),
572
+ children: s.image ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Crest, { scope: s }) : s.label
573
+ },
574
+ String(s.competitionId ?? "all")
575
+ )) }),
576
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRow, { stats: current.stats }, String(current.competitionId ?? "all"))
577
+ ] });
578
+ }
579
+ function CardScopes({ scopes }) {
580
+ if (scopes.length === 0) return null;
581
+ if (scopes.length === 1) {
582
+ const only = scopes[0];
583
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
584
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ss-w-ecard-tag", children: [
585
+ only.image ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Crest, { scope: only }) : null,
586
+ only.label
587
+ ] }),
588
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(StatRow, { stats: only.stats })
589
+ ] });
590
+ }
591
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(ScopeChips, { scopes });
592
+ }
593
+ function EntityCardItem({ c }) {
594
+ const season = c.scopes.length > 0 ? c.season : void 0;
595
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-ecard", "data-kind": c.kind, children: [
596
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-ecard-head", children: [
494
597
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
495
598
  "span",
496
599
  {
@@ -502,13 +605,23 @@ function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMore
502
605
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ss-w-meta", children: [
503
606
  c.kind !== "generic" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-kind", children: c.kind }) : null,
504
607
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-nm", children: c.name }),
505
- c.sub ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-sub", children: c.sub }) : null
506
- ] }),
507
- c.stats.map((s) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ss-w-stat", children: [
508
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("b", { children: s.value }),
509
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: s.label })
510
- ] }, s.label))
511
- ] }, c.id)),
608
+ c.sub || season ? /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "ss-w-sub", children: [
609
+ c.sub,
610
+ c.sub && season ? " \xB7 " : "",
611
+ season ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "ss-w-season", children: season }) : null
612
+ ] }) : null
613
+ ] })
614
+ ] }),
615
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CardScopes, { scopes: c.scopes })
616
+ ] });
617
+ }
618
+ function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMoreLabel = (n) => `Show ${n} more` }) {
619
+ const [expanded, setExpanded] = (0, import_react2.useState)(false);
620
+ if (cards.length === 0) return null;
621
+ const visible = expanded ? cards : cards.slice(0, MAX_VISIBLE);
622
+ const overflow = cards.length - MAX_VISIBLE;
623
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "ss-w-cards", children: [
624
+ visible.map((c) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(EntityCardItem, { c }, c.id)),
512
625
  overflow > 0 ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { type: "button", className: "ss-w-cards-more", disabled, onClick: () => setExpanded((v) => !v), children: expanded ? showLessLabel : showMoreLabel(overflow) }) : null
513
626
  ] });
514
627
  }
@@ -570,13 +683,12 @@ function withTimeout(value) {
570
683
  new Promise((_, reject) => setTimeout(() => reject(new Error("add-to-slip timed out")), ADD_TO_SLIP_TIMEOUT_MS))
571
684
  ]);
572
685
  }
573
- function AddToSlipButton({ it, slip, ui }) {
686
+ function SlipButton({ selection, shownOdds, marketName, slip, ui }) {
574
687
  const [pending, setPending] = (0, import_react4.useState)(false);
575
688
  const [settled, setSettled] = (0, import_react4.useState)(false);
576
689
  const [toast, setToast] = (0, import_react4.useState)(null);
577
690
  const dismiss = (0, import_react4.useCallback)(() => setToast(null), []);
578
691
  const handler = slip.onAddToSlip;
579
- const selection = it.addToSlipEnabled ? toSlipSelection(it, slip.conversationId) : null;
580
692
  if (!ui || !handler || !selection) return null;
581
693
  const onClick = async () => {
582
694
  setPending(true);
@@ -584,11 +696,11 @@ function AddToSlipButton({ it, slip, ui }) {
584
696
  const raw = await withTimeout(Promise.resolve(handler(selection)));
585
697
  if (raw != null && !(0, import_widget_sdk3.isAddToSlipResult)(raw)) throw new Error("malformed add-to-slip result");
586
698
  const result = raw ?? void 0;
587
- setToast(resultToToast(result, it.odds ?? selection.oddsDecimal.toFixed(2), ui));
699
+ setToast(resultToToast(result, shownOdds || selection.oddsDecimal.toFixed(2), ui));
588
700
  setSettled(isTerminal(result));
589
701
  if (wasAdded(result) && slip.onMarketClicked) {
590
702
  try {
591
- slip.onMarketClicked(it.market);
703
+ slip.onMarketClicked(marketName);
592
704
  } catch {
593
705
  }
594
706
  }
@@ -612,6 +724,10 @@ function AddToSlipButton({ it, slip, ui }) {
612
724
  /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Toast, { toast, onDismiss: dismiss })
613
725
  ] });
614
726
  }
727
+ function AddToSlipButton({ it, slip, ui }) {
728
+ const selection = it.addToSlipEnabled ? toSlipSelection(it, slip.conversationId) : null;
729
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(SlipButton, { selection, shownOdds: it.odds ?? "", marketName: it.market, slip, ui });
730
+ }
615
731
 
616
732
  // src/components/BettingInsightList.tsx
617
733
  var import_jsx_runtime9 = require("react/jsx-runtime");
@@ -684,38 +800,88 @@ function BettingInsightList({ insights, ui, slip }) {
684
800
  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)) });
685
801
  }
686
802
 
687
- // src/components/ActionButtons.tsx
803
+ // src/components/MarketOddsCardList.tsx
804
+ var import_react6 = require("react");
688
805
  var import_jsx_runtime10 = require("react/jsx-runtime");
806
+ function OddsRow({ vm, row, ui, slip }) {
807
+ const selection = vm.addToSlipEnabled ? marketOddsRowToSlipSelection(vm, row, slip.conversationId) : null;
808
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mo-row", children: [
809
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-mo-sel", children: row.selection }),
810
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-mo-price", children: row.odds }),
811
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SlipButton, { selection, shownOdds: row.odds, marketName: vm.market, slip, ui })
812
+ ] });
813
+ }
814
+ function formatUpdated(iso) {
815
+ if (!iso) return void 0;
816
+ const date = new Date(iso);
817
+ if (Number.isNaN(date.getTime())) return void 0;
818
+ return date.toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit" });
819
+ }
820
+ function MarketOddsCard({ vm, ui, slip }) {
821
+ const [open, setOpen] = (0, import_react6.useState)(false);
822
+ const updated = formatUpdated(vm.updatedAt);
823
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mocard", children: [
824
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mo-head", children: [
825
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-mo-market", children: vm.market }),
826
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-mo-tag", children: ui?.marketPrices ?? "Market prices" })
827
+ ] }),
828
+ vm.fixture || vm.competition ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mo-fixture", children: [
829
+ vm.fixture ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: vm.fixture }) : null,
830
+ vm.competition ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-mo-comp", children: vm.competition }) : null
831
+ ] }) : null,
832
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mo-rows", children: [
833
+ vm.mainRows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(OddsRow, { vm, row, ui, slip }, row.key)),
834
+ open ? vm.moreRows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(OddsRow, { vm, row, ui, slip }, row.key)) : null
835
+ ] }),
836
+ 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: [
837
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: open ? ui?.hideLines ?? "Hide lines" : `${ui?.moreLines ?? "More lines"} (${vm.moreRows.length})` }),
838
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: `ss-w-bi-chev${open ? " ss-w-bi-chev-up" : ""}`, "aria-hidden": "true" })
839
+ ] }) : null,
840
+ vm.bookmaker || updated ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-mo-foot", children: [
841
+ vm.bookmaker ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: vm.bookmaker }) : null,
842
+ updated ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { children: `${ui?.updatedLabel ?? "updated"} ${updated}` }) : null
843
+ ] }) : null,
844
+ vm.disclaimer ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "ss-w-bi-disc", children: vm.disclaimer }) : null
845
+ ] });
846
+ }
847
+ function MarketOddsCardList({ cards, ui, slip }) {
848
+ if (!cards || cards.length === 0) return null;
849
+ 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)) });
850
+ }
851
+
852
+ // src/components/ActionButtons.tsx
853
+ var import_jsx_runtime11 = require("react/jsx-runtime");
689
854
  function ActionButtons({ actions, loading, disabled, onAct }) {
690
855
  if (actions.length === 0 && !loading) return null;
691
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "ss-w-actions", children: [
692
- 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}`)),
693
- actions.length === 0 && loading ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
694
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-abtn-shimmer" }),
695
- /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "ss-w-abtn-shimmer" })
856
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ss-w-actions", children: [
857
+ 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}`)),
858
+ actions.length === 0 && loading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
859
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-abtn-shimmer" }),
860
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "ss-w-abtn-shimmer" })
696
861
  ] }) : null
697
862
  ] });
698
863
  }
699
864
 
700
865
  // src/components/ChatMessage.tsx
701
- var import_jsx_runtime11 = require("react/jsx-runtime");
866
+ var import_jsx_runtime12 = require("react/jsx-runtime");
702
867
  function ChatMessage({ message, disabled, onAct, ui, slip }) {
703
868
  if (message.role === "user") {
704
- 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 }) });
869
+ 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 }) });
705
870
  }
706
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "ss-w-msg ss-w-bot", children: [
707
- message.text ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Markdown, { text: message.text }) : null,
708
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
709
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(BettingInsightList, { insights: message.insights, ui, slip }),
710
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
871
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "ss-w-msg ss-w-bot", children: [
872
+ message.text ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(Markdown, { text: message.text }) : null,
873
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
874
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(BettingInsightList, { insights: message.insights, ui, slip }),
875
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
876
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
711
877
  ] });
712
878
  }
713
879
 
714
880
  // src/components/IntentChips.tsx
715
- var import_jsx_runtime12 = require("react/jsx-runtime");
881
+ var import_jsx_runtime13 = require("react/jsx-runtime");
716
882
  function IntentChips({ chips, onPick }) {
717
883
  if (chips.length === 0) return null;
718
- 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}`)) });
884
+ 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}`)) });
719
885
  }
720
886
 
721
887
  // src/scroll.ts
@@ -725,17 +891,17 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
725
891
  }
726
892
 
727
893
  // src/components/MessageList.tsx
728
- var import_jsx_runtime13 = require("react/jsx-runtime");
894
+ var import_jsx_runtime14 = require("react/jsx-runtime");
729
895
  function MessageList({ controller, ui, slip }) {
730
896
  const { state, send } = controller;
731
- const listRef = (0, import_react6.useRef)(null);
732
- const endRef = (0, import_react6.useRef)(null);
733
- const pinnedRef = (0, import_react6.useRef)(true);
734
- (0, import_react6.useEffect)(() => {
897
+ const listRef = (0, import_react7.useRef)(null);
898
+ const endRef = (0, import_react7.useRef)(null);
899
+ const pinnedRef = (0, import_react7.useRef)(true);
900
+ (0, import_react7.useEffect)(() => {
735
901
  if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
736
902
  }, [state.messages, state.status]);
737
903
  const empty = state.messages.length === 0;
738
- return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
904
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
739
905
  "div",
740
906
  {
741
907
  className: "ss-w-list",
@@ -745,7 +911,7 @@ function MessageList({ controller, ui, slip }) {
745
911
  if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
746
912
  },
747
913
  children: [
748
- state.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
914
+ state.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
749
915
  ChatMessage,
750
916
  {
751
917
  message: m,
@@ -756,25 +922,25 @@ function MessageList({ controller, ui, slip }) {
756
922
  },
757
923
  m.id
758
924
  )),
759
- state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(ActionButtons, { actions: [], loading: true, onAct: () => {
925
+ state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ActionButtons, { actions: [], loading: true, onAct: () => {
760
926
  } }) : null,
761
- state.status ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { className: "ss-w-status", children: state.status }) : null,
762
- state.error ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "ss-w-error", role: "alert", children: [
763
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
764
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
927
+ state.status ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "ss-w-status", children: state.status }) : null,
928
+ state.error ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "ss-w-error", role: "alert", children: [
929
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
930
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
765
931
  ] }) : null,
766
- empty ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
767
- /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("div", { ref: endRef })
932
+ empty ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
933
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { ref: endRef })
768
934
  ]
769
935
  }
770
936
  );
771
937
  }
772
938
 
773
939
  // src/components/ChatInput.tsx
774
- var import_react7 = require("react");
775
- var import_jsx_runtime14 = require("react/jsx-runtime");
940
+ var import_react8 = require("react");
941
+ var import_jsx_runtime15 = require("react/jsx-runtime");
776
942
  function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
777
- const [value, setValue] = (0, import_react7.useState)("");
943
+ const [value, setValue] = (0, import_react8.useState)("");
778
944
  const submit = () => {
779
945
  const t = value.trim();
780
946
  if (!t) return;
@@ -787,18 +953,18 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
787
953
  submit();
788
954
  }
789
955
  };
790
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "ss-w-input", children: [
791
- /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
792
- 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, {}) })
956
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ss-w-input", children: [
957
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
958
+ 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, {}) })
793
959
  ] });
794
960
  }
795
961
 
796
962
  // src/components/ChatPanel.tsx
797
- var import_jsx_runtime15 = require("react/jsx-runtime");
963
+ var import_jsx_runtime16 = require("react/jsx-runtime");
798
964
  var FULLSCREEN_QUERY = "(max-width: 639px)";
799
965
  function useIsModal() {
800
- const [modal, setModal] = (0, import_react8.useState)(false);
801
- (0, import_react8.useEffect)(() => {
966
+ const [modal, setModal] = (0, import_react9.useState)(false);
967
+ (0, import_react9.useEffect)(() => {
802
968
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
803
969
  const mql = window.matchMedia(FULLSCREEN_QUERY);
804
970
  const sync = () => setModal(mql.matches);
@@ -811,17 +977,17 @@ function useIsModal() {
811
977
  function ChatPanel({ controller, ui, slip }) {
812
978
  const { state, close, stop, send } = controller;
813
979
  const isModal = useIsModal();
814
- (0, import_react8.useEffect)(() => {
980
+ (0, import_react9.useEffect)(() => {
815
981
  const onKey = (e) => {
816
982
  if (e.key === "Escape") close();
817
983
  };
818
984
  window.addEventListener("keydown", onKey);
819
985
  return () => window.removeEventListener("keydown", onKey);
820
986
  }, [close]);
821
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
822
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
823
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(MessageList, { controller, ui, slip }),
824
- /* @__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 })
987
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
988
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
989
+ /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MessageList, { controller, ui, slip }),
990
+ /* @__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 })
825
991
  ] });
826
992
  }
827
993
 
@@ -909,7 +1075,8 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
909
1075
 
910
1076
  /* entity cards */
911
1077
  .ss-w-cards { display: flex; flex-direction: column; gap: 7px; }
912
- .ss-w-ecard { display: flex; align-items: center; gap: 11px; width: 100%; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 11px; text-align: left; }
1078
+ .ss-w-ecard { display: flex; flex-direction: column; align-items: stretch; gap: 8px; width: 100%; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 11px; text-align: left; }
1079
+ .ss-w-ecard-head { display: flex; align-items: center; gap: 11px; }
913
1080
  .ss-w-thumb { width: 40px; height: 40px; flex: none; border-radius: 50%; display: grid; place-items: center; font-size: 13px; font-weight: 700; color: #fff; background-color: var(--ss-w-nav); background-size: cover; background-position: center; }
914
1081
  .ss-w-thumb.ss-w-team { border-radius: 10px; }
915
1082
  .ss-w-ecard[data-kind="player"] .ss-w-thumb { background-color: var(--ss-w-accent); }
@@ -917,7 +1084,14 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
917
1084
  .ss-w-kind { font-size: 8.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ss-w-faint); line-height: 1.3; }
918
1085
  .ss-w-nm { font-weight: 660; }
919
1086
  .ss-w-sub { font-size: 11px; color: var(--ss-w-faint); }
920
- .ss-w-stat { margin-left: auto; text-align: center; }
1087
+ .ss-w-season { color: var(--ss-w-accent); font-weight: 660; }
1088
+ .ss-w-ecard-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; }
1089
+ .ss-w-ecard-chip { display: inline-flex; align-items: center; font-size: 11px; font-weight: 600; border: none; border-radius: 7px; padding: 4px 8px; color: var(--ss-w-faint); background: none; cursor: pointer; }
1090
+ .ss-w-ecard-chip[aria-pressed="true"] { color: var(--ss-w-ink); background: var(--ss-w-panel); box-shadow: 0 1px 2px rgba(0,0,0,.08); }
1091
+ .ss-w-ecard-crest { width: 16px; height: 16px; display: block; border-radius: 4px; background-size: cover; background-position: center; }
1092
+ .ss-w-ecard-tag { display: inline-flex; align-items: center; gap: 6px; align-self: flex-start; font-size: 11px; font-weight: 600; color: var(--ss-w-faint); }
1093
+ .ss-w-ecard-stats { display: flex; gap: 16px; }
1094
+ .ss-w-stat { text-align: center; }
921
1095
  .ss-w-stat b { display: block; font-variant-numeric: tabular-nums; }
922
1096
  .ss-w-stat span { font-size: 9px; text-transform: uppercase; color: var(--ss-w-faint); }
923
1097
  .ss-w-cards-more { align-self: flex-start; font-size: 11.5px; font-weight: 560; color: var(--ss-w-accent); background: none; border: none; cursor: pointer; padding: 3px 2px; }
@@ -957,6 +1131,22 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
957
1131
  .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; }
958
1132
  .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); }
959
1133
 
1134
+ /* market odds card */
1135
+ .ss-w-mocards { display: flex; flex-direction: column; gap: 7px; }
1136
+ .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; }
1137
+ .ss-w-mo-head { display: flex; align-items: center; gap: 8px; }
1138
+ .ss-w-mo-market { font-size: 13px; font-weight: 700; color: var(--ss-w-ink); }
1139
+ .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; }
1140
+ .ss-w-mo-fixture { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--ss-w-faint); min-width: 0; }
1141
+ .ss-w-mo-comp { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1142
+ .ss-w-mo-rows { display: flex; flex-direction: column; gap: 4px; }
1143
+ .ss-w-mo-row { display: flex; align-items: center; gap: 10px; padding: 4px 0; border-bottom: 1px dashed var(--ss-w-line); }
1144
+ .ss-w-mo-row:last-child { border-bottom: none; }
1145
+ .ss-w-mo-sel { font-size: 12.5px; font-weight: 600; color: var(--ss-w-ink); }
1146
+ .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); }
1147
+ .ss-w-mo-row .ss-w-slip { margin: 0; }
1148
+ .ss-w-mo-foot { display: flex; align-items: center; gap: 8px; font-size: 10.5px; color: var(--ss-w-faint); }
1149
+
960
1150
  /* action buttons */
961
1151
  .ss-w-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
962
1152
  .ss-w-abtn { font-size: 12px; font-weight: 560; color: var(--ss-w-accent); background: var(--ss-w-panel); border: 1px solid var(--ss-w-accent); border-radius: 16px; padding: 6px 12px; cursor: pointer; }
@@ -994,7 +1184,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
994
1184
  `;
995
1185
 
996
1186
  // src/StatsWidget.tsx
997
- var import_jsx_runtime16 = require("react/jsx-runtime");
1187
+ var import_jsx_runtime17 = require("react/jsx-runtime");
998
1188
  function buildClient(props) {
999
1189
  try {
1000
1190
  if ("client" in props && props.client) {
@@ -1019,11 +1209,11 @@ function injectStyles(root) {
1019
1209
  function StatsWidget(props) {
1020
1210
  const injectedClient = "client" in props ? props.client : void 0;
1021
1211
  const cfg = props.config;
1022
- const built = (0, import_react9.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
1023
- const ui = (0, import_react9.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
1024
- const rootRef = (0, import_react9.useRef)(null);
1212
+ const built = (0, import_react10.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
1213
+ const ui = (0, import_react10.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
1214
+ const rootRef = (0, import_react10.useRef)(null);
1025
1215
  const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0);
1026
- const slip = (0, import_react9.useMemo)(
1216
+ const slip = (0, import_react10.useMemo)(
1027
1217
  () => ({
1028
1218
  onAddToSlip: props.config?.onAddToSlip,
1029
1219
  conversationId: controller.state.conversationId ?? void 0,
@@ -1036,14 +1226,14 @@ function StatsWidget(props) {
1036
1226
  }),
1037
1227
  [props.config?.onAddToSlip, controller.state.conversationId, built]
1038
1228
  );
1039
- (0, import_react9.useEffect)(() => {
1229
+ (0, import_react10.useEffect)(() => {
1040
1230
  if (!built || !ui.injectStyles) return;
1041
1231
  const node = rootRef.current?.getRootNode();
1042
1232
  if (node) injectStyles(node);
1043
1233
  }, [built, ui.injectStyles]);
1044
1234
  if (!built) return null;
1045
1235
  const attrs = themeAttrs(ui);
1046
- 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)(
1236
+ 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)(
1047
1237
  ChatFab,
1048
1238
  {
1049
1239
  label: ui.launcherLabel,