@sensiblestats/widget-react 0.11.0 → 0.13.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 +28 -0
- package/dist/index.cjs +339 -107
- 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 +339 -107
- package/dist/index.js.map +1 -1
- package/dist/styles.css +31 -4
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -28,6 +28,19 @@ var STRINGS = {
|
|
|
28
28
|
oddsShortened: "shortened",
|
|
29
29
|
oddsDrifted: "drifted",
|
|
30
30
|
updatedLabel: "updated",
|
|
31
|
+
priceMovement: "Price movement",
|
|
32
|
+
openingLabel: "Opening",
|
|
33
|
+
currentLabel: "Current",
|
|
34
|
+
movementWindow: (hours) => hours % 24 === 0 ? `Last ${hours / 24}d` : `Last ${hours}h`,
|
|
35
|
+
notEnoughHistory: "No price history recorded yet for this selection.",
|
|
36
|
+
singlePricePoint: "Only one price recorded so far for this selection.",
|
|
37
|
+
selectionSuspended: "This selection is not currently available to bet.",
|
|
38
|
+
excludedSuspended: (n) => `${n} suspended price${n === 1 ? "" : "s"} excluded from this chart.`,
|
|
39
|
+
chartTimeLabel: (ms) => new Intl.DateTimeFormat("en-GB", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(ms)),
|
|
40
|
+
chartAria: (opening, current, direction, hours) => {
|
|
41
|
+
const span = hours % 24 === 0 ? `${hours / 24} day${hours / 24 === 1 ? "" : "s"}` : `${hours} hour${hours === 1 ? "" : "s"}`;
|
|
42
|
+
return `Price movement over the last ${span}: opened at ${opening}, currently ${current}, ${direction}.`;
|
|
43
|
+
},
|
|
31
44
|
inSlip: "In slip",
|
|
32
45
|
slipAdded: (n) => n === void 0 ? "Added to slip" : `Added to slip (${n} selection${n === 1 ? "" : "s"})`,
|
|
33
46
|
slipDuplicate: "Already in your slip",
|
|
@@ -61,6 +74,19 @@ var STRINGS = {
|
|
|
61
74
|
oddsShortened: "d\xFC\u015Ft\xFC",
|
|
62
75
|
oddsDrifted: "y\xFCkseldi",
|
|
63
76
|
updatedLabel: "g\xFCncellendi",
|
|
77
|
+
priceMovement: "Oran hareketi",
|
|
78
|
+
openingLabel: "A\xE7\u0131l\u0131\u015F",
|
|
79
|
+
currentLabel: "G\xFCncel",
|
|
80
|
+
movementWindow: (hours) => hours % 24 === 0 ? `Son ${hours / 24} g\xFCn` : `Son ${hours} saat`,
|
|
81
|
+
notEnoughHistory: "Bu se\xE7im i\xE7in hen\xFCz oran ge\xE7mi\u015Fi kaydedilmedi.",
|
|
82
|
+
singlePricePoint: "Bu se\xE7im i\xE7in \u015Fimdiye kadar yaln\u0131zca bir fiyat kaydedildi.",
|
|
83
|
+
selectionSuspended: "Bu se\xE7im \u015Fu anda bahse kapal\u0131.",
|
|
84
|
+
excludedSuspended: (n) => `${n} ask\u0131ya al\u0131nm\u0131\u015F fiyat bu grafikten hari\xE7 tutuldu.`,
|
|
85
|
+
chartTimeLabel: (ms) => new Intl.DateTimeFormat("tr-TR", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(ms)),
|
|
86
|
+
chartAria: (opening, current, direction, hours) => {
|
|
87
|
+
const span = hours % 24 === 0 ? `${hours / 24} g\xFCn` : `${hours} saat`;
|
|
88
|
+
return `Son ${span} i\xE7indeki oran hareketi: ${opening} ile a\xE7\u0131ld\u0131, \u015Fu anda ${current}, ${direction}.`;
|
|
89
|
+
},
|
|
64
90
|
inSlip: "Kuponda",
|
|
65
91
|
slipAdded: (n) => n === void 0 ? "Kupona eklendi" : `Kupona eklendi (${n} se\xE7im)`,
|
|
66
92
|
slipDuplicate: "Bu se\xE7im kuponunuzda zaten var",
|
|
@@ -110,6 +136,16 @@ function normalizeUi(cfg) {
|
|
|
110
136
|
oddsShortened: strings.oddsShortened,
|
|
111
137
|
oddsDrifted: strings.oddsDrifted,
|
|
112
138
|
updatedLabel: strings.updatedLabel,
|
|
139
|
+
priceMovement: strings.priceMovement,
|
|
140
|
+
openingLabel: strings.openingLabel,
|
|
141
|
+
currentLabel: strings.currentLabel,
|
|
142
|
+
movementWindow: strings.movementWindow,
|
|
143
|
+
notEnoughHistory: strings.notEnoughHistory,
|
|
144
|
+
singlePricePoint: strings.singlePricePoint,
|
|
145
|
+
selectionSuspended: strings.selectionSuspended,
|
|
146
|
+
excludedSuspended: strings.excludedSuspended,
|
|
147
|
+
chartTimeLabel: strings.chartTimeLabel,
|
|
148
|
+
chartAria: strings.chartAria,
|
|
113
149
|
addToSlip: strings.addToSlip,
|
|
114
150
|
inSlip: strings.inSlip,
|
|
115
151
|
slipAdded: strings.slipAdded,
|
|
@@ -148,6 +184,54 @@ import { WidgetSdkError as WidgetSdkError2 } from "@sensiblestats/widget-sdk";
|
|
|
148
184
|
// src/reducer.ts
|
|
149
185
|
import "@sensiblestats/widget-sdk";
|
|
150
186
|
|
|
187
|
+
// src/chart.ts
|
|
188
|
+
var CHART_VIEW_W = 320;
|
|
189
|
+
var CHART_VIEW_H = 140;
|
|
190
|
+
var PLOT_X0 = 40;
|
|
191
|
+
var PLOT_X1 = 312;
|
|
192
|
+
var PLOT_Y0 = 10;
|
|
193
|
+
var PLOT_Y1 = 110;
|
|
194
|
+
var MARKER_MAX_POINTS = 8;
|
|
195
|
+
var FLAT_DOMAIN_PAD = 0.05;
|
|
196
|
+
var round2 = (n) => Math.round(n * 100) / 100;
|
|
197
|
+
function projectSeries(samples) {
|
|
198
|
+
if (samples.length < 2) return null;
|
|
199
|
+
const tMin = samples[0].atMs;
|
|
200
|
+
const tMax = samples[samples.length - 1].atMs;
|
|
201
|
+
if (tMax <= tMin) return null;
|
|
202
|
+
const prices = samples.map((s) => s.price);
|
|
203
|
+
const pMin = Math.min(...prices);
|
|
204
|
+
const pMax = Math.max(...prices);
|
|
205
|
+
let yMin;
|
|
206
|
+
let yMax;
|
|
207
|
+
if (pMax === pMin) {
|
|
208
|
+
yMin = pMin - FLAT_DOMAIN_PAD;
|
|
209
|
+
yMax = pMax + FLAT_DOMAIN_PAD;
|
|
210
|
+
} else {
|
|
211
|
+
const pad = (pMax - pMin) * 0.1;
|
|
212
|
+
yMin = pMin - pad;
|
|
213
|
+
yMax = pMax + pad;
|
|
214
|
+
}
|
|
215
|
+
const x = (atMs) => round2(PLOT_X0 + (atMs - tMin) / (tMax - tMin) * (PLOT_X1 - PLOT_X0));
|
|
216
|
+
const y = (price) => round2(PLOT_Y1 - (price - yMin) / (yMax - yMin) * (PLOT_Y1 - PLOT_Y0));
|
|
217
|
+
const projected = samples.map((s) => ({ x: x(s.atMs), y: y(s.price) }));
|
|
218
|
+
const path = projected.map((p, i) => `${i === 0 ? "M" : "L"}${p.x} ${p.y}`).join(" ");
|
|
219
|
+
return {
|
|
220
|
+
path,
|
|
221
|
+
markers: samples.length <= MARKER_MAX_POINTS ? projected : [],
|
|
222
|
+
// The real extremes, never the padded bounds: a gridline label is a price, and the padded
|
|
223
|
+
// bounds are prices nobody was ever offered. A flat series has one real extreme, not two —
|
|
224
|
+
// two identical gridlines would collide on both React key and rendered text.
|
|
225
|
+
gridlines: pMax === pMin ? [{ y: y(pMax), price: pMax }] : [
|
|
226
|
+
{ y: y(pMax), price: pMax },
|
|
227
|
+
{ y: y(pMin), price: pMin }
|
|
228
|
+
],
|
|
229
|
+
viewBox: `0 0 ${CHART_VIEW_W} ${CHART_VIEW_H}`,
|
|
230
|
+
startMs: tMin,
|
|
231
|
+
endMs: tMax
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
151
235
|
// src/model.ts
|
|
152
236
|
function str(v) {
|
|
153
237
|
return typeof v === "string" && v.length > 0 ? v : void 0;
|
|
@@ -213,6 +297,9 @@ function toInsightVM(insight) {
|
|
|
213
297
|
const fixture = home && away ? `${home} vs ${away}` : void 0;
|
|
214
298
|
const competition = str(insight.competitionName);
|
|
215
299
|
const scopes = Array.isArray(insight.scopes) ? insight.scopes.map((s) => ({ key: str(s.key), label: str(s.label), stats: readStatCells(s.stats) })).filter((s) => !!s.key && !!s.label) : void 0;
|
|
300
|
+
const previousValue = insight.odds?.previousValue;
|
|
301
|
+
const previous = typeof previousValue === "number" && Number.isFinite(previousValue) ? previousValue : void 0;
|
|
302
|
+
const direction = insight.odds?.movementDirection === "shortened" || insight.odds?.movementDirection === "drifted" ? insight.odds.movementDirection : void 0;
|
|
216
303
|
return {
|
|
217
304
|
id: str(insight.insightId) ?? `${str(insight.ruleId) ?? "insight"}:${num(insight.matchId) ?? ""}`,
|
|
218
305
|
market,
|
|
@@ -232,7 +319,9 @@ function toInsightVM(insight) {
|
|
|
232
319
|
matchOddsId,
|
|
233
320
|
addToSlipEnabled: insight.addToSlipEnabled === true,
|
|
234
321
|
oddsDecimal: typeof oddsValue === "number" && Number.isFinite(oddsValue) ? oddsValue : void 0,
|
|
235
|
-
quotedAtUtc: str(insight.odds?.quotedAtUtc)
|
|
322
|
+
quotedAtUtc: str(insight.odds?.quotedAtUtc),
|
|
323
|
+
previousOdds: previous !== void 0 && direction !== void 0 ? previous.toFixed(2) : void 0,
|
|
324
|
+
movementDirection: previous !== void 0 && direction !== void 0 ? direction : void 0
|
|
236
325
|
};
|
|
237
326
|
}
|
|
238
327
|
function toSlipSelection(vm, conversationId) {
|
|
@@ -285,6 +374,41 @@ function toMarketOddsVM(card) {
|
|
|
285
374
|
addToSlipEnabled: card.addToSlipEnabled === true
|
|
286
375
|
};
|
|
287
376
|
}
|
|
377
|
+
function toOddsMovementVM(card) {
|
|
378
|
+
const raw = Array.isArray(card.points) ? card.points : [];
|
|
379
|
+
const samples = [];
|
|
380
|
+
for (const p of raw) {
|
|
381
|
+
const price = typeof p.price === "number" && Number.isFinite(p.price) ? p.price : void 0;
|
|
382
|
+
const atMs = typeof p.atUtc === "string" ? Date.parse(p.atUtc) : NaN;
|
|
383
|
+
if (price === void 0 || Number.isNaN(atMs)) continue;
|
|
384
|
+
samples.push({ atMs, price });
|
|
385
|
+
}
|
|
386
|
+
samples.sort((a, b) => a.atMs - b.atMs);
|
|
387
|
+
const asNumber = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
388
|
+
const opening = asNumber(card.openingPrice);
|
|
389
|
+
const current = asNumber(card.currentPrice);
|
|
390
|
+
const home = str(card.homeTeamName);
|
|
391
|
+
const away = str(card.awayTeamName);
|
|
392
|
+
const selection = [str(card.selectionLabel), str(card.line)].filter(Boolean).join(" ");
|
|
393
|
+
const bookmakerId = typeof card.bookmakerId === "number" ? card.bookmakerId : "";
|
|
394
|
+
return {
|
|
395
|
+
id: `${typeof card.matchId === "number" ? card.matchId : 0}:${str(card.marketDeveloperName) ?? ""}:${selection}:${bookmakerId}`,
|
|
396
|
+
market: str(card.marketLabel) ?? str(card.marketDeveloperName) ?? "",
|
|
397
|
+
selection,
|
|
398
|
+
fixture: home && away ? `${home} vs ${away}` : void 0,
|
|
399
|
+
competition: str(card.competitionName),
|
|
400
|
+
bookmaker: str(card.bookmakerName),
|
|
401
|
+
opening: opening !== void 0 ? opening.toFixed(2) : "",
|
|
402
|
+
current: current !== void 0 ? current.toFixed(2) : "",
|
|
403
|
+
direction: card.movementDirection === "shortened" || card.movementDirection === "drifted" ? card.movementDirection : void 0,
|
|
404
|
+
windowHours: asNumber(card.windowHours) ?? 0,
|
|
405
|
+
excludedCount: asNumber(card.excludedSuspendedCount) ?? 0,
|
|
406
|
+
isSuspended: card.isSuspended === true,
|
|
407
|
+
disclaimer: str(card.disclaimer),
|
|
408
|
+
geometry: projectSeries(samples),
|
|
409
|
+
observationCount: samples.length
|
|
410
|
+
};
|
|
411
|
+
}
|
|
288
412
|
function marketOddsRowToSlipSelection(vm, row, conversationId) {
|
|
289
413
|
if (row.matchOddsId === void 0 || row.oddsDecimal === void 0) return null;
|
|
290
414
|
const selection = { matchOddsId: row.matchOddsId, matchId: vm.matchId, oddsDecimal: row.oddsDecimal };
|
|
@@ -325,6 +449,8 @@ function applyEvent(state, event) {
|
|
|
325
449
|
return { ...state, messages: mapActive(state, (t) => ({ ...t, insights: [...t.insights, toInsightVM(event.data.insight)] })) };
|
|
326
450
|
case "market_odds":
|
|
327
451
|
return { ...state, messages: mapActive(state, (t) => ({ ...t, marketOdds: [...t.marketOdds, toMarketOddsVM(event.data.card)] })) };
|
|
452
|
+
case "odds_movement":
|
|
453
|
+
return { ...state, messages: mapActive(state, (t) => ({ ...t, oddsMovement: [...t.oddsMovement, toOddsMovementVM(event.data.card)] })) };
|
|
328
454
|
case "intent_chip": {
|
|
329
455
|
const chip = toChipVM(event.data.intent);
|
|
330
456
|
return chip ? { ...state, starters: [...state.starters, chip] } : state;
|
|
@@ -360,11 +486,11 @@ function reducer(state, action) {
|
|
|
360
486
|
return { ...state, isOpen: false, isStreaming: false, status: null, actionsLoading: false };
|
|
361
487
|
case "USER_SENT": {
|
|
362
488
|
const user = { role: "user", id: action.userId, text: action.displayText ?? action.input.message };
|
|
363
|
-
const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], marketOdds: [], done: false };
|
|
489
|
+
const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], marketOdds: [], oddsMovement: [], done: false };
|
|
364
490
|
return { ...state, messages: [...state.messages, user, assistant], isStreaming: true, status: null, actionsLoading: false, starters: [], error: null, lastUserInput: action.input, lastDisplayText: action.displayText ?? null };
|
|
365
491
|
}
|
|
366
492
|
case "GREETING_SENT": {
|
|
367
|
-
const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], marketOdds: [], done: false, isGreeting: true };
|
|
493
|
+
const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], insights: [], marketOdds: [], oddsMovement: [], done: false, isGreeting: true };
|
|
368
494
|
return { ...state, messages: [...state.messages, assistant], isStreaming: true, status: null, error: null };
|
|
369
495
|
}
|
|
370
496
|
case "DISMISS_POPUP":
|
|
@@ -721,20 +847,41 @@ function AddToSlipButton({ it, slip, ui }) {
|
|
|
721
847
|
return /* @__PURE__ */ jsx8(SlipButton, { selection, shownOdds: it.odds ?? "", marketName: it.market, slip, ui });
|
|
722
848
|
}
|
|
723
849
|
|
|
724
|
-
// src/components/
|
|
850
|
+
// src/components/OddsMovementBadge.tsx
|
|
725
851
|
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
852
|
+
function OddsMovementBadge({
|
|
853
|
+
previous,
|
|
854
|
+
current,
|
|
855
|
+
direction,
|
|
856
|
+
ui
|
|
857
|
+
}) {
|
|
858
|
+
if (previous === void 0 || direction === void 0) return null;
|
|
859
|
+
const directionWord2 = direction === "shortened" ? ui?.oddsShortened ?? "shortened" : ui?.oddsDrifted ?? "drifted";
|
|
860
|
+
const label = ui?.oddsMoved?.(previous, current, directionWord2) ?? `was ${previous}, now ${current}, ${directionWord2}`;
|
|
861
|
+
return (
|
|
862
|
+
// Strikethrough is invisible to screen readers, so the whole was/now/direction sentence rides
|
|
863
|
+
// on one aria-label and the visual glyphs are hidden from the accessibility tree.
|
|
864
|
+
/* @__PURE__ */ jsxs6("span", { className: "ss-w-move", role: "img", "aria-label": label, children: [
|
|
865
|
+
/* @__PURE__ */ jsx9("span", { className: "ss-w-move-prev", "aria-hidden": "true", children: previous }),
|
|
866
|
+
/* @__PURE__ */ jsx9("span", { className: "ss-w-move-arrow", "aria-hidden": "true", children: direction === "shortened" ? "\u25BC" : "\u25B2" })
|
|
867
|
+
] })
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// src/components/BettingInsightList.tsx
|
|
872
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
726
873
|
function StatRows2({ stats }) {
|
|
727
|
-
return /* @__PURE__ */
|
|
728
|
-
/* @__PURE__ */
|
|
729
|
-
/* @__PURE__ */
|
|
874
|
+
return /* @__PURE__ */ jsx10("div", { className: "ss-w-bi-rows", children: stats.map((s) => /* @__PURE__ */ jsxs7("div", { className: "ss-w-bi-row", children: [
|
|
875
|
+
/* @__PURE__ */ jsx10("span", { className: "ss-w-bi-row-lbl", children: s.label }),
|
|
876
|
+
/* @__PURE__ */ jsx10("span", { className: "ss-w-bi-row-num", children: s.value })
|
|
730
877
|
] }, `${s.label}:${s.value}`)) });
|
|
731
878
|
}
|
|
732
879
|
function ScopedStats({ scopes }) {
|
|
733
880
|
const [active, setActive] = useState3(0);
|
|
734
881
|
const current = scopes[active] ?? scopes[0] ?? { key: "", label: "", stats: [] };
|
|
735
|
-
return /* @__PURE__ */
|
|
736
|
-
/* @__PURE__ */
|
|
737
|
-
/* @__PURE__ */
|
|
882
|
+
return /* @__PURE__ */ jsxs7("div", { className: "ss-w-bi-scoped", children: [
|
|
883
|
+
/* @__PURE__ */ jsx10("div", { className: "ss-w-bi-scopes", role: "group", children: scopes.map((s, i) => /* @__PURE__ */ jsx10("button", { type: "button", className: "ss-w-bi-chip", "aria-pressed": i === active, onClick: () => setActive(i), children: s.label }, s.key)) }),
|
|
884
|
+
/* @__PURE__ */ jsx10(StatRows2, { stats: current.stats }, current.key)
|
|
738
885
|
] });
|
|
739
886
|
}
|
|
740
887
|
function StatsDisclosure({ it, ui }) {
|
|
@@ -742,8 +889,8 @@ function StatsDisclosure({ it, ui }) {
|
|
|
742
889
|
const hasScopes = !!it.scopes && it.scopes.length >= 2;
|
|
743
890
|
const hasStats = hasScopes || it.stats.length > 0;
|
|
744
891
|
if (!hasStats) return null;
|
|
745
|
-
return /* @__PURE__ */
|
|
746
|
-
/* @__PURE__ */
|
|
892
|
+
return /* @__PURE__ */ jsxs7("div", { className: "ss-w-bi-stats", children: [
|
|
893
|
+
/* @__PURE__ */ jsxs7(
|
|
747
894
|
"button",
|
|
748
895
|
{
|
|
749
896
|
type: "button",
|
|
@@ -751,131 +898,189 @@ function StatsDisclosure({ it, ui }) {
|
|
|
751
898
|
"aria-expanded": open,
|
|
752
899
|
onClick: () => setOpen((v) => !v),
|
|
753
900
|
children: [
|
|
754
|
-
/* @__PURE__ */
|
|
755
|
-
/* @__PURE__ */
|
|
901
|
+
/* @__PURE__ */ jsx10("span", { children: open ? ui?.hideStats ?? "Hide stats" : ui?.showStats ?? "Show stats" }),
|
|
902
|
+
/* @__PURE__ */ jsx10("span", { className: `ss-w-bi-chev${open ? " ss-w-bi-chev-up" : ""}`, "aria-hidden": "true" })
|
|
756
903
|
]
|
|
757
904
|
}
|
|
758
905
|
),
|
|
759
|
-
open ? hasScopes ? /* @__PURE__ */
|
|
906
|
+
open ? hasScopes ? /* @__PURE__ */ jsx10(ScopedStats, { scopes: it.scopes }) : /* @__PURE__ */ jsx10(StatRows2, { stats: it.stats }) : null
|
|
760
907
|
] });
|
|
761
908
|
}
|
|
762
909
|
function InsightCard({ it, ui, slip }) {
|
|
763
910
|
const live = it.isLive || it.phase === "live";
|
|
764
|
-
return /* @__PURE__ */
|
|
765
|
-
live ? /* @__PURE__ */
|
|
766
|
-
/* @__PURE__ */
|
|
911
|
+
return /* @__PURE__ */ jsxs7("div", { className: "ss-w-binsight", children: [
|
|
912
|
+
live ? /* @__PURE__ */ jsx10("div", { className: "ss-w-bi-status", children: /* @__PURE__ */ jsxs7("span", { className: "ss-w-bi-live", children: [
|
|
913
|
+
/* @__PURE__ */ jsx10("span", { className: "ss-w-bi-live-dot", "aria-hidden": "true" }),
|
|
767
914
|
ui?.liveLabel ?? "Live"
|
|
768
915
|
] }) }) : null,
|
|
769
|
-
/* @__PURE__ */
|
|
770
|
-
/* @__PURE__ */
|
|
771
|
-
/* @__PURE__ */
|
|
772
|
-
it.selection ? /* @__PURE__ */
|
|
773
|
-
it.fixture || it.competition ? /* @__PURE__ */
|
|
774
|
-
it.fixture ? /* @__PURE__ */
|
|
775
|
-
it.competition ? /* @__PURE__ */
|
|
916
|
+
/* @__PURE__ */ jsxs7("div", { className: "ss-w-bi-head", children: [
|
|
917
|
+
/* @__PURE__ */ jsxs7("span", { className: "ss-w-bi-mkt", children: [
|
|
918
|
+
/* @__PURE__ */ jsx10("span", { className: "ss-w-bi-market", children: it.market }),
|
|
919
|
+
it.selection ? /* @__PURE__ */ jsx10("span", { className: "ss-w-bi-sel", children: it.selection }) : null,
|
|
920
|
+
it.fixture || it.competition ? /* @__PURE__ */ jsxs7("span", { className: "ss-w-bi-match", children: [
|
|
921
|
+
it.fixture ? /* @__PURE__ */ jsx10("span", { className: "ss-w-bi-fixture", children: it.fixture }) : null,
|
|
922
|
+
it.competition ? /* @__PURE__ */ jsx10("span", { className: "ss-w-bi-comp", children: it.competition }) : null
|
|
776
923
|
] }) : null
|
|
777
924
|
] }),
|
|
778
|
-
/* @__PURE__ */
|
|
779
|
-
it.odds ? /* @__PURE__ */
|
|
780
|
-
/* @__PURE__ */
|
|
781
|
-
/* @__PURE__ */
|
|
782
|
-
|
|
925
|
+
/* @__PURE__ */ jsxs7("div", { className: "ss-w-bi-action", children: [
|
|
926
|
+
it.odds ? /* @__PURE__ */ jsxs7("span", { className: "ss-w-bi-odds", children: [
|
|
927
|
+
/* @__PURE__ */ jsx10("span", { className: "ss-w-bi-odds-cap", children: ui?.oddsLabel ?? "Odds" }),
|
|
928
|
+
/* @__PURE__ */ jsx10("b", { children: it.odds }),
|
|
929
|
+
/* @__PURE__ */ jsx10(OddsMovementBadge, { previous: it.previousOdds, current: it.odds, direction: it.movementDirection, ui }),
|
|
930
|
+
it.bookmaker ? /* @__PURE__ */ jsx10("span", { className: "ss-w-bi-book", children: it.bookmaker }) : null
|
|
783
931
|
] }) : null,
|
|
784
|
-
/* @__PURE__ */
|
|
932
|
+
/* @__PURE__ */ jsx10(AddToSlipButton, { it, slip, ui })
|
|
785
933
|
] })
|
|
786
934
|
] }),
|
|
787
|
-
it.text ? /* @__PURE__ */
|
|
788
|
-
/* @__PURE__ */
|
|
789
|
-
it.disclaimer ? /* @__PURE__ */
|
|
935
|
+
it.text ? /* @__PURE__ */ jsx10("p", { className: "ss-w-bi-text", children: it.text }) : null,
|
|
936
|
+
/* @__PURE__ */ jsx10(StatsDisclosure, { it, ui }),
|
|
937
|
+
it.disclaimer ? /* @__PURE__ */ jsx10("p", { className: "ss-w-bi-disc", children: it.disclaimer }) : null
|
|
790
938
|
] });
|
|
791
939
|
}
|
|
792
940
|
function BettingInsightList({ insights, ui, slip }) {
|
|
793
941
|
if (!insights || insights.length === 0) return null;
|
|
794
|
-
return /* @__PURE__ */
|
|
942
|
+
return /* @__PURE__ */ jsx10("div", { className: "ss-w-binsights", children: insights.map((it) => /* @__PURE__ */ jsx10(InsightCard, { it, ui, slip }, it.id)) });
|
|
795
943
|
}
|
|
796
944
|
|
|
797
945
|
// src/components/MarketOddsCardList.tsx
|
|
798
946
|
import { useState as useState4 } from "react";
|
|
799
|
-
import { jsx as
|
|
947
|
+
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
800
948
|
function OddsRow({ vm, row, ui, slip }) {
|
|
801
949
|
const selection = vm.addToSlipEnabled ? marketOddsRowToSlipSelection(vm, row, slip.conversationId) : null;
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
/* @__PURE__ */
|
|
806
|
-
row.
|
|
807
|
-
// Strikethrough is invisible to screen readers, so the whole was/now/direction sentence
|
|
808
|
-
// rides on one aria-label and the visual glyphs are hidden from the accessibility tree.
|
|
809
|
-
/* @__PURE__ */ jsxs7("span", { className: "ss-w-mo-move", role: "img", "aria-label": movementLabel, children: [
|
|
810
|
-
/* @__PURE__ */ jsx10("span", { className: "ss-w-mo-prev", "aria-hidden": "true", children: row.previousOdds }),
|
|
811
|
-
/* @__PURE__ */ jsx10("span", { className: "ss-w-mo-arrow", "aria-hidden": "true", children: row.movementDirection === "shortened" ? "\u25BC" : "\u25B2" })
|
|
812
|
-
] })
|
|
813
|
-
) : null,
|
|
814
|
-
/* @__PURE__ */ jsx10("span", { className: "ss-w-mo-price", children: row.odds }),
|
|
815
|
-
/* @__PURE__ */ jsx10(SlipButton, { selection, shownOdds: row.odds, marketName: vm.market, slip, ui })
|
|
950
|
+
return /* @__PURE__ */ jsxs8("div", { className: "ss-w-mo-row", children: [
|
|
951
|
+
/* @__PURE__ */ jsx11("span", { className: "ss-w-mo-sel", children: row.selection }),
|
|
952
|
+
/* @__PURE__ */ jsx11(OddsMovementBadge, { previous: row.previousOdds, current: row.odds, direction: row.movementDirection, ui }),
|
|
953
|
+
/* @__PURE__ */ jsx11("span", { className: "ss-w-mo-price", children: row.odds }),
|
|
954
|
+
/* @__PURE__ */ jsx11(SlipButton, { selection, shownOdds: row.odds, marketName: vm.market, slip, ui })
|
|
816
955
|
] });
|
|
817
956
|
}
|
|
818
957
|
function MarketOddsCard({ vm, ui, slip }) {
|
|
819
958
|
const [open, setOpen] = useState4(false);
|
|
820
|
-
return /* @__PURE__ */
|
|
821
|
-
/* @__PURE__ */
|
|
822
|
-
/* @__PURE__ */
|
|
823
|
-
/* @__PURE__ */
|
|
959
|
+
return /* @__PURE__ */ jsxs8("div", { className: "ss-w-mocard", children: [
|
|
960
|
+
/* @__PURE__ */ jsxs8("div", { className: "ss-w-mo-head", children: [
|
|
961
|
+
/* @__PURE__ */ jsx11("span", { className: "ss-w-mo-market", children: vm.market }),
|
|
962
|
+
/* @__PURE__ */ jsx11("span", { className: "ss-w-mo-tag", children: ui?.marketPrices ?? "Market prices" })
|
|
824
963
|
] }),
|
|
825
|
-
vm.fixture || vm.competition ? /* @__PURE__ */
|
|
826
|
-
vm.fixture ? /* @__PURE__ */
|
|
827
|
-
vm.competition ? /* @__PURE__ */
|
|
964
|
+
vm.fixture || vm.competition ? /* @__PURE__ */ jsxs8("div", { className: "ss-w-mo-fixture", children: [
|
|
965
|
+
vm.fixture ? /* @__PURE__ */ jsx11("span", { children: vm.fixture }) : null,
|
|
966
|
+
vm.competition ? /* @__PURE__ */ jsx11("span", { className: "ss-w-mo-comp", children: vm.competition }) : null
|
|
828
967
|
] }) : null,
|
|
829
|
-
/* @__PURE__ */
|
|
830
|
-
vm.mainRows.map((row) => /* @__PURE__ */
|
|
831
|
-
open ? vm.moreRows.map((row) => /* @__PURE__ */
|
|
968
|
+
/* @__PURE__ */ jsxs8("div", { className: "ss-w-mo-rows", children: [
|
|
969
|
+
vm.mainRows.map((row) => /* @__PURE__ */ jsx11(OddsRow, { vm, row, ui, slip }, row.key)),
|
|
970
|
+
open ? vm.moreRows.map((row) => /* @__PURE__ */ jsx11(OddsRow, { vm, row, ui, slip }, row.key)) : null
|
|
832
971
|
] }),
|
|
833
|
-
vm.moreRows.length > 0 ? /* @__PURE__ */
|
|
834
|
-
/* @__PURE__ */
|
|
835
|
-
/* @__PURE__ */
|
|
972
|
+
vm.moreRows.length > 0 ? /* @__PURE__ */ jsxs8("button", { type: "button", className: "ss-w-bi-toggle", "aria-expanded": open, onClick: () => setOpen((v) => !v), children: [
|
|
973
|
+
/* @__PURE__ */ jsx11("span", { children: open ? ui?.hideLines ?? "Hide lines" : `${ui?.moreLines ?? "More lines"} (${vm.moreRows.length})` }),
|
|
974
|
+
/* @__PURE__ */ jsx11("span", { className: `ss-w-bi-chev${open ? " ss-w-bi-chev-up" : ""}`, "aria-hidden": "true" })
|
|
836
975
|
] }) : null,
|
|
837
|
-
vm.bookmaker ? /* @__PURE__ */
|
|
838
|
-
vm.disclaimer ? /* @__PURE__ */
|
|
976
|
+
vm.bookmaker ? /* @__PURE__ */ jsx11("div", { className: "ss-w-mo-foot", children: /* @__PURE__ */ jsx11("span", { children: vm.bookmaker }) }) : null,
|
|
977
|
+
vm.disclaimer ? /* @__PURE__ */ jsx11("p", { className: "ss-w-bi-disc", children: vm.disclaimer }) : null
|
|
839
978
|
] });
|
|
840
979
|
}
|
|
841
980
|
function MarketOddsCardList({ cards, ui, slip }) {
|
|
842
981
|
if (!cards || cards.length === 0) return null;
|
|
843
|
-
return /* @__PURE__ */
|
|
982
|
+
return /* @__PURE__ */ jsx11("div", { className: "ss-w-mocards", children: cards.map((vm) => /* @__PURE__ */ jsx11(MarketOddsCard, { vm, ui, slip }, vm.id)) });
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
// src/components/OddsMovementCard.tsx
|
|
986
|
+
import { Fragment, jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
987
|
+
function directionWord(vm, ui) {
|
|
988
|
+
if (vm.direction === "shortened") return ui?.oddsShortened ?? "shortened";
|
|
989
|
+
if (vm.direction === "drifted") return ui?.oddsDrifted ?? "drifted";
|
|
990
|
+
return "";
|
|
991
|
+
}
|
|
992
|
+
function OddsMovementCard({ vm, ui }) {
|
|
993
|
+
const aria = ui?.chartAria ? ui.chartAria(vm.opening, vm.current, directionWord(vm, ui), vm.windowHours) : `Price movement over the last ${vm.windowHours} hours: opened at ${vm.opening}, currently ${vm.current}, ${directionWord(vm, ui)}.`;
|
|
994
|
+
return /* @__PURE__ */ jsxs9("div", { className: "ss-w-omcard", children: [
|
|
995
|
+
/* @__PURE__ */ jsxs9("div", { className: "ss-w-om-head", children: [
|
|
996
|
+
/* @__PURE__ */ jsx12("span", { className: "ss-w-om-market", children: vm.market }),
|
|
997
|
+
/* @__PURE__ */ jsx12("span", { className: "ss-w-om-tag", children: ui?.priceMovement ?? "Price movement" })
|
|
998
|
+
] }),
|
|
999
|
+
vm.fixture ? /* @__PURE__ */ jsxs9("div", { className: "ss-w-om-fixture", children: [
|
|
1000
|
+
/* @__PURE__ */ jsx12("span", { children: vm.fixture }),
|
|
1001
|
+
vm.competition ? /* @__PURE__ */ jsx12("span", { className: "ss-w-om-comp", children: vm.competition }) : null
|
|
1002
|
+
] }) : null,
|
|
1003
|
+
/* @__PURE__ */ jsx12("div", { className: "ss-w-om-sel", children: vm.selection }),
|
|
1004
|
+
/* @__PURE__ */ jsx12(OddsMovementBadge, { previous: vm.opening, current: vm.current, direction: vm.direction, ui }),
|
|
1005
|
+
vm.geometry === null ? /* @__PURE__ */ jsx12("div", { className: "ss-w-om-empty", children: vm.observationCount === 1 ? ui?.singlePricePoint ?? "Only one price recorded so far for this selection." : ui?.notEnoughHistory ?? "No price history recorded yet for this selection." }) : /* @__PURE__ */ jsxs9(
|
|
1006
|
+
"svg",
|
|
1007
|
+
{
|
|
1008
|
+
className: "ss-w-om-chart",
|
|
1009
|
+
viewBox: vm.geometry.viewBox,
|
|
1010
|
+
width: "100%",
|
|
1011
|
+
role: "img",
|
|
1012
|
+
"aria-label": aria,
|
|
1013
|
+
children: [
|
|
1014
|
+
vm.geometry.gridlines.map((g) => /* @__PURE__ */ jsxs9("g", { children: [
|
|
1015
|
+
/* @__PURE__ */ jsx12("line", { className: "ss-w-om-grid", x1: 40, y1: g.y, x2: CHART_VIEW_W - 8, y2: g.y }),
|
|
1016
|
+
/* @__PURE__ */ jsx12("text", { className: "ss-w-om-ylab", x: 34, y: g.y + 3, textAnchor: "end", children: g.price.toFixed(2) })
|
|
1017
|
+
] }, g.price)),
|
|
1018
|
+
/* @__PURE__ */ jsx12("path", { className: "ss-w-om-line", d: vm.geometry.path, fill: "none" }),
|
|
1019
|
+
vm.geometry.markers.map((m, i) => /* @__PURE__ */ jsx12("circle", { className: "ss-w-om-dot", cx: m.x, cy: m.y, r: 2.5 }, i)),
|
|
1020
|
+
ui?.chartTimeLabel ? /* @__PURE__ */ jsxs9(Fragment, { children: [
|
|
1021
|
+
/* @__PURE__ */ jsx12("text", { className: "ss-w-om-xlab", x: 40, y: CHART_VIEW_H - 8, textAnchor: "start", children: ui.chartTimeLabel(vm.geometry.startMs) }),
|
|
1022
|
+
/* @__PURE__ */ jsx12("text", { className: "ss-w-om-xlab", x: CHART_VIEW_W - 8, y: CHART_VIEW_H - 8, textAnchor: "end", children: ui.chartTimeLabel(vm.geometry.endMs) })
|
|
1023
|
+
] }) : /* @__PURE__ */ jsx12("text", { className: "ss-w-om-xlab", x: 40, y: CHART_VIEW_H - 8, children: ui?.movementWindow ? ui.movementWindow(vm.windowHours) : `Last ${vm.windowHours}h` })
|
|
1024
|
+
]
|
|
1025
|
+
}
|
|
1026
|
+
),
|
|
1027
|
+
/* @__PURE__ */ jsxs9("div", { className: "ss-w-om-foot", children: [
|
|
1028
|
+
/* @__PURE__ */ jsxs9("span", { children: [
|
|
1029
|
+
ui?.openingLabel ?? "Opening",
|
|
1030
|
+
" ",
|
|
1031
|
+
/* @__PURE__ */ jsx12("b", { children: vm.opening })
|
|
1032
|
+
] }),
|
|
1033
|
+
/* @__PURE__ */ jsxs9("span", { children: [
|
|
1034
|
+
ui?.currentLabel ?? "Current",
|
|
1035
|
+
" ",
|
|
1036
|
+
/* @__PURE__ */ jsx12("b", { children: vm.current })
|
|
1037
|
+
] })
|
|
1038
|
+
] }),
|
|
1039
|
+
vm.bookmaker ? /* @__PURE__ */ jsx12("div", { className: "ss-w-om-foot", children: /* @__PURE__ */ jsx12("span", { children: vm.bookmaker }) }) : null,
|
|
1040
|
+
vm.excludedCount > 0 ? /* @__PURE__ */ jsx12("div", { className: "ss-w-om-excl", children: ui?.excludedSuspended ? ui.excludedSuspended(vm.excludedCount) : `${vm.excludedCount} suspended price(s) excluded from this chart.` }) : null,
|
|
1041
|
+
vm.isSuspended ? /* @__PURE__ */ jsx12("div", { className: "ss-w-om-susp", children: ui?.selectionSuspended ?? "This selection is not currently available to bet." }) : null,
|
|
1042
|
+
vm.disclaimer ? /* @__PURE__ */ jsx12("div", { className: "ss-w-om-disc", children: vm.disclaimer }) : null
|
|
1043
|
+
] });
|
|
1044
|
+
}
|
|
1045
|
+
function OddsMovementCardList({ cards, ui }) {
|
|
1046
|
+
if (!cards || cards.length === 0) return null;
|
|
1047
|
+
return /* @__PURE__ */ jsx12("div", { className: "ss-w-omcards", children: cards.map((vm) => /* @__PURE__ */ jsx12(OddsMovementCard, { vm, ui }, vm.id)) });
|
|
844
1048
|
}
|
|
845
1049
|
|
|
846
1050
|
// src/components/ActionButtons.tsx
|
|
847
|
-
import { Fragment, jsx as
|
|
1051
|
+
import { Fragment as Fragment2, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
848
1052
|
function ActionButtons({ actions, loading, disabled, onAct }) {
|
|
849
1053
|
if (actions.length === 0 && !loading) return null;
|
|
850
|
-
return /* @__PURE__ */
|
|
851
|
-
actions.map((a, i) => /* @__PURE__ */
|
|
852
|
-
actions.length === 0 && loading ? /* @__PURE__ */
|
|
853
|
-
/* @__PURE__ */
|
|
854
|
-
/* @__PURE__ */
|
|
1054
|
+
return /* @__PURE__ */ jsxs10("div", { className: "ss-w-actions", children: [
|
|
1055
|
+
actions.map((a, i) => /* @__PURE__ */ jsx13("button", { type: "button", className: "ss-w-abtn", disabled, onClick: () => onAct(a), children: a.label }, `${a.label}-${i}`)),
|
|
1056
|
+
actions.length === 0 && loading ? /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
1057
|
+
/* @__PURE__ */ jsx13("span", { className: "ss-w-abtn-shimmer" }),
|
|
1058
|
+
/* @__PURE__ */ jsx13("span", { className: "ss-w-abtn-shimmer" })
|
|
855
1059
|
] }) : null
|
|
856
1060
|
] });
|
|
857
1061
|
}
|
|
858
1062
|
|
|
859
1063
|
// src/components/ChatMessage.tsx
|
|
860
|
-
import { jsx as
|
|
1064
|
+
import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
861
1065
|
function ChatMessage({ message, disabled, onAct, ui, slip }) {
|
|
862
1066
|
if (message.role === "user") {
|
|
863
|
-
return /* @__PURE__ */
|
|
1067
|
+
return /* @__PURE__ */ jsx14("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx14("div", { className: "ss-w-bubble", children: message.text }) });
|
|
864
1068
|
}
|
|
865
|
-
return /* @__PURE__ */
|
|
866
|
-
message.text ? /* @__PURE__ */
|
|
867
|
-
/* @__PURE__ */
|
|
868
|
-
/* @__PURE__ */
|
|
869
|
-
/* @__PURE__ */
|
|
870
|
-
/* @__PURE__ */
|
|
1069
|
+
return /* @__PURE__ */ jsxs11("div", { className: "ss-w-msg ss-w-bot", children: [
|
|
1070
|
+
message.text ? /* @__PURE__ */ jsx14(Markdown, { text: message.text }) : null,
|
|
1071
|
+
/* @__PURE__ */ jsx14(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
|
|
1072
|
+
/* @__PURE__ */ jsx14(BettingInsightList, { insights: message.insights, ui, slip }),
|
|
1073
|
+
/* @__PURE__ */ jsx14(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
|
|
1074
|
+
/* @__PURE__ */ jsx14(OddsMovementCardList, { cards: message.oddsMovement, ui }),
|
|
1075
|
+
/* @__PURE__ */ jsx14(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
|
|
871
1076
|
] });
|
|
872
1077
|
}
|
|
873
1078
|
|
|
874
1079
|
// src/components/IntentChips.tsx
|
|
875
|
-
import { jsx as
|
|
1080
|
+
import { jsx as jsx15 } from "react/jsx-runtime";
|
|
876
1081
|
function IntentChips({ chips, onPick }) {
|
|
877
1082
|
if (chips.length === 0) return null;
|
|
878
|
-
return /* @__PURE__ */
|
|
1083
|
+
return /* @__PURE__ */ jsx15("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx15("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
|
|
879
1084
|
}
|
|
880
1085
|
|
|
881
1086
|
// src/scroll.ts
|
|
@@ -885,7 +1090,7 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
|
|
|
885
1090
|
}
|
|
886
1091
|
|
|
887
1092
|
// src/components/MessageList.tsx
|
|
888
|
-
import { jsx as
|
|
1093
|
+
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
889
1094
|
function MessageList({ controller, ui, slip }) {
|
|
890
1095
|
const { state, send } = controller;
|
|
891
1096
|
const listRef = useRef2(null);
|
|
@@ -895,7 +1100,7 @@ function MessageList({ controller, ui, slip }) {
|
|
|
895
1100
|
if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
|
|
896
1101
|
}, [state.messages, state.status]);
|
|
897
1102
|
const empty = state.messages.length === 0;
|
|
898
|
-
return /* @__PURE__ */
|
|
1103
|
+
return /* @__PURE__ */ jsxs12(
|
|
899
1104
|
"div",
|
|
900
1105
|
{
|
|
901
1106
|
className: "ss-w-list",
|
|
@@ -905,7 +1110,7 @@ function MessageList({ controller, ui, slip }) {
|
|
|
905
1110
|
if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
|
|
906
1111
|
},
|
|
907
1112
|
children: [
|
|
908
|
-
state.messages.map((m) => /* @__PURE__ */
|
|
1113
|
+
state.messages.map((m) => /* @__PURE__ */ jsx16(
|
|
909
1114
|
ChatMessage,
|
|
910
1115
|
{
|
|
911
1116
|
message: m,
|
|
@@ -916,15 +1121,15 @@ function MessageList({ controller, ui, slip }) {
|
|
|
916
1121
|
},
|
|
917
1122
|
m.id
|
|
918
1123
|
)),
|
|
919
|
-
state.isStreaming && state.actionsLoading ? /* @__PURE__ */
|
|
1124
|
+
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx16(ActionButtons, { actions: [], loading: true, onAct: () => {
|
|
920
1125
|
} }) : null,
|
|
921
|
-
state.status ? /* @__PURE__ */
|
|
922
|
-
state.error ? /* @__PURE__ */
|
|
923
|
-
/* @__PURE__ */
|
|
924
|
-
/* @__PURE__ */
|
|
1126
|
+
state.status ? /* @__PURE__ */ jsx16("div", { className: "ss-w-status", children: state.status }) : null,
|
|
1127
|
+
state.error ? /* @__PURE__ */ jsxs12("div", { className: "ss-w-error", role: "alert", children: [
|
|
1128
|
+
/* @__PURE__ */ jsx16("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
|
|
1129
|
+
/* @__PURE__ */ jsx16("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
|
|
925
1130
|
] }) : null,
|
|
926
|
-
empty ? /* @__PURE__ */
|
|
927
|
-
/* @__PURE__ */
|
|
1131
|
+
empty ? /* @__PURE__ */ jsx16(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
|
|
1132
|
+
/* @__PURE__ */ jsx16("div", { ref: endRef })
|
|
928
1133
|
]
|
|
929
1134
|
}
|
|
930
1135
|
);
|
|
@@ -932,7 +1137,7 @@ function MessageList({ controller, ui, slip }) {
|
|
|
932
1137
|
|
|
933
1138
|
// src/components/ChatInput.tsx
|
|
934
1139
|
import { useState as useState5 } from "react";
|
|
935
|
-
import { jsx as
|
|
1140
|
+
import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
936
1141
|
function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
|
|
937
1142
|
const [value, setValue] = useState5("");
|
|
938
1143
|
const submit = () => {
|
|
@@ -947,14 +1152,14 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
|
|
|
947
1152
|
submit();
|
|
948
1153
|
}
|
|
949
1154
|
};
|
|
950
|
-
return /* @__PURE__ */
|
|
951
|
-
/* @__PURE__ */
|
|
952
|
-
streaming ? /* @__PURE__ */
|
|
1155
|
+
return /* @__PURE__ */ jsxs13("div", { className: "ss-w-input", children: [
|
|
1156
|
+
/* @__PURE__ */ jsx17("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
|
|
1157
|
+
streaming ? /* @__PURE__ */ jsx17("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ jsx17("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx17("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ jsx17(SendIcon, {}) })
|
|
953
1158
|
] });
|
|
954
1159
|
}
|
|
955
1160
|
|
|
956
1161
|
// src/components/ChatPanel.tsx
|
|
957
|
-
import { jsx as
|
|
1162
|
+
import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
958
1163
|
var FULLSCREEN_QUERY = "(max-width: 639px)";
|
|
959
1164
|
function useIsModal() {
|
|
960
1165
|
const [modal, setModal] = useState6(false);
|
|
@@ -978,10 +1183,10 @@ function ChatPanel({ controller, ui, slip }) {
|
|
|
978
1183
|
window.addEventListener("keydown", onKey);
|
|
979
1184
|
return () => window.removeEventListener("keydown", onKey);
|
|
980
1185
|
}, [close]);
|
|
981
|
-
return /* @__PURE__ */
|
|
982
|
-
/* @__PURE__ */
|
|
983
|
-
/* @__PURE__ */
|
|
984
|
-
/* @__PURE__ */
|
|
1186
|
+
return /* @__PURE__ */ jsxs14("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
|
|
1187
|
+
/* @__PURE__ */ jsx18(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
|
|
1188
|
+
/* @__PURE__ */ jsx18(MessageList, { controller, ui, slip }),
|
|
1189
|
+
/* @__PURE__ */ jsx18(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
|
|
985
1190
|
] });
|
|
986
1191
|
}
|
|
987
1192
|
|
|
@@ -1170,13 +1375,40 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
1170
1375
|
.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); }
|
|
1171
1376
|
/* Movement: BOTH directions share --ss-w-faint on purpose. Green-good/red-bad would frame a
|
|
1172
1377
|
shortening price as a discount, which inverts what it actually means for the bettor. */
|
|
1173
|
-
.ss-w-
|
|
1174
|
-
.ss-w-
|
|
1175
|
-
.ss-w-
|
|
1176
|
-
|
|
1378
|
+
.ss-w-move { display: inline-flex; align-items: baseline; gap: 4px; color: var(--ss-w-faint); }
|
|
1379
|
+
.ss-w-move-prev { font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 12px; text-decoration: line-through; }
|
|
1380
|
+
.ss-w-move-arrow { font-size: 9px; line-height: 1; }
|
|
1381
|
+
/* margin-left:auto is row layout, not badge styling -- it belongs to the market-odds row, which is
|
|
1382
|
+
a flex row, and must NOT follow the badge into the insight card's odds column. */
|
|
1383
|
+
.ss-w-mo-row .ss-w-move { margin-left: auto; }
|
|
1384
|
+
.ss-w-mo-row .ss-w-move + .ss-w-mo-price { margin-left: 0; }
|
|
1177
1385
|
.ss-w-mo-row .ss-w-slip { margin: 0; }
|
|
1178
1386
|
.ss-w-mo-foot { display: flex; align-items: center; gap: 8px; font-size: 10.5px; color: var(--ss-w-faint); }
|
|
1179
1387
|
|
|
1388
|
+
/* odds movement card */
|
|
1389
|
+
.ss-w-omcards { display: flex; flex-direction: column; gap: 7px; }
|
|
1390
|
+
.ss-w-omcard { 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; }
|
|
1391
|
+
.ss-w-om-head { display: flex; align-items: center; gap: 8px; }
|
|
1392
|
+
.ss-w-om-market { font-size: 13px; font-weight: 700; color: var(--ss-w-ink); }
|
|
1393
|
+
.ss-w-om-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; }
|
|
1394
|
+
.ss-w-om-fixture { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--ss-w-faint); min-width: 0; }
|
|
1395
|
+
.ss-w-om-comp { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1396
|
+
.ss-w-om-sel { font-size: 12.5px; font-weight: 600; color: var(--ss-w-ink); }
|
|
1397
|
+
.ss-w-om-chart { display: block; width: 100%; height: auto; }
|
|
1398
|
+
/* Single neutral accent in BOTH directions, deliberately. Colouring a shortening price green
|
|
1399
|
+
frames it as a discount, which inverts what it means for the bettor -- the same reason
|
|
1400
|
+
.ss-w-move keeps both directions on --ss-w-faint. */
|
|
1401
|
+
.ss-w-om-line { stroke: var(--ss-w-accent); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
|
|
1402
|
+
.ss-w-om-dot { fill: var(--ss-w-accent); }
|
|
1403
|
+
.ss-w-om-grid { stroke: var(--ss-w-line); stroke-width: 1; }
|
|
1404
|
+
.ss-w-om-ylab, .ss-w-om-xlab { fill: var(--ss-w-faint); font-size: 9px; font-family: ui-monospace, monospace; }
|
|
1405
|
+
.ss-w-om-empty { font-size: 11.5px; color: var(--ss-w-faint); padding: 6px 0; }
|
|
1406
|
+
.ss-w-om-foot { display: flex; align-items: center; gap: 14px; font-size: 11.5px; color: var(--ss-w-faint); }
|
|
1407
|
+
.ss-w-om-foot b { font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 13px; color: var(--ss-w-ink); }
|
|
1408
|
+
.ss-w-om-excl { font-size: 10.5px; color: var(--ss-w-faint); }
|
|
1409
|
+
.ss-w-om-susp { font-size: 11px; font-weight: 600; color: #c0392b; }
|
|
1410
|
+
.ss-w-om-disc { font-size: 10px; font-style: italic; color: var(--ss-w-faint); }
|
|
1411
|
+
|
|
1180
1412
|
/* action buttons */
|
|
1181
1413
|
.ss-w-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
|
|
1182
1414
|
.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; }
|
|
@@ -1214,7 +1446,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
1214
1446
|
`;
|
|
1215
1447
|
|
|
1216
1448
|
// src/StatsWidget.tsx
|
|
1217
|
-
import { jsx as
|
|
1449
|
+
import { jsx as jsx19 } from "react/jsx-runtime";
|
|
1218
1450
|
function buildClient(props) {
|
|
1219
1451
|
try {
|
|
1220
1452
|
if ("client" in props && props.client) {
|
|
@@ -1263,7 +1495,7 @@ function StatsWidget(props) {
|
|
|
1263
1495
|
}, [built, ui.injectStyles]);
|
|
1264
1496
|
if (!built) return null;
|
|
1265
1497
|
const attrs = themeAttrs(ui);
|
|
1266
|
-
return /* @__PURE__ */
|
|
1498
|
+
return /* @__PURE__ */ jsx19("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__ */ jsx19(ChatPanel, { controller, ui, slip }) : /* @__PURE__ */ jsx19(
|
|
1267
1499
|
ChatFab,
|
|
1268
1500
|
{
|
|
1269
1501
|
label: ui.launcherLabel,
|