@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/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
|
|
@@ -42,6 +42,12 @@ var STRINGS = {
|
|
|
42
42
|
const drift = direction ? `, ${direction}` : "";
|
|
43
43
|
return `Price movement over the last ${span}: opened at ${opening}, currently ${current}${drift}.`;
|
|
44
44
|
},
|
|
45
|
+
clubMatchPreviewHeading: "Match preview",
|
|
46
|
+
clubTeamDnaHeading: "How they play",
|
|
47
|
+
clubFormHeading: "Recent form",
|
|
48
|
+
clubCountdownLabel: "Countdown to kick-off",
|
|
49
|
+
clubKickoff: (ms) => new Intl.DateTimeFormat("en-GB", { weekday: "short", day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(ms)),
|
|
50
|
+
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
51
|
inSlip: "In slip",
|
|
46
52
|
slipAdded: (n) => n === void 0 ? "Added to slip" : `Added to slip (${n} selection${n === 1 ? "" : "s"})`,
|
|
47
53
|
slipDuplicate: "Already in your slip",
|
|
@@ -89,6 +95,12 @@ var STRINGS = {
|
|
|
89
95
|
const drift = direction ? `, ${direction}` : "";
|
|
90
96
|
return `Son ${span} i\xE7indeki oran hareketi: ${opening} ile a\xE7\u0131ld\u0131, \u015Fu anda ${current}${drift}.`;
|
|
91
97
|
},
|
|
98
|
+
clubMatchPreviewHeading: "Ma\xE7 \xF6nizlemesi",
|
|
99
|
+
clubTeamDnaHeading: "Nas\u0131l oynuyorlar",
|
|
100
|
+
clubFormHeading: "Son form",
|
|
101
|
+
clubCountdownLabel: "Ba\u015Flama vuru\u015Funa kalan",
|
|
102
|
+
clubKickoff: (ms) => new Intl.DateTimeFormat("tr-TR", { weekday: "short", day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(ms)),
|
|
103
|
+
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
104
|
inSlip: "Kuponda",
|
|
93
105
|
slipAdded: (n) => n === void 0 ? "Kupona eklendi" : `Kupona eklendi (${n} se\xE7im)`,
|
|
94
106
|
slipDuplicate: "Bu se\xE7im kuponunuzda zaten var",
|
|
@@ -148,6 +160,12 @@ function normalizeUi(cfg) {
|
|
|
148
160
|
excludedSuspended: strings.excludedSuspended,
|
|
149
161
|
chartTimeLabel: strings.chartTimeLabel,
|
|
150
162
|
chartAria: strings.chartAria,
|
|
163
|
+
clubMatchPreviewHeading: strings.clubMatchPreviewHeading,
|
|
164
|
+
clubTeamDnaHeading: strings.clubTeamDnaHeading,
|
|
165
|
+
clubFormHeading: strings.clubFormHeading,
|
|
166
|
+
clubCountdownLabel: strings.clubCountdownLabel,
|
|
167
|
+
clubKickoff: strings.clubKickoff,
|
|
168
|
+
clubKickoffAria: strings.clubKickoffAria,
|
|
151
169
|
addToSlip: strings.addToSlip,
|
|
152
170
|
inSlip: strings.inSlip,
|
|
153
171
|
slipAdded: strings.slipAdded,
|
|
@@ -494,7 +512,8 @@ function applyEvent(state, event) {
|
|
|
494
512
|
messages: mapActive(state, (t) => ({
|
|
495
513
|
...t,
|
|
496
514
|
done: true,
|
|
497
|
-
greetingDashboard: event.data.dashboard ?? void 0
|
|
515
|
+
greetingDashboard: event.data.dashboard ?? void 0,
|
|
516
|
+
clubDashboard: event.data.clubDashboard ?? void 0
|
|
498
517
|
}))
|
|
499
518
|
};
|
|
500
519
|
}
|
|
@@ -661,7 +680,7 @@ function ChatFab({ label, greeting, popupText, onOpen, onDismissPopup, dismissLa
|
|
|
661
680
|
}
|
|
662
681
|
|
|
663
682
|
// src/components/ChatPanel.tsx
|
|
664
|
-
import { useEffect as
|
|
683
|
+
import { useEffect as useEffect5, useState as useState8 } from "react";
|
|
665
684
|
|
|
666
685
|
// src/components/ChatHeader.tsx
|
|
667
686
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -674,7 +693,7 @@ function ChatHeader({ onClose, closeLabel = "Close chat" }) {
|
|
|
674
693
|
}
|
|
675
694
|
|
|
676
695
|
// src/components/MessageList.tsx
|
|
677
|
-
import { useEffect as
|
|
696
|
+
import { useEffect as useEffect4, useRef as useRef2 } from "react";
|
|
678
697
|
|
|
679
698
|
// src/components/Markdown.tsx
|
|
680
699
|
import ReactMarkdown from "react-markdown";
|
|
@@ -1158,28 +1177,184 @@ function GreetingDashboard({ dashboard, onTap }) {
|
|
|
1158
1177
|
] });
|
|
1159
1178
|
}
|
|
1160
1179
|
|
|
1161
|
-
// src/components/
|
|
1180
|
+
// src/components/club-dashboard/GreetingHeaderBlock.tsx
|
|
1162
1181
|
import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1163
|
-
function
|
|
1182
|
+
function GreetingHeaderBlock({ block }) {
|
|
1183
|
+
return /* @__PURE__ */ jsxs13("div", { className: "ss-w-cdash-greeting", children: [
|
|
1184
|
+
/* @__PURE__ */ jsx16("div", { className: "ss-w-cdash-greeting-title", children: block.title }),
|
|
1185
|
+
/* @__PURE__ */ jsx16("div", { className: "ss-w-cdash-greeting-subtitle", children: block.subtitle })
|
|
1186
|
+
] });
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
// src/components/club-dashboard/FixtureHeroBlock.tsx
|
|
1190
|
+
import { useState as useState5 } from "react";
|
|
1191
|
+
import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1192
|
+
function initials2(name) {
|
|
1193
|
+
return name.split(/\s+/).filter(Boolean).slice(0, 2).map((w) => w[0].toUpperCase()).join("");
|
|
1194
|
+
}
|
|
1195
|
+
function Crest2({ url, name }) {
|
|
1196
|
+
const [broken, setBroken] = useState5(false);
|
|
1197
|
+
if (!url || broken) {
|
|
1198
|
+
return /* @__PURE__ */ jsx17("span", { className: "ss-w-cdash-crest ss-w-cdash-crest--fallback", "aria-hidden": "true", children: initials2(name) });
|
|
1199
|
+
}
|
|
1200
|
+
return /* @__PURE__ */ jsx17("img", { className: "ss-w-cdash-crest", src: url, alt: "", "aria-hidden": "true", onError: () => setBroken(true) });
|
|
1201
|
+
}
|
|
1202
|
+
function FixtureHeroBlock({
|
|
1203
|
+
block,
|
|
1204
|
+
ui,
|
|
1205
|
+
onAction
|
|
1206
|
+
}) {
|
|
1207
|
+
return /* @__PURE__ */ jsxs14("div", { className: "ss-w-cdash-fixture", children: [
|
|
1208
|
+
/* @__PURE__ */ jsxs14("div", { className: "ss-w-cdash-tie", children: [
|
|
1209
|
+
/* @__PURE__ */ jsx17(Crest2, { url: block.homeCrestUrl, name: block.homeName }),
|
|
1210
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-cdash-team", children: block.homeName }),
|
|
1211
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-cdash-vs", children: "v" }),
|
|
1212
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-cdash-team", children: block.awayName }),
|
|
1213
|
+
/* @__PURE__ */ jsx17(Crest2, { url: block.awayCrestUrl, name: block.awayName })
|
|
1214
|
+
] }),
|
|
1215
|
+
/* @__PURE__ */ jsxs14("div", { className: "ss-w-cdash-fixture-meta", children: [
|
|
1216
|
+
/* @__PURE__ */ jsx17("span", { className: "ss-w-cdash-kickoff", children: ui.clubKickoff(block.kickoffUnix * 1e3) }),
|
|
1217
|
+
block.venue ? /* @__PURE__ */ jsx17("span", { className: "ss-w-cdash-venue", children: block.venue }) : null,
|
|
1218
|
+
block.competition ? /* @__PURE__ */ jsx17("span", { className: "ss-w-cdash-comp", children: block.competition }) : null
|
|
1219
|
+
] }),
|
|
1220
|
+
/* @__PURE__ */ jsx17("button", { type: "button", className: "ss-w-cdash-cta", onClick: () => onAction(block.ctaActionValue, block.ctaLabel), children: block.ctaLabel })
|
|
1221
|
+
] });
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
// src/components/club-dashboard/FormGuideBlock.tsx
|
|
1225
|
+
import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1226
|
+
function FormGuideBlock({ block }) {
|
|
1227
|
+
if (block.rows.length === 0) return null;
|
|
1228
|
+
return /* @__PURE__ */ jsx18("div", { className: "ss-w-cdash-form", children: block.rows.map((row) => /* @__PURE__ */ jsxs15("div", { className: "ss-w-cdash-form-row", children: [
|
|
1229
|
+
/* @__PURE__ */ jsxs15("span", { className: "ss-w-cdash-form-name", children: [
|
|
1230
|
+
row.name,
|
|
1231
|
+
row.position != null ? ` #${row.position}` : ""
|
|
1232
|
+
] }),
|
|
1233
|
+
/* @__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)) })
|
|
1234
|
+
] }, row.name)) });
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
// src/components/club-dashboard/MatchPreviewBlock.tsx
|
|
1238
|
+
import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1239
|
+
function MatchPreviewBlock({
|
|
1240
|
+
block,
|
|
1241
|
+
ui,
|
|
1242
|
+
onAction
|
|
1243
|
+
}) {
|
|
1244
|
+
if (block.lines.length === 0) return null;
|
|
1245
|
+
const heading = ui.clubMatchPreviewHeading;
|
|
1246
|
+
return /* @__PURE__ */ jsxs16("button", { type: "button", className: "ss-w-cdash-lines", onClick: () => onAction(block.actionValue, heading), children: [
|
|
1247
|
+
/* @__PURE__ */ jsx19("span", { className: "ss-w-cdash-lines-heading", children: heading }),
|
|
1248
|
+
block.lines.map((line, i) => /* @__PURE__ */ jsx19("p", { children: line }, i))
|
|
1249
|
+
] });
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
// src/components/club-dashboard/TeamDnaBlock.tsx
|
|
1253
|
+
import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1254
|
+
function TeamDnaBlock({
|
|
1255
|
+
block,
|
|
1256
|
+
ui,
|
|
1257
|
+
onAction
|
|
1258
|
+
}) {
|
|
1259
|
+
if (block.lines.length === 0) return null;
|
|
1260
|
+
const heading = ui.clubTeamDnaHeading;
|
|
1261
|
+
return /* @__PURE__ */ jsxs17("button", { type: "button", className: "ss-w-cdash-lines", onClick: () => onAction(block.actionValue, heading), children: [
|
|
1262
|
+
/* @__PURE__ */ jsx20("span", { className: "ss-w-cdash-lines-heading", children: heading }),
|
|
1263
|
+
block.lines.map((line, i) => /* @__PURE__ */ jsx20("p", { children: line }, i))
|
|
1264
|
+
] });
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
// src/components/club-dashboard/CountdownBlock.tsx
|
|
1268
|
+
import { useEffect as useEffect3, useState as useState6 } from "react";
|
|
1269
|
+
import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1270
|
+
var DAY_MS = 24 * 3600 * 1e3;
|
|
1271
|
+
function pad2(n) {
|
|
1272
|
+
return String(n).padStart(2, "0");
|
|
1273
|
+
}
|
|
1274
|
+
function splitRemaining(remainingMs) {
|
|
1275
|
+
const totalSeconds = Math.max(0, Math.floor(remainingMs / 1e3));
|
|
1276
|
+
const h = Math.floor(totalSeconds / 3600);
|
|
1277
|
+
const m = Math.floor(totalSeconds % 3600 / 60);
|
|
1278
|
+
const s = totalSeconds % 60;
|
|
1279
|
+
return { h: pad2(h), m: pad2(m), s: pad2(s) };
|
|
1280
|
+
}
|
|
1281
|
+
function CountdownBlock({ block, ui }) {
|
|
1282
|
+
const [remainingMs, setRemainingMs] = useState6(() => block.kickoffUnix * 1e3 - Date.now());
|
|
1283
|
+
useEffect3(() => {
|
|
1284
|
+
setRemainingMs(block.kickoffUnix * 1e3 - Date.now());
|
|
1285
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
|
1286
|
+
const id = setInterval(() => {
|
|
1287
|
+
setRemainingMs(block.kickoffUnix * 1e3 - Date.now());
|
|
1288
|
+
}, 1e3);
|
|
1289
|
+
return () => clearInterval(id);
|
|
1290
|
+
}, [block.kickoffUnix]);
|
|
1291
|
+
if (remainingMs <= 0) return null;
|
|
1292
|
+
if (remainingMs > DAY_MS) return null;
|
|
1293
|
+
const { h, m, s } = splitRemaining(remainingMs);
|
|
1294
|
+
return /* @__PURE__ */ jsxs18("div", { className: "ss-w-cdash-cd", "aria-label": ui.clubKickoffAria(block.kickoffUnix * 1e3), children: [
|
|
1295
|
+
/* @__PURE__ */ jsxs18("div", { className: "ss-w-cdash-cd-clock", "aria-hidden": "true", children: [
|
|
1296
|
+
/* @__PURE__ */ jsx21("span", { className: "ss-w-cdash-cd-h", children: h }),
|
|
1297
|
+
"h",
|
|
1298
|
+
" ",
|
|
1299
|
+
/* @__PURE__ */ jsx21("span", { className: "ss-w-cdash-cd-m", children: m }),
|
|
1300
|
+
"m",
|
|
1301
|
+
" ",
|
|
1302
|
+
/* @__PURE__ */ jsx21("span", { className: "ss-w-cdash-cd-s", children: s }),
|
|
1303
|
+
"s"
|
|
1304
|
+
] }),
|
|
1305
|
+
/* @__PURE__ */ jsx21("div", { className: "ss-w-cdash-cd-label", children: ui.clubCountdownLabel })
|
|
1306
|
+
] });
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
// src/components/club-dashboard/ClubDashboard.tsx
|
|
1310
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
1311
|
+
var CLUB_BLOCK_REGISTRY = {
|
|
1312
|
+
greetingHeader: ({ block }) => block.type === "greetingHeader" ? /* @__PURE__ */ jsx22(GreetingHeaderBlock, { block }) : null,
|
|
1313
|
+
fixtureHero: ({ block, ui, onAction }) => block.type === "fixtureHero" ? /* @__PURE__ */ jsx22(FixtureHeroBlock, { block, ui, onAction }) : null,
|
|
1314
|
+
formGuide: ({ block, ui }) => block.type === "formGuide" ? /* @__PURE__ */ jsx22(FormGuideBlock, { block, ui }) : null,
|
|
1315
|
+
matchPreview: ({ block, ui, onAction }) => block.type === "matchPreview" ? /* @__PURE__ */ jsx22(MatchPreviewBlock, { block, ui, onAction }) : null,
|
|
1316
|
+
teamDna: ({ block, ui, onAction }) => block.type === "teamDna" ? /* @__PURE__ */ jsx22(TeamDnaBlock, { block, ui, onAction }) : null,
|
|
1317
|
+
countdown: ({ block, ui }) => block.type === "countdown" ? /* @__PURE__ */ jsx22(CountdownBlock, { block, ui }) : null
|
|
1318
|
+
};
|
|
1319
|
+
function ClubDashboard({
|
|
1320
|
+
dashboard,
|
|
1321
|
+
ui,
|
|
1322
|
+
onAction
|
|
1323
|
+
}) {
|
|
1324
|
+
if (!dashboard.blocks.length) return null;
|
|
1325
|
+
return /* @__PURE__ */ jsx22("div", { className: "ss-w-cdash", children: dashboard.blocks.map((block, i) => {
|
|
1326
|
+
const Comp = Object.prototype.hasOwnProperty.call(CLUB_BLOCK_REGISTRY, block.type) ? CLUB_BLOCK_REGISTRY[block.type] : void 0;
|
|
1327
|
+
if (!Comp) {
|
|
1328
|
+
console.warn(`[club-dashboard] no renderer for block type "${block.type}"`);
|
|
1329
|
+
return null;
|
|
1330
|
+
}
|
|
1331
|
+
return /* @__PURE__ */ jsx22("div", { className: "ss-w-cdash-block", children: /* @__PURE__ */ jsx22(Comp, { block, ui, onAction }) }, i);
|
|
1332
|
+
}) });
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
// src/components/ChatMessage.tsx
|
|
1336
|
+
import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1337
|
+
function ChatMessage({ message, disabled, onAct, onTap, onClubAction, ui, slip }) {
|
|
1164
1338
|
if (message.role === "user") {
|
|
1165
|
-
return /* @__PURE__ */
|
|
1339
|
+
return /* @__PURE__ */ jsx23("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx23("div", { className: "ss-w-bubble", children: message.text }) });
|
|
1166
1340
|
}
|
|
1167
|
-
return /* @__PURE__ */
|
|
1168
|
-
message.text && !message.greetingDashboard ? /* @__PURE__ */
|
|
1169
|
-
message.greetingDashboard ? /* @__PURE__ */
|
|
1170
|
-
/* @__PURE__ */
|
|
1171
|
-
/* @__PURE__ */
|
|
1172
|
-
/* @__PURE__ */
|
|
1173
|
-
/* @__PURE__ */
|
|
1174
|
-
/* @__PURE__ */
|
|
1341
|
+
return /* @__PURE__ */ jsxs19("div", { className: "ss-w-msg ss-w-bot", children: [
|
|
1342
|
+
message.text && !message.greetingDashboard && !message.clubDashboard ? /* @__PURE__ */ jsx23(Markdown, { text: message.text }) : null,
|
|
1343
|
+
message.greetingDashboard ? /* @__PURE__ */ jsx23(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
|
|
1344
|
+
message.clubDashboard && ui ? /* @__PURE__ */ jsx23(ClubDashboard, { dashboard: message.clubDashboard, ui, onAction: onClubAction }) : null,
|
|
1345
|
+
/* @__PURE__ */ jsx23(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
|
|
1346
|
+
/* @__PURE__ */ jsx23(BettingInsightList, { insights: message.insights, ui, slip }),
|
|
1347
|
+
/* @__PURE__ */ jsx23(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
|
|
1348
|
+
/* @__PURE__ */ jsx23(OddsMovementCardList, { cards: message.oddsMovement, ui }),
|
|
1349
|
+
/* @__PURE__ */ jsx23(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
|
|
1175
1350
|
] });
|
|
1176
1351
|
}
|
|
1177
1352
|
|
|
1178
1353
|
// src/components/IntentChips.tsx
|
|
1179
|
-
import { jsx as
|
|
1354
|
+
import { jsx as jsx24 } from "react/jsx-runtime";
|
|
1180
1355
|
function IntentChips({ chips, onPick }) {
|
|
1181
1356
|
if (chips.length === 0) return null;
|
|
1182
|
-
return /* @__PURE__ */
|
|
1357
|
+
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
1358
|
}
|
|
1184
1359
|
|
|
1185
1360
|
// src/scroll.ts
|
|
@@ -1189,18 +1364,18 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
|
|
|
1189
1364
|
}
|
|
1190
1365
|
|
|
1191
1366
|
// src/components/MessageList.tsx
|
|
1192
|
-
import { jsx as
|
|
1367
|
+
import { jsx as jsx25, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1193
1368
|
function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
1194
1369
|
const { state, send } = controller;
|
|
1195
1370
|
const listRef = useRef2(null);
|
|
1196
1371
|
const endRef = useRef2(null);
|
|
1197
1372
|
const pinnedRef = useRef2(true);
|
|
1198
|
-
|
|
1373
|
+
useEffect4(() => {
|
|
1199
1374
|
if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
|
|
1200
1375
|
}, [state.messages, state.status]);
|
|
1201
1376
|
const hasUserMessage = state.messages.some((m) => m.role === "user");
|
|
1202
1377
|
const hasDashboard = state.messages.some((m) => m.role === "assistant" && m.greetingDashboard);
|
|
1203
|
-
return /* @__PURE__ */
|
|
1378
|
+
return /* @__PURE__ */ jsxs20(
|
|
1204
1379
|
"div",
|
|
1205
1380
|
{
|
|
1206
1381
|
className: "ss-w-list",
|
|
@@ -1210,7 +1385,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1210
1385
|
if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
|
|
1211
1386
|
},
|
|
1212
1387
|
children: [
|
|
1213
|
-
state.messages.map((m) => /* @__PURE__ */
|
|
1388
|
+
state.messages.map((m) => /* @__PURE__ */ jsx25(
|
|
1214
1389
|
ChatMessage,
|
|
1215
1390
|
{
|
|
1216
1391
|
message: m,
|
|
@@ -1220,30 +1395,31 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1220
1395
|
send(tile.actionValue, { displayText: tile.title });
|
|
1221
1396
|
if (m.role === "assistant" && m.greetingDashboard) onDashboardTap?.(tile, index, m.greetingDashboard.layout);
|
|
1222
1397
|
},
|
|
1398
|
+
onClubAction: (actionValue, displayText) => send(actionValue, { displayText }),
|
|
1223
1399
|
ui,
|
|
1224
1400
|
slip
|
|
1225
1401
|
},
|
|
1226
1402
|
m.id
|
|
1227
1403
|
)),
|
|
1228
|
-
state.isStreaming && state.actionsLoading ? /* @__PURE__ */
|
|
1404
|
+
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx25(ActionButtons, { actions: [], loading: true, onAct: () => {
|
|
1229
1405
|
} }) : null,
|
|
1230
|
-
state.status ? /* @__PURE__ */
|
|
1231
|
-
state.error ? /* @__PURE__ */
|
|
1232
|
-
/* @__PURE__ */
|
|
1233
|
-
/* @__PURE__ */
|
|
1406
|
+
state.status ? /* @__PURE__ */ jsx25("div", { className: "ss-w-status", children: state.status }) : null,
|
|
1407
|
+
state.error ? /* @__PURE__ */ jsxs20("div", { className: "ss-w-error", role: "alert", children: [
|
|
1408
|
+
/* @__PURE__ */ jsx25("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
|
|
1409
|
+
/* @__PURE__ */ jsx25("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
|
|
1234
1410
|
] }) : null,
|
|
1235
|
-
!hasUserMessage && !hasDashboard ? /* @__PURE__ */
|
|
1236
|
-
/* @__PURE__ */
|
|
1411
|
+
!hasUserMessage && !hasDashboard ? /* @__PURE__ */ jsx25(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
|
|
1412
|
+
/* @__PURE__ */ jsx25("div", { ref: endRef })
|
|
1237
1413
|
]
|
|
1238
1414
|
}
|
|
1239
1415
|
);
|
|
1240
1416
|
}
|
|
1241
1417
|
|
|
1242
1418
|
// src/components/ChatInput.tsx
|
|
1243
|
-
import { useState as
|
|
1244
|
-
import { jsx as
|
|
1419
|
+
import { useState as useState7 } from "react";
|
|
1420
|
+
import { jsx as jsx26, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1245
1421
|
function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
|
|
1246
|
-
const [value, setValue] =
|
|
1422
|
+
const [value, setValue] = useState7("");
|
|
1247
1423
|
const submit = () => {
|
|
1248
1424
|
const t = value.trim();
|
|
1249
1425
|
if (!t) return;
|
|
@@ -1256,18 +1432,18 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
|
|
|
1256
1432
|
submit();
|
|
1257
1433
|
}
|
|
1258
1434
|
};
|
|
1259
|
-
return /* @__PURE__ */
|
|
1260
|
-
/* @__PURE__ */
|
|
1261
|
-
streaming ? /* @__PURE__ */
|
|
1435
|
+
return /* @__PURE__ */ jsxs21("div", { className: "ss-w-input", children: [
|
|
1436
|
+
/* @__PURE__ */ jsx26("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
|
|
1437
|
+
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
1438
|
] });
|
|
1263
1439
|
}
|
|
1264
1440
|
|
|
1265
1441
|
// src/components/ChatPanel.tsx
|
|
1266
|
-
import { jsx as
|
|
1442
|
+
import { jsx as jsx27, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1267
1443
|
var FULLSCREEN_QUERY = "(max-width: 639px)";
|
|
1268
1444
|
function useIsModal() {
|
|
1269
|
-
const [modal, setModal] =
|
|
1270
|
-
|
|
1445
|
+
const [modal, setModal] = useState8(false);
|
|
1446
|
+
useEffect5(() => {
|
|
1271
1447
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
1272
1448
|
const mql = window.matchMedia(FULLSCREEN_QUERY);
|
|
1273
1449
|
const sync = () => setModal(mql.matches);
|
|
@@ -1280,17 +1456,17 @@ function useIsModal() {
|
|
|
1280
1456
|
function ChatPanel({ controller, ui, slip, onDashboardTap }) {
|
|
1281
1457
|
const { state, close, stop, send } = controller;
|
|
1282
1458
|
const isModal = useIsModal();
|
|
1283
|
-
|
|
1459
|
+
useEffect5(() => {
|
|
1284
1460
|
const onKey = (e) => {
|
|
1285
1461
|
if (e.key === "Escape") close();
|
|
1286
1462
|
};
|
|
1287
1463
|
window.addEventListener("keydown", onKey);
|
|
1288
1464
|
return () => window.removeEventListener("keydown", onKey);
|
|
1289
1465
|
}, [close]);
|
|
1290
|
-
return /* @__PURE__ */
|
|
1291
|
-
/* @__PURE__ */
|
|
1292
|
-
/* @__PURE__ */
|
|
1293
|
-
/* @__PURE__ */
|
|
1466
|
+
return /* @__PURE__ */ jsxs22("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
|
|
1467
|
+
/* @__PURE__ */ jsx27(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
|
|
1468
|
+
/* @__PURE__ */ jsx27(MessageList, { controller, ui, slip, onDashboardTap }),
|
|
1469
|
+
/* @__PURE__ */ jsx27(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
|
|
1294
1470
|
] });
|
|
1295
1471
|
}
|
|
1296
1472
|
|
|
@@ -1615,10 +1791,46 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
1615
1791
|
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(2) { background: #eef3fb; }
|
|
1616
1792
|
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(3) { background: #fdf6ec; }
|
|
1617
1793
|
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(4) { background: #f5eefb; }
|
|
1794
|
+
|
|
1795
|
+
/* club match-day dashboard */
|
|
1796
|
+
.ss-w-cdash { display: flex; flex-direction: column; gap: 10px; padding: 4px 0; }
|
|
1797
|
+
.ss-w-cdash-block { display: flex; flex-direction: column; }
|
|
1798
|
+
|
|
1799
|
+
.ss-w-cdash-greeting { display: flex; flex-direction: column; gap: 2px; }
|
|
1800
|
+
.ss-w-cdash-greeting-title { font-weight: 700; font-size: 14px; color: var(--ss-w-ink); }
|
|
1801
|
+
.ss-w-cdash-greeting-subtitle { font-size: 11.5px; color: var(--ss-w-faint); }
|
|
1802
|
+
|
|
1803
|
+
.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; }
|
|
1804
|
+
.ss-w-cdash-tie { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; }
|
|
1805
|
+
.ss-w-cdash-team { font-weight: 650; font-size: 13px; color: var(--ss-w-ink); min-width: 0; overflow-wrap: anywhere; }
|
|
1806
|
+
.ss-w-cdash-vs { font-size: 11px; color: var(--ss-w-faint); font-weight: 600; }
|
|
1807
|
+
.ss-w-cdash-crest { flex: none; width: 28px; height: 28px; border-radius: 50%; object-fit: cover; background: var(--ss-w-bubble); }
|
|
1808
|
+
.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)); }
|
|
1809
|
+
.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); }
|
|
1810
|
+
.ss-w-cdash-kickoff { font-weight: 700; color: var(--ss-w-ink); }
|
|
1811
|
+
.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; }
|
|
1812
|
+
|
|
1813
|
+
.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; }
|
|
1814
|
+
.ss-w-cdash-form-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
|
|
1815
|
+
.ss-w-cdash-form-name { font-size: 12px; font-weight: 600; color: var(--ss-w-ink); min-width: 0; overflow-wrap: anywhere; }
|
|
1816
|
+
.ss-w-cdash-form-pills { flex: none; display: flex; align-items: center; gap: 3px; }
|
|
1817
|
+
.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; }
|
|
1818
|
+
.ss-w-cdash-pill--w { background: color-mix(in srgb, var(--ss-w-pos) 88%, black); }
|
|
1819
|
+
.ss-w-cdash-pill--d { background: var(--ss-w-faint); }
|
|
1820
|
+
.ss-w-cdash-pill--l { background: color-mix(in srgb, var(--ss-w-neg) 88%, black); }
|
|
1821
|
+
|
|
1822
|
+
.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; }
|
|
1823
|
+
.ss-w-cdash-lines-heading { font-size: 12px; font-weight: 700; color: var(--ss-w-accent); }
|
|
1824
|
+
.ss-w-cdash-lines p { font-size: 12px; line-height: 1.4; color: var(--ss-w-ink); }
|
|
1825
|
+
|
|
1826
|
+
.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; }
|
|
1827
|
+
.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); }
|
|
1828
|
+
.ss-w-cdash-cd-h, .ss-w-cdash-cd-m, .ss-w-cdash-cd-s { color: var(--ss-w-accent); }
|
|
1829
|
+
.ss-w-cdash-cd-label { font-size: 11px; font-weight: 600; color: var(--ss-w-faint); }
|
|
1618
1830
|
`;
|
|
1619
1831
|
|
|
1620
1832
|
// src/StatsWidget.tsx
|
|
1621
|
-
import { jsx as
|
|
1833
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
1622
1834
|
function buildClient(props) {
|
|
1623
1835
|
try {
|
|
1624
1836
|
if ("client" in props && props.client) {
|
|
@@ -1664,14 +1876,14 @@ function StatsWidget(props) {
|
|
|
1664
1876
|
() => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input)) : void 0,
|
|
1665
1877
|
[built]
|
|
1666
1878
|
);
|
|
1667
|
-
|
|
1879
|
+
useEffect6(() => {
|
|
1668
1880
|
if (!built || !ui.injectStyles) return;
|
|
1669
1881
|
const node = rootRef.current?.getRootNode();
|
|
1670
1882
|
if (node) injectStyles(node);
|
|
1671
1883
|
}, [built, ui.injectStyles]);
|
|
1672
1884
|
if (!built) return null;
|
|
1673
1885
|
const attrs = themeAttrs(ui);
|
|
1674
|
-
return /* @__PURE__ */
|
|
1886
|
+
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
1887
|
ChatFab,
|
|
1676
1888
|
{
|
|
1677
1889
|
label: ui.launcherLabel,
|