@sensiblestats/widget-react 0.98.0 → 0.99.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/README.md +11 -0
- package/dist/index.cjs +111 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +195 -102
- package/dist/index.js.map +1 -1
- package/dist/styles.css +47 -7
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @sensiblestats/widget-react
|
|
2
2
|
|
|
3
|
+
## 0.99.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 5d0450f: Add `config.page`. The greeting request carries the page the widget sits on, and a page change while mounted re-greets until the visitor has typed or tapped. Greeting tiles show `meta` in the hero's top row and draw `imageCodes` as text marks when there are no images.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [5d0450f]
|
|
12
|
+
- @sensiblestats/widget-sdk@0.66.0
|
|
13
|
+
|
|
3
14
|
## 0.98.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -38,6 +38,17 @@ Common `config` fields:
|
|
|
38
38
|
and personalization to your signed-in user.
|
|
39
39
|
- White-label branding — `logoUrl`, `headerTitle`, `accent`, `cta`, `surface`,
|
|
40
40
|
`ink`. Absent → the default SensibleStats look.
|
|
41
|
+
- `page` — the page the widget sits on, `{ sport, kind?, matchId?, competitionId? }`, so the
|
|
42
|
+
first greeting can lead with it. Football ids are bare (`'4820113'`); NBA ids are
|
|
43
|
+
`'nba:game:{id}'` or digits. `sport` is `'football'` or `'nba'`; the server answers any other
|
|
44
|
+
value with `unsupported_sport` and the visitor sees today's greeting, so check your spelling
|
|
45
|
+
rather than expecting a client-side error. The page rides on greeting requests only, never on a
|
|
46
|
+
typed or tapped turn. Omit it and the request is unchanged.
|
|
47
|
+
|
|
48
|
+
If `page` changes while the widget is mounted, it re-greets with the new page (after 300 ms,
|
|
49
|
+
so quick changes collapse), but only while the visitor has not typed or tapped anything. Once
|
|
50
|
+
the visitor has sent a message, a page change does nothing. The re-greet reads and writes no
|
|
51
|
+
stored conversation.
|
|
41
52
|
|
|
42
53
|
## Hooks: the slip and the match page
|
|
43
54
|
|
package/dist/index.cjs
CHANGED
|
@@ -1137,7 +1137,7 @@ function isEnglishLocale(locale) {
|
|
|
1137
1137
|
}
|
|
1138
1138
|
|
|
1139
1139
|
// src/version.generated.ts
|
|
1140
|
-
var WIDGET_REACT_VERSION = "0.
|
|
1140
|
+
var WIDGET_REACT_VERSION = "0.99.0";
|
|
1141
1141
|
|
|
1142
1142
|
// src/config.ts
|
|
1143
1143
|
function normalizeUi(cfg) {
|
|
@@ -2772,7 +2772,9 @@ function seedIdCounter(messages) {
|
|
|
2772
2772
|
}
|
|
2773
2773
|
return max + 1;
|
|
2774
2774
|
}
|
|
2775
|
-
|
|
2775
|
+
var pageKey = (p) => p ? `${p.sport}|${p.kind ?? "section"}|${p.matchId ?? p.competitionId ?? ""}` : "";
|
|
2776
|
+
var REGREET_DEBOUNCE_MS = 300;
|
|
2777
|
+
function useChatStream(conversation, starterTexts, greetingMessage, storageKey, page) {
|
|
2776
2778
|
const boot = (0, import_react3.useMemo)(() => {
|
|
2777
2779
|
const payload = storageKey ? readConversation(storageKey, Date.now()) : null;
|
|
2778
2780
|
const restored = payload != null && payload !== "expired" && payload !== "corrupt";
|
|
@@ -2790,6 +2792,10 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
2790
2792
|
stateRef.current = state;
|
|
2791
2793
|
const greetedRef = (0, import_react3.useRef)(boot.restored);
|
|
2792
2794
|
const hydratedRef = (0, import_react3.useRef)(false);
|
|
2795
|
+
const pageRef = (0, import_react3.useRef)(page);
|
|
2796
|
+
pageRef.current = page;
|
|
2797
|
+
const greetedKeyRef = (0, import_react3.useRef)(null);
|
|
2798
|
+
const greetingCancelledRef = (0, import_react3.useRef)(false);
|
|
2793
2799
|
const restoreSeededRef = (0, import_react3.useRef)(false);
|
|
2794
2800
|
if (!restoreSeededRef.current) {
|
|
2795
2801
|
restoreSeededRef.current = true;
|
|
@@ -2865,7 +2871,13 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
2865
2871
|
handleRef.current?.cancel();
|
|
2866
2872
|
clearFallbackTimer();
|
|
2867
2873
|
dispatch({ kind: "GREETING_SENT", assistantId: nextId() });
|
|
2868
|
-
|
|
2874
|
+
greetedKeyRef.current = pageKey(pageRef.current);
|
|
2875
|
+
greetingCancelledRef.current = false;
|
|
2876
|
+
const handle = conversation.send({
|
|
2877
|
+
message,
|
|
2878
|
+
clientCapabilities: [...CLIENT_CAPABILITIES],
|
|
2879
|
+
...pageRef.current ? { page: pageRef.current } : {}
|
|
2880
|
+
});
|
|
2869
2881
|
handleRef.current = handle;
|
|
2870
2882
|
armFallbackTimer(() => dispatch({ kind: "STOP" }));
|
|
2871
2883
|
consume(handle, () => dispatch({ kind: "STOP" }));
|
|
@@ -2878,6 +2890,24 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
|
|
|
2878
2890
|
greetedRef.current = true;
|
|
2879
2891
|
fireGreeting();
|
|
2880
2892
|
}, [fireGreeting]);
|
|
2893
|
+
const key = pageKey(page);
|
|
2894
|
+
(0, import_react3.useEffect)(() => {
|
|
2895
|
+
if (greetedKeyRef.current === null) return;
|
|
2896
|
+
if (key === greetedKeyRef.current && !greetingCancelledRef.current) return;
|
|
2897
|
+
if (stateRef.current.messages.some((m) => m.role === "user")) return;
|
|
2898
|
+
if (key !== greetedKeyRef.current && handleRef.current) {
|
|
2899
|
+
handleRef.current.cancel();
|
|
2900
|
+
greetingCancelledRef.current = true;
|
|
2901
|
+
}
|
|
2902
|
+
const timer = setTimeout(() => {
|
|
2903
|
+
if (stateRef.current.messages.some((m) => m.role === "user")) return;
|
|
2904
|
+
idRef.current = 0;
|
|
2905
|
+
conversation.conversationId = void 0;
|
|
2906
|
+
dispatch({ kind: "RESET" });
|
|
2907
|
+
fireGreeting();
|
|
2908
|
+
}, REGREET_DEBOUNCE_MS);
|
|
2909
|
+
return () => clearTimeout(timer);
|
|
2910
|
+
}, [key, conversation, fireGreeting]);
|
|
2881
2911
|
(0, import_react3.useEffect)(() => {
|
|
2882
2912
|
if (!state.limit) return;
|
|
2883
2913
|
const delay = limitDelayMs(state.limit, Date.now());
|
|
@@ -6009,8 +6039,27 @@ function splitOdds(subtitle) {
|
|
|
6009
6039
|
const m = ODDS_TAIL.exec(subtitle);
|
|
6010
6040
|
return m ? { text: subtitle.slice(0, m.index), odds: m[1] } : { text: subtitle };
|
|
6011
6041
|
}
|
|
6042
|
+
function codesOf(tile) {
|
|
6043
|
+
return (tile.imageCodes ?? []).filter(Boolean);
|
|
6044
|
+
}
|
|
6045
|
+
function Marks({ codes }) {
|
|
6046
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: codes.map((code, i) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash__mark", children: code }, `${code}-${i}`)) });
|
|
6047
|
+
}
|
|
6048
|
+
var KEEP_MAX = 16;
|
|
6049
|
+
function DotSeparated({ text }) {
|
|
6050
|
+
const parts = text.split(" \xB7 ");
|
|
6051
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: parts.map((part, i) => {
|
|
6052
|
+
const piece = i < parts.length - 1 ? `${part} \xB7` : part;
|
|
6053
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { children: [
|
|
6054
|
+
i > 0 ? " " : null,
|
|
6055
|
+
part.length <= KEEP_MAX ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash__keep", children: piece }) : piece
|
|
6056
|
+
] }, i);
|
|
6057
|
+
}) });
|
|
6058
|
+
}
|
|
6059
|
+
var keepDashWithHome = (title) => title.replace(/ - /g, "\xA0- ");
|
|
6012
6060
|
function HeroTile({ tile, onTap, oddsLabel }) {
|
|
6013
6061
|
const crests = (tile.imageUrls ?? []).filter(Boolean).slice(0, 2);
|
|
6062
|
+
const codes = crests.length === 0 ? codesOf(tile).slice(0, 2) : [];
|
|
6014
6063
|
const sub = tile.subtitle ? splitOdds(tile.subtitle) : null;
|
|
6015
6064
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("button", { type: "button", className: "ss-w-dash-tile ss-w-dash-tile--full ss-w-dash-hero", onClick: onTap, children: [
|
|
6016
6065
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: "ss-w-dash-hero-top", children: [
|
|
@@ -6022,36 +6071,40 @@ function HeroTile({ tile, onTap, oddsLabel }) {
|
|
|
6022
6071
|
] }),
|
|
6023
6072
|
sub.odds
|
|
6024
6073
|
] }) : null,
|
|
6074
|
+
!sub?.odds && tile.meta ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash__meta", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DotSeparated, { text: tile.meta }) }) : null,
|
|
6025
6075
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-hero-chevron", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ChevronIcon, {}) })
|
|
6026
6076
|
] }),
|
|
6027
6077
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: "ss-w-dash-hero-main", children: [
|
|
6028
|
-
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-hero-visual", "aria-hidden": "true", children: crests.length > 0 ? crests.map((url, i) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-hero-crest", style: { backgroundImage: `url(${url})` } }, `${url}-${i}`)) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-hero-icon", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DashboardIcon, { name: tile.icon }) }) }),
|
|
6078
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-hero-visual", "aria-hidden": "true", children: crests.length > 0 ? crests.map((url, i) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-hero-crest", style: { backgroundImage: `url(${url})` } }, `${url}-${i}`)) : codes.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Marks, { codes }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-hero-icon", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DashboardIcon, { name: tile.icon }) }) }),
|
|
6029
6079
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: "ss-w-dash-hero-body", children: [
|
|
6030
|
-
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-hero-title", children: tile.title }),
|
|
6031
|
-
sub?.text ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-hero-subtitle", children: sub.text }) : null
|
|
6080
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-hero-title", children: keepDashWithHome(tile.title) }),
|
|
6081
|
+
sub?.text ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-hero-subtitle", children: sub.text.includes(" \xB7 ") ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DotSeparated, { text: sub.text }) : sub.text }) : null
|
|
6032
6082
|
] })
|
|
6033
6083
|
] })
|
|
6034
6084
|
] });
|
|
6035
6085
|
}
|
|
6086
|
+
var PRICE_BADGE = /^\d+(?:[.,]\d+)?$/;
|
|
6036
6087
|
function DashboardTile({ tile, onTap, oddsLabel = "Odds", badgePlacement = "meta" }) {
|
|
6037
6088
|
if (tile.emphasis === "hero") return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(HeroTile, { tile, onTap, oddsLabel });
|
|
6038
6089
|
const spanClass = tile.span === "full" ? "ss-w-dash-tile--full" : "ss-w-dash-tile--half";
|
|
6039
6090
|
const crests = (tile.imageUrls ?? []).slice(0, MAX_CRESTS);
|
|
6040
|
-
const endBadge = badgePlacement === "end" && tile.badgeText ? tile.badgeText : null;
|
|
6091
|
+
const endBadge = badgePlacement === "end" && tile.badgeText && PRICE_BADGE.test(tile.badgeText.trim()) ? tile.badgeText : null;
|
|
6041
6092
|
const metaBadge = endBadge ? null : tile.badgeText;
|
|
6042
|
-
const
|
|
6093
|
+
const codes = crests.length === 0 ? codesOf(tile).slice(0, MAX_CRESTS) : [];
|
|
6094
|
+
const hasMeta = Boolean(metaBadge) || crests.length > 0 || codes.length > 0;
|
|
6043
6095
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("button", { type: "button", className: `ss-w-dash-tile ${spanClass}`, onClick: onTap, children: [
|
|
6044
6096
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-tile-icon", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DashboardIcon, { name: tile.icon }) }),
|
|
6045
6097
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: "ss-w-dash-tile-body", children: [
|
|
6046
6098
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-tile-title", children: tile.title }),
|
|
6047
|
-
tile.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-tile-subtitle", children: tile.subtitle }) : null,
|
|
6099
|
+
tile.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-tile-subtitle", children: tile.subtitle.includes(" \xB7 ") ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(DotSeparated, { text: tile.subtitle }) : tile.subtitle }) : null,
|
|
6048
6100
|
hasMeta ? (
|
|
6049
6101
|
// Badge + crests sit on their own row, below the title, instead of sharing the
|
|
6050
6102
|
// icon/chevron row -- at the half-span tile's ~180px width a nowrap badge alone
|
|
6051
6103
|
// (~70px) left the title column at ~22px and forced character-by-character wrapping.
|
|
6052
6104
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: "ss-w-dash-tile-meta", children: [
|
|
6053
6105
|
metaBadge ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-tile-badge", children: metaBadge }) : null,
|
|
6054
|
-
crests.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-tile-crests", children: crests.map((url, i) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-tile-crest", style: { backgroundImage: `url(${url})` }, "aria-hidden": "true" }, `${url}-${i}`)) }) : null
|
|
6106
|
+
crests.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-tile-crests", children: crests.map((url, i) => /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-tile-crest", style: { backgroundImage: `url(${url})` }, "aria-hidden": "true" }, `${url}-${i}`)) }) : null,
|
|
6107
|
+
codes.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: "ss-w-dash-tile-crests", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Marks, { codes }) }) : null
|
|
6055
6108
|
] })
|
|
6056
6109
|
) : null
|
|
6057
6110
|
] }),
|
|
@@ -13513,7 +13566,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
13513
13566
|
}
|
|
13514
13567
|
|
|
13515
13568
|
/* greeting dashboard */
|
|
13516
|
-
.ss-w-dash { display: flex; flex-direction: column; gap: 12px; padding: 4px 0; }
|
|
13569
|
+
.ss-w-dash { display: flex; flex-direction: column; gap: 12px; padding: 4px 0; container-type: inline-size; }
|
|
13517
13570
|
.ss-w-dash-head { display: flex; flex-direction: column; }
|
|
13518
13571
|
.ss-w-dash-title { font-size: 18px; font-weight: 700; line-height: 1.25; color: var(--ss-w-ink); }
|
|
13519
13572
|
.ss-w-dash-subtitle { font-size: 13px; color: var(--ss-w-faint); margin-top: 4px; }
|
|
@@ -13534,8 +13587,8 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
13534
13587
|
.ss-w-dash-tile-icon { flex: none; display: grid; place-items: center; width: 28px; height: 28px; font-size: 22px; }
|
|
13535
13588
|
.ss-w-dash-emoji { line-height: 1; }
|
|
13536
13589
|
.ss-w-dash-tile-body { min-width: 0; display: flex; flex-direction: column; gap: 2px; flex: 1; }
|
|
13537
|
-
.ss-w-dash-tile-title { min-width: 0; font-weight: 600; font-size: 14px; overflow-wrap:
|
|
13538
|
-
.ss-w-dash-tile-subtitle { min-width: 0; font-size: 11.5px; color: var(--ss-w-faint); overflow-wrap:
|
|
13590
|
+
.ss-w-dash-tile-title { min-width: 0; font-weight: 600; font-size: 14px; overflow-wrap: break-word; hyphens: manual; }
|
|
13591
|
+
.ss-w-dash-tile-subtitle { min-width: 0; font-size: 11.5px; color: var(--ss-w-faint); overflow-wrap: break-word; hyphens: manual; }
|
|
13539
13592
|
.ss-w-dash-tile-chevron { flex: none; margin-left: auto; width: 14px; height: 14px; color: var(--ss-w-faint); }
|
|
13540
13593
|
|
|
13541
13594
|
/* Badge + crests share their own row, below the title -- NOT the icon/chevron row. A
|
|
@@ -13557,6 +13610,18 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
13557
13610
|
families (.ss-w-binsight, .ss-w-mocard, .ss-w-omcard) so a market tile reads as
|
|
13558
13611
|
part of that visual language rather than a plain unstyled button. */
|
|
13559
13612
|
.ss-w-dash--market .ss-w-dash-tile { border-left: 3px solid var(--ss-w-accent); }
|
|
13613
|
+
/* Market's two columns need the text column to fit a word: a half tile spends ~91px on border, padding,
|
|
13614
|
+
icon and chevron, and a Turkish word such as "kar\u015F\u0131la\u015Fma" is ~80px at 14px bold. Measured against the
|
|
13615
|
+
dashboard's own width (a container query, not the viewport, since the panel is min(460px, 100vw - 40px)
|
|
13616
|
+
and a host can embed it narrower still). Below 306px the half tiles are a single full-width column;
|
|
13617
|
+
up to 349px the decorative chevron is dropped, as Discovery already does, to give the words ~24px back.
|
|
13618
|
+
Words never break inside a tile (overflow-wrap: break-word), so the column count is what protects them. */
|
|
13619
|
+
@container (max-width: 305px) {
|
|
13620
|
+
.ss-w-dash--market .ss-w-dash-grid { grid-template-columns: minmax(0, 1fr); }
|
|
13621
|
+
}
|
|
13622
|
+
@container (min-width: 306px) and (max-width: 349px) {
|
|
13623
|
+
.ss-w-dash--market .ss-w-dash-tile--half .ss-w-dash-tile-chevron { display: none; }
|
|
13624
|
+
}
|
|
13560
13625
|
|
|
13561
13626
|
/* Goal: one column of compact cards, 60px tall with the badge as a price column on the right.
|
|
13562
13627
|
The value ink is accent mixed toward ink (as on the hero price): plain light accent on the
|
|
@@ -13577,7 +13642,8 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
13577
13642
|
/* Discovery: tinted bento cards. Stacked (icon over text) so a two- or three-word title gets
|
|
13578
13643
|
the tile's full width instead of the ~100px left of a horizontal icon+chevron -- that narrow
|
|
13579
13644
|
column was wrapping "Most interesting angles" onto three lines. The chevron is dropped (the
|
|
13580
|
-
whole card is the tap target)
|
|
13645
|
+
whole card is the tap target). The badge + crest row wraps (the base .ss-w-dash-tile-meta rule): an
|
|
13646
|
+
"8 matches" pill plus three crests is wider than a half tile at a 320px viewport. */
|
|
13581
13647
|
.ss-w-dash--discovery .ss-w-dash-tile { flex-direction: column; align-items: stretch; gap: 8px; }
|
|
13582
13648
|
.ss-w-dash--discovery .ss-w-dash-tile-icon { align-self: flex-start; width: 32px; height: 32px; font-size: 26px; }
|
|
13583
13649
|
.ss-w-dash--discovery .ss-w-dash-tile-chevron { display: none; }
|
|
@@ -13585,7 +13651,6 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
13585
13651
|
(horizontal, centre-aligned) rather than stacking like the narrow half tiles. */
|
|
13586
13652
|
.ss-w-dash--discovery .ss-w-dash-tile--full { flex-direction: row; align-items: center; }
|
|
13587
13653
|
.ss-w-dash--discovery .ss-w-dash-tile--full .ss-w-dash-tile-icon { align-self: center; }
|
|
13588
|
-
.ss-w-dash--discovery .ss-w-dash-tile-meta { flex-wrap: nowrap; }
|
|
13589
13654
|
.ss-w-dash--discovery .ss-w-dash-tile-crest { width: 22px; height: 22px; }
|
|
13590
13655
|
/* Tints mix a hue into the live panel rather than hardcoding a light pastel, so a dark
|
|
13591
13656
|
operator surface (e.g. --ss-w-panel set from a navy \`surface\` brand color) yields a
|
|
@@ -13638,8 +13703,8 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
13638
13703
|
background: color-mix(in srgb, var(--ss-w-accent) 14%, var(--ss-w-panel));
|
|
13639
13704
|
}
|
|
13640
13705
|
.ss-w-dash-hero-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
|
|
13641
|
-
.ss-w-dash-hero-title { font-size: 15.5px; font-weight: 700; line-height: 1.25; color: var(--ss-w-ink); overflow-wrap:
|
|
13642
|
-
.ss-w-dash-hero-subtitle { font-size: 12px; color: var(--ss-w-faint); overflow-wrap:
|
|
13706
|
+
.ss-w-dash-hero-title { font-size: 15.5px; font-weight: 700; line-height: 1.25; color: var(--ss-w-ink); overflow-wrap: break-word; hyphens: manual; }
|
|
13707
|
+
.ss-w-dash-hero-subtitle { font-size: 12px; color: var(--ss-w-faint); overflow-wrap: break-word; hyphens: manual; }
|
|
13643
13708
|
.ss-w-dash-hero-price {
|
|
13644
13709
|
flex: none; margin-left: auto;
|
|
13645
13710
|
font-family: ui-monospace, monospace; font-variant-numeric: tabular-nums; font-size: 15px; font-weight: 750; line-height: 1;
|
|
@@ -13648,6 +13713,34 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
13648
13713
|
border: 1px solid color-mix(in srgb, var(--ss-w-accent) 30%, var(--ss-w-panel));
|
|
13649
13714
|
}
|
|
13650
13715
|
|
|
13716
|
+
/* Page-grounded additions (u591). Both only exist when the server sends \`meta\` / \`imageCodes\`, so an
|
|
13717
|
+
older payload never reaches these rules. */
|
|
13718
|
+
/* Hero meta ("Tip-off 02:30 \xB7 in 6 h", "Ba\u015Flama 02:30 \xB7 40 dk sonra"): takes the price's seat in the top
|
|
13719
|
+
row. It is the one flexible item there -- the badge and chevron keep their width -- and it wraps
|
|
13720
|
+
rather than truncates: at the narrowest panel the Turkish line (27 characters) has ~120px beside
|
|
13721
|
+
the badge, and the minutes-to-tip are the part a cut-off "40..." would lose. The component keeps
|
|
13722
|
+
each short " \xB7 "-separated part whole, so a wrap falls at the dot instead of inside "40 dk sonra". */
|
|
13723
|
+
.ss-w-dash__keep { white-space: nowrap; }
|
|
13724
|
+
.ss-w-dash__meta {
|
|
13725
|
+
flex: 1 1 auto; min-width: 0; text-align: right; overflow-wrap: break-word; hyphens: manual;
|
|
13726
|
+
font-size: 11.5px; font-weight: 600; line-height: 1.2; font-variant-numeric: tabular-nums;
|
|
13727
|
+
color: var(--ss-w-faint);
|
|
13728
|
+
}
|
|
13729
|
+
/* Text marks stand in for crests until the server has images: 22px circle / 7.5px in a tile, 40px /
|
|
13730
|
+
12px in the hero (u592 D23). Ring and tint follow the crests they replace; unlike crests they do not
|
|
13731
|
+
overlap, because the letters are the content and an overlap would clip the first one. */
|
|
13732
|
+
.ss-w-dash__mark {
|
|
13733
|
+
flex: none; display: grid; place-items: center; box-sizing: border-box;
|
|
13734
|
+
width: 22px; height: 22px; border-radius: 50%; overflow: hidden;
|
|
13735
|
+
font-size: 7.5px; font-weight: 700; line-height: 1; letter-spacing: .02em; white-space: nowrap;
|
|
13736
|
+
color: var(--ss-w-ink);
|
|
13737
|
+
background-color: color-mix(in srgb, var(--ss-w-ink) 6%, var(--ss-w-panel));
|
|
13738
|
+
box-shadow: 0 0 0 2px var(--ss-w-panel), 0 0 0 3px color-mix(in srgb, var(--ss-w-ink) 10%, var(--ss-w-panel));
|
|
13739
|
+
}
|
|
13740
|
+
.ss-w-dash__mark + .ss-w-dash__mark { margin-left: 3px; }
|
|
13741
|
+
.ss-w-dash-hero-visual .ss-w-dash__mark { width: 40px; height: 40px; font-size: 12px; }
|
|
13742
|
+
.ss-w-dash-hero-visual .ss-w-dash__mark + .ss-w-dash__mark { margin-left: 6px; }
|
|
13743
|
+
|
|
13651
13744
|
/* club fan pre-match dashboard */
|
|
13652
13745
|
.ss-w-fandash { display: flex; flex-direction: column; gap: 10px; padding: 4px 0; }
|
|
13653
13746
|
.ss-w-fandash-greeting { font-size: 14px; font-weight: 600; line-height: 1.3; color: var(--ss-w-ink); }
|
|
@@ -14919,7 +15012,7 @@ function StatsWidget(props) {
|
|
|
14919
15012
|
introDoneRef.current = true;
|
|
14920
15013
|
}, []);
|
|
14921
15014
|
const convStorageKey = `ss-w-conv:${cfg?.operatorId ?? ""}:${cfg?.userContext?.operatorUserId ?? "anon"}`;
|
|
14922
|
-
const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0, convStorageKey);
|
|
15015
|
+
const controller = useChatStream(built?.conversation ?? emptyConversation(), ui.starters, built ? ui.greetingMessage : void 0, convStorageKey, cfg?.page);
|
|
14923
15016
|
const [hasToday, setHasToday] = (0, import_react74.useState)(false);
|
|
14924
15017
|
const liveCardWatches = (0, import_react74.useMemo)(() => {
|
|
14925
15018
|
const seen = /* @__PURE__ */ new Set();
|