@sensiblestats/widget-react 0.25.0 → 0.26.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 +239 -115
- 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 +201 -77
- package/dist/index.js.map +1 -1
- package/dist/styles.css +25 -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 useEffect7, useMemo as useMemo2, useRef as useRef4 } from "react";
|
|
3
3
|
import { StatsWidgetClient } from "@sensiblestats/widget-sdk";
|
|
4
4
|
|
|
5
5
|
// src/kickoff.ts
|
|
@@ -225,7 +225,7 @@ function themeAttrs(ui) {
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
// src/useChatStream.ts
|
|
228
|
-
import { useCallback, useEffect, useMemo, useReducer, useRef } from "react";
|
|
228
|
+
import { useCallback, useEffect as useEffect2, useMemo, useReducer, useRef as useRef2 } from "react";
|
|
229
229
|
import { WidgetSdkError as WidgetSdkError2 } from "@sensiblestats/widget-sdk";
|
|
230
230
|
|
|
231
231
|
// src/reducer.ts
|
|
@@ -567,20 +567,63 @@ function reducer(state, action) {
|
|
|
567
567
|
return { ...state, error: action.error, isStreaming: false, status: null, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, done: true })) };
|
|
568
568
|
case "STOP":
|
|
569
569
|
return { ...state, isStreaming: false, status: null, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, done: true })) };
|
|
570
|
+
case "LIVE_REFRESH":
|
|
571
|
+
return {
|
|
572
|
+
...state,
|
|
573
|
+
messages: state.messages.map(
|
|
574
|
+
(m) => m.role === "assistant" && m.id === action.messageId && m.clubFanDashboard ? { ...m, clubFanDashboard: { ...m.clubFanDashboard, cards: action.cards } } : m
|
|
575
|
+
)
|
|
576
|
+
};
|
|
570
577
|
}
|
|
571
578
|
}
|
|
572
579
|
|
|
580
|
+
// src/useLiveDashboardPoll.ts
|
|
581
|
+
import { useEffect, useRef } from "react";
|
|
582
|
+
var POLL_INTERVAL_MS = 6e4;
|
|
583
|
+
function findLiveMessageId(messages) {
|
|
584
|
+
for (const m of messages.slice().reverse()) {
|
|
585
|
+
if (m.role === "assistant" && m.clubFanDashboard?.phase === "live") return m.id;
|
|
586
|
+
}
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
589
|
+
function useLiveDashboardPoll({ messages, dispatch, getLiveDashboard }) {
|
|
590
|
+
const messageId = findLiveMessageId(messages);
|
|
591
|
+
const inFlightRef = useRef(false);
|
|
592
|
+
useEffect(() => {
|
|
593
|
+
if (!messageId) return;
|
|
594
|
+
const tick = async () => {
|
|
595
|
+
if (inFlightRef.current) return;
|
|
596
|
+
inFlightRef.current = true;
|
|
597
|
+
try {
|
|
598
|
+
const payload = await getLiveDashboard();
|
|
599
|
+
if (payload && payload.phase === "live") {
|
|
600
|
+
dispatch({ kind: "LIVE_REFRESH", messageId, cards: payload.cards });
|
|
601
|
+
} else {
|
|
602
|
+
window.clearInterval(id);
|
|
603
|
+
}
|
|
604
|
+
} catch {
|
|
605
|
+
} finally {
|
|
606
|
+
inFlightRef.current = false;
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
const id = window.setInterval(() => {
|
|
610
|
+
void tick();
|
|
611
|
+
}, POLL_INTERVAL_MS);
|
|
612
|
+
return () => window.clearInterval(id);
|
|
613
|
+
}, [messageId, dispatch, getLiveDashboard]);
|
|
614
|
+
}
|
|
615
|
+
|
|
573
616
|
// src/useChatStream.ts
|
|
574
617
|
var STREAM_FALLBACK_TIMEOUT_MS = 3e4;
|
|
575
618
|
function useChatStream(conversation, starterTexts, greetingMessage) {
|
|
576
619
|
const seed = useMemo(() => initialState(starterTexts.map(chipFromText)), []);
|
|
577
620
|
const [state, dispatch] = useReducer(reducer, seed);
|
|
578
|
-
const idRef =
|
|
579
|
-
const handleRef =
|
|
580
|
-
const fallbackTimerRef =
|
|
581
|
-
const stateRef =
|
|
621
|
+
const idRef = useRef2(0);
|
|
622
|
+
const handleRef = useRef2(null);
|
|
623
|
+
const fallbackTimerRef = useRef2(null);
|
|
624
|
+
const stateRef = useRef2(state);
|
|
582
625
|
stateRef.current = state;
|
|
583
|
-
const greetedRef =
|
|
626
|
+
const greetedRef = useRef2(false);
|
|
584
627
|
const nextId = () => `m${idRef.current++}`;
|
|
585
628
|
const clearFallbackTimer = useCallback(() => {
|
|
586
629
|
if (fallbackTimerRef.current != null) {
|
|
@@ -636,7 +679,7 @@ function useChatStream(conversation, starterTexts, greetingMessage) {
|
|
|
636
679
|
const last = stateRef.current.lastUserInput;
|
|
637
680
|
if (last) run(last, stateRef.current.lastDisplayText ?? void 0);
|
|
638
681
|
}, [run]);
|
|
639
|
-
|
|
682
|
+
useEffect2(() => {
|
|
640
683
|
const message = greetingMessage?.trim();
|
|
641
684
|
if (!message || greetedRef.current) return;
|
|
642
685
|
greetedRef.current = true;
|
|
@@ -670,6 +713,8 @@ function useChatStream(conversation, starterTexts, greetingMessage) {
|
|
|
670
713
|
else open();
|
|
671
714
|
}, [close, open]);
|
|
672
715
|
const dismissPopup = useCallback(() => dispatch({ kind: "DISMISS_POPUP" }), []);
|
|
716
|
+
const getLiveDashboard = useCallback(() => conversation.getLiveDashboard(), [conversation]);
|
|
717
|
+
useLiveDashboardPoll({ messages: state.messages, dispatch, getLiveDashboard });
|
|
673
718
|
return { state, open, close, stop, toggle, send, retry, dismissPopup };
|
|
674
719
|
}
|
|
675
720
|
|
|
@@ -730,7 +775,7 @@ function ChatFab({ label, greeting, popupText, onOpen, onDismissPopup, dismissLa
|
|
|
730
775
|
}
|
|
731
776
|
|
|
732
777
|
// src/components/ChatPanel.tsx
|
|
733
|
-
import { useEffect as
|
|
778
|
+
import { useEffect as useEffect6, useState as useState8 } from "react";
|
|
734
779
|
|
|
735
780
|
// src/components/ChatHeader.tsx
|
|
736
781
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -743,7 +788,7 @@ function ChatHeader({ onClose, closeLabel = "Close chat" }) {
|
|
|
743
788
|
}
|
|
744
789
|
|
|
745
790
|
// src/components/MessageList.tsx
|
|
746
|
-
import { useEffect as
|
|
791
|
+
import { useEffect as useEffect5, useRef as useRef3 } from "react";
|
|
747
792
|
|
|
748
793
|
// src/components/Markdown.tsx
|
|
749
794
|
import ReactMarkdown from "react-markdown";
|
|
@@ -898,11 +943,11 @@ function resultToToast(result, shownOdds, ui) {
|
|
|
898
943
|
}
|
|
899
944
|
|
|
900
945
|
// src/components/Toast.tsx
|
|
901
|
-
import { useEffect as
|
|
946
|
+
import { useEffect as useEffect3 } from "react";
|
|
902
947
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
903
948
|
var DISMISS_MS = 4e3;
|
|
904
949
|
function Toast({ toast, onDismiss }) {
|
|
905
|
-
|
|
950
|
+
useEffect3(() => {
|
|
906
951
|
if (!toast) return;
|
|
907
952
|
const timer = setTimeout(onDismiss, DISMISS_MS);
|
|
908
953
|
return () => clearTimeout(timer);
|
|
@@ -1370,7 +1415,7 @@ function FixtureCard({ card, onTap }) {
|
|
|
1370
1415
|
}
|
|
1371
1416
|
|
|
1372
1417
|
// src/components/dashboard/CountdownCard.tsx
|
|
1373
|
-
import { Fragment as Fragment6, useEffect as
|
|
1418
|
+
import { Fragment as Fragment6, useEffect as useEffect4, useState as useState6 } from "react";
|
|
1374
1419
|
import { jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1375
1420
|
var DAY_MS = 864e5;
|
|
1376
1421
|
function remainingSegments(kickoffUnix, nowMs, withSeconds) {
|
|
@@ -1394,7 +1439,7 @@ function CountdownCard({ card }) {
|
|
|
1394
1439
|
() => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches
|
|
1395
1440
|
);
|
|
1396
1441
|
const withinDay = data.kickoffUnix * 1e3 - nowMs < DAY_MS;
|
|
1397
|
-
|
|
1442
|
+
useEffect4(() => {
|
|
1398
1443
|
if (reduced) return;
|
|
1399
1444
|
const id = window.setInterval(() => setNowMs(Date.now()), withinDay ? 1e3 : 6e4);
|
|
1400
1445
|
return () => window.clearInterval(id);
|
|
@@ -1650,85 +1695,139 @@ function RestingFormCard({ card }) {
|
|
|
1650
1695
|
] });
|
|
1651
1696
|
}
|
|
1652
1697
|
|
|
1653
|
-
// src/components/dashboard/
|
|
1698
|
+
// src/components/dashboard/LiveScoreCard.tsx
|
|
1654
1699
|
import { jsx as jsx34, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
1700
|
+
function LiveScoreCard({ card }) {
|
|
1701
|
+
const { data } = card;
|
|
1702
|
+
return /* @__PURE__ */ jsxs30("div", { className: "ss-w-fandash-card ss-w-fandash-live-score", children: [
|
|
1703
|
+
card.title ? /* @__PURE__ */ jsx34("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
1704
|
+
/* @__PURE__ */ jsxs30("div", { className: "ss-w-fandash-postmatch-scoreboard", children: [
|
|
1705
|
+
/* @__PURE__ */ jsx34("span", { className: "ss-w-fandash-postmatch-team", children: data.homeName }),
|
|
1706
|
+
/* @__PURE__ */ jsx34("span", { className: "ss-w-fandash-postmatch-score", children: data.homeScore }),
|
|
1707
|
+
/* @__PURE__ */ jsx34("span", { className: "ss-w-fandash-postmatch-sep", children: "\u2013" }),
|
|
1708
|
+
/* @__PURE__ */ jsx34("span", { className: "ss-w-fandash-postmatch-score", children: data.awayScore }),
|
|
1709
|
+
/* @__PURE__ */ jsx34("span", { className: "ss-w-fandash-postmatch-team ss-w-fandash-postmatch-team--away", children: data.awayName })
|
|
1710
|
+
] }),
|
|
1711
|
+
/* @__PURE__ */ jsxs30("div", { className: "ss-w-fandash-live-meta", children: [
|
|
1712
|
+
/* @__PURE__ */ jsx34("span", { className: "ss-w-fandash-live-minute", children: data.minuteLabel }),
|
|
1713
|
+
/* @__PURE__ */ jsx34("span", { className: "ss-w-fandash-live-status", children: data.statusLabel }),
|
|
1714
|
+
data.competition ? /* @__PURE__ */ jsx34("span", { className: "ss-w-fandash-live-competition", children: data.competition }) : null
|
|
1715
|
+
] })
|
|
1716
|
+
] });
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
// src/components/dashboard/LiveTimelineCard.tsx
|
|
1720
|
+
import { jsx as jsx35, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
1721
|
+
function LiveTimelineCard({ card }) {
|
|
1722
|
+
return /* @__PURE__ */ jsxs31("div", { className: "ss-w-fandash-card ss-w-fandash-live-timeline", children: [
|
|
1723
|
+
card.title ? /* @__PURE__ */ jsx35("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
1724
|
+
/* @__PURE__ */ jsx35("ul", { className: "ss-w-fandash-live-event-list", children: card.data.events.map((e, i) => /* @__PURE__ */ jsxs31("li", { className: e.scoredByClub ? "ss-w-fandash-live-event ss-w-fandash-live-event--club" : "ss-w-fandash-live-event", children: [
|
|
1725
|
+
/* @__PURE__ */ jsx35("span", { className: "ss-w-fandash-live-event-minute", children: e.minuteLabel }),
|
|
1726
|
+
/* @__PURE__ */ jsx35("span", { className: "ss-w-fandash-live-event-text", children: e.text })
|
|
1727
|
+
] }, `${e.minuteLabel}-${i}`)) })
|
|
1728
|
+
] });
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
// src/components/dashboard/LiveStatsCard.tsx
|
|
1732
|
+
import { jsx as jsx36, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
1733
|
+
function LiveStatsCard({ card }) {
|
|
1734
|
+
const { data } = card;
|
|
1735
|
+
return /* @__PURE__ */ jsxs32("div", { className: "ss-w-fandash-card ss-w-fandash-live-stats", children: [
|
|
1736
|
+
card.title ? /* @__PURE__ */ jsx36("span", { className: "ss-w-fandash-eyebrow", children: card.title }) : null,
|
|
1737
|
+
/* @__PURE__ */ jsx36("div", { className: "ss-w-fandash-live-stat-rows", children: data.rows.map((r, i) => /* @__PURE__ */ jsxs32("div", { className: "ss-w-fandash-live-stat-row", children: [
|
|
1738
|
+
/* @__PURE__ */ jsx36("span", { className: "ss-w-fandash-live-stat-value", children: r.clubValue }),
|
|
1739
|
+
/* @__PURE__ */ jsx36("span", { className: "ss-w-fandash-live-stat-label", children: r.label }),
|
|
1740
|
+
/* @__PURE__ */ jsx36("span", { className: "ss-w-fandash-live-stat-value", children: r.oppValue })
|
|
1741
|
+
] }, `${r.label}-${i}`)) }),
|
|
1742
|
+
data.momentumLabel ? /* @__PURE__ */ jsx36("span", { className: "ss-w-fandash-live-momentum", children: data.momentumLabel }) : null
|
|
1743
|
+
] });
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
// src/components/dashboard/ClubFanDashboard.tsx
|
|
1747
|
+
import { jsx as jsx37, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
1655
1748
|
function ClubFanDashboard({ dashboard, onTap, ui }) {
|
|
1656
1749
|
const orderedCards = [
|
|
1657
1750
|
...dashboard.cards.filter((c) => c.cardType === "countdown"),
|
|
1658
1751
|
...dashboard.cards.filter((c) => c.cardType !== "countdown")
|
|
1659
1752
|
];
|
|
1660
|
-
return /* @__PURE__ */
|
|
1661
|
-
dashboard.greeting ? /* @__PURE__ */
|
|
1662
|
-
/* @__PURE__ */
|
|
1753
|
+
return /* @__PURE__ */ jsxs33("div", { className: "ss-w-fandash", children: [
|
|
1754
|
+
dashboard.greeting ? /* @__PURE__ */ jsx37("span", { className: "ss-w-fandash-greeting", children: dashboard.greeting }) : null,
|
|
1755
|
+
/* @__PURE__ */ jsx37("div", { className: "ss-w-fandash-cards", children: orderedCards.map((card, i) => /* @__PURE__ */ jsx37(FanDashboardCard, { card, onTap, ui }, `${card.cardType}-${i}`)) })
|
|
1663
1756
|
] });
|
|
1664
1757
|
}
|
|
1665
1758
|
function FanDashboardCard({ card, onTap, ui }) {
|
|
1666
1759
|
switch (card.cardType) {
|
|
1667
1760
|
case "fixture":
|
|
1668
|
-
return /* @__PURE__ */
|
|
1761
|
+
return /* @__PURE__ */ jsx37(FixtureCard, { card, onTap });
|
|
1669
1762
|
case "countdown":
|
|
1670
|
-
return /* @__PURE__ */
|
|
1763
|
+
return /* @__PURE__ */ jsx37(CountdownCard, { card });
|
|
1671
1764
|
case "injuries":
|
|
1672
|
-
return /* @__PURE__ */
|
|
1765
|
+
return /* @__PURE__ */ jsx37(InjuriesCard, { card });
|
|
1673
1766
|
case "reasonToBet":
|
|
1674
|
-
return /* @__PURE__ */
|
|
1767
|
+
return /* @__PURE__ */ jsx37(ReasonToBetCard, { card });
|
|
1675
1768
|
case "whereToWatch":
|
|
1676
|
-
return /* @__PURE__ */
|
|
1769
|
+
return /* @__PURE__ */ jsx37(WhereToWatchCard, { card });
|
|
1677
1770
|
case "congestedWeek":
|
|
1678
|
-
return /* @__PURE__ */
|
|
1771
|
+
return /* @__PURE__ */ jsx37(CongestedWeekCard, { card, onTap });
|
|
1679
1772
|
case "transfers":
|
|
1680
|
-
return /* @__PURE__ */
|
|
1773
|
+
return /* @__PURE__ */ jsx37(TransfersCard, { card });
|
|
1681
1774
|
case "links":
|
|
1682
|
-
return /* @__PURE__ */
|
|
1775
|
+
return /* @__PURE__ */ jsx37(LinksCard, { card });
|
|
1683
1776
|
case "postMatchResult":
|
|
1684
|
-
return /* @__PURE__ */
|
|
1777
|
+
return /* @__PURE__ */ jsx37(PostMatchResultCard, { card });
|
|
1685
1778
|
case "postMatchGoals":
|
|
1686
|
-
return /* @__PURE__ */
|
|
1779
|
+
return /* @__PURE__ */ jsx37(PostMatchGoalsCard, { card, onTap });
|
|
1687
1780
|
case "postMatchStats":
|
|
1688
|
-
return /* @__PURE__ */
|
|
1781
|
+
return /* @__PURE__ */ jsx37(PostMatchStatsCard, { card, onTap });
|
|
1689
1782
|
case "postMatchTopPerformer":
|
|
1690
|
-
return /* @__PURE__ */
|
|
1783
|
+
return /* @__PURE__ */ jsx37(PostMatchTopPerformerCard, { card, onTap });
|
|
1691
1784
|
case "postMatchNext":
|
|
1692
|
-
return /* @__PURE__ */
|
|
1785
|
+
return /* @__PURE__ */ jsx37(PostMatchNextCard, { card });
|
|
1693
1786
|
case "restingNext":
|
|
1694
|
-
return /* @__PURE__ */
|
|
1787
|
+
return /* @__PURE__ */ jsx37(RestingNextCard, { card, onTap });
|
|
1695
1788
|
case "restingStandings":
|
|
1696
|
-
return /* @__PURE__ */
|
|
1789
|
+
return /* @__PURE__ */ jsx37(RestingStandingsCard, { card, onTap });
|
|
1697
1790
|
case "restingSeason":
|
|
1698
|
-
return /* @__PURE__ */
|
|
1791
|
+
return /* @__PURE__ */ jsx37(RestingSeasonCard, { card, onTap });
|
|
1699
1792
|
case "restingForm":
|
|
1700
|
-
return /* @__PURE__ */
|
|
1793
|
+
return /* @__PURE__ */ jsx37(RestingFormCard, { card });
|
|
1794
|
+
case "liveScore":
|
|
1795
|
+
return /* @__PURE__ */ jsx37(LiveScoreCard, { card });
|
|
1796
|
+
case "liveTimeline":
|
|
1797
|
+
return /* @__PURE__ */ jsx37(LiveTimelineCard, { card });
|
|
1798
|
+
case "liveStats":
|
|
1799
|
+
return /* @__PURE__ */ jsx37(LiveStatsCard, { card });
|
|
1701
1800
|
default:
|
|
1702
1801
|
return null;
|
|
1703
1802
|
}
|
|
1704
1803
|
}
|
|
1705
1804
|
|
|
1706
1805
|
// src/components/ChatMessage.tsx
|
|
1707
|
-
import { jsx as
|
|
1806
|
+
import { jsx as jsx38, jsxs as jsxs34 } from "react/jsx-runtime";
|
|
1708
1807
|
function ChatMessage({ message, disabled, onAct, onTap, ui, slip }) {
|
|
1709
1808
|
if (message.role === "user") {
|
|
1710
|
-
return /* @__PURE__ */
|
|
1809
|
+
return /* @__PURE__ */ jsx38("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx38("div", { className: "ss-w-bubble", children: message.text }) });
|
|
1711
1810
|
}
|
|
1712
1811
|
const onFanDashboardTap = (card) => {
|
|
1713
1812
|
onAct({ message: card.actionLabel, actionRef: card.actionRef, label: card.actionLabel || card.title || "" });
|
|
1714
1813
|
};
|
|
1715
|
-
return /* @__PURE__ */
|
|
1716
|
-
message.text && !message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */
|
|
1717
|
-
message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */
|
|
1718
|
-
message.clubFanDashboard ? /* @__PURE__ */
|
|
1719
|
-
/* @__PURE__ */
|
|
1720
|
-
/* @__PURE__ */
|
|
1721
|
-
/* @__PURE__ */
|
|
1722
|
-
/* @__PURE__ */
|
|
1723
|
-
/* @__PURE__ */
|
|
1814
|
+
return /* @__PURE__ */ jsxs34("div", { className: "ss-w-msg ss-w-bot", children: [
|
|
1815
|
+
message.text && !message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ jsx38(Markdown, { text: message.text }) : null,
|
|
1816
|
+
message.greetingDashboard && !message.clubFanDashboard ? /* @__PURE__ */ jsx38(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
|
|
1817
|
+
message.clubFanDashboard ? /* @__PURE__ */ jsx38(ClubFanDashboard, { dashboard: message.clubFanDashboard, onTap: onFanDashboardTap, ui }) : null,
|
|
1818
|
+
/* @__PURE__ */ jsx38(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
|
|
1819
|
+
/* @__PURE__ */ jsx38(BettingInsightList, { insights: message.insights, ui, slip }),
|
|
1820
|
+
/* @__PURE__ */ jsx38(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
|
|
1821
|
+
/* @__PURE__ */ jsx38(OddsMovementCardList, { cards: message.oddsMovement, ui }),
|
|
1822
|
+
/* @__PURE__ */ jsx38(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
|
|
1724
1823
|
] });
|
|
1725
1824
|
}
|
|
1726
1825
|
|
|
1727
1826
|
// src/components/IntentChips.tsx
|
|
1728
|
-
import { jsx as
|
|
1827
|
+
import { jsx as jsx39 } from "react/jsx-runtime";
|
|
1729
1828
|
function IntentChips({ chips, onPick }) {
|
|
1730
1829
|
if (chips.length === 0) return null;
|
|
1731
|
-
return /* @__PURE__ */
|
|
1830
|
+
return /* @__PURE__ */ jsx39("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx39("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
|
|
1732
1831
|
}
|
|
1733
1832
|
|
|
1734
1833
|
// src/scroll.ts
|
|
@@ -1738,18 +1837,18 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
|
|
|
1738
1837
|
}
|
|
1739
1838
|
|
|
1740
1839
|
// src/components/MessageList.tsx
|
|
1741
|
-
import { jsx as
|
|
1840
|
+
import { jsx as jsx40, jsxs as jsxs35 } from "react/jsx-runtime";
|
|
1742
1841
|
function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
1743
1842
|
const { state, send } = controller;
|
|
1744
|
-
const listRef =
|
|
1745
|
-
const endRef =
|
|
1746
|
-
const pinnedRef =
|
|
1747
|
-
|
|
1843
|
+
const listRef = useRef3(null);
|
|
1844
|
+
const endRef = useRef3(null);
|
|
1845
|
+
const pinnedRef = useRef3(true);
|
|
1846
|
+
useEffect5(() => {
|
|
1748
1847
|
if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
|
|
1749
1848
|
}, [state.messages, state.status]);
|
|
1750
1849
|
const hasUserMessage = state.messages.some((m) => m.role === "user");
|
|
1751
1850
|
const hasDashboard = state.messages.some((m) => m.role === "assistant" && (m.greetingDashboard || m.clubFanDashboard));
|
|
1752
|
-
return /* @__PURE__ */
|
|
1851
|
+
return /* @__PURE__ */ jsxs35(
|
|
1753
1852
|
"div",
|
|
1754
1853
|
{
|
|
1755
1854
|
className: "ss-w-list",
|
|
@@ -1759,7 +1858,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1759
1858
|
if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
|
|
1760
1859
|
},
|
|
1761
1860
|
children: [
|
|
1762
|
-
state.messages.map((m) => /* @__PURE__ */
|
|
1861
|
+
state.messages.map((m) => /* @__PURE__ */ jsx40(
|
|
1763
1862
|
ChatMessage,
|
|
1764
1863
|
{
|
|
1765
1864
|
message: m,
|
|
@@ -1774,15 +1873,15 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1774
1873
|
},
|
|
1775
1874
|
m.id
|
|
1776
1875
|
)),
|
|
1777
|
-
state.isStreaming && state.actionsLoading ? /* @__PURE__ */
|
|
1876
|
+
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx40(ActionButtons, { actions: [], loading: true, onAct: () => {
|
|
1778
1877
|
} }) : null,
|
|
1779
|
-
state.status ? /* @__PURE__ */
|
|
1780
|
-
state.error ? /* @__PURE__ */
|
|
1781
|
-
/* @__PURE__ */
|
|
1782
|
-
/* @__PURE__ */
|
|
1878
|
+
state.status ? /* @__PURE__ */ jsx40("div", { className: "ss-w-status", children: state.status }) : null,
|
|
1879
|
+
state.error ? /* @__PURE__ */ jsxs35("div", { className: "ss-w-error", role: "alert", children: [
|
|
1880
|
+
/* @__PURE__ */ jsx40("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
|
|
1881
|
+
/* @__PURE__ */ jsx40("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
|
|
1783
1882
|
] }) : null,
|
|
1784
|
-
!hasUserMessage && !hasDashboard ? /* @__PURE__ */
|
|
1785
|
-
/* @__PURE__ */
|
|
1883
|
+
!hasUserMessage && !hasDashboard ? /* @__PURE__ */ jsx40(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
|
|
1884
|
+
/* @__PURE__ */ jsx40("div", { ref: endRef })
|
|
1786
1885
|
]
|
|
1787
1886
|
}
|
|
1788
1887
|
);
|
|
@@ -1790,7 +1889,7 @@ function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
|
1790
1889
|
|
|
1791
1890
|
// src/components/ChatInput.tsx
|
|
1792
1891
|
import { useState as useState7 } from "react";
|
|
1793
|
-
import { jsx as
|
|
1892
|
+
import { jsx as jsx41, jsxs as jsxs36 } from "react/jsx-runtime";
|
|
1794
1893
|
function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
|
|
1795
1894
|
const [value, setValue] = useState7("");
|
|
1796
1895
|
const submit = () => {
|
|
@@ -1805,18 +1904,18 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
|
|
|
1805
1904
|
submit();
|
|
1806
1905
|
}
|
|
1807
1906
|
};
|
|
1808
|
-
return /* @__PURE__ */
|
|
1809
|
-
/* @__PURE__ */
|
|
1810
|
-
streaming ? /* @__PURE__ */
|
|
1907
|
+
return /* @__PURE__ */ jsxs36("div", { className: "ss-w-input", children: [
|
|
1908
|
+
/* @__PURE__ */ jsx41("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
|
|
1909
|
+
streaming ? /* @__PURE__ */ jsx41("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ jsx41("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx41("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ jsx41(SendIcon, {}) })
|
|
1811
1910
|
] });
|
|
1812
1911
|
}
|
|
1813
1912
|
|
|
1814
1913
|
// src/components/ChatPanel.tsx
|
|
1815
|
-
import { jsx as
|
|
1914
|
+
import { jsx as jsx42, jsxs as jsxs37 } from "react/jsx-runtime";
|
|
1816
1915
|
var FULLSCREEN_QUERY = "(max-width: 639px)";
|
|
1817
1916
|
function useIsModal() {
|
|
1818
1917
|
const [modal, setModal] = useState8(false);
|
|
1819
|
-
|
|
1918
|
+
useEffect6(() => {
|
|
1820
1919
|
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
1821
1920
|
const mql = window.matchMedia(FULLSCREEN_QUERY);
|
|
1822
1921
|
const sync = () => setModal(mql.matches);
|
|
@@ -1829,17 +1928,17 @@ function useIsModal() {
|
|
|
1829
1928
|
function ChatPanel({ controller, ui, slip, onDashboardTap }) {
|
|
1830
1929
|
const { state, close, stop, send } = controller;
|
|
1831
1930
|
const isModal = useIsModal();
|
|
1832
|
-
|
|
1931
|
+
useEffect6(() => {
|
|
1833
1932
|
const onKey = (e) => {
|
|
1834
1933
|
if (e.key === "Escape") close();
|
|
1835
1934
|
};
|
|
1836
1935
|
window.addEventListener("keydown", onKey);
|
|
1837
1936
|
return () => window.removeEventListener("keydown", onKey);
|
|
1838
1937
|
}, [close]);
|
|
1839
|
-
return /* @__PURE__ */
|
|
1840
|
-
/* @__PURE__ */
|
|
1841
|
-
/* @__PURE__ */
|
|
1842
|
-
/* @__PURE__ */
|
|
1938
|
+
return /* @__PURE__ */ jsxs37("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
|
|
1939
|
+
/* @__PURE__ */ jsx42(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
|
|
1940
|
+
/* @__PURE__ */ jsx42(MessageList, { controller, ui, slip, onDashboardTap }),
|
|
1941
|
+
/* @__PURE__ */ jsx42(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
|
|
1843
1942
|
] });
|
|
1844
1943
|
}
|
|
1845
1944
|
|
|
@@ -2351,10 +2450,35 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
2351
2450
|
|
|
2352
2451
|
/* resting form: reuses the fixture last-five pill classes (ss-w-fandash-form / -form-pill) */
|
|
2353
2452
|
.ss-w-fandash-resting-form .ss-w-fandash-form { margin-top: 2px; }
|
|
2453
|
+
|
|
2454
|
+
/* live score: reuses the post-match scoreboard classes for the team/score row, with its own
|
|
2455
|
+
meta row -- minute called out in the accent color, status and competition beside it */
|
|
2456
|
+
.ss-w-fandash-live-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 11.5px; color: var(--ss-w-faint); }
|
|
2457
|
+
.ss-w-fandash-live-minute { flex: none; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-accent); }
|
|
2458
|
+
.ss-w-fandash-live-status { font-weight: 650; color: var(--ss-w-ink); }
|
|
2459
|
+
.ss-w-fandash-live-competition { color: var(--ss-w-faint); }
|
|
2460
|
+
|
|
2461
|
+
/* live timeline: a plain event list, the club's own events picked out in the accent color --
|
|
2462
|
+
same mechanism as the post-match goal list, plus a minute column */
|
|
2463
|
+
.ss-w-fandash-live-event-list { list-style: none; display: flex; flex-direction: column; gap: 4px; }
|
|
2464
|
+
.ss-w-fandash-live-event { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; color: var(--ss-w-faint); }
|
|
2465
|
+
.ss-w-fandash-live-event--club { color: var(--ss-w-ink); font-weight: 650; }
|
|
2466
|
+
.ss-w-fandash-live-event-minute { flex: none; font-variant-numeric: tabular-nums; color: var(--ss-w-faint); }
|
|
2467
|
+
.ss-w-fandash-live-event--club .ss-w-fandash-live-event-minute { color: var(--ss-w-accent); }
|
|
2468
|
+
.ss-w-fandash-live-event-text { min-width: 0; }
|
|
2469
|
+
|
|
2470
|
+
/* live stats: club value / stat label / opponent value, same 3-column layout as post-match
|
|
2471
|
+
stats, plus an optional momentum line underneath */
|
|
2472
|
+
.ss-w-fandash-live-stat-rows { display: flex; flex-direction: column; gap: 5px; }
|
|
2473
|
+
.ss-w-fandash-live-stat-row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px; }
|
|
2474
|
+
.ss-w-fandash-live-stat-value { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ss-w-ink); }
|
|
2475
|
+
.ss-w-fandash-live-stat-row .ss-w-fandash-live-stat-value:last-child { text-align: right; }
|
|
2476
|
+
.ss-w-fandash-live-stat-label { font-size: 10.5px; text-align: center; color: var(--ss-w-faint); text-transform: uppercase; letter-spacing: .03em; }
|
|
2477
|
+
.ss-w-fandash-live-momentum { font-size: 11.5px; font-weight: 650; color: var(--ss-w-accent); }
|
|
2354
2478
|
`;
|
|
2355
2479
|
|
|
2356
2480
|
// src/StatsWidget.tsx
|
|
2357
|
-
import { jsx as
|
|
2481
|
+
import { jsx as jsx43 } from "react/jsx-runtime";
|
|
2358
2482
|
function buildClient(props) {
|
|
2359
2483
|
try {
|
|
2360
2484
|
if ("client" in props && props.client) {
|
|
@@ -2381,7 +2505,7 @@ function StatsWidget(props) {
|
|
|
2381
2505
|
const cfg = props.config;
|
|
2382
2506
|
const built = useMemo2(() => buildClient(props), [injectedClient, cfg?.operatorId, cfg?.publicKey, cfg?.baseUrl]);
|
|
2383
2507
|
const ui = useMemo2(() => normalizeUi(props.config ?? { operatorId: "", publicKey: "" }), [props.config]);
|
|
2384
|
-
const rootRef =
|
|
2508
|
+
const rootRef = useRef4(null);
|
|
2385
2509
|
const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0);
|
|
2386
2510
|
const slip = useMemo2(
|
|
2387
2511
|
() => ({
|
|
@@ -2400,14 +2524,14 @@ function StatsWidget(props) {
|
|
|
2400
2524
|
() => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input)) : void 0,
|
|
2401
2525
|
[built]
|
|
2402
2526
|
);
|
|
2403
|
-
|
|
2527
|
+
useEffect7(() => {
|
|
2404
2528
|
if (!built || !ui.injectStyles) return;
|
|
2405
2529
|
const node = rootRef.current?.getRootNode();
|
|
2406
2530
|
if (node) injectStyles(node);
|
|
2407
2531
|
}, [built, ui.injectStyles]);
|
|
2408
2532
|
if (!built) return null;
|
|
2409
2533
|
const attrs = themeAttrs(ui);
|
|
2410
|
-
return /* @__PURE__ */
|
|
2534
|
+
return /* @__PURE__ */ jsx43("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__ */ jsx43(ChatPanel, { controller, ui, slip, onDashboardTap }) : /* @__PURE__ */ jsx43(
|
|
2411
2535
|
ChatFab,
|
|
2412
2536
|
{
|
|
2413
2537
|
label: ui.launcherLabel,
|