@sensiblestats/widget-react 0.14.1 → 0.16.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 +22 -0
- package/dist/index.cjs +277 -57
- 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 +269 -49
- package/dist/index.js.map +1 -1
- package/dist/styles.css +37 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/StatsWidget.tsx
|
|
2
|
-
import { useEffect as
|
|
2
|
+
import { useEffect as useEffect6, useMemo as useMemo2, useRef as useRef3 } from "react";
|
|
3
3
|
import { StatsWidgetClient } from "@sensiblestats/widget-sdk";
|
|
4
4
|
|
|
5
5
|
// src/i18n.ts
|
|
@@ -20,6 +20,7 @@ var STRINGS = {
|
|
|
20
20
|
hideStats: "Hide stats",
|
|
21
21
|
oddsLabel: "Odds",
|
|
22
22
|
liveLabel: "Live",
|
|
23
|
+
marketFloorLabel: "Market price \xB7 not a value pick",
|
|
23
24
|
addToSlip: "Add to slip",
|
|
24
25
|
marketPrices: "Market prices",
|
|
25
26
|
moreLines: "More lines",
|
|
@@ -42,6 +43,12 @@ var STRINGS = {
|
|
|
42
43
|
const drift = direction ? `, ${direction}` : "";
|
|
43
44
|
return `Price movement over the last ${span}: opened at ${opening}, currently ${current}${drift}.`;
|
|
44
45
|
},
|
|
46
|
+
clubMatchPreviewHeading: "Match preview",
|
|
47
|
+
clubTeamDnaHeading: "How they play",
|
|
48
|
+
clubFormHeading: "Recent form",
|
|
49
|
+
clubCountdownLabel: "Countdown to kick-off",
|
|
50
|
+
clubKickoff: (ms) => new Intl.DateTimeFormat("en-GB", { weekday: "short", day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(ms)),
|
|
51
|
+
clubKickoffAria: (ms) => `Kick-off ${new Intl.DateTimeFormat("en-GB", { weekday: "long", day: "numeric", month: "long", hour: "2-digit", minute: "2-digit" }).format(new Date(ms))}`,
|
|
45
52
|
inSlip: "In slip",
|
|
46
53
|
slipAdded: (n) => n === void 0 ? "Added to slip" : `Added to slip (${n} selection${n === 1 ? "" : "s"})`,
|
|
47
54
|
slipDuplicate: "Already in your slip",
|
|
@@ -67,6 +74,7 @@ var STRINGS = {
|
|
|
67
74
|
hideStats: "\u0130statistikleri gizle",
|
|
68
75
|
oddsLabel: "Oran",
|
|
69
76
|
liveLabel: "Canl\u0131",
|
|
77
|
+
marketFloorLabel: "Piyasa oran\u0131 \xB7 de\u011Fer bahsi de\u011Fil",
|
|
70
78
|
addToSlip: "Kupona ekle",
|
|
71
79
|
marketPrices: "Piyasa fiyatlar\u0131",
|
|
72
80
|
moreLines: "Di\u011Fer \xE7izgiler",
|
|
@@ -89,6 +97,12 @@ var STRINGS = {
|
|
|
89
97
|
const drift = direction ? `, ${direction}` : "";
|
|
90
98
|
return `Son ${span} i\xE7indeki oran hareketi: ${opening} ile a\xE7\u0131ld\u0131, \u015Fu anda ${current}${drift}.`;
|
|
91
99
|
},
|
|
100
|
+
clubMatchPreviewHeading: "Ma\xE7 \xF6nizlemesi",
|
|
101
|
+
clubTeamDnaHeading: "Nas\u0131l oynuyorlar",
|
|
102
|
+
clubFormHeading: "Son form",
|
|
103
|
+
clubCountdownLabel: "Ba\u015Flama vuru\u015Funa kalan",
|
|
104
|
+
clubKickoff: (ms) => new Intl.DateTimeFormat("tr-TR", { weekday: "short", day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(ms)),
|
|
105
|
+
clubKickoffAria: (ms) => `Ba\u015Flama vuru\u015Fu ${new Intl.DateTimeFormat("tr-TR", { weekday: "long", day: "numeric", month: "long", hour: "2-digit", minute: "2-digit" }).format(new Date(ms))}`,
|
|
92
106
|
inSlip: "Kuponda",
|
|
93
107
|
slipAdded: (n) => n === void 0 ? "Kupona eklendi" : `Kupona eklendi (${n} se\xE7im)`,
|
|
94
108
|
slipDuplicate: "Bu se\xE7im kuponunuzda zaten var",
|
|
@@ -131,6 +145,7 @@ function normalizeUi(cfg) {
|
|
|
131
145
|
hideStats: strings.hideStats,
|
|
132
146
|
oddsLabel: strings.oddsLabel,
|
|
133
147
|
liveLabel: strings.liveLabel,
|
|
148
|
+
marketFloorLabel: strings.marketFloorLabel,
|
|
134
149
|
marketPrices: strings.marketPrices,
|
|
135
150
|
moreLines: strings.moreLines,
|
|
136
151
|
hideLines: strings.hideLines,
|
|
@@ -148,6 +163,12 @@ function normalizeUi(cfg) {
|
|
|
148
163
|
excludedSuspended: strings.excludedSuspended,
|
|
149
164
|
chartTimeLabel: strings.chartTimeLabel,
|
|
150
165
|
chartAria: strings.chartAria,
|
|
166
|
+
clubMatchPreviewHeading: strings.clubMatchPreviewHeading,
|
|
167
|
+
clubTeamDnaHeading: strings.clubTeamDnaHeading,
|
|
168
|
+
clubFormHeading: strings.clubFormHeading,
|
|
169
|
+
clubCountdownLabel: strings.clubCountdownLabel,
|
|
170
|
+
clubKickoff: strings.clubKickoff,
|
|
171
|
+
clubKickoffAria: strings.clubKickoffAria,
|
|
151
172
|
addToSlip: strings.addToSlip,
|
|
152
173
|
inSlip: strings.inSlip,
|
|
153
174
|
slipAdded: strings.slipAdded,
|
|
@@ -341,6 +362,7 @@ function toInsightVM(insight) {
|
|
|
341
362
|
matchId: typeof insight.matchId === "number" ? insight.matchId : 0,
|
|
342
363
|
matchOddsId,
|
|
343
364
|
addToSlipEnabled: insight.addToSlipEnabled === true,
|
|
365
|
+
isMarketFloor: insight.isMarketFloor === true,
|
|
344
366
|
oddsDecimal: typeof oddsValue === "number" && Number.isFinite(oddsValue) ? oddsValue : void 0,
|
|
345
367
|
quotedAtUtc: str(insight.odds?.quotedAtUtc),
|
|
346
368
|
previousOdds: previous !== void 0 && direction !== void 0 ? previous.toFixed(2) : void 0,
|
|
@@ -494,7 +516,8 @@ function applyEvent(state, event) {
|
|
|
494
516
|
messages: mapActive(state, (t) => ({
|
|
495
517
|
...t,
|
|
496
518
|
done: true,
|
|
497
|
-
greetingDashboard: event.data.dashboard ?? void 0
|
|
519
|
+
greetingDashboard: event.data.dashboard ?? void 0,
|
|
520
|
+
clubDashboard: event.data.clubDashboard ?? void 0
|
|
498
521
|
}))
|
|
499
522
|
};
|
|
500
523
|
}
|
|
@@ -661,7 +684,7 @@ function ChatFab({ label, greeting, popupText, onOpen, onDismissPopup, dismissLa
|
|
|
661
684
|
}
|
|
662
685
|
|
|
663
686
|
// src/components/ChatPanel.tsx
|
|
664
|
-
import { useEffect as
|
|
687
|
+
import { useEffect as useEffect5, useState as useState8 } from "react";
|
|
665
688
|
|
|
666
689
|
// src/components/ChatHeader.tsx
|
|
667
690
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -674,7 +697,7 @@ function ChatHeader({ onClose, closeLabel = "Close chat" }) {
|
|
|
674
697
|
}
|
|
675
698
|
|
|
676
699
|
// src/components/MessageList.tsx
|
|
677
|
-
import { useEffect as
|
|
700
|
+
import { useEffect as useEffect4, useRef as useRef2 } from "react";
|
|
678
701
|
|
|
679
702
|
// src/components/Markdown.tsx
|
|
680
703
|
import ReactMarkdown from "react-markdown";
|
|
@@ -958,10 +981,13 @@ function StatsDisclosure({ it, ui }) {
|
|
|
958
981
|
function InsightCard({ it, ui, slip }) {
|
|
959
982
|
const live = it.isLive || it.phase === "live";
|
|
960
983
|
return /* @__PURE__ */ jsxs7("div", { className: "ss-w-binsight", children: [
|
|
961
|
-
live ? /* @__PURE__ */
|
|
962
|
-
/* @__PURE__ */
|
|
963
|
-
|
|
964
|
-
|
|
984
|
+
live || it.isMarketFloor ? /* @__PURE__ */ jsxs7("div", { className: "ss-w-bi-status", children: [
|
|
985
|
+
live ? /* @__PURE__ */ jsxs7("span", { className: "ss-w-bi-live", children: [
|
|
986
|
+
/* @__PURE__ */ jsx10("span", { className: "ss-w-bi-live-dot", "aria-hidden": "true" }),
|
|
987
|
+
ui?.liveLabel ?? "Live"
|
|
988
|
+
] }) : null,
|
|
989
|
+
it.isMarketFloor ? /* @__PURE__ */ jsx10("span", { className: "ss-w-bi-floor", children: ui?.marketFloorLabel ?? "Market price \xB7 not a value pick" }) : null
|
|
990
|
+
] }) : null,
|
|
965
991
|
/* @__PURE__ */ jsxs7("div", { className: "ss-w-bi-head", children: [
|
|
966
992
|
/* @__PURE__ */ jsxs7("span", { className: "ss-w-bi-mkt", children: [
|
|
967
993
|
/* @__PURE__ */ jsx10("span", { className: "ss-w-bi-market", children: it.market }),
|
|
@@ -1158,28 +1184,184 @@ function GreetingDashboard({ dashboard, onTap }) {
|
|
|
1158
1184
|
] });
|
|
1159
1185
|
}
|
|
1160
1186
|
|
|
1161
|
-
// src/components/
|
|
1187
|
+
// src/components/club-dashboard/GreetingHeaderBlock.tsx
|
|
1162
1188
|
import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1163
|
-
function
|
|
1189
|
+
function GreetingHeaderBlock({ block }) {
|
|
1190
|
+
return /* @__PURE__ */ jsxs13("div", { className: "ss-w-cdash-greeting", children: [
|
|
1191
|
+
/* @__PURE__ */ jsx16("div", { className: "ss-w-cdash-greeting-title", children: block.title }),
|
|
1192
|
+
/* @__PURE__ */ jsx16("div", { className: "ss-w-cdash-greeting-subtitle", children: block.subtitle })
|
|
1193
|
+
] });
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
// src/components/club-dashboard/FixtureHeroBlock.tsx
|
|
1197
|
+
import { useState as useState5 } from "react";
|
|
1198
|
+
import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1199
|
+
function initials2(name) {
|
|
1200
|
+
return name.split(/\s+/).filter(Boolean).slice(0, 2).map((w) => w[0].toUpperCase()).join("");
|
|
1201
|
+
}
|
|
1202
|
+
function Crest2({ url, name }) {
|
|
1203
|
+
const [broken, setBroken] = useState5(false);
|
|
1204
|
+
if (!url || broken) {
|
|
1205
|
+
return /* @__PURE__ */ jsx17("span", { className: "ss-w-cdash-crest ss-w-cdash-crest--fallback", "aria-hidden": "true", children: initials2(name) });
|
|
1206
|
+
}
|
|
1207
|
+
return /* @__PURE__ */ jsx17("img", { className: "ss-w-cdash-crest", src: url, alt: "", "aria-hidden": "true", onError: () => setBroken(true) });
|
|
1208
|
+
}
|
|
1209
|
+
function FixtureHeroBlock({
|
|
1210
|
+
block,
|
|
1211
|
+
ui,
|
|
1212
|
+
onAction
|
|
1213
|
+
}) {
|
|
1214
|
+
return /* @__PURE__ */ jsxs14("div", { className: "ss-w-cdash-fixture", children: [
|
|
1215
|
+
/* @__PURE__ */ jsxs14("div", { className: "ss-w-cdash-tie", children: [
|
|
1216
|
+
/* @__PURE__ */ jsx17(Crest2, { url: block.homeCrestUrl, name: block.homeName }),
|
|
1217
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-cdash-team", children: block.homeName }),
|
|
1218
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-cdash-vs", children: "v" }),
|
|
1219
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-cdash-team", children: block.awayName }),
|
|
1220
|
+
/* @__PURE__ */ jsx17(Crest2, { url: block.awayCrestUrl, name: block.awayName })
|
|
1221
|
+
] }),
|
|
1222
|
+
/* @__PURE__ */ jsxs14("div", { className: "ss-w-cdash-fixture-meta", children: [
|
|
1223
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-cdash-kickoff", children: ui.clubKickoff(block.kickoffUnix * 1e3) }),
|
|
1224
|
+
block.venue ? /* @__PURE__ */ jsx17("span", { className: "ss-w-cdash-venue", children: block.venue }) : null,
|
|
1225
|
+
block.competition ? /* @__PURE__ */ jsx17("span", { className: "ss-w-cdash-comp", children: block.competition }) : null
|
|
1226
|
+
] }),
|
|
1227
|
+
/* @__PURE__ */ jsx17("button", { type: "button", className: "ss-w-cdash-cta", onClick: () => onAction(block.ctaActionValue, block.ctaLabel), children: block.ctaLabel })
|
|
1228
|
+
] });
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
// src/components/club-dashboard/FormGuideBlock.tsx
|
|
1232
|
+
import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1233
|
+
function FormGuideBlock({ block }) {
|
|
1234
|
+
if (block.rows.length === 0) return null;
|
|
1235
|
+
return /* @__PURE__ */ jsx18("div", { className: "ss-w-cdash-form", children: block.rows.map((row) => /* @__PURE__ */ jsxs15("div", { className: "ss-w-cdash-form-row", children: [
|
|
1236
|
+
/* @__PURE__ */ jsxs15("span", { className: "ss-w-cdash-form-name", children: [
|
|
1237
|
+
row.name,
|
|
1238
|
+
row.position != null ? ` #${row.position}` : ""
|
|
1239
|
+
] }),
|
|
1240
|
+
/* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-form-pills", children: row.last5.map((r, i) => /* @__PURE__ */ jsx18("span", { className: `ss-w-cdash-pill ss-w-cdash-pill--${r.toLowerCase()}`, children: r }, i)) })
|
|
1241
|
+
] }, row.name)) });
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
// src/components/club-dashboard/MatchPreviewBlock.tsx
|
|
1245
|
+
import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1246
|
+
function MatchPreviewBlock({
|
|
1247
|
+
block,
|
|
1248
|
+
ui,
|
|
1249
|
+
onAction
|
|
1250
|
+
}) {
|
|
1251
|
+
if (block.lines.length === 0) return null;
|
|
1252
|
+
const heading = ui.clubMatchPreviewHeading;
|
|
1253
|
+
return /* @__PURE__ */ jsxs16("button", { type: "button", className: "ss-w-cdash-lines", onClick: () => onAction(block.actionValue, heading), children: [
|
|
1254
|
+
/* @__PURE__ */ jsx19("span", { className: "ss-w-cdash-lines-heading", children: heading }),
|
|
1255
|
+
block.lines.map((line, i) => /* @__PURE__ */ jsx19("p", { children: line }, i))
|
|
1256
|
+
] });
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
// src/components/club-dashboard/TeamDnaBlock.tsx
|
|
1260
|
+
import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1261
|
+
function TeamDnaBlock({
|
|
1262
|
+
block,
|
|
1263
|
+
ui,
|
|
1264
|
+
onAction
|
|
1265
|
+
}) {
|
|
1266
|
+
if (block.lines.length === 0) return null;
|
|
1267
|
+
const heading = ui.clubTeamDnaHeading;
|
|
1268
|
+
return /* @__PURE__ */ jsxs17("button", { type: "button", className: "ss-w-cdash-lines", onClick: () => onAction(block.actionValue, heading), children: [
|
|
1269
|
+
/* @__PURE__ */ jsx20("span", { className: "ss-w-cdash-lines-heading", children: heading }),
|
|
1270
|
+
block.lines.map((line, i) => /* @__PURE__ */ jsx20("p", { children: line }, i))
|
|
1271
|
+
] });
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// src/components/club-dashboard/CountdownBlock.tsx
|
|
1275
|
+
import { useEffect as useEffect3, useState as useState6 } from "react";
|
|
1276
|
+
import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1277
|
+
var DAY_MS = 24 * 3600 * 1e3;
|
|
1278
|
+
function pad2(n) {
|
|
1279
|
+
return String(n).padStart(2, "0");
|
|
1280
|
+
}
|
|
1281
|
+
function splitRemaining(remainingMs) {
|
|
1282
|
+
const totalSeconds = Math.max(0, Math.floor(remainingMs / 1e3));
|
|
1283
|
+
const h = Math.floor(totalSeconds / 3600);
|
|
1284
|
+
const m = Math.floor(totalSeconds % 3600 / 60);
|
|
1285
|
+
const s = totalSeconds % 60;
|
|
1286
|
+
return { h: pad2(h), m: pad2(m), s: pad2(s) };
|
|
1287
|
+
}
|
|
1288
|
+
function CountdownBlock({ block, ui }) {
|
|
1289
|
+
const [remainingMs, setRemainingMs] = useState6(() => block.kickoffUnix * 1e3 - Date.now());
|
|
1290
|
+
useEffect3(() => {
|
|
1291
|
+
setRemainingMs(block.kickoffUnix * 1e3 - Date.now());
|
|
1292
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
1293
|
+
const id = setInterval(() => {
|
|
1294
|
+
setRemainingMs(block.kickoffUnix * 1e3 - Date.now());
|
|
1295
|
+
}, 1e3);
|
|
1296
|
+
return () => clearInterval(id);
|
|
1297
|
+
}, [block.kickoffUnix]);
|
|
1298
|
+
if (remainingMs <= 0) return null;
|
|
1299
|
+
if (remainingMs > DAY_MS) return null;
|
|
1300
|
+
const { h, m, s } = splitRemaining(remainingMs);
|
|
1301
|
+
return /* @__PURE__ */ jsxs18("div", { className: "ss-w-cdash-cd", "aria-label": ui.clubKickoffAria(block.kickoffUnix * 1e3), children: [
|
|
1302
|
+
/* @__PURE__ */ jsxs18("div", { className: "ss-w-cdash-cd-clock", "aria-hidden": "true", children: [
|
|
1303
|
+
/* @__PURE__ */ jsx21("span", { className: "ss-w-cdash-cd-h", children: h }),
|
|
1304
|
+
"h",
|
|
1305
|
+
" ",
|
|
1306
|
+
/* @__PURE__ */ jsx21("span", { className: "ss-w-cdash-cd-m", children: m }),
|
|
1307
|
+
"m",
|
|
1308
|
+
" ",
|
|
1309
|
+
/* @__PURE__ */ jsx21("span", { className: "ss-w-cdash-cd-s", children: s }),
|
|
1310
|
+
"s"
|
|
1311
|
+
] }),
|
|
1312
|
+
/* @__PURE__ */ jsx21("div", { className: "ss-w-cdash-cd-label", children: ui.clubCountdownLabel })
|
|
1313
|
+
] });
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// src/components/club-dashboard/ClubDashboard.tsx
|
|
1317
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
1318
|
+
var CLUB_BLOCK_REGISTRY = {
|
|
1319
|
+
greetingHeader: ({ block }) => block.type === "greetingHeader" ? /* @__PURE__ */ jsx22(GreetingHeaderBlock, { block }) : null,
|
|
1320
|
+
fixtureHero: ({ block, ui, onAction }) => block.type === "fixtureHero" ? /* @__PURE__ */ jsx22(FixtureHeroBlock, { block, ui, onAction }) : null,
|
|
1321
|
+
formGuide: ({ block, ui }) => block.type === "formGuide" ? /* @__PURE__ */ jsx22(FormGuideBlock, { block, ui }) : null,
|
|
1322
|
+
matchPreview: ({ block, ui, onAction }) => block.type === "matchPreview" ? /* @__PURE__ */ jsx22(MatchPreviewBlock, { block, ui, onAction }) : null,
|
|
1323
|
+
teamDna: ({ block, ui, onAction }) => block.type === "teamDna" ? /* @__PURE__ */ jsx22(TeamDnaBlock, { block, ui, onAction }) : null,
|
|
1324
|
+
countdown: ({ block, ui }) => block.type === "countdown" ? /* @__PURE__ */ jsx22(CountdownBlock, { block, ui }) : null
|
|
1325
|
+
};
|
|
1326
|
+
function ClubDashboard({
|
|
1327
|
+
dashboard,
|
|
1328
|
+
ui,
|
|
1329
|
+
onAction
|
|
1330
|
+
}) {
|
|
1331
|
+
if (!dashboard.blocks.length) return null;
|
|
1332
|
+
return /* @__PURE__ */ jsx22("div", { className: "ss-w-cdash", children: dashboard.blocks.map((block, i) => {
|
|
1333
|
+
const Comp = Object.prototype.hasOwnProperty.call(CLUB_BLOCK_REGISTRY, block.type) ? CLUB_BLOCK_REGISTRY[block.type] : void 0;
|
|
1334
|
+
if (!Comp) {
|
|
1335
|
+
console.warn(`[club-dashboard] no renderer for block type "${block.type}"`);
|
|
1336
|
+
return null;
|
|
1337
|
+
}
|
|
1338
|
+
return /* @__PURE__ */ jsx22("div", { className: "ss-w-cdash-block", children: /* @__PURE__ */ jsx22(Comp, { block, ui, onAction }) }, i);
|
|
1339
|
+
}) });
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
// src/components/ChatMessage.tsx
|
|
1343
|
+
import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1344
|
+
function ChatMessage({ message, disabled, onAct, onTap, onClubAction, ui, slip }) {
|
|
1164
1345
|
if (message.role === "user") {
|
|
1165
|
-
return /* @__PURE__ */
|
|
1346
|
+
return /* @__PURE__ */ jsx23("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx23("div", { className: "ss-w-bubble", children: message.text }) });
|
|
1166
1347
|
}
|
|
1167
|
-
return /* @__PURE__ */
|
|
1168
|
-
message.text && !message.greetingDashboard ? /* @__PURE__ */
|
|
1169
|
-
message.greetingDashboard ? /* @__PURE__ */
|
|
1170
|
-
/* @__PURE__ */
|
|
1171
|
-
/* @__PURE__ */
|
|
1172
|
-
/* @__PURE__ */
|
|
1173
|
-
/* @__PURE__ */
|
|
1174
|
-
/* @__PURE__ */
|
|
1348
|
+
return /* @__PURE__ */ jsxs19("div", { className: "ss-w-msg ss-w-bot", children: [
|
|
1349
|
+
message.text && !message.greetingDashboard && !message.clubDashboard ? /* @__PURE__ */ jsx23(Markdown, { text: message.text }) : null,
|
|
1350
|
+
message.greetingDashboard ? /* @__PURE__ */ jsx23(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
|
|
1351
|
+
message.clubDashboard && ui ? /* @__PURE__ */ jsx23(ClubDashboard, { dashboard: message.clubDashboard, ui, onAction: onClubAction }) : null,
|
|
1352
|
+
/* @__PURE__ */ jsx23(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
|
|
1353
|
+
/* @__PURE__ */ jsx23(BettingInsightList, { insights: message.insights, ui, slip }),
|
|
1354
|
+
/* @__PURE__ */ jsx23(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
|
|
1355
|
+
/* @__PURE__ */ jsx23(OddsMovementCardList, { cards: message.oddsMovement, ui }),
|
|
1356
|
+
/* @__PURE__ */ jsx23(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
|
|
1175
1357
|
] });
|
|
1176
1358
|
}
|
|
1177
1359
|
|
|
1178
1360
|
// src/components/IntentChips.tsx
|
|
1179
|
-
import { jsx as
|
|
1361
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
1180
1362
|
function IntentChips({ chips, onPick }) {
|
|
1181
1363
|
if (chips.length === 0) return null;
|
|
1182
|
-
return /* @__PURE__ */
|
|
1364
|
+
return /* @__PURE__ */ jsx24("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx24("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
|
|
1183
1365
|
}
|
|
1184
1366
|
|
|
1185
1367
|
// src/scroll.ts
|
|
@@ -1189,18 +1371,18 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
|
|
|
1189
1371
|
}
|
|
1190
1372
|
|
|
1191
1373
|
// src/components/MessageList.tsx
|
|
1192
|
-
import { jsx as
|
|
1374
|
+
import { jsx as jsx25, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1193
1375
|
function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
1194
1376
|
const { state, send } = controller;
|
|
1195
1377
|
const listRef = useRef2(null);
|
|
1196
1378
|
const endRef = useRef2(null);
|
|
1197
1379
|
const pinnedRef = useRef2(true);
|
|
1198
|
-
|
|
1380
|
+
useEffect4(() => {
|
|
1199
1381
|
if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
|
|
1200
1382
|
}, [state.messages, state.status]);
|
|
1201
1383
|
const hasUserMessage = state.messages.some((m) => m.role === "user");
|
|
1202
1384
|
const hasDashboard = state.messages.some((m) => m.role === "assistant" && m.greetingDashboard);
|
|
1203
|
-
return /* @__PURE__ */
|
|
1385
|
+
return /* @__PURE__ */ jsxs20(
|
|
1204
1386
|
"div",
|
|
1205
1387
|
{
|
|
1206
1388
|
className: "ss-w-list",
|
|
@@ -1210,7 +1392,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1210
1392
|
if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
|
|
1211
1393
|
},
|
|
1212
1394
|
children: [
|
|
1213
|
-
state.messages.map((m) => /* @__PURE__ */
|
|
1395
|
+
state.messages.map((m) => /* @__PURE__ */ jsx25(
|
|
1214
1396
|
ChatMessage,
|
|
1215
1397
|
{
|
|
1216
1398
|
message: m,
|
|
@@ -1220,30 +1402,31 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1220
1402
|
send(tile.actionValue, { displayText: tile.title });
|
|
1221
1403
|
if (m.role === "assistant" && m.greetingDashboard) onDashboardTap?.(tile, index, m.greetingDashboard.layout);
|
|
1222
1404
|
},
|
|
1405
|
+
onClubAction: (actionValue, displayText) => send(actionValue, { displayText }),
|
|
1223
1406
|
ui,
|
|
1224
1407
|
slip
|
|
1225
1408
|
},
|
|
1226
1409
|
m.id
|
|
1227
1410
|
)),
|
|
1228
|
-
state.isStreaming && state.actionsLoading ? /* @__PURE__ */
|
|
1411
|
+
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx25(ActionButtons, { actions: [], loading: true, onAct: () => {
|
|
1229
1412
|
} }) : null,
|
|
1230
|
-
state.status ? /* @__PURE__ */
|
|
1231
|
-
state.error ? /* @__PURE__ */
|
|
1232
|
-
/* @__PURE__ */
|
|
1233
|
-
/* @__PURE__ */
|
|
1413
|
+
state.status ? /* @__PURE__ */ jsx25("div", { className: "ss-w-status", children: state.status }) : null,
|
|
1414
|
+
state.error ? /* @__PURE__ */ jsxs20("div", { className: "ss-w-error", role: "alert", children: [
|
|
1415
|
+
/* @__PURE__ */ jsx25("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
|
|
1416
|
+
/* @__PURE__ */ jsx25("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
|
|
1234
1417
|
] }) : null,
|
|
1235
|
-
!hasUserMessage && !hasDashboard ? /* @__PURE__ */
|
|
1236
|
-
/* @__PURE__ */
|
|
1418
|
+
!hasUserMessage && !hasDashboard ? /* @__PURE__ */ jsx25(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
|
|
1419
|
+
/* @__PURE__ */ jsx25("div", { ref: endRef })
|
|
1237
1420
|
]
|
|
1238
1421
|
}
|
|
1239
1422
|
);
|
|
1240
1423
|
}
|
|
1241
1424
|
|
|
1242
1425
|
// src/components/ChatInput.tsx
|
|
1243
|
-
import { useState as
|
|
1244
|
-
import { jsx as
|
|
1426
|
+
import { useState as useState7 } from "react";
|
|
1427
|
+
import { jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1245
1428
|
function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
|
|
1246
|
-
const [value, setValue] =
|
|
1429
|
+
const [value, setValue] = useState7("");
|
|
1247
1430
|
const submit = () => {
|
|
1248
1431
|
const t = value.trim();
|
|
1249
1432
|
if (!t) return;
|
|
@@ -1256,18 +1439,18 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
|
|
|
1256
1439
|
submit();
|
|
1257
1440
|
}
|
|
1258
1441
|
};
|
|
1259
|
-
return /* @__PURE__ */
|
|
1260
|
-
/* @__PURE__ */
|
|
1261
|
-
streaming ? /* @__PURE__ */
|
|
1442
|
+
return /* @__PURE__ */ jsxs21("div", { className: "ss-w-input", children: [
|
|
1443
|
+
/* @__PURE__ */ jsx26("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
|
|
1444
|
+
streaming ? /* @__PURE__ */ jsx26("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ jsx26("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx26("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ jsx26(SendIcon, {}) })
|
|
1262
1445
|
] });
|
|
1263
1446
|
}
|
|
1264
1447
|
|
|
1265
1448
|
// src/components/ChatPanel.tsx
|
|
1266
|
-
import { jsx as
|
|
1449
|
+
import { jsx as jsx27, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1267
1450
|
var FULLSCREEN_QUERY = "(max-width: 639px)";
|
|
1268
1451
|
function useIsModal() {
|
|
1269
|
-
const [modal, setModal] =
|
|
1270
|
-
|
|
1452
|
+
const [modal, setModal] = useState8(false);
|
|
1453
|
+
useEffect5(() => {
|
|
1271
1454
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
1272
1455
|
const mql = window.matchMedia(FULLSCREEN_QUERY);
|
|
1273
1456
|
const sync = () => setModal(mql.matches);
|
|
@@ -1280,17 +1463,17 @@ function useIsModal() {
|
|
|
1280
1463
|
function ChatPanel({ controller, ui, slip, onDashboardTap }) {
|
|
1281
1464
|
const { state, close, stop, send } = controller;
|
|
1282
1465
|
const isModal = useIsModal();
|
|
1283
|
-
|
|
1466
|
+
useEffect5(() => {
|
|
1284
1467
|
const onKey = (e) => {
|
|
1285
1468
|
if (e.key === "Escape") close();
|
|
1286
1469
|
};
|
|
1287
1470
|
window.addEventListener("keydown", onKey);
|
|
1288
1471
|
return () => window.removeEventListener("keydown", onKey);
|
|
1289
1472
|
}, [close]);
|
|
1290
|
-
return /* @__PURE__ */
|
|
1291
|
-
/* @__PURE__ */
|
|
1292
|
-
/* @__PURE__ */
|
|
1293
|
-
/* @__PURE__ */
|
|
1473
|
+
return /* @__PURE__ */ jsxs22("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
|
|
1474
|
+
/* @__PURE__ */ jsx27(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
|
|
1475
|
+
/* @__PURE__ */ jsx27(MessageList, { controller, ui, slip, onDashboardTap }),
|
|
1476
|
+
/* @__PURE__ */ jsx27(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
|
|
1294
1477
|
] });
|
|
1295
1478
|
}
|
|
1296
1479
|
|
|
@@ -1434,6 +1617,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
1434
1617
|
.ss-w-bi-comp { font-size: 10.5px; color: var(--ss-w-faint); font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
1435
1618
|
.ss-w-bi-live { font-size: 8.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: #fff; background: #c0392b; border-radius: 4px; padding: 1px 5px; display: inline-flex; align-items: center; gap: 4px; flex: none; }
|
|
1436
1619
|
.ss-w-bi-live-dot { width: 5px; height: 5px; border-radius: 50%; background: #fff; }
|
|
1620
|
+
.ss-w-bi-floor { font-size: 8.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--ss-w-faint); background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 4px; padding: 1px 5px; display: inline-flex; align-items: center; flex: none; }
|
|
1437
1621
|
.ss-w-bi-head { display: flex; align-items: flex-start; gap: 10px; }
|
|
1438
1622
|
.ss-w-bi-mkt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
|
|
1439
1623
|
.ss-w-bi-market { font-size: 15px; font-weight: 700; line-height: 1.25; color: var(--ss-w-ink); }
|
|
@@ -1615,10 +1799,46 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
1615
1799
|
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(2) { background: #eef3fb; }
|
|
1616
1800
|
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(3) { background: #fdf6ec; }
|
|
1617
1801
|
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(4) { background: #f5eefb; }
|
|
1802
|
+
|
|
1803
|
+
/* club match-day dashboard */
|
|
1804
|
+
.ss-w-cdash { display: flex; flex-direction: column; gap: 10px; padding: 4px 0; }
|
|
1805
|
+
.ss-w-cdash-block { display: flex; flex-direction: column; }
|
|
1806
|
+
|
|
1807
|
+
.ss-w-cdash-greeting { display: flex; flex-direction: column; gap: 2px; }
|
|
1808
|
+
.ss-w-cdash-greeting-title { font-weight: 700; font-size: 14px; color: var(--ss-w-ink); }
|
|
1809
|
+
.ss-w-cdash-greeting-subtitle { font-size: 11.5px; color: var(--ss-w-faint); }
|
|
1810
|
+
|
|
1811
|
+
.ss-w-cdash-fixture { display: flex; flex-direction: column; align-items: center; gap: 8px; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 12px; text-align: center; }
|
|
1812
|
+
.ss-w-cdash-tie { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; }
|
|
1813
|
+
.ss-w-cdash-team { font-weight: 650; font-size: 13px; color: var(--ss-w-ink); min-width: 0; overflow-wrap: anywhere; }
|
|
1814
|
+
.ss-w-cdash-vs { font-size: 11px; color: var(--ss-w-faint); font-weight: 600; }
|
|
1815
|
+
.ss-w-cdash-crest { flex: none; width: 28px; height: 28px; border-radius: 50%; object-fit: cover; background: var(--ss-w-bubble); }
|
|
1816
|
+
.ss-w-cdash-crest--fallback { display: grid; place-items: center; font-size: 10.5px; font-weight: 700; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 16%, var(--ss-w-panel)); }
|
|
1817
|
+
.ss-w-cdash-fixture-meta { display: flex; align-items: center; justify-content: center; gap: 6px; flex-wrap: wrap; font-size: 11px; color: var(--ss-w-faint); }
|
|
1818
|
+
.ss-w-cdash-kickoff { font-weight: 700; color: var(--ss-w-ink); }
|
|
1819
|
+
.ss-w-cdash-cta { font: inherit; font-weight: 700; font-size: 12px; color: var(--ss-w-cta-ink); background: var(--ss-w-cta); border: none; border-radius: 999px; padding: 8px 18px; cursor: pointer; }
|
|
1820
|
+
|
|
1821
|
+
.ss-w-cdash-form { display: flex; flex-direction: column; gap: 6px; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 10px 12px; }
|
|
1822
|
+
.ss-w-cdash-form-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
1823
|
+
.ss-w-cdash-form-name { font-size: 12px; font-weight: 600; color: var(--ss-w-ink); min-width: 0; overflow-wrap: anywhere; }
|
|
1824
|
+
.ss-w-cdash-form-pills { flex: none; display: flex; align-items: center; gap: 3px; }
|
|
1825
|
+
.ss-w-cdash-pill { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; border-radius: 50%; font-size: 9.5px; font-weight: 700; color: #fff; }
|
|
1826
|
+
.ss-w-cdash-pill--w { background: color-mix(in srgb, var(--ss-w-pos) 88%, black); }
|
|
1827
|
+
.ss-w-cdash-pill--d { background: var(--ss-w-faint); }
|
|
1828
|
+
.ss-w-cdash-pill--l { background: color-mix(in srgb, var(--ss-w-neg) 88%, black); }
|
|
1829
|
+
|
|
1830
|
+
.ss-w-cdash-lines { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; width: 100%; text-align: left; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-left: 3px solid var(--ss-w-accent); border-radius: 12px; padding: 10px 12px; font: inherit; cursor: pointer; }
|
|
1831
|
+
.ss-w-cdash-lines-heading { font-size: 12px; font-weight: 700; color: var(--ss-w-accent); }
|
|
1832
|
+
.ss-w-cdash-lines p { font-size: 12px; line-height: 1.4; color: var(--ss-w-ink); }
|
|
1833
|
+
|
|
1834
|
+
.ss-w-cdash-cd { display: flex; flex-direction: column; align-items: center; gap: 4px; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 10px 12px; text-align: center; }
|
|
1835
|
+
.ss-w-cdash-cd-clock { display: flex; align-items: baseline; justify-content: center; gap: 2px; font-variant-numeric: tabular-nums; font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; font-weight: 700; font-size: 18px; color: var(--ss-w-ink); }
|
|
1836
|
+
.ss-w-cdash-cd-h, .ss-w-cdash-cd-m, .ss-w-cdash-cd-s { color: var(--ss-w-accent); }
|
|
1837
|
+
.ss-w-cdash-cd-label { font-size: 11px; font-weight: 600; color: var(--ss-w-faint); }
|
|
1618
1838
|
`;
|
|
1619
1839
|
|
|
1620
1840
|
// src/StatsWidget.tsx
|
|
1621
|
-
import { jsx as
|
|
1841
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
1622
1842
|
function buildClient(props) {
|
|
1623
1843
|
try {
|
|
1624
1844
|
if ("client" in props && props.client) {
|
|
@@ -1664,14 +1884,14 @@ function StatsWidget(props) {
|
|
|
1664
1884
|
() => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input)) : void 0,
|
|
1665
1885
|
[built]
|
|
1666
1886
|
);
|
|
1667
|
-
|
|
1887
|
+
useEffect6(() => {
|
|
1668
1888
|
if (!built || !ui.injectStyles) return;
|
|
1669
1889
|
const node = rootRef.current?.getRootNode();
|
|
1670
1890
|
if (node) injectStyles(node);
|
|
1671
1891
|
}, [built, ui.injectStyles]);
|
|
1672
1892
|
if (!built) return null;
|
|
1673
1893
|
const attrs = themeAttrs(ui);
|
|
1674
|
-
return /* @__PURE__ */
|
|
1894
|
+
return /* @__PURE__ */ jsx28("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__ */ jsx28(ChatPanel, { controller, ui, slip, onDashboardTap }) : /* @__PURE__ */ jsx28(
|
|
1675
1895
|
ChatFab,
|
|
1676
1896
|
{
|
|
1677
1897
|
label: ui.launcherLabel,
|