@sensiblestats/widget-react 0.20.0 → 0.22.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/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/StatsWidget.tsx
2
- import { useEffect as useEffect6, useMemo as useMemo2, useRef as useRef3 } from "react";
2
+ import { useEffect as useEffect5, useMemo as useMemo2, useRef as useRef3 } from "react";
3
3
  import { StatsWidgetClient } from "@sensiblestats/widget-sdk";
4
4
 
5
5
  // src/kickoff.ts
@@ -55,12 +55,6 @@ var STRINGS = {
55
55
  const drift = direction ? `, ${direction}` : "";
56
56
  return `Price movement over the last ${span}: opened at ${opening}, currently ${current}${drift}.`;
57
57
  },
58
- clubMatchPreviewHeading: "Match preview",
59
- clubTeamDnaHeading: "How they play",
60
- clubFormHeading: "Recent form",
61
- clubCountdownLabel: "Countdown to kick-off",
62
- clubKickoff: (ms) => new Intl.DateTimeFormat("en-GB", { weekday: "short", day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(ms)),
63
- 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))}`,
64
58
  inSlip: "In slip",
65
59
  slipAdded: (n) => n === void 0 ? "Added to slip" : `Added to slip (${n} selection${n === 1 ? "" : "s"})`,
66
60
  slipDuplicate: "Already in your slip",
@@ -112,12 +106,6 @@ var STRINGS = {
112
106
  const drift = direction ? `, ${direction}` : "";
113
107
  return `Son ${span} i\xE7indeki oran hareketi: ${opening} ile a\xE7\u0131ld\u0131, \u015Fu anda ${current}${drift}.`;
114
108
  },
115
- clubMatchPreviewHeading: "Ma\xE7 \xF6nizlemesi",
116
- clubTeamDnaHeading: "Nas\u0131l oynuyorlar",
117
- clubFormHeading: "Son form",
118
- clubCountdownLabel: "Ba\u015Flama vuru\u015Funa kalan",
119
- clubKickoff: (ms) => new Intl.DateTimeFormat("tr-TR", { weekday: "short", day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" }).format(new Date(ms)),
120
- 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))}`,
121
109
  inSlip: "Kuponda",
122
110
  slipAdded: (n) => n === void 0 ? "Kupona eklendi" : `Kupona eklendi (${n} se\xE7im)`,
123
111
  slipDuplicate: "Bu se\xE7im kuponunuzda zaten var",
@@ -181,12 +169,6 @@ function normalizeUi(cfg) {
181
169
  excludedSuspended: strings.excludedSuspended,
182
170
  chartTimeLabel: strings.chartTimeLabel,
183
171
  chartAria: strings.chartAria,
184
- clubMatchPreviewHeading: strings.clubMatchPreviewHeading,
185
- clubTeamDnaHeading: strings.clubTeamDnaHeading,
186
- clubFormHeading: strings.clubFormHeading,
187
- clubCountdownLabel: strings.clubCountdownLabel,
188
- clubKickoff: strings.clubKickoff,
189
- clubKickoffAria: strings.clubKickoffAria,
190
172
  addToSlip: strings.addToSlip,
191
173
  inSlip: strings.inSlip,
192
174
  slipAdded: strings.slipAdded,
@@ -549,8 +531,7 @@ function applyEvent(state, event) {
549
531
  messages: mapActive(state, (t) => ({
550
532
  ...t,
551
533
  done: true,
552
- greetingDashboard: event.data.dashboard ?? void 0,
553
- clubDashboard: event.data.clubDashboard ?? void 0
534
+ greetingDashboard: event.data.dashboard ?? void 0
554
535
  }))
555
536
  };
556
537
  }
@@ -747,7 +728,7 @@ function ChatFab({ label, greeting, popupText, onOpen, onDismissPopup, dismissLa
747
728
  }
748
729
 
749
730
  // src/components/ChatPanel.tsx
750
- import { useEffect as useEffect5, useState as useState9 } from "react";
731
+ import { useEffect as useEffect4, useState as useState7 } from "react";
751
732
 
752
733
  // src/components/ChatHeader.tsx
753
734
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
@@ -760,7 +741,7 @@ function ChatHeader({ onClose, closeLabel = "Close chat" }) {
760
741
  }
761
742
 
762
743
  // src/components/MessageList.tsx
763
- import { useEffect as useEffect4, useRef as useRef2 } from "react";
744
+ import { useEffect as useEffect3, useRef as useRef2 } from "react";
764
745
 
765
746
  // src/components/Markdown.tsx
766
747
  import ReactMarkdown from "react-markdown";
@@ -1306,218 +1287,28 @@ function GreetingDashboard({ dashboard, onTap }) {
1306
1287
  ] });
1307
1288
  }
1308
1289
 
1309
- // src/components/club-dashboard/GreetingHeaderBlock.tsx
1310
- import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
1311
- function GreetingHeaderBlock({ block }) {
1312
- return /* @__PURE__ */ jsxs13("div", { className: "ss-w-cdash-greeting", children: [
1313
- /* @__PURE__ */ jsx17("div", { className: "ss-w-cdash-greeting-title", children: block.title }),
1314
- /* @__PURE__ */ jsx17("div", { className: "ss-w-cdash-greeting-subtitle", children: block.subtitle })
1315
- ] });
1316
- }
1317
-
1318
- // src/components/club-dashboard/FixtureHeroBlock.tsx
1319
- import { useState as useState6 } from "react";
1320
- import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
1321
- function initials3(name) {
1322
- return name.split(/\s+/).filter(Boolean).slice(0, 2).map((w) => w[0].toUpperCase()).join("");
1323
- }
1324
- function Crest3({ url, name }) {
1325
- const [broken, setBroken] = useState6(false);
1326
- if (!url || broken) {
1327
- return /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-crest ss-w-cdash-crest--fallback", "aria-hidden": "true", children: initials3(name) });
1328
- }
1329
- return /* @__PURE__ */ jsx18("img", { className: "ss-w-cdash-crest", src: url, alt: "", "aria-hidden": "true", onError: () => setBroken(true) });
1330
- }
1331
- function FixtureHeroBlock({
1332
- block,
1333
- ui,
1334
- onAction
1335
- }) {
1336
- return /* @__PURE__ */ jsxs14("div", { className: "ss-w-cdash-fixture", children: [
1337
- /* @__PURE__ */ jsxs14("div", { className: "ss-w-cdash-tie", children: [
1338
- /* @__PURE__ */ jsx18(Crest3, { url: block.homeCrestUrl, name: block.homeName }),
1339
- /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-team", children: block.homeName }),
1340
- /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-vs", children: "v" }),
1341
- /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-team", children: block.awayName }),
1342
- /* @__PURE__ */ jsx18(Crest3, { url: block.awayCrestUrl, name: block.awayName })
1343
- ] }),
1344
- /* @__PURE__ */ jsxs14("div", { className: "ss-w-cdash-fixture-meta", children: [
1345
- /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-kickoff", children: ui.clubKickoff(block.kickoffUnix * 1e3) }),
1346
- block.venue ? /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-venue", children: block.venue }) : null,
1347
- block.competition ? /* @__PURE__ */ jsx18("span", { className: "ss-w-cdash-comp", children: block.competition }) : null
1348
- ] }),
1349
- /* @__PURE__ */ jsx18("button", { type: "button", className: "ss-w-cdash-cta", onClick: () => onAction(block.ctaLabel, { actionRef: block.ctaActionRef }), children: block.ctaLabel })
1350
- ] });
1351
- }
1352
-
1353
- // src/components/club-dashboard/FormGuideBlock.tsx
1354
- import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
1355
- function FormGuideBlock({ block }) {
1356
- if (block.rows.length === 0) return null;
1357
- return /* @__PURE__ */ jsx19("div", { className: "ss-w-cdash-form", children: block.rows.map((row) => /* @__PURE__ */ jsxs15("div", { className: "ss-w-cdash-form-row", children: [
1358
- /* @__PURE__ */ jsxs15("span", { className: "ss-w-cdash-form-name", children: [
1359
- row.name,
1360
- row.position != null ? ` #${row.position}` : ""
1361
- ] }),
1362
- /* @__PURE__ */ jsx19("span", { className: "ss-w-cdash-form-pills", children: row.last5.map((r, i) => /* @__PURE__ */ jsx19("span", { className: `ss-w-cdash-pill ss-w-cdash-pill--${r.toLowerCase()}`, children: r }, i)) })
1363
- ] }, row.name)) });
1364
- }
1365
-
1366
- // src/components/club-dashboard/MatchPreviewBlock.tsx
1367
- import { jsx as jsx20, jsxs as jsxs16 } from "react/jsx-runtime";
1368
- function MatchPreviewBlock({
1369
- block,
1370
- ui,
1371
- onAction
1372
- }) {
1373
- if (block.facts.length === 0) return null;
1374
- const heading = ui.clubMatchPreviewHeading;
1375
- return /* @__PURE__ */ jsxs16("div", { className: "ss-w-cdash-lines", children: [
1376
- /* @__PURE__ */ jsx20("span", { className: "ss-w-cdash-lines-heading", children: heading }),
1377
- block.facts.map(
1378
- (f, i) => f.actionRef ? /* @__PURE__ */ jsxs16(
1379
- "button",
1380
- {
1381
- type: "button",
1382
- className: "ss-w-cdash-fact",
1383
- onClick: () => onAction(f.label ?? f.sentence, { actionRef: f.actionRef }),
1384
- children: [
1385
- f.sentence,
1386
- f.label ? /* @__PURE__ */ jsxs16("span", { className: "ss-w-cdash-fact-label", children: [
1387
- " ",
1388
- f.label
1389
- ] }) : null
1390
- ]
1391
- },
1392
- i
1393
- ) : /* @__PURE__ */ jsx20("p", { children: f.sentence }, i)
1394
- )
1395
- ] });
1396
- }
1397
-
1398
- // src/components/club-dashboard/TeamDnaBlock.tsx
1399
- import { jsx as jsx21, jsxs as jsxs17 } from "react/jsx-runtime";
1400
- function TeamDnaBlock({
1401
- block,
1402
- ui,
1403
- onAction
1404
- }) {
1405
- if (block.facts.length === 0) return null;
1406
- const heading = ui.clubTeamDnaHeading;
1407
- return /* @__PURE__ */ jsxs17("div", { className: "ss-w-cdash-lines", children: [
1408
- /* @__PURE__ */ jsx21("span", { className: "ss-w-cdash-lines-heading", children: heading }),
1409
- block.facts.map(
1410
- (f, i) => f.actionRef ? /* @__PURE__ */ jsxs17(
1411
- "button",
1412
- {
1413
- type: "button",
1414
- className: "ss-w-cdash-fact",
1415
- onClick: () => onAction(f.label ?? f.sentence, { actionRef: f.actionRef }),
1416
- children: [
1417
- f.sentence,
1418
- f.label ? /* @__PURE__ */ jsxs17("span", { className: "ss-w-cdash-fact-label", children: [
1419
- " ",
1420
- f.label
1421
- ] }) : null
1422
- ]
1423
- },
1424
- i
1425
- ) : /* @__PURE__ */ jsx21("p", { children: f.sentence }, i)
1426
- )
1427
- ] });
1428
- }
1429
-
1430
- // src/components/club-dashboard/CountdownBlock.tsx
1431
- import { useEffect as useEffect3, useState as useState7 } from "react";
1432
- import { jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
1433
- var DAY_MS = 24 * 3600 * 1e3;
1434
- function pad2(n) {
1435
- return String(n).padStart(2, "0");
1436
- }
1437
- function splitRemaining(remainingMs) {
1438
- const totalSeconds = Math.max(0, Math.floor(remainingMs / 1e3));
1439
- const h = Math.floor(totalSeconds / 3600);
1440
- const m = Math.floor(totalSeconds % 3600 / 60);
1441
- const s = totalSeconds % 60;
1442
- return { h: pad2(h), m: pad2(m), s: pad2(s) };
1443
- }
1444
- function CountdownBlock({ block, ui }) {
1445
- const [remainingMs, setRemainingMs] = useState7(() => block.kickoffUnix * 1e3 - Date.now());
1446
- useEffect3(() => {
1447
- setRemainingMs(block.kickoffUnix * 1e3 - Date.now());
1448
- if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
1449
- const id = setInterval(() => {
1450
- setRemainingMs(block.kickoffUnix * 1e3 - Date.now());
1451
- }, 1e3);
1452
- return () => clearInterval(id);
1453
- }, [block.kickoffUnix]);
1454
- if (remainingMs <= 0) return null;
1455
- if (remainingMs > DAY_MS) return null;
1456
- const { h, m, s } = splitRemaining(remainingMs);
1457
- return /* @__PURE__ */ jsxs18("div", { className: "ss-w-cdash-cd", "aria-label": ui.clubKickoffAria(block.kickoffUnix * 1e3), children: [
1458
- /* @__PURE__ */ jsxs18("div", { className: "ss-w-cdash-cd-clock", "aria-hidden": "true", children: [
1459
- /* @__PURE__ */ jsx22("span", { className: "ss-w-cdash-cd-h", children: h }),
1460
- "h",
1461
- " ",
1462
- /* @__PURE__ */ jsx22("span", { className: "ss-w-cdash-cd-m", children: m }),
1463
- "m",
1464
- " ",
1465
- /* @__PURE__ */ jsx22("span", { className: "ss-w-cdash-cd-s", children: s }),
1466
- "s"
1467
- ] }),
1468
- /* @__PURE__ */ jsx22("div", { className: "ss-w-cdash-cd-label", children: ui.clubCountdownLabel })
1469
- ] });
1470
- }
1471
-
1472
- // src/components/club-dashboard/ClubDashboard.tsx
1473
- import { jsx as jsx23 } from "react/jsx-runtime";
1474
- var CLUB_BLOCK_REGISTRY = {
1475
- greetingHeader: ({ block }) => block.type === "greetingHeader" ? /* @__PURE__ */ jsx23(GreetingHeaderBlock, { block }) : null,
1476
- fixtureHero: ({ block, ui, onAction }) => block.type === "fixtureHero" ? /* @__PURE__ */ jsx23(FixtureHeroBlock, { block, ui, onAction }) : null,
1477
- formGuide: ({ block, ui }) => block.type === "formGuide" ? /* @__PURE__ */ jsx23(FormGuideBlock, { block, ui }) : null,
1478
- matchPreview: ({ block, ui, onAction }) => block.type === "matchPreview" ? /* @__PURE__ */ jsx23(MatchPreviewBlock, { block, ui, onAction }) : null,
1479
- teamDna: ({ block, ui, onAction }) => block.type === "teamDna" ? /* @__PURE__ */ jsx23(TeamDnaBlock, { block, ui, onAction }) : null,
1480
- countdown: ({ block, ui }) => block.type === "countdown" ? /* @__PURE__ */ jsx23(CountdownBlock, { block, ui }) : null
1481
- };
1482
- function ClubDashboard({
1483
- dashboard,
1484
- ui,
1485
- onAction
1486
- }) {
1487
- if (!dashboard.blocks.length) return null;
1488
- return /* @__PURE__ */ jsx23("div", { className: "ss-w-cdash", children: dashboard.blocks.map((block, i) => {
1489
- const Comp = Object.prototype.hasOwnProperty.call(CLUB_BLOCK_REGISTRY, block.type) ? CLUB_BLOCK_REGISTRY[block.type] : void 0;
1490
- if (!Comp) {
1491
- console.warn(`[club-dashboard] no renderer for block type "${block.type}"`);
1492
- return null;
1493
- }
1494
- return /* @__PURE__ */ jsx23("div", { className: "ss-w-cdash-block", children: /* @__PURE__ */ jsx23(Comp, { block, ui, onAction }) }, i);
1495
- }) });
1496
- }
1497
-
1498
1290
  // src/components/ChatMessage.tsx
1499
- import { jsx as jsx24, jsxs as jsxs19 } from "react/jsx-runtime";
1500
- function ChatMessage({ message, disabled, onAct, onTap, onClubAction, ui, slip }) {
1291
+ import { jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
1292
+ function ChatMessage({ message, disabled, onAct, onTap, ui, slip }) {
1501
1293
  if (message.role === "user") {
1502
- return /* @__PURE__ */ jsx24("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx24("div", { className: "ss-w-bubble", children: message.text }) });
1294
+ return /* @__PURE__ */ jsx17("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx17("div", { className: "ss-w-bubble", children: message.text }) });
1503
1295
  }
1504
- return /* @__PURE__ */ jsxs19("div", { className: "ss-w-msg ss-w-bot", children: [
1505
- message.text && !message.greetingDashboard && !message.clubDashboard ? /* @__PURE__ */ jsx24(Markdown, { text: message.text }) : null,
1506
- message.greetingDashboard ? /* @__PURE__ */ jsx24(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
1507
- message.clubDashboard && ui ? /* @__PURE__ */ jsx24(ClubDashboard, { dashboard: message.clubDashboard, ui, onAction: onClubAction }) : null,
1508
- /* @__PURE__ */ jsx24(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
1509
- /* @__PURE__ */ jsx24(BettingInsightList, { insights: message.insights, ui, slip }),
1510
- /* @__PURE__ */ jsx24(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
1511
- /* @__PURE__ */ jsx24(OddsMovementCardList, { cards: message.oddsMovement, ui }),
1512
- /* @__PURE__ */ jsx24(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
1296
+ return /* @__PURE__ */ jsxs13("div", { className: "ss-w-msg ss-w-bot", children: [
1297
+ message.text && !message.greetingDashboard ? /* @__PURE__ */ jsx17(Markdown, { text: message.text }) : null,
1298
+ message.greetingDashboard ? /* @__PURE__ */ jsx17(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
1299
+ /* @__PURE__ */ jsx17(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
1300
+ /* @__PURE__ */ jsx17(BettingInsightList, { insights: message.insights, ui, slip }),
1301
+ /* @__PURE__ */ jsx17(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
1302
+ /* @__PURE__ */ jsx17(OddsMovementCardList, { cards: message.oddsMovement, ui }),
1303
+ /* @__PURE__ */ jsx17(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
1513
1304
  ] });
1514
1305
  }
1515
1306
 
1516
1307
  // src/components/IntentChips.tsx
1517
- import { jsx as jsx25 } from "react/jsx-runtime";
1308
+ import { jsx as jsx18 } from "react/jsx-runtime";
1518
1309
  function IntentChips({ chips, onPick }) {
1519
1310
  if (chips.length === 0) return null;
1520
- return /* @__PURE__ */ jsx25("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx25("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
1311
+ return /* @__PURE__ */ jsx18("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx18("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
1521
1312
  }
1522
1313
 
1523
1314
  // src/scroll.ts
@@ -1527,18 +1318,18 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
1527
1318
  }
1528
1319
 
1529
1320
  // src/components/MessageList.tsx
1530
- import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
1321
+ import { jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
1531
1322
  function MessageList({ controller, ui, slip, onDashboardTap }) {
1532
1323
  const { state, send } = controller;
1533
1324
  const listRef = useRef2(null);
1534
1325
  const endRef = useRef2(null);
1535
1326
  const pinnedRef = useRef2(true);
1536
- useEffect4(() => {
1327
+ useEffect3(() => {
1537
1328
  if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
1538
1329
  }, [state.messages, state.status]);
1539
1330
  const hasUserMessage = state.messages.some((m) => m.role === "user");
1540
1331
  const hasDashboard = state.messages.some((m) => m.role === "assistant" && m.greetingDashboard);
1541
- return /* @__PURE__ */ jsxs20(
1332
+ return /* @__PURE__ */ jsxs14(
1542
1333
  "div",
1543
1334
  {
1544
1335
  className: "ss-w-list",
@@ -1548,7 +1339,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1548
1339
  if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
1549
1340
  },
1550
1341
  children: [
1551
- state.messages.map((m) => /* @__PURE__ */ jsx26(
1342
+ state.messages.map((m) => /* @__PURE__ */ jsx19(
1552
1343
  ChatMessage,
1553
1344
  {
1554
1345
  message: m,
@@ -1558,31 +1349,30 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
1558
1349
  send(tile.actionValue, { displayText: tile.title });
1559
1350
  if (m.role === "assistant" && m.greetingDashboard) onDashboardTap?.(tile, index, m.greetingDashboard.layout);
1560
1351
  },
1561
- onClubAction: (label, opts) => send(label, { actionRef: opts.actionRef }),
1562
1352
  ui,
1563
1353
  slip
1564
1354
  },
1565
1355
  m.id
1566
1356
  )),
1567
- state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx26(ActionButtons, { actions: [], loading: true, onAct: () => {
1357
+ state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx19(ActionButtons, { actions: [], loading: true, onAct: () => {
1568
1358
  } }) : null,
1569
- state.status ? /* @__PURE__ */ jsx26("div", { className: "ss-w-status", children: state.status }) : null,
1570
- state.error ? /* @__PURE__ */ jsxs20("div", { className: "ss-w-error", role: "alert", children: [
1571
- /* @__PURE__ */ jsx26("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
1572
- /* @__PURE__ */ jsx26("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
1359
+ state.status ? /* @__PURE__ */ jsx19("div", { className: "ss-w-status", children: state.status }) : null,
1360
+ state.error ? /* @__PURE__ */ jsxs14("div", { className: "ss-w-error", role: "alert", children: [
1361
+ /* @__PURE__ */ jsx19("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
1362
+ /* @__PURE__ */ jsx19("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
1573
1363
  ] }) : null,
1574
- !hasUserMessage && !hasDashboard ? /* @__PURE__ */ jsx26(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
1575
- /* @__PURE__ */ jsx26("div", { ref: endRef })
1364
+ !hasUserMessage && !hasDashboard ? /* @__PURE__ */ jsx19(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
1365
+ /* @__PURE__ */ jsx19("div", { ref: endRef })
1576
1366
  ]
1577
1367
  }
1578
1368
  );
1579
1369
  }
1580
1370
 
1581
1371
  // src/components/ChatInput.tsx
1582
- import { useState as useState8 } from "react";
1583
- import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
1372
+ import { useState as useState6 } from "react";
1373
+ import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
1584
1374
  function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
1585
- const [value, setValue] = useState8("");
1375
+ const [value, setValue] = useState6("");
1586
1376
  const submit = () => {
1587
1377
  const t = value.trim();
1588
1378
  if (!t) return;
@@ -1595,18 +1385,18 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
1595
1385
  submit();
1596
1386
  }
1597
1387
  };
1598
- return /* @__PURE__ */ jsxs21("div", { className: "ss-w-input", children: [
1599
- /* @__PURE__ */ jsx27("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
1600
- streaming ? /* @__PURE__ */ jsx27("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ jsx27("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx27("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ jsx27(SendIcon, {}) })
1388
+ return /* @__PURE__ */ jsxs15("div", { className: "ss-w-input", children: [
1389
+ /* @__PURE__ */ jsx20("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
1390
+ streaming ? /* @__PURE__ */ jsx20("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ jsx20("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx20("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ jsx20(SendIcon, {}) })
1601
1391
  ] });
1602
1392
  }
1603
1393
 
1604
1394
  // src/components/ChatPanel.tsx
1605
- import { jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
1395
+ import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
1606
1396
  var FULLSCREEN_QUERY = "(max-width: 639px)";
1607
1397
  function useIsModal() {
1608
- const [modal, setModal] = useState9(false);
1609
- useEffect5(() => {
1398
+ const [modal, setModal] = useState7(false);
1399
+ useEffect4(() => {
1610
1400
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
1611
1401
  const mql = window.matchMedia(FULLSCREEN_QUERY);
1612
1402
  const sync = () => setModal(mql.matches);
@@ -1619,17 +1409,17 @@ function useIsModal() {
1619
1409
  function ChatPanel({ controller, ui, slip, onDashboardTap }) {
1620
1410
  const { state, close, stop, send } = controller;
1621
1411
  const isModal = useIsModal();
1622
- useEffect5(() => {
1412
+ useEffect4(() => {
1623
1413
  const onKey = (e) => {
1624
1414
  if (e.key === "Escape") close();
1625
1415
  };
1626
1416
  window.addEventListener("keydown", onKey);
1627
1417
  return () => window.removeEventListener("keydown", onKey);
1628
1418
  }, [close]);
1629
- return /* @__PURE__ */ jsxs22("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
1630
- /* @__PURE__ */ jsx28(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
1631
- /* @__PURE__ */ jsx28(MessageList, { controller, ui, slip, onDashboardTap }),
1632
- /* @__PURE__ */ jsx28(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
1419
+ return /* @__PURE__ */ jsxs16("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
1420
+ /* @__PURE__ */ jsx21(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
1421
+ /* @__PURE__ */ jsx21(MessageList, { controller, ui, slip, onDashboardTap }),
1422
+ /* @__PURE__ */ jsx21(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
1633
1423
  ] });
1634
1424
  }
1635
1425
 
@@ -1982,49 +1772,10 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
1982
1772
  .ss-w-dash--discovery .ss-w-dash-tile:nth-child(2) { background: #eef3fb; }
1983
1773
  .ss-w-dash--discovery .ss-w-dash-tile:nth-child(3) { background: #fdf6ec; }
1984
1774
  .ss-w-dash--discovery .ss-w-dash-tile:nth-child(4) { background: #f5eefb; }
1985
-
1986
- /* club match-day dashboard */
1987
- .ss-w-cdash { display: flex; flex-direction: column; gap: 10px; padding: 4px 0; }
1988
- .ss-w-cdash-block { display: flex; flex-direction: column; }
1989
-
1990
- .ss-w-cdash-greeting { display: flex; flex-direction: column; gap: 2px; }
1991
- .ss-w-cdash-greeting-title { font-weight: 700; font-size: 14px; color: var(--ss-w-ink); }
1992
- .ss-w-cdash-greeting-subtitle { font-size: 11.5px; color: var(--ss-w-faint); }
1993
-
1994
- .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; }
1995
- .ss-w-cdash-tie { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; }
1996
- .ss-w-cdash-team { font-weight: 650; font-size: 13px; color: var(--ss-w-ink); min-width: 0; overflow-wrap: anywhere; }
1997
- .ss-w-cdash-vs { font-size: 11px; color: var(--ss-w-faint); font-weight: 600; }
1998
- .ss-w-cdash-crest { flex: none; width: 28px; height: 28px; border-radius: 50%; object-fit: cover; background: var(--ss-w-bubble); }
1999
- .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)); }
2000
- .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); }
2001
- .ss-w-cdash-kickoff { font-weight: 700; color: var(--ss-w-ink); }
2002
- .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; }
2003
-
2004
- .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; }
2005
- .ss-w-cdash-form-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
2006
- .ss-w-cdash-form-name { font-size: 12px; font-weight: 600; color: var(--ss-w-ink); min-width: 0; overflow-wrap: anywhere; }
2007
- .ss-w-cdash-form-pills { flex: none; display: flex; align-items: center; gap: 3px; }
2008
- .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; }
2009
- .ss-w-cdash-pill--w { background: color-mix(in srgb, var(--ss-w-pos) 88%, black); }
2010
- .ss-w-cdash-pill--d { background: var(--ss-w-faint); }
2011
- .ss-w-cdash-pill--l { background: color-mix(in srgb, var(--ss-w-neg) 88%, black); }
2012
-
2013
- .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; }
2014
- .ss-w-cdash-lines-heading { font-size: 12px; font-weight: 700; color: var(--ss-w-accent); }
2015
- .ss-w-cdash-lines p { font-size: 12px; line-height: 1.4; color: var(--ss-w-ink); margin: 0; }
2016
- .ss-w-cdash-fact { display: block; width: 100%; text-align: left; font: inherit; font-size: 12px; line-height: 1.4; color: var(--ss-w-ink); background: none; border: none; padding: 0; cursor: pointer; }
2017
- .ss-w-cdash-fact:hover, .ss-w-cdash-fact:focus-visible { color: var(--ss-w-accent); text-decoration: underline; }
2018
- .ss-w-cdash-fact-label { font-size: 11px; font-weight: 700; color: var(--ss-w-accent); }
2019
-
2020
- .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; }
2021
- .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); }
2022
- .ss-w-cdash-cd-h, .ss-w-cdash-cd-m, .ss-w-cdash-cd-s { color: var(--ss-w-accent); }
2023
- .ss-w-cdash-cd-label { font-size: 11px; font-weight: 600; color: var(--ss-w-faint); }
2024
1775
  `;
2025
1776
 
2026
1777
  // src/StatsWidget.tsx
2027
- import { jsx as jsx29 } from "react/jsx-runtime";
1778
+ import { jsx as jsx22 } from "react/jsx-runtime";
2028
1779
  function buildClient(props) {
2029
1780
  try {
2030
1781
  if ("client" in props && props.client) {
@@ -2070,14 +1821,14 @@ function StatsWidget(props) {
2070
1821
  () => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input)) : void 0,
2071
1822
  [built]
2072
1823
  );
2073
- useEffect6(() => {
1824
+ useEffect5(() => {
2074
1825
  if (!built || !ui.injectStyles) return;
2075
1826
  const node = rootRef.current?.getRootNode();
2076
1827
  if (node) injectStyles(node);
2077
1828
  }, [built, ui.injectStyles]);
2078
1829
  if (!built) return null;
2079
1830
  const attrs = themeAttrs(ui);
2080
- return /* @__PURE__ */ jsx29("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__ */ jsx29(ChatPanel, { controller, ui, slip, onDashboardTap }) : /* @__PURE__ */ jsx29(
1831
+ return /* @__PURE__ */ jsx22("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__ */ jsx22(ChatPanel, { controller, ui, slip, onDashboardTap }) : /* @__PURE__ */ jsx22(
2081
1832
  ChatFab,
2082
1833
  {
2083
1834
  label: ui.launcherLabel,