@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 +26 -0
- package/dist/index.cjs +269 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +257 -67
- package/dist/index.js.map +1 -1
- package/dist/styles.css +26 -2
- package/package.json +2 -2
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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.
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
] }
|
|
511
|
-
] }
|
|
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
|
|
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,
|
|
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(
|
|
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/
|
|
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,
|
|
692
|
-
actions.map((a, i) => /* @__PURE__ */ (0,
|
|
693
|
-
actions.length === 0 && loading ? /* @__PURE__ */ (0,
|
|
694
|
-
/* @__PURE__ */ (0,
|
|
695
|
-
/* @__PURE__ */ (0,
|
|
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
|
|
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,
|
|
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,
|
|
707
|
-
message.text ? /* @__PURE__ */ (0,
|
|
708
|
-
/* @__PURE__ */ (0,
|
|
709
|
-
/* @__PURE__ */ (0,
|
|
710
|
-
/* @__PURE__ */ (0,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
732
|
-
const endRef = (0,
|
|
733
|
-
const pinnedRef = (0,
|
|
734
|
-
(0,
|
|
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,
|
|
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,
|
|
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,
|
|
925
|
+
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ActionButtons, { actions: [], loading: true, onAct: () => {
|
|
760
926
|
} }) : null,
|
|
761
|
-
state.status ? /* @__PURE__ */ (0,
|
|
762
|
-
state.error ? /* @__PURE__ */ (0,
|
|
763
|
-
/* @__PURE__ */ (0,
|
|
764
|
-
/* @__PURE__ */ (0,
|
|
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,
|
|
767
|
-
/* @__PURE__ */ (0,
|
|
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
|
|
775
|
-
var
|
|
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,
|
|
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,
|
|
791
|
-
/* @__PURE__ */ (0,
|
|
792
|
-
streaming ? /* @__PURE__ */ (0,
|
|
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
|
|
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,
|
|
801
|
-
(0,
|
|
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,
|
|
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,
|
|
822
|
-
/* @__PURE__ */ (0,
|
|
823
|
-
/* @__PURE__ */ (0,
|
|
824
|
-
/* @__PURE__ */ (0,
|
|
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:
|
|
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-
|
|
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
|
|
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,
|
|
1023
|
-
const ui = (0,
|
|
1024
|
-
const rootRef = (0,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|