@sensiblestats/widget-react 0.13.1 → 0.14.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 +264 -44
- 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 +312 -92
- package/dist/index.js.map +1 -1
- package/dist/styles.css +55 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @sensiblestats/widget-react
|
|
2
2
|
|
|
3
|
+
## 0.14.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Render the greeting dashboard carried on `turn_complete`: tile grid, CSS, and tap handling that turns a tile into a follow-up chat message. Older servers that never send `dashboard` render nothing new. `widget-embed` also now generates and persists a first-party anonymous visitor id (localStorage, falls back to in-memory) so greeting dashboards can be tiered for anonymous visitors.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
- @sensiblestats/widget-sdk@0.12.0
|
|
13
|
+
|
|
3
14
|
## 0.13.1
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -37,7 +37,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
37
37
|
|
|
38
38
|
// src/StatsWidget.tsx
|
|
39
39
|
var import_react10 = require("react");
|
|
40
|
-
var
|
|
40
|
+
var import_widget_sdk5 = require("@sensiblestats/widget-sdk");
|
|
41
41
|
|
|
42
42
|
// src/i18n.ts
|
|
43
43
|
var STRINGS = {
|
|
@@ -205,6 +205,27 @@ function toUserContext(cfg) {
|
|
|
205
205
|
return cfg.userContext && Object.keys(cfg.userContext).length > 0 ? cfg.userContext : void 0;
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
+
// src/dashboardTap.ts
|
|
209
|
+
var import_widget_sdk = require("@sensiblestats/widget-sdk");
|
|
210
|
+
function isSignalEntityType(value) {
|
|
211
|
+
return Object.prototype.hasOwnProperty.call(import_widget_sdk.ENTITY_TYPE_CODES, value);
|
|
212
|
+
}
|
|
213
|
+
function createDashboardTapSignal(sendSignal) {
|
|
214
|
+
return (tile, index, layout) => {
|
|
215
|
+
if (!isSignalEntityType(tile.signal.entityType)) return;
|
|
216
|
+
try {
|
|
217
|
+
void sendSignal({
|
|
218
|
+
signalType: "MarketView",
|
|
219
|
+
entityType: tile.signal.entityType,
|
|
220
|
+
entityId: tile.signal.entityId,
|
|
221
|
+
dashboardLayout: layout,
|
|
222
|
+
tileIndex: index
|
|
223
|
+
}).catch(() => void 0);
|
|
224
|
+
} catch {
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
208
229
|
// src/theme.ts
|
|
209
230
|
function themeAttrs(ui) {
|
|
210
231
|
const style = {
|
|
@@ -218,10 +239,10 @@ function themeAttrs(ui) {
|
|
|
218
239
|
|
|
219
240
|
// src/useChatStream.ts
|
|
220
241
|
var import_react = require("react");
|
|
221
|
-
var
|
|
242
|
+
var import_widget_sdk3 = require("@sensiblestats/widget-sdk");
|
|
222
243
|
|
|
223
244
|
// src/reducer.ts
|
|
224
|
-
var
|
|
245
|
+
var import_widget_sdk2 = require("@sensiblestats/widget-sdk");
|
|
225
246
|
|
|
226
247
|
// src/chart.ts
|
|
227
248
|
var CHART_VIEW_W = 320;
|
|
@@ -507,7 +528,11 @@ function applyEvent(state, event) {
|
|
|
507
528
|
actionsLoading: false,
|
|
508
529
|
popupText,
|
|
509
530
|
conversationId: event.data.conversationId ?? state.conversationId,
|
|
510
|
-
messages: mapActive(state, (t) => ({
|
|
531
|
+
messages: mapActive(state, (t) => ({
|
|
532
|
+
...t,
|
|
533
|
+
done: true,
|
|
534
|
+
greetingDashboard: event.data.dashboard ?? void 0
|
|
535
|
+
}))
|
|
511
536
|
};
|
|
512
537
|
}
|
|
513
538
|
case "error": {
|
|
@@ -559,7 +584,7 @@ function useChatStream(conversation, starterTexts, greetingMessage) {
|
|
|
559
584
|
try {
|
|
560
585
|
for await (const event of handle) dispatch({ kind: "EVENT", event });
|
|
561
586
|
} catch (err) {
|
|
562
|
-
if (err instanceof
|
|
587
|
+
if (err instanceof import_widget_sdk3.WidgetSdkError) onError(err);
|
|
563
588
|
else if (err?.name === "AbortError") {
|
|
564
589
|
} else throw err;
|
|
565
590
|
} finally {
|
|
@@ -630,6 +655,89 @@ function SendIcon() {
|
|
|
630
655
|
function CloseIcon() {
|
|
631
656
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "none", d: "M18 6L6 18M6 6l12 12", stroke: "currentColor", strokeWidth: "2" }) });
|
|
632
657
|
}
|
|
658
|
+
function FootballIcon() {
|
|
659
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: [
|
|
660
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "9", fill: "none", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
661
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M12 7l3.5 2.5-1.3 4.1H9.8L8.5 9.5z" })
|
|
662
|
+
] });
|
|
663
|
+
}
|
|
664
|
+
function GoalNetIcon() {
|
|
665
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: [
|
|
666
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "3", y: "5", width: "18", height: "12", fill: "none", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
667
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "none", stroke: "currentColor", strokeWidth: "1", d: "M3 9h18M3 13h18M8 5v12M16 5v12" })
|
|
668
|
+
] });
|
|
669
|
+
}
|
|
670
|
+
function BttsIcon() {
|
|
671
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: [
|
|
672
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "8", cy: "12", r: "4.5", fill: "none", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
673
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "16", cy: "12", r: "4.5", fill: "none", stroke: "currentColor", strokeWidth: "1.6" })
|
|
674
|
+
] });
|
|
675
|
+
}
|
|
676
|
+
function CardIcon() {
|
|
677
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "6", y: "3", width: "12", height: "18", rx: "1.5", fill: "currentColor" }) });
|
|
678
|
+
}
|
|
679
|
+
function CornerFlagIcon() {
|
|
680
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: [
|
|
681
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "none", stroke: "currentColor", strokeWidth: "1.6", d: "M6 21V4" }),
|
|
682
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M6 4l11 3.5L6 11z" })
|
|
683
|
+
] });
|
|
684
|
+
}
|
|
685
|
+
function PlayerIcon() {
|
|
686
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: [
|
|
687
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "7", r: "3.2", fill: "currentColor" }),
|
|
688
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M5 20c0-4 3-6.5 7-6.5s7 2.5 7 6.5z" })
|
|
689
|
+
] });
|
|
690
|
+
}
|
|
691
|
+
function TargetIcon() {
|
|
692
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: [
|
|
693
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "8.5", fill: "none", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
694
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "4.5", fill: "none", stroke: "currentColor", strokeWidth: "1.6" }),
|
|
695
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("circle", { cx: "12", cy: "12", r: "1.4", fill: "currentColor" })
|
|
696
|
+
] });
|
|
697
|
+
}
|
|
698
|
+
function TrophyIcon() {
|
|
699
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: [
|
|
700
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M7 3h10v4a5 5 0 0 1-5 5 5 5 0 0 1-5-5zM9 15h6v3H9zM7 19h10v2H7z" }),
|
|
701
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "none", stroke: "currentColor", strokeWidth: "1.4", d: "M7 5H4v2a3 3 0 0 0 3 3M17 5h3v2a3 3 0 0 1-3 3" })
|
|
702
|
+
] });
|
|
703
|
+
}
|
|
704
|
+
function ChartLineIcon() {
|
|
705
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", d: "M4 17l5-6 4 3 7-9" }) });
|
|
706
|
+
}
|
|
707
|
+
function ChartBarIcon() {
|
|
708
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M5 12h3v8H5zM10.5 7h3v13h-3zM16 15h3v5h-3z" }) });
|
|
709
|
+
}
|
|
710
|
+
function ChatIcon() {
|
|
711
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "none", stroke: "currentColor", strokeWidth: "1.6", d: "M4 5h16v11H9l-4 4z" }) });
|
|
712
|
+
}
|
|
713
|
+
function FireIcon() {
|
|
714
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M12 2c1 3-2 4-2 7a3 3 0 0 0 6 0c1 1 2 2.5 2 4.5A6 6 0 0 1 6 14c0-4 3-5 3-9 1 1 1.5 2 1.5 3C11.5 6 11 4 12 2z" }) });
|
|
715
|
+
}
|
|
716
|
+
function SparkleIcon() {
|
|
717
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "currentColor", d: "M12 2l1.6 6.4L20 10l-6.4 1.6L12 18l-1.6-6.4L4 10l6.4-1.6z" }) });
|
|
718
|
+
}
|
|
719
|
+
function ChevronIcon() {
|
|
720
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", d: "M9 5l7 7-7 7" }) });
|
|
721
|
+
}
|
|
722
|
+
var DASHBOARD_ICONS = {
|
|
723
|
+
football: FootballIcon,
|
|
724
|
+
"goal-net": GoalNetIcon,
|
|
725
|
+
btts: BttsIcon,
|
|
726
|
+
card: CardIcon,
|
|
727
|
+
"corner-flag": CornerFlagIcon,
|
|
728
|
+
player: PlayerIcon,
|
|
729
|
+
target: TargetIcon,
|
|
730
|
+
trophy: TrophyIcon,
|
|
731
|
+
"chart-line": ChartLineIcon,
|
|
732
|
+
"chart-bar": ChartBarIcon,
|
|
733
|
+
chat: ChatIcon,
|
|
734
|
+
fire: FireIcon,
|
|
735
|
+
sparkle: SparkleIcon
|
|
736
|
+
};
|
|
737
|
+
function DashboardIcon({ name }) {
|
|
738
|
+
const Glyph = DASHBOARD_ICONS[name] ?? TargetIcon;
|
|
739
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Glyph, {});
|
|
740
|
+
}
|
|
633
741
|
|
|
634
742
|
// src/components/GreetingBubble.tsx
|
|
635
743
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
@@ -788,7 +896,7 @@ var import_react5 = require("react");
|
|
|
788
896
|
|
|
789
897
|
// src/components/AddToSlipButton.tsx
|
|
790
898
|
var import_react4 = require("react");
|
|
791
|
-
var
|
|
899
|
+
var import_widget_sdk4 = require("@sensiblestats/widget-sdk");
|
|
792
900
|
|
|
793
901
|
// src/slip.ts
|
|
794
902
|
var ADD_TO_SLIP_TIMEOUT_MS = 1e4;
|
|
@@ -851,7 +959,7 @@ function SlipButton({ selection, shownOdds, marketName, slip, ui }) {
|
|
|
851
959
|
setPending(true);
|
|
852
960
|
try {
|
|
853
961
|
const raw = await withTimeout(Promise.resolve(handler(selection)));
|
|
854
|
-
if (raw != null && !(0,
|
|
962
|
+
if (raw != null && !(0, import_widget_sdk4.isAddToSlipResult)(raw)) throw new Error("malformed add-to-slip result");
|
|
855
963
|
const result = raw ?? void 0;
|
|
856
964
|
setToast(resultToToast(result, shownOdds || selection.oddsDecimal.toFixed(2), ui));
|
|
857
965
|
setSettled(isTerminal(result));
|
|
@@ -1101,27 +1209,75 @@ function ActionButtons({ actions, loading, disabled, onAct }) {
|
|
|
1101
1209
|
] });
|
|
1102
1210
|
}
|
|
1103
1211
|
|
|
1104
|
-
// src/components/
|
|
1212
|
+
// src/components/dashboard/DashboardTile.tsx
|
|
1105
1213
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1106
|
-
|
|
1214
|
+
var MAX_CRESTS = 3;
|
|
1215
|
+
function DashboardTile({ tile, onTap }) {
|
|
1216
|
+
const spanClass = tile.span === "full" ? "ss-w-dash-tile--full" : "ss-w-dash-tile--half";
|
|
1217
|
+
const crests = (tile.imageUrls ?? []).slice(0, MAX_CRESTS);
|
|
1218
|
+
const hasMeta = Boolean(tile.badgeText) || crests.length > 0;
|
|
1219
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("button", { type: "button", className: `ss-w-dash-tile ${spanClass}`, onClick: onTap, children: [
|
|
1220
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ss-w-dash-tile-icon", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(DashboardIcon, { name: tile.icon }) }),
|
|
1221
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "ss-w-dash-tile-body", children: [
|
|
1222
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ss-w-dash-tile-title", children: tile.title }),
|
|
1223
|
+
tile.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ss-w-dash-tile-subtitle", children: tile.subtitle }) : null,
|
|
1224
|
+
hasMeta ? (
|
|
1225
|
+
// Badge + crests sit on their own row, below the title, instead of sharing the
|
|
1226
|
+
// icon/chevron row -- at the half-span tile's ~180px width a nowrap badge alone
|
|
1227
|
+
// (~70px) left the title column at ~22px and forced character-by-character wrapping.
|
|
1228
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "ss-w-dash-tile-meta", children: [
|
|
1229
|
+
tile.badgeText ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ss-w-dash-tile-badge", children: tile.badgeText }) : null,
|
|
1230
|
+
crests.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ss-w-dash-tile-crests", children: crests.map((url, i) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ss-w-dash-tile-crest", style: { backgroundImage: `url(${url})` }, "aria-hidden": "true" }, `${url}-${i}`)) }) : null
|
|
1231
|
+
] })
|
|
1232
|
+
) : null
|
|
1233
|
+
] }),
|
|
1234
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "ss-w-dash-tile-chevron", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ChevronIcon, {}) })
|
|
1235
|
+
] });
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
// src/components/dashboard/GreetingDashboard.tsx
|
|
1239
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
1240
|
+
var LAYOUT_CLASSES = {
|
|
1241
|
+
market: "ss-w-dash--market",
|
|
1242
|
+
goal: "ss-w-dash--goal",
|
|
1243
|
+
discovery: "ss-w-dash--discovery"
|
|
1244
|
+
};
|
|
1245
|
+
function layoutClass(layout) {
|
|
1246
|
+
return Object.prototype.hasOwnProperty.call(LAYOUT_CLASSES, layout) ? LAYOUT_CLASSES[layout] : LAYOUT_CLASSES.goal;
|
|
1247
|
+
}
|
|
1248
|
+
function GreetingDashboard({ dashboard, onTap }) {
|
|
1249
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: `ss-w-dash ${layoutClass(dashboard.layout)}`, children: [
|
|
1250
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "ss-w-dash-head", children: [
|
|
1251
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "ss-w-dash-title", children: dashboard.title }),
|
|
1252
|
+
dashboard.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "ss-w-dash-subtitle", children: dashboard.subtitle }) : null
|
|
1253
|
+
] }),
|
|
1254
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "ss-w-dash-grid", children: dashboard.tiles.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(DashboardTile, { tile: t, onTap: () => onTap(t, i) }, `${t.title}-${i}`)) }),
|
|
1255
|
+
dashboard.footerTip ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "ss-w-dash-foot", children: dashboard.footerTip }) : null
|
|
1256
|
+
] });
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
// src/components/ChatMessage.tsx
|
|
1260
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1261
|
+
function ChatMessage({ message, disabled, onAct, onTap, ui, slip }) {
|
|
1107
1262
|
if (message.role === "user") {
|
|
1108
|
-
return /* @__PURE__ */ (0,
|
|
1263
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "ss-w-bubble", children: message.text }) });
|
|
1109
1264
|
}
|
|
1110
|
-
return /* @__PURE__ */ (0,
|
|
1111
|
-
message.text ? /* @__PURE__ */ (0,
|
|
1112
|
-
/* @__PURE__ */ (0,
|
|
1113
|
-
/* @__PURE__ */ (0,
|
|
1114
|
-
/* @__PURE__ */ (0,
|
|
1115
|
-
/* @__PURE__ */ (0,
|
|
1116
|
-
/* @__PURE__ */ (0,
|
|
1265
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "ss-w-msg ss-w-bot", children: [
|
|
1266
|
+
message.text ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Markdown, { text: message.text }) : null,
|
|
1267
|
+
message.greetingDashboard ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(GreetingDashboard, { dashboard: message.greetingDashboard, onTap }) : null,
|
|
1268
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
|
|
1269
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(BettingInsightList, { insights: message.insights, ui, slip }),
|
|
1270
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(MarketOddsCardList, { cards: message.marketOdds, ui, slip }),
|
|
1271
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(OddsMovementCardList, { cards: message.oddsMovement, ui }),
|
|
1272
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
|
|
1117
1273
|
] });
|
|
1118
1274
|
}
|
|
1119
1275
|
|
|
1120
1276
|
// src/components/IntentChips.tsx
|
|
1121
|
-
var
|
|
1277
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1122
1278
|
function IntentChips({ chips, onPick }) {
|
|
1123
1279
|
if (chips.length === 0) return null;
|
|
1124
|
-
return /* @__PURE__ */ (0,
|
|
1280
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
|
|
1125
1281
|
}
|
|
1126
1282
|
|
|
1127
1283
|
// src/scroll.ts
|
|
@@ -1131,8 +1287,8 @@ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
|
|
|
1131
1287
|
}
|
|
1132
1288
|
|
|
1133
1289
|
// src/components/MessageList.tsx
|
|
1134
|
-
var
|
|
1135
|
-
function MessageList({ controller, ui, slip }) {
|
|
1290
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1291
|
+
function MessageList({ controller, ui, slip, onDashboardTap }) {
|
|
1136
1292
|
const { state, send } = controller;
|
|
1137
1293
|
const listRef = (0, import_react7.useRef)(null);
|
|
1138
1294
|
const endRef = (0, import_react7.useRef)(null);
|
|
@@ -1140,8 +1296,9 @@ function MessageList({ controller, ui, slip }) {
|
|
|
1140
1296
|
(0, import_react7.useEffect)(() => {
|
|
1141
1297
|
if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
|
|
1142
1298
|
}, [state.messages, state.status]);
|
|
1143
|
-
const
|
|
1144
|
-
|
|
1299
|
+
const hasUserMessage = state.messages.some((m) => m.role === "user");
|
|
1300
|
+
const hasDashboard = state.messages.some((m) => m.role === "assistant" && m.greetingDashboard);
|
|
1301
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
|
|
1145
1302
|
"div",
|
|
1146
1303
|
{
|
|
1147
1304
|
className: "ss-w-list",
|
|
@@ -1151,26 +1308,30 @@ function MessageList({ controller, ui, slip }) {
|
|
|
1151
1308
|
if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
|
|
1152
1309
|
},
|
|
1153
1310
|
children: [
|
|
1154
|
-
state.messages.map((m) => /* @__PURE__ */ (0,
|
|
1311
|
+
state.messages.map((m) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1155
1312
|
ChatMessage,
|
|
1156
1313
|
{
|
|
1157
1314
|
message: m,
|
|
1158
1315
|
disabled: state.isStreaming,
|
|
1159
1316
|
onAct: (a) => send(a.message, { actionId: a.actionId, displayText: a.label }),
|
|
1317
|
+
onTap: (tile, index) => {
|
|
1318
|
+
send(tile.actionValue, { displayText: tile.title });
|
|
1319
|
+
if (m.role === "assistant" && m.greetingDashboard) onDashboardTap?.(tile, index, m.greetingDashboard.layout);
|
|
1320
|
+
},
|
|
1160
1321
|
ui,
|
|
1161
1322
|
slip
|
|
1162
1323
|
},
|
|
1163
1324
|
m.id
|
|
1164
1325
|
)),
|
|
1165
|
-
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0,
|
|
1326
|
+
state.isStreaming && state.actionsLoading ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(ActionButtons, { actions: [], loading: true, onAct: () => {
|
|
1166
1327
|
} }) : null,
|
|
1167
|
-
state.status ? /* @__PURE__ */ (0,
|
|
1168
|
-
state.error ? /* @__PURE__ */ (0,
|
|
1169
|
-
/* @__PURE__ */ (0,
|
|
1170
|
-
/* @__PURE__ */ (0,
|
|
1328
|
+
state.status ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "ss-w-status", children: state.status }) : null,
|
|
1329
|
+
state.error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "ss-w-error", role: "alert", children: [
|
|
1330
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
|
|
1331
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
|
|
1171
1332
|
] }) : null,
|
|
1172
|
-
|
|
1173
|
-
/* @__PURE__ */ (0,
|
|
1333
|
+
!hasUserMessage && !hasDashboard ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
|
|
1334
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: endRef })
|
|
1174
1335
|
]
|
|
1175
1336
|
}
|
|
1176
1337
|
);
|
|
@@ -1178,7 +1339,7 @@ function MessageList({ controller, ui, slip }) {
|
|
|
1178
1339
|
|
|
1179
1340
|
// src/components/ChatInput.tsx
|
|
1180
1341
|
var import_react8 = require("react");
|
|
1181
|
-
var
|
|
1342
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1182
1343
|
function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop response", sendLabel = "Send message" }) {
|
|
1183
1344
|
const [value, setValue] = (0, import_react8.useState)("");
|
|
1184
1345
|
const submit = () => {
|
|
@@ -1193,14 +1354,14 @@ function ChatInput({ placeholder, streaming, onSend, onStop, stopLabel = "Stop r
|
|
|
1193
1354
|
submit();
|
|
1194
1355
|
}
|
|
1195
1356
|
};
|
|
1196
|
-
return /* @__PURE__ */ (0,
|
|
1197
|
-
/* @__PURE__ */ (0,
|
|
1198
|
-
streaming ? /* @__PURE__ */ (0,
|
|
1357
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "ss-w-input", children: [
|
|
1358
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
|
|
1359
|
+
streaming ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": stopLabel, onClick: onStop, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("button", { type: "button", className: "ss-w-send", "aria-label": sendLabel, onClick: submit, children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SendIcon, {}) })
|
|
1199
1360
|
] });
|
|
1200
1361
|
}
|
|
1201
1362
|
|
|
1202
1363
|
// src/components/ChatPanel.tsx
|
|
1203
|
-
var
|
|
1364
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1204
1365
|
var FULLSCREEN_QUERY = "(max-width: 639px)";
|
|
1205
1366
|
function useIsModal() {
|
|
1206
1367
|
const [modal, setModal] = (0, import_react9.useState)(false);
|
|
@@ -1214,7 +1375,7 @@ function useIsModal() {
|
|
|
1214
1375
|
}, []);
|
|
1215
1376
|
return modal;
|
|
1216
1377
|
}
|
|
1217
|
-
function ChatPanel({ controller, ui, slip }) {
|
|
1378
|
+
function ChatPanel({ controller, ui, slip, onDashboardTap }) {
|
|
1218
1379
|
const { state, close, stop, send } = controller;
|
|
1219
1380
|
const isModal = useIsModal();
|
|
1220
1381
|
(0, import_react9.useEffect)(() => {
|
|
@@ -1224,10 +1385,10 @@ function ChatPanel({ controller, ui, slip }) {
|
|
|
1224
1385
|
window.addEventListener("keydown", onKey);
|
|
1225
1386
|
return () => window.removeEventListener("keydown", onKey);
|
|
1226
1387
|
}, [close]);
|
|
1227
|
-
return /* @__PURE__ */ (0,
|
|
1228
|
-
/* @__PURE__ */ (0,
|
|
1229
|
-
/* @__PURE__ */ (0,
|
|
1230
|
-
/* @__PURE__ */ (0,
|
|
1388
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": ui.chatDialogLabel, children: [
|
|
1389
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ChatHeader, { onClose: close, closeLabel: ui.closeChat }),
|
|
1390
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(MessageList, { controller, ui, slip, onDashboardTap }),
|
|
1391
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop, stopLabel: ui.stopResponse, sendLabel: ui.sendMessage })
|
|
1231
1392
|
] });
|
|
1232
1393
|
}
|
|
1233
1394
|
|
|
@@ -1484,17 +1645,72 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
|
|
|
1484
1645
|
@media (prefers-reduced-motion: reduce) {
|
|
1485
1646
|
.ss-w-abtn-shimmer { animation: none; }
|
|
1486
1647
|
}
|
|
1648
|
+
|
|
1649
|
+
/* greeting dashboard */
|
|
1650
|
+
.ss-w-dash { display: flex; flex-direction: column; gap: 12px; padding: 4px 0; }
|
|
1651
|
+
.ss-w-dash-head { display: flex; flex-direction: column; }
|
|
1652
|
+
.ss-w-dash-title { font-size: 18px; font-weight: 700; line-height: 1.25; color: var(--ss-w-ink); }
|
|
1653
|
+
.ss-w-dash-subtitle { font-size: 13px; color: var(--ss-w-faint); margin-top: 4px; }
|
|
1654
|
+
.ss-w-dash-foot { font-size: 11px; color: var(--ss-w-faint); margin-top: 2px; }
|
|
1655
|
+
|
|
1656
|
+
.ss-w-dash-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
|
|
1657
|
+
.ss-w-dash-tile--half { grid-column: span 1; }
|
|
1658
|
+
.ss-w-dash-tile--full { grid-column: 1 / -1; }
|
|
1659
|
+
|
|
1660
|
+
.ss-w-dash-tile {
|
|
1661
|
+
display: flex; align-items: center; gap: 10px;
|
|
1662
|
+
padding: 12px; border-radius: 14px; border: 1px solid var(--ss-w-line);
|
|
1663
|
+
background: var(--ss-w-panel); text-align: left; cursor: pointer; width: 100%;
|
|
1664
|
+
color: inherit; font: inherit;
|
|
1665
|
+
min-width: 0; /* the panel is min(400px, 100vw - 40px): without this,
|
|
1666
|
+
a long Turkish title blows the column out */
|
|
1667
|
+
}
|
|
1668
|
+
.ss-w-dash-tile-icon { flex: none; display: grid; place-items: center; width: 20px; height: 20px; color: var(--ss-w-accent); font-size: 18px; }
|
|
1669
|
+
.ss-w-dash-tile-body { min-width: 0; display: flex; flex-direction: column; gap: 2px; flex: 1; }
|
|
1670
|
+
.ss-w-dash-tile-title { min-width: 0; font-weight: 600; font-size: 14px; overflow-wrap: anywhere; }
|
|
1671
|
+
.ss-w-dash-tile-subtitle { min-width: 0; font-size: 11.5px; color: var(--ss-w-faint); overflow-wrap: anywhere; }
|
|
1672
|
+
.ss-w-dash-tile-chevron { flex: none; margin-left: auto; width: 14px; height: 14px; color: var(--ss-w-faint); }
|
|
1673
|
+
|
|
1674
|
+
/* Badge + crests share their own row, below the title -- NOT the icon/chevron row. A
|
|
1675
|
+
nowrap badge (~70px) sitting beside icon+chevron in a ~180px half-span tile left the
|
|
1676
|
+
title column at ~22px wide, wrapping every title character-by-character. Here the row
|
|
1677
|
+
gets the body's full width, so a badge this size is comfortably inside it. */
|
|
1678
|
+
.ss-w-dash-tile-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
|
|
1679
|
+
.ss-w-dash-tile-badge { flex: none; font-size: 10.5px; font-weight: 700; color: var(--ss-w-accent); background: color-mix(in srgb, var(--ss-w-accent) 13%, var(--ss-w-panel)); border-radius: 999px; padding: 3px 9px; white-space: nowrap; }
|
|
1680
|
+
|
|
1681
|
+
.ss-w-dash-tile-crests { flex: none; display: flex; align-items: center; }
|
|
1682
|
+
.ss-w-dash-tile-crest {
|
|
1683
|
+
width: 18px; height: 18px; border-radius: 50%;
|
|
1684
|
+
background-size: cover; background-position: center; background-color: var(--ss-w-bubble);
|
|
1685
|
+
margin-left: -6px; border: 2px solid var(--ss-w-panel); /* overlapping stack, capped at 3 in DashboardTile */
|
|
1686
|
+
}
|
|
1687
|
+
.ss-w-dash-tile-crest:first-child { margin-left: 0; }
|
|
1688
|
+
|
|
1689
|
+
/* Market: default 2-column grid, tiles get the same accent rail as the other card
|
|
1690
|
+
families (.ss-w-binsight, .ss-w-mocard, .ss-w-omcard) so a market tile reads as
|
|
1691
|
+
part of that visual language rather than a plain unstyled button. */
|
|
1692
|
+
.ss-w-dash--market .ss-w-dash-tile { border-left: 3px solid var(--ss-w-accent); }
|
|
1693
|
+
|
|
1694
|
+
/* Goal: one column of pill rows. */
|
|
1695
|
+
.ss-w-dash--goal .ss-w-dash-grid { grid-template-columns: 1fr; }
|
|
1696
|
+
.ss-w-dash--goal .ss-w-dash-tile { border-radius: 999px; padding: 10px 14px; }
|
|
1697
|
+
|
|
1698
|
+
/* Discovery: tinted bento cards. */
|
|
1699
|
+
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(1) { background: #eef8f1; }
|
|
1700
|
+
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(2) { background: #eef3fb; }
|
|
1701
|
+
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(3) { background: #fdf6ec; }
|
|
1702
|
+
.ss-w-dash--discovery .ss-w-dash-tile:nth-child(4) { background: #f5eefb; }
|
|
1487
1703
|
`;
|
|
1488
1704
|
|
|
1489
1705
|
// src/StatsWidget.tsx
|
|
1490
|
-
var
|
|
1706
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1491
1707
|
function buildClient(props) {
|
|
1492
1708
|
try {
|
|
1493
1709
|
if ("client" in props && props.client) {
|
|
1494
1710
|
return { client: props.client, conversation: props.client.conversation() };
|
|
1495
1711
|
}
|
|
1496
1712
|
const cfg = props.config;
|
|
1497
|
-
const client = new
|
|
1713
|
+
const client = new import_widget_sdk5.StatsWidgetClient(toClientOptions(cfg));
|
|
1498
1714
|
return { client, conversation: client.conversation(toUserContext(cfg)) };
|
|
1499
1715
|
} catch (err) {
|
|
1500
1716
|
console.error("[StatsWidget] failed to initialize:", err instanceof Error ? err.message : err);
|
|
@@ -1529,6 +1745,10 @@ function StatsWidget(props) {
|
|
|
1529
1745
|
}),
|
|
1530
1746
|
[props.config?.onAddToSlip, controller.state.conversationId, built]
|
|
1531
1747
|
);
|
|
1748
|
+
const onDashboardTap = (0, import_react10.useMemo)(
|
|
1749
|
+
() => built ? createDashboardTapSignal((input) => built.conversation.sendSignal(input)) : void 0,
|
|
1750
|
+
[built]
|
|
1751
|
+
);
|
|
1532
1752
|
(0, import_react10.useEffect)(() => {
|
|
1533
1753
|
if (!built || !ui.injectStyles) return;
|
|
1534
1754
|
const node = rootRef.current?.getRootNode();
|
|
@@ -1536,7 +1756,7 @@ function StatsWidget(props) {
|
|
|
1536
1756
|
}, [built, ui.injectStyles]);
|
|
1537
1757
|
if (!built) return null;
|
|
1538
1758
|
const attrs = themeAttrs(ui);
|
|
1539
|
-
return /* @__PURE__ */ (0,
|
|
1759
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { ref: rootRef, className: "ss-w-root", "data-ss-w-theme": attrs["data-ss-w-theme"], "data-ss-w-pos": attrs["data-ss-w-pos"], style: attrs.style, children: controller.state.isOpen ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(ChatPanel, { controller, ui, slip, onDashboardTap }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1540
1760
|
ChatFab,
|
|
1541
1761
|
{
|
|
1542
1762
|
label: ui.launcherLabel,
|