@sensiblestats/widget-react 0.14.1 → 0.15.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 +11 -0
- package/dist/index.cjs +265 -53
- 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 -45
- package/dist/index.js.map +1 -1
- package/dist/styles.css +36 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @sensiblestats/widget-react
|
|
2
2
|
|
|
3
|
+
## 0.15.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Render the Club Match-Day dashboard (typed blocks: greeting header, fixture hero, form guide, match preview, team DNA, countdown) delivered on `turn_complete.clubDashboard`.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
- @sensiblestats/widget-sdk@0.13.0
|
|
13
|
+
|
|
3
14
|
## 0.14.1
|
|
4
15
|
|
|
5
16
|
### 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_react12 = require("react");
|
|
40
40
|
var import_widget_sdk5 = require("@sensiblestats/widget-sdk");
|
|
41
41
|
|
|
42
42
|
// src/i18n.ts
|
|
@@ -79,6 +79,12 @@ var STRINGS = {
|
|
|
79
79
|
const drift = direction ? `, ${direction}` : "";
|
|
80
80
|
return `Price movement over the last ${span}: opened at ${opening}, currently ${current}${drift}.`;
|
|
81
81
|
},
|
|
82
|
+
clubMatchPreviewHeading: "Match preview",
|
|
83
|
+
clubTeamDnaHeading: "How they play",
|
|
84
|
+
clubFormHeading: "Recent form",
|
|
85
|
+
clubCountdownLabel: "Countdown to kick-off",
|
|
86
|
+
clubKickoff: (ms) => new Intl.DateTimeFormat("en-GB", { weekday: "short", day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(ms)),
|
|
87
|
+
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))}`,
|
|
82
88
|
inSlip: "In slip",
|
|
83
89
|
slipAdded: (n) => n === void 0 ? "Added to slip" : `Added to slip (${n} selection${n === 1 ? "" : "s"})`,
|
|
84
90
|
slipDuplicate: "Already in your slip",
|
|
@@ -126,6 +132,12 @@ var STRINGS = {
|
|
|
126
132
|
const drift = direction ? `, ${direction}` : "";
|
|
127
133
|
return `Son ${span} i\xE7indeki oran hareketi: ${opening} ile a\xE7\u0131ld\u0131, \u015Fu anda ${current}${drift}.`;
|
|
128
134
|
},
|
|
135
|
+
clubMatchPreviewHeading: "Ma\xE7 \xF6nizlemesi",
|
|
136
|
+
clubTeamDnaHeading: "Nas\u0131l oynuyorlar",
|
|
137
|
+
clubFormHeading: "Son form",
|
|
138
|
+
clubCountdownLabel: "Ba\u015Flama vuru\u015Funa kalan",
|
|
139
|
+
clubKickoff: (ms) => new Intl.DateTimeFormat("tr-TR", { weekday: "short", day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(ms)),
|
|
140
|
+
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))}`,
|
|
129
141
|
inSlip: "Kuponda",
|
|
130
142
|
slipAdded: (n) => n === void 0 ? "Kupona eklendi" : `Kupona eklendi (${n} se\xE7im)`,
|
|
131
143
|
slipDuplicate: "Bu se\xE7im kuponunuzda zaten var",
|
|
@@ -185,6 +197,12 @@ function normalizeUi(cfg) {
|
|
|
185
197
|
excludedSuspended: strings.excludedSuspended,
|
|
186
198
|
chartTimeLabel: strings.chartTimeLabel,
|
|
187
199
|
chartAria: strings.chartAria,
|
|
200
|
+
clubMatchPreviewHeading: strings.clubMatchPreviewHeading,
|
|
201
|
+
clubTeamDnaHeading: strings.clubTeamDnaHeading,
|
|
202
|
+
clubFormHeading: strings.clubFormHeading,
|
|
203
|
+
clubCountdownLabel: strings.clubCountdownLabel,
|
|
204
|
+
clubKickoff: strings.clubKickoff,
|
|
205
|
+
clubKickoffAria: strings.clubKickoffAria,
|
|
188
206
|
addToSlip: strings.addToSlip,
|
|
189
207
|
inSlip: strings.inSlip,
|
|
190
208
|
slipAdded: strings.slipAdded,
|
|
@@ -531,7 +549,8 @@ function applyEvent(state, event) {
|
|
|
531
549
|
messages: mapActive(state, (t) => ({
|
|
532
550
|
...t,
|
|
533
551
|
done: true,
|
|
534
|
-
greetingDashboard: event.data.dashboard ?? void 0
|
|
552
|
+
greetingDashboard: event.data.dashboard ?? void 0,
|
|
553
|
+
clubDashboard: event.data.clubDashboard ?? void 0
|
|
535
554
|
}))
|
|
536
555
|
};
|
|
537
556
|
}
|
|
@@ -698,7 +717,7 @@ function ChatFab({ label, greeting, popupText, onOpen, onDismissPopup, dismissLa
|
|
|
698
717
|
}
|
|
699
718
|
|
|
700
719
|
// src/components/ChatPanel.tsx
|
|
701
|
-
var
|
|
720
|
+
var import_react11 = require("react");
|
|
702
721
|
|
|
703
722
|
// src/components/ChatHeader.tsx
|
|
704
723
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
@@ -711,7 +730,7 @@ function ChatHeader({ onClose, closeLabel = "Close chat" }) {
|
|
|
711
730
|
}
|
|
712
731
|
|
|
713
732
|
// src/components/MessageList.tsx
|
|
714
|
-
var
|
|
733
|
+
var import_react9 = require("react");
|
|
715
734
|
|
|
716
735
|
// src/components/Markdown.tsx
|
|
717
736
|
var import_react_markdown = __toESM(require("react-markdown"), 1);
|
|
@@ -1195,28 +1214,184 @@ function GreetingDashboard({ dashboard, onTap }) {
|
|
|
1195
1214
|
] });
|
|
1196
1215
|
}
|
|
1197
1216
|
|
|
1198
|
-
// src/components/
|
|
1217
|
+
// src/components/club-dashboard/GreetingHeaderBlock.tsx
|
|
1199
1218
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1200
|
-
function
|
|
1219
|
+
function GreetingHeaderBlock({ block }) {
|
|
1220
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ss-w-cdash-greeting", children: [
|
|
1221
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "ss-w-cdash-greeting-title", children: block.title }),
|
|
1222
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "ss-w-cdash-greeting-subtitle", children: block.subtitle })
|
|
1223
|
+
] });
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// src/components/club-dashboard/FixtureHeroBlock.tsx
|
|
1227
|
+
var import_react7 = require("react");
|
|
1228
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1229
|
+
function initials2(name) {
|
|
1230
|
+
return name.split(/\s+/).filter(Boolean).slice(0, 2).map((w) => w[0].toUpperCase()).join("");
|
|
1231
|
+
}
|
|
1232
|
+
function Crest2({ url, name }) {
|
|
1233
|
+
const [broken, setBroken] = (0, import_react7.useState)(false);
|
|
1234
|
+
if (!url || broken) {
|
|
1235
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ss-w-cdash-crest ss-w-cdash-crest--fallback", "aria-hidden": "true", children: initials2(name) });
|
|
1236
|
+
}
|
|
1237
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("img", { className: "ss-w-cdash-crest", src: url, alt: "", "aria-hidden": "true", onError: () => setBroken(true) });
|
|
1238
|
+
}
|
|
1239
|
+
function FixtureHeroBlock({
|
|
1240
|
+
block,
|
|
1241
|
+
ui,
|
|
1242
|
+
onAction
|
|
1243
|
+
}) {
|
|
1244
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "ss-w-cdash-fixture", children: [
|
|
1245
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "ss-w-cdash-tie", children: [
|
|
1246
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Crest2, { url: block.homeCrestUrl, name: block.homeName }),
|
|
1247
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ss-w-cdash-team", children: block.homeName }),
|
|
1248
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ss-w-cdash-vs", children: "v" }),
|
|
1249
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ss-w-cdash-team", children: block.awayName }),
|
|
1250
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Crest2, { url: block.awayCrestUrl, name: block.awayName })
|
|
1251
|
+
] }),
|
|
1252
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "ss-w-cdash-fixture-meta", children: [
|
|
1253
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ss-w-cdash-kickoff", children: ui.clubKickoff(block.kickoffUnix * 1e3) }),
|
|
1254
|
+
block.venue ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ss-w-cdash-venue", children: block.venue }) : null,
|
|
1255
|
+
block.competition ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "ss-w-cdash-comp", children: block.competition }) : null
|
|
1256
|
+
] }),
|
|
1257
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", className: "ss-w-cdash-cta", onClick: () => onAction(block.ctaActionValue, block.ctaLabel), children: block.ctaLabel })
|
|
1258
|
+
] });
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
// src/components/club-dashboard/FormGuideBlock.tsx
|
|
1262
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1263
|
+
function FormGuideBlock({ block }) {
|
|
1264
|
+
if (block.rows.length === 0) return null;
|
|
1265
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "ss-w-cdash-form", children: block.rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "ss-w-cdash-form-row", children: [
|
|
1266
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "ss-w-cdash-form-name", children: [
|
|
1267
|
+
row.name,
|
|
1268
|
+
row.position != null ? ` #${row.position}` : ""
|
|
1269
|
+
] }),
|
|
1270
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "ss-w-cdash-form-pills", children: row.last5.map((r, i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: `ss-w-cdash-pill ss-w-cdash-pill--${r.toLowerCase()}`, children: r }, i)) })
|
|
1271
|
+
] }, row.name)) });
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
// src/components/club-dashboard/MatchPreviewBlock.tsx
|
|
1275
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1276
|
+
function MatchPreviewBlock({
|
|
1277
|
+
block,
|
|
1278
|
+
ui,
|
|
1279
|
+
onAction
|
|
1280
|
+
}) {
|
|
1281
|
+
if (block.lines.length === 0) return null;
|
|
1282
|
+
const heading = ui.clubMatchPreviewHeading;
|
|
1283
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("button", { type: "button", className: "ss-w-cdash-lines", onClick: () => onAction(block.actionValue, heading), children: [
|
|
1284
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "ss-w-cdash-lines-heading", children: heading }),
|
|
1285
|
+
block.lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { children: line }, i))
|
|
1286
|
+
] });
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
// src/components/club-dashboard/TeamDnaBlock.tsx
|
|
1290
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1291
|
+
function TeamDnaBlock({
|
|
1292
|
+
block,
|
|
1293
|
+
ui,
|
|
1294
|
+
onAction
|
|
1295
|
+
}) {
|
|
1296
|
+
if (block.lines.length === 0) return null;
|
|
1297
|
+
const heading = ui.clubTeamDnaHeading;
|
|
1298
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("button", { type: "button", className: "ss-w-cdash-lines", onClick: () => onAction(block.actionValue, heading), children: [
|
|
1299
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ss-w-cdash-lines-heading", children: heading }),
|
|
1300
|
+
block.lines.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("p", { children: line }, i))
|
|
1301
|
+
] });
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
// src/components/club-dashboard/CountdownBlock.tsx
|
|
1305
|
+
var import_react8 = require("react");
|
|
1306
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1307
|
+
var DAY_MS = 24 * 3600 * 1e3;
|
|
1308
|
+
function pad2(n) {
|
|
1309
|
+
return String(n).padStart(2, "0");
|
|
1310
|
+
}
|
|
1311
|
+
function splitRemaining(remainingMs) {
|
|
1312
|
+
const totalSeconds = Math.max(0, Math.floor(remainingMs / 1e3));
|
|
1313
|
+
const h = Math.floor(totalSeconds / 3600);
|
|
1314
|
+
const m = Math.floor(totalSeconds % 3600 / 60);
|
|
1315
|
+
const s = totalSeconds % 60;
|
|
1316
|
+
return { h: pad2(h), m: pad2(m), s: pad2(s) };
|
|
1317
|
+
}
|
|
1318
|
+
function CountdownBlock({ block, ui }) {
|
|
1319
|
+
const [remainingMs, setRemainingMs] = (0, import_react8.useState)(() => block.kickoffUnix * 1e3 - Date.now());
|
|
1320
|
+
(0, import_react8.useEffect)(() => {
|
|
1321
|
+
setRemainingMs(block.kickoffUnix * 1e3 - Date.now());
|
|
1322
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
1323
|
+
const id = setInterval(() => {
|
|
1324
|
+
setRemainingMs(block.kickoffUnix * 1e3 - Date.now());
|
|
1325
|
+
}, 1e3);
|
|
1326
|
+
return () => clearInterval(id);
|
|
1327
|
+
}, [block.kickoffUnix]);
|
|
1328
|
+
if (remainingMs <= 0) return null;
|
|
1329
|
+
if (remainingMs > DAY_MS) return null;
|
|
1330
|
+
const { h, m, s } = splitRemaining(remainingMs);
|
|
1331
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "ss-w-cdash-cd", "aria-label": ui.clubKickoffAria(block.kickoffUnix * 1e3), children: [
|
|
1332
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "ss-w-cdash-cd-clock", "aria-hidden": "true", children: [
|
|
1333
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "ss-w-cdash-cd-h", children: h }),
|
|
1334
|
+
"h",
|
|
1335
|
+
" ",
|
|
1336
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "ss-w-cdash-cd-m", children: m }),
|
|
1337
|
+
"m",
|
|
1338
|
+
" ",
|
|
1339
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "ss-w-cdash-cd-s", children: s }),
|
|
1340
|
+
"s"
|
|
1341
|
+
] }),
|
|
1342
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "ss-w-cdash-cd-label", children: ui.clubCountdownLabel })
|
|
1343
|
+
] });
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
// src/components/club-dashboard/ClubDashboard.tsx
|
|
1347
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1348
|
+
var CLUB_BLOCK_REGISTRY = {
|
|
1349
|
+
greetingHeader: ({ block }) => block.type === "greetingHeader" ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(GreetingHeaderBlock, { block }) : null,
|
|
1350
|
+
fixtureHero: ({ block, ui, onAction }) => block.type === "fixtureHero" ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(FixtureHeroBlock, { block, ui, onAction }) : null,
|
|
1351
|
+
formGuide: ({ block, ui }) => block.type === "formGuide" ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(FormGuideBlock, { block, ui }) : null,
|
|
1352
|
+
matchPreview: ({ block, ui, onAction }) => block.type === "matchPreview" ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(MatchPreviewBlock, { block, ui, onAction }) : null,
|
|
1353
|
+
teamDna: ({ block, ui, onAction }) => block.type === "teamDna" ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(TeamDnaBlock, { block, ui, onAction }) : null,
|
|
1354
|
+
countdown: ({ block, ui }) => block.type === "countdown" ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CountdownBlock, { block, ui }) : null
|
|
1355
|
+
};
|
|
1356
|
+
function ClubDashboard({
|
|
1357
|
+
dashboard,
|
|
1358
|
+
ui,
|
|
1359
|
+
onAction
|
|
1360
|
+
}) {
|
|
1361
|
+
if (!dashboard.blocks.length) return null;
|
|
1362
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "ss-w-cdash", children: dashboard.blocks.map((block, i) => {
|
|
1363
|
+
const Comp = Object.prototype.hasOwnProperty.call(CLUB_BLOCK_REGISTRY, block.type) ? CLUB_BLOCK_REGISTRY[block.type] : void 0;
|
|
1364
|
+
if (!Comp) {
|
|
1365
|
+
console.warn(`[club-dashboard] no renderer for block type "${block.type}"`);
|
|
1366
|
+
return null;
|
|
1367
|
+
}
|
|
1368
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "ss-w-cdash-block", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Comp, { block, ui, onAction }) }, i);
|
|
1369
|
+
}) });
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// src/components/ChatMessage.tsx
|
|
1373
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1374
|
+
function ChatMessage({ message, disabled, onAct, onTap, onClubAction, ui, slip }) {
|
|
1201
1375
|
if (message.role === "user") {
|
|
1202
|
-
return /* @__PURE__ */ (0,
|
|
1376
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "ss-w-bubble", children: message.text }) });
|
|
1203
1377
|
}
|
|
1204
|
-
return /* @__PURE__ */ (0,
|
|
1205
|
-
message.text && !message.greetingDashboard ? /* @__PURE__ */ (0,
|
|
1206
|
-
message.greetingDashboard ? /* @__PURE__ */ (0,
|
|
1207
|
-
/* @__PURE__ */ (0,
|
|
1208
|
-
/* @__PURE__ */ (0,
|
|
1209
|
-
/* @__PURE__ */ (0,
|
|
1210
|
-
/* @__PURE__ */ (0,
|
|
1211
|
-
/* @__PURE__ */ (0,
|
|
1378
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "ss-w-msg ss-w-bot", children: [
|
|
1379
|
+
message.text && !message.greetingDashboard && !message.clubDashboard ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Markdown, { text: message.text }) : null,
|
|
1380
|
+
message.greetingDashboard ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
|
|
1381
|
+
message.clubDashboard && ui ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ClubDashboard, { dashboard: message.clubDashboard, ui, onAction: onClubAction }) : null,
|
|
1382
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
|
|
1383
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(BettingInsightList, { insights: message.insights, ui, slip }),
|
|
1384
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
|
|
1385
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(OddsMovementCardList, { cards: message.oddsMovement, ui }),
|
|
1386
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
|
|
1212
1387
|
] });
|
|
1213
1388
|
}
|
|
1214
1389
|
|
|
1215
1390
|
// src/components/IntentChips.tsx
|
|
1216
|
-
var
|
|
1391
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1217
1392
|
function IntentChips({ chips, onPick }) {
|
|
1218
1393
|
if (chips.length === 0) return null;
|
|
1219
|
-
return /* @__PURE__ */ (0,
|
|
1394
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
|
|
1220
1395
|
}
|
|
1221
1396
|
|
|
1222
1397
|
// src/scroll.ts
|
|
@@ -1226,18 +1401,18 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
|
|
|
1226
1401
|
}
|
|
1227
1402
|
|
|
1228
1403
|
// src/components/MessageList.tsx
|
|
1229
|
-
var
|
|
1404
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1230
1405
|
function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
1231
1406
|
const { state, send } = controller;
|
|
1232
|
-
const listRef = (0,
|
|
1233
|
-
const endRef = (0,
|
|
1234
|
-
const pinnedRef = (0,
|
|
1235
|
-
(0,
|
|
1407
|
+
const listRef = (0, import_react9.useRef)(null);
|
|
1408
|
+
const endRef = (0, import_react9.useRef)(null);
|
|
1409
|
+
const pinnedRef = (0, import_react9.useRef)(true);
|
|
1410
|
+
(0, import_react9.useEffect)(() => {
|
|
1236
1411
|
if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
|
|
1237
1412
|
}, [state.messages, state.status]);
|
|
1238
1413
|
const hasUserMessage = state.messages.some((m) => m.role === "user");
|
|
1239
1414
|
const hasDashboard = state.messages.some((m) => m.role === "assistant" && m.greetingDashboard);
|
|
1240
|
-
return /* @__PURE__ */ (0,
|
|
1415
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
1241
1416
|
"div",
|
|
1242
1417
|
{
|
|
1243
1418
|
className: "ss-w-list",
|
|
@@ -1247,7 +1422,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1247
1422
|
if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
|
|
1248
1423
|
},
|
|
1249
1424
|
children: [
|
|
1250
|
-
state.messages.map((m) => /* @__PURE__ */ (0,
|
|
1425
|
+
state.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1251
1426
|
ChatMessage,
|
|
1252
1427
|
{
|
|
1253
1428
|
message: m,
|
|
@@ -1257,30 +1432,31 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1257
1432
|
send(tile.actionValue, { displayText: tile.title });
|
|
1258
1433
|
if (m.role === "assistant" && m.greetingDashboard) onDashboardTap?.(tile, index, m.greetingDashboard.layout);
|
|
1259
1434
|
},
|
|
1435
|
+
onClubAction: (actionValue, displayText) => send(actionValue, { displayText }),
|
|
1260
1436
|
ui,
|
|
1261
1437
|
slip
|
|
1262
1438
|
},
|
|
1263
1439
|
m.id
|
|
1264
1440
|
)),
|
|
1265
|
-
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0,
|
|
1441
|
+
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ActionButtons, { actions: [], loading: true, onAct: () => {
|
|
1266
1442
|
} }) : null,
|
|
1267
|
-
state.status ? /* @__PURE__ */ (0,
|
|
1268
|
-
state.error ? /* @__PURE__ */ (0,
|
|
1269
|
-
/* @__PURE__ */ (0,
|
|
1270
|
-
/* @__PURE__ */ (0,
|
|
1443
|
+
state.status ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "ss-w-status", children: state.status }) : null,
|
|
1444
|
+
state.error ? /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "ss-w-error", role: "alert", children: [
|
|
1445
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
|
|
1446
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
|
|
1271
1447
|
] }) : null,
|
|
1272
|
-
!hasUserMessage && !hasDashboard ? /* @__PURE__ */ (0,
|
|
1273
|
-
/* @__PURE__ */ (0,
|
|
1448
|
+
!hasUserMessage && !hasDashboard ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
|
|
1449
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { ref: endRef })
|
|
1274
1450
|
]
|
|
1275
1451
|
}
|
|
1276
1452
|
);
|
|
1277
1453
|
}
|
|
1278
1454
|
|
|
1279
1455
|
// src/components/ChatInput.tsx
|
|
1280
|
-
var
|
|
1281
|
-
var
|
|
1456
|
+
var import_react10 = require("react");
|
|
1457
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1282
1458
|
function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
|
|
1283
|
-
const [value, setValue] = (0,
|
|
1459
|
+
const [value, setValue] = (0, import_react10.useState)("");
|
|
1284
1460
|
const submit = () => {
|
|
1285
1461
|
const t = value.trim();
|
|
1286
1462
|
if (!t) return;
|
|
@@ -1293,18 +1469,18 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
|
|
|
1293
1469
|
submit();
|
|
1294
1470
|
}
|
|
1295
1471
|
};
|
|
1296
|
-
return /* @__PURE__ */ (0,
|
|
1297
|
-
/* @__PURE__ */ (0,
|
|
1298
|
-
streaming ? /* @__PURE__ */ (0,
|
|
1472
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "ss-w-input", children: [
|
|
1473
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
|
|
1474
|
+
streaming ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(SendIcon, {}) })
|
|
1299
1475
|
] });
|
|
1300
1476
|
}
|
|
1301
1477
|
|
|
1302
1478
|
// src/components/ChatPanel.tsx
|
|
1303
|
-
var
|
|
1479
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1304
1480
|
var FULLSCREEN_QUERY = "(max-width: 639px)";
|
|
1305
1481
|
function useIsModal() {
|
|
1306
|
-
const [modal, setModal] = (0,
|
|
1307
|
-
(0,
|
|
1482
|
+
const [modal, setModal] = (0, import_react11.useState)(false);
|
|
1483
|
+
(0, import_react11.useEffect)(() => {
|
|
1308
1484
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
1309
1485
|
const mql = window.matchMedia(FULLSCREEN_QUERY);
|
|
1310
1486
|
const sync = () => setModal(mql.matches);
|
|
@@ -1317,17 +1493,17 @@ function useIsModal() {
|
|
|
1317
1493
|
function ChatPanel({ controller, ui, slip, onDashboardTap }) {
|
|
1318
1494
|
const { state, close, stop, send } = controller;
|
|
1319
1495
|
const isModal = useIsModal();
|
|
1320
|
-
(0,
|
|
1496
|
+
(0, import_react11.useEffect)(() => {
|
|
1321
1497
|
const onKey = (e) => {
|
|
1322
1498
|
if (e.key === "Escape") close();
|
|
1323
1499
|
};
|
|
1324
1500
|
window.addEventListener("keydown", onKey);
|
|
1325
1501
|
return () => window.removeEventListener("keydown", onKey);
|
|
1326
1502
|
}, [close]);
|
|
1327
|
-
return /* @__PURE__ */ (0,
|
|
1328
|
-
/* @__PURE__ */ (0,
|
|
1329
|
-
/* @__PURE__ */ (0,
|
|
1330
|
-
/* @__PURE__ */ (0,
|
|
1503
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
|
|
1504
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
|
|
1505
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(MessageList, { controller, ui, slip, onDashboardTap }),
|
|
1506
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
|
|
1331
1507
|
] });
|
|
1332
1508
|
}
|
|
1333
1509
|
|
|
@@ -1652,10 +1828,46 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
1652
1828
|
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(2) { background: #eef3fb; }
|
|
1653
1829
|
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(3) { background: #fdf6ec; }
|
|
1654
1830
|
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(4) { background: #f5eefb; }
|
|
1831
|
+
|
|
1832
|
+
/* club match-day dashboard */
|
|
1833
|
+
.ss-w-cdash { display: flex; flex-direction: column; gap: 10px; padding: 4px 0; }
|
|
1834
|
+
.ss-w-cdash-block { display: flex; flex-direction: column; }
|
|
1835
|
+
|
|
1836
|
+
.ss-w-cdash-greeting { display: flex; flex-direction: column; gap: 2px; }
|
|
1837
|
+
.ss-w-cdash-greeting-title { font-weight: 700; font-size: 14px; color: var(--ss-w-ink); }
|
|
1838
|
+
.ss-w-cdash-greeting-subtitle { font-size: 11.5px; color: var(--ss-w-faint); }
|
|
1839
|
+
|
|
1840
|
+
.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; }
|
|
1841
|
+
.ss-w-cdash-tie { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; }
|
|
1842
|
+
.ss-w-cdash-team { font-weight: 650; font-size: 13px; color: var(--ss-w-ink); min-width: 0; overflow-wrap: anywhere; }
|
|
1843
|
+
.ss-w-cdash-vs { font-size: 11px; color: var(--ss-w-faint); font-weight: 600; }
|
|
1844
|
+
.ss-w-cdash-crest { flex: none; width: 28px; height: 28px; border-radius: 50%; object-fit: cover; background: var(--ss-w-bubble); }
|
|
1845
|
+
.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)); }
|
|
1846
|
+
.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); }
|
|
1847
|
+
.ss-w-cdash-kickoff { font-weight: 700; color: var(--ss-w-ink); }
|
|
1848
|
+
.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; }
|
|
1849
|
+
|
|
1850
|
+
.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; }
|
|
1851
|
+
.ss-w-cdash-form-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
1852
|
+
.ss-w-cdash-form-name { font-size: 12px; font-weight: 600; color: var(--ss-w-ink); min-width: 0; overflow-wrap: anywhere; }
|
|
1853
|
+
.ss-w-cdash-form-pills { flex: none; display: flex; align-items: center; gap: 3px; }
|
|
1854
|
+
.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; }
|
|
1855
|
+
.ss-w-cdash-pill--w { background: color-mix(in srgb, var(--ss-w-pos) 88%, black); }
|
|
1856
|
+
.ss-w-cdash-pill--d { background: var(--ss-w-faint); }
|
|
1857
|
+
.ss-w-cdash-pill--l { background: color-mix(in srgb, var(--ss-w-neg) 88%, black); }
|
|
1858
|
+
|
|
1859
|
+
.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; }
|
|
1860
|
+
.ss-w-cdash-lines-heading { font-size: 12px; font-weight: 700; color: var(--ss-w-accent); }
|
|
1861
|
+
.ss-w-cdash-lines p { font-size: 12px; line-height: 1.4; color: var(--ss-w-ink); }
|
|
1862
|
+
|
|
1863
|
+
.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; }
|
|
1864
|
+
.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); }
|
|
1865
|
+
.ss-w-cdash-cd-h, .ss-w-cdash-cd-m, .ss-w-cdash-cd-s { color: var(--ss-w-accent); }
|
|
1866
|
+
.ss-w-cdash-cd-label { font-size: 11px; font-weight: 600; color: var(--ss-w-faint); }
|
|
1655
1867
|
`;
|
|
1656
1868
|
|
|
1657
1869
|
// src/StatsWidget.tsx
|
|
1658
|
-
var
|
|
1870
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1659
1871
|
function buildClient(props) {
|
|
1660
1872
|
try {
|
|
1661
1873
|
if ("client" in props && props.client) {
|
|
@@ -1680,11 +1892,11 @@ function injectStyles(root) {
|
|
|
1680
1892
|
function StatsWidget(props) {
|
|
1681
1893
|
const injectedClient = "client" in props ? props.client : void 0;
|
|
1682
1894
|
const cfg = props.config;
|
|
1683
|
-
const built = (0,
|
|
1684
|
-
const ui = (0,
|
|
1685
|
-
const rootRef = (0,
|
|
1895
|
+
const built = (0, import_react12.useMemo)(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
|
|
1896
|
+
const ui = (0, import_react12.useMemo)(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
|
|
1897
|
+
const rootRef = (0, import_react12.useRef)(null);
|
|
1686
1898
|
const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0);
|
|
1687
|
-
const slip = (0,
|
|
1899
|
+
const slip = (0, import_react12.useMemo)(
|
|
1688
1900
|
() => ({
|
|
1689
1901
|
onAddToSlip: props.config?.onAddToSlip,
|
|
1690
1902
|
conversationId: controller.state.conversationId ?? void 0,
|
|
@@ -1697,18 +1909,18 @@ function StatsWidget(props) {
|
|
|
1697
1909
|
}),
|
|
1698
1910
|
[props.config?.onAddToSlip, controller.state.conversationId, built]
|
|
1699
1911
|
);
|
|
1700
|
-
const onDashboardTap = (0,
|
|
1912
|
+
const onDashboardTap = (0, import_react12.useMemo)(
|
|
1701
1913
|
() => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input)) : void 0,
|
|
1702
1914
|
[built]
|
|
1703
1915
|
);
|
|
1704
|
-
(0,
|
|
1916
|
+
(0, import_react12.useEffect)(() => {
|
|
1705
1917
|
if (!built || !ui.injectStyles) return;
|
|
1706
1918
|
const node = rootRef.current?.getRootNode();
|
|
1707
1919
|
if (node) injectStyles(node);
|
|
1708
1920
|
}, [built, ui.injectStyles]);
|
|
1709
1921
|
if (!built) return null;
|
|
1710
1922
|
const attrs = themeAttrs(ui);
|
|
1711
|
-
return /* @__PURE__ */ (0,
|
|
1923
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.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_runtime28.jsx)(ChatPanel, { controller, ui, slip, onDashboardTap }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1712
1924
|
ChatFab,
|
|
1713
1925
|
{
|
|
1714
1926
|
label: ui.launcherLabel,
|