@sensiblestats/widget-react 0.0.0 → 0.1.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
@@ -51,38 +51,33 @@ function str(v) {
51
51
  function num(v) {
52
52
  return typeof v === "number" && Number.isFinite(v) ? String(v) : void 0;
53
53
  }
54
- var STAT_KEYS = [
55
- ["goals", "Goals"],
56
- ["assists", "Assists"],
57
- ["appearances", "Apps"],
58
- ["xg", "xG"],
59
- ["form", "Form"],
60
- ["rating", "Rating"]
61
- ];
62
54
  function readStats(card) {
55
+ if (!Array.isArray(card.stats)) return [];
63
56
  const cells = [];
64
- for (const [key, label] of STAT_KEYS) {
65
- const value = num(card[key]) ?? str(card[key]);
66
- if (value !== void 0) cells.push({ label, value });
57
+ for (const entry of card.stats) {
58
+ const label = str(entry.label);
59
+ const value = num(entry.value) ?? str(entry.value);
60
+ if (label !== void 0 && value !== void 0) cells.push({ label, value });
67
61
  }
68
62
  return cells;
69
63
  }
70
64
  function toCardVM(card) {
71
- const kind = card.entityType === "Player" ? "player" : card.entityType === "Team" ? "team" : "generic";
65
+ const entityType = str(card.entityType)?.toLowerCase();
66
+ const kind = entityType === "player" ? "player" : entityType === "team" ? "team" : "generic";
72
67
  return {
73
68
  id: String(card.entityId),
74
69
  kind,
75
70
  name: card.canonicalName,
76
- image: str(card.imageUrl) ?? str(card.image_url) ?? str(card.logoUrl) ?? str(card.logo),
71
+ image: str(card.imagePath) ?? str(card.imageUrl) ?? str(card.image_url) ?? str(card.logoUrl) ?? str(card.logo),
77
72
  sub: str(card.subtitle) ?? str(card.teamName) ?? str(card.leagueName) ?? str(card.position),
78
73
  stats: readStats(card),
79
74
  query: card.canonicalName
80
75
  };
81
76
  }
82
77
  function toActionVM(action) {
83
- const label = str(action.label) ?? str(action.text) ?? str(action.title) ?? "Continue";
84
- const actionId = str(action.id) ?? str(action.actionId);
85
- const message = str(action.message) ?? str(action.query) ?? label;
78
+ const label = str(action.actionLabel) ?? str(action.label) ?? str(action.text) ?? str(action.title) ?? "Continue";
79
+ const actionId = str(action.actionId) ?? str(action.id);
80
+ const message = str(action.actionValue) ?? str(action.message) ?? str(action.query) ?? label;
86
81
  return { label, actionId, message };
87
82
  }
88
83
  function toChipVM(intent) {
@@ -95,7 +90,7 @@ function chipFromText(text) {
95
90
 
96
91
  // src/reducer.ts
97
92
  function initialState(starters) {
98
- return { isOpen: false, messages: [], isStreaming: false, status: null, actionsLoading: false, starters, error: null, lastUserInput: null };
93
+ return { isOpen: false, messages: [], isStreaming: false, status: null, actionsLoading: false, starters, error: null, lastUserInput: null, lastDisplayText: null };
99
94
  }
100
95
  function mapActive(state, fn) {
101
96
  const last = state.messages[state.messages.length - 1];
@@ -107,8 +102,9 @@ function applyEvent(state, event) {
107
102
  case "progress":
108
103
  return { ...state, status: event.data.message };
109
104
  case "answer":
110
- return { ...state, messages: mapActive(state, (t) => ({ ...t, text: t.text + event.data.text })) };
105
+ return { ...state, messages: mapActive(state, (t) => ({ ...t, text: event.data.text })) };
111
106
  case "entity_card":
107
+ if (event.data.card.isUsed === false) return state;
112
108
  return { ...state, messages: mapActive(state, (t) => ({ ...t, cards: [...t.cards, toCardVM(event.data.card)] })) };
113
109
  case "action_button":
114
110
  return { ...state, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, actions: [...t.actions, toActionVM(event.data.action)] })) };
@@ -131,9 +127,9 @@ function reducer(state, action) {
131
127
  case "CLOSE":
132
128
  return { ...state, isOpen: false, isStreaming: false, status: null, actionsLoading: false };
133
129
  case "USER_SENT": {
134
- const user = { role: "user", id: action.userId, text: action.input.message };
130
+ const user = { role: "user", id: action.userId, text: action.displayText ?? action.input.message };
135
131
  const assistant = { role: "assistant", id: action.assistantId, text: "", cards: [], actions: [], done: false };
136
- return { ...state, messages: [...state.messages, user, assistant], isStreaming: true, status: null, actionsLoading: false, starters: [], error: null, lastUserInput: action.input };
132
+ return { ...state, messages: [...state.messages, user, assistant], isStreaming: true, status: null, actionsLoading: false, starters: [], error: null, lastUserInput: action.input, lastDisplayText: action.displayText ?? null };
137
133
  }
138
134
  case "EVENT":
139
135
  return applyEvent(state, action.event);
@@ -153,9 +149,9 @@ function useChatStream(conversation, starterTexts) {
153
149
  const stateRef = useRef(state);
154
150
  stateRef.current = state;
155
151
  const nextId = () => `m${idRef.current++}`;
156
- const run = useCallback((input) => {
152
+ const run = useCallback((input, displayText) => {
157
153
  handleRef.current?.cancel();
158
- dispatch({ kind: "USER_SENT", input, userId: nextId(), assistantId: nextId() });
154
+ dispatch({ kind: "USER_SENT", input, userId: nextId(), assistantId: nextId(), displayText });
159
155
  const handle = conversation.send(input);
160
156
  handleRef.current = handle;
161
157
  (async () => {
@@ -170,14 +166,14 @@ function useChatStream(conversation, starterTexts) {
170
166
  }
171
167
  })();
172
168
  }, [conversation]);
173
- const send = useCallback((text, actionId) => {
169
+ const send = useCallback((text, opts) => {
174
170
  const trimmed = text.trim();
175
171
  if (!trimmed) return;
176
- run({ message: trimmed, actionId });
172
+ run({ message: trimmed, actionId: opts?.actionId }, opts?.displayText);
177
173
  }, [run]);
178
174
  const retry = useCallback(() => {
179
175
  const last = stateRef.current.lastUserInput;
180
- if (last) run(last);
176
+ if (last) run(last, stateRef.current.lastDisplayText ?? void 0);
181
177
  }, [run]);
182
178
  const open = useCallback(() => dispatch({ kind: "OPEN" }), []);
183
179
  const close = useCallback(() => {
@@ -198,9 +194,15 @@ function useChatStream(conversation, starterTexts) {
198
194
  }
199
195
 
200
196
  // src/components/Icons.tsx
201
- import { jsx } from "react/jsx-runtime";
197
+ import { jsx, jsxs } from "react/jsx-runtime";
202
198
  function SsMark({ className }) {
203
- return /* @__PURE__ */ jsx("svg", { className, viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fill: "#00F400", d: "M12 2l2.5 6.5L21 11l-6.5 2.5L12 20l-2.5-6.5L3 11l6.5-2.5z" }) });
199
+ return /* @__PURE__ */ jsx("svg", { className, viewBox: "0 0 100 100", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ jsxs("g", { fill: "none", stroke: "#15c917", strokeWidth: "8.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
200
+ /* @__PURE__ */ jsx("circle", { cx: "50", cy: "44", r: "37" }),
201
+ /* @__PURE__ */ jsx("line", { x1: "15", y1: "44", x2: "85", y2: "44" }),
202
+ /* @__PURE__ */ jsx("path", { d: "M36 44 V24 H64 V44" }),
203
+ /* @__PURE__ */ jsx("path", { d: "M36 44 V63 H64 V44" }),
204
+ /* @__PURE__ */ jsx("path", { d: "M46 63 L43 96 L61 61" })
205
+ ] }) });
204
206
  }
205
207
  function SendIcon() {
206
208
  return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fill: "currentColor", d: "M3 20l18-8L3 4v6l12 2-12 2z" }) });
@@ -219,21 +221,21 @@ function GreetingBubble({ text }) {
219
221
  }
220
222
 
221
223
  // src/components/ChatFab.tsx
222
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
224
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
223
225
  function ChatFab({ label, open, greeting, onToggle }) {
224
- return /* @__PURE__ */ jsxs("div", { className: "ss-w-fab-wrap", children: [
226
+ return /* @__PURE__ */ jsxs2("div", { className: "ss-w-fab-wrap", children: [
225
227
  !open && greeting ? /* @__PURE__ */ jsx3(GreetingBubble, { text: greeting }) : null,
226
228
  /* @__PURE__ */ jsx3("button", { type: "button", className: "ss-w-fab", "aria-label": label, "aria-expanded": open, onClick: onToggle, children: /* @__PURE__ */ jsx3(SsMark, { className: "ss-w-fab-mark" }) })
227
229
  ] });
228
230
  }
229
231
 
230
232
  // src/components/ChatPanel.tsx
231
- import { useEffect as useEffect2 } from "react";
233
+ import { useEffect as useEffect2, useState as useState3 } from "react";
232
234
 
233
235
  // src/components/ChatHeader.tsx
234
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
236
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
235
237
  function ChatHeader({ onClose }) {
236
- return /* @__PURE__ */ jsxs2("header", { className: "ss-w-header", children: [
238
+ return /* @__PURE__ */ jsxs3("header", { className: "ss-w-header", children: [
237
239
  /* @__PURE__ */ jsx4(SsMark, { className: "ss-w-header-mark" }),
238
240
  /* @__PURE__ */ jsx4("span", { className: "ss-w-header-title", children: "SensibleStats" }),
239
241
  /* @__PURE__ */ jsx4("button", { type: "button", className: "ss-w-close", "aria-label": "Close chat", onClick: onClose, children: /* @__PURE__ */ jsx4(CloseIcon, {}) })
@@ -259,40 +261,49 @@ function Markdown({ text }) {
259
261
  }
260
262
 
261
263
  // src/components/EntityCardList.tsx
262
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
264
+ import { useState } from "react";
265
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
266
+ var MAX_VISIBLE = 3;
263
267
  function initials(name) {
264
268
  return name.split(/\s+/).slice(0, 2).map((w) => w[0] ?? "").join("").toUpperCase();
265
269
  }
266
- function EntityCardList({ cards, onSelect }) {
270
+ function EntityCardList({ cards, disabled, onSelect }) {
271
+ const [expanded, setExpanded] = useState(false);
267
272
  if (cards.length === 0) return null;
268
- return /* @__PURE__ */ jsx6("div", { className: "ss-w-cards", children: cards.map((c) => /* @__PURE__ */ jsxs3("button", { type: "button", className: "ss-w-ecard", onClick: () => onSelect(c.query), children: [
269
- /* @__PURE__ */ jsx6(
270
- "span",
271
- {
272
- className: c.kind === "team" ? "ss-w-thumb ss-w-team" : "ss-w-thumb",
273
- style: c.image ? { backgroundImage: `url(${c.image})` } : void 0,
274
- children: c.image ? "" : initials(c.name)
275
- }
276
- ),
277
- /* @__PURE__ */ jsxs3("span", { className: "ss-w-meta", children: [
278
- /* @__PURE__ */ jsx6("span", { className: "ss-w-nm", children: c.name }),
279
- c.sub ? /* @__PURE__ */ jsx6("span", { className: "ss-w-sub", children: c.sub }) : null
280
- ] }),
281
- c.stats.map((s) => /* @__PURE__ */ jsxs3("span", { className: "ss-w-stat", children: [
282
- /* @__PURE__ */ jsx6("b", { children: s.value }),
283
- /* @__PURE__ */ jsx6("span", { children: s.label })
284
- ] }, s.label)),
285
- /* @__PURE__ */ jsx6("span", { className: "ss-w-chev", children: /* @__PURE__ */ jsx6(ChevronIcon, {}) })
286
- ] }, c.id)) });
273
+ const visible = expanded ? cards : cards.slice(0, MAX_VISIBLE);
274
+ const overflow = cards.length - MAX_VISIBLE;
275
+ return /* @__PURE__ */ jsxs4("div", { className: "ss-w-cards", children: [
276
+ visible.map((c) => /* @__PURE__ */ jsxs4("button", { type: "button", className: "ss-w-ecard", "data-kind": c.kind, disabled, onClick: () => onSelect(c.query), children: [
277
+ /* @__PURE__ */ jsx6(
278
+ "span",
279
+ {
280
+ className: c.kind === "team" ? "ss-w-thumb ss-w-team" : "ss-w-thumb",
281
+ style: c.image ? { backgroundImage: `url(${c.image})` } : void 0,
282
+ children: c.image ? "" : initials(c.name)
283
+ }
284
+ ),
285
+ /* @__PURE__ */ jsxs4("span", { className: "ss-w-meta", children: [
286
+ c.kind !== "generic" ? /* @__PURE__ */ jsx6("span", { className: "ss-w-kind", children: c.kind }) : null,
287
+ /* @__PURE__ */ jsx6("span", { className: "ss-w-nm", children: c.name }),
288
+ c.sub ? /* @__PURE__ */ jsx6("span", { className: "ss-w-sub", children: c.sub }) : null
289
+ ] }),
290
+ c.stats.map((s) => /* @__PURE__ */ jsxs4("span", { className: "ss-w-stat", children: [
291
+ /* @__PURE__ */ jsx6("b", { children: s.value }),
292
+ /* @__PURE__ */ jsx6("span", { children: s.label })
293
+ ] }, s.label)),
294
+ /* @__PURE__ */ jsx6("span", { className: "ss-w-chev", children: /* @__PURE__ */ jsx6(ChevronIcon, {}) })
295
+ ] }, c.id)),
296
+ overflow > 0 ? /* @__PURE__ */ jsx6("button", { type: "button", className: "ss-w-cards-more", disabled, onClick: () => setExpanded((v) => !v), children: expanded ? "Show less" : `Show ${overflow} more` }) : null
297
+ ] });
287
298
  }
288
299
 
289
300
  // src/components/ActionButtons.tsx
290
- import { Fragment, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
291
- function ActionButtons({ actions, loading, onAct }) {
301
+ import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
302
+ function ActionButtons({ actions, loading, disabled, onAct }) {
292
303
  if (actions.length === 0 && !loading) return null;
293
- return /* @__PURE__ */ jsxs4("div", { className: "ss-w-actions", children: [
294
- actions.map((a, i) => /* @__PURE__ */ jsx7("button", { type: "button", className: "ss-w-abtn", onClick: () => onAct(a.message, a.actionId), children: a.label }, `${a.label}-${i}`)),
295
- actions.length === 0 && loading ? /* @__PURE__ */ jsxs4(Fragment, { children: [
304
+ return /* @__PURE__ */ jsxs5("div", { className: "ss-w-actions", children: [
305
+ actions.map((a, i) => /* @__PURE__ */ jsx7("button", { type: "button", className: "ss-w-abtn", disabled, onClick: () => onAct(a), children: a.label }, `${a.label}-${i}`)),
306
+ actions.length === 0 && loading ? /* @__PURE__ */ jsxs5(Fragment, { children: [
296
307
  /* @__PURE__ */ jsx7("span", { className: "ss-w-abtn-shimmer" }),
297
308
  /* @__PURE__ */ jsx7("span", { className: "ss-w-abtn-shimmer" })
298
309
  ] }) : null
@@ -300,15 +311,15 @@ function ActionButtons({ actions, loading, onAct }) {
300
311
  }
301
312
 
302
313
  // src/components/ChatMessage.tsx
303
- import { jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
304
- function ChatMessage({ message, onAct, onSelect }) {
314
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
315
+ function ChatMessage({ message, disabled, onAct, onSelect }) {
305
316
  if (message.role === "user") {
306
317
  return /* @__PURE__ */ jsx8("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx8("div", { className: "ss-w-bubble", children: message.text }) });
307
318
  }
308
- return /* @__PURE__ */ jsxs5("div", { className: "ss-w-msg ss-w-bot", children: [
319
+ return /* @__PURE__ */ jsxs6("div", { className: "ss-w-msg ss-w-bot", children: [
309
320
  message.text ? /* @__PURE__ */ jsx8(Markdown, { text: message.text }) : null,
310
- /* @__PURE__ */ jsx8(EntityCardList, { cards: message.cards, onSelect }),
311
- /* @__PURE__ */ jsx8(ActionButtons, { actions: message.actions, loading: false, onAct })
321
+ /* @__PURE__ */ jsx8(EntityCardList, { cards: message.cards, disabled, onSelect }),
322
+ /* @__PURE__ */ jsx8(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
312
323
  ] });
313
324
  }
314
325
 
@@ -320,7 +331,7 @@ function IntentChips({ chips, onPick }) {
320
331
  }
321
332
 
322
333
  // src/components/MessageList.tsx
323
- import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
334
+ import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
324
335
  function MessageList({ controller, ui }) {
325
336
  void ui;
326
337
  const { state, send } = controller;
@@ -329,12 +340,21 @@ function MessageList({ controller, ui }) {
329
340
  endRef.current?.scrollIntoView({ block: "end" });
330
341
  }, [state.messages, state.status]);
331
342
  const empty = state.messages.length === 0;
332
- return /* @__PURE__ */ jsxs6("div", { className: "ss-w-list", "aria-live": "polite", children: [
333
- state.messages.map((m) => /* @__PURE__ */ jsx10(ChatMessage, { message: m, onAct: (msg, id) => send(msg, id), onSelect: (q) => send(q) }, m.id)),
343
+ return /* @__PURE__ */ jsxs7("div", { className: "ss-w-list", "aria-live": "polite", children: [
344
+ state.messages.map((m) => /* @__PURE__ */ jsx10(
345
+ ChatMessage,
346
+ {
347
+ message: m,
348
+ disabled: state.isStreaming,
349
+ onAct: (a) => send(a.message, { actionId: a.actionId, displayText: a.label }),
350
+ onSelect: (q) => send(q)
351
+ },
352
+ m.id
353
+ )),
334
354
  state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx10(ActionButtons, { actions: [], loading: true, onAct: () => {
335
355
  } }) : null,
336
356
  state.status ? /* @__PURE__ */ jsx10("div", { className: "ss-w-status", children: state.status }) : null,
337
- state.error ? /* @__PURE__ */ jsxs6("div", { className: "ss-w-error", role: "alert", children: [
357
+ state.error ? /* @__PURE__ */ jsxs7("div", { className: "ss-w-error", role: "alert", children: [
338
358
  /* @__PURE__ */ jsx10("span", { children: "message" in state.error ? state.error.message : "Something went wrong." }),
339
359
  /* @__PURE__ */ jsx10("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: "Retry" })
340
360
  ] }) : null,
@@ -344,10 +364,10 @@ function MessageList({ controller, ui }) {
344
364
  }
345
365
 
346
366
  // src/components/ChatInput.tsx
347
- import { useState } from "react";
348
- import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
367
+ import { useState as useState2 } from "react";
368
+ import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
349
369
  function ChatInput({ placeholder, streaming, onSend, onStop }) {
350
- const [value, setValue] = useState("");
370
+ const [value, setValue] = useState2("");
351
371
  const submit = () => {
352
372
  const t = value.trim();
353
373
  if (!t) return;
@@ -360,16 +380,30 @@ function ChatInput({ placeholder, streaming, onSend, onStop }) {
360
380
  submit();
361
381
  }
362
382
  };
363
- return /* @__PURE__ */ jsxs7("div", { className: "ss-w-input", children: [
364
- /* @__PURE__ */ jsx11("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, onChange: (e) => setValue(e.target.value), onKeyDown }),
383
+ return /* @__PURE__ */ jsxs8("div", { className: "ss-w-input", children: [
384
+ /* @__PURE__ */ jsx11("textarea", { className: "ss-w-textarea", rows: 1, placeholder, value, disabled: streaming, onChange: (e) => setValue(e.target.value), onKeyDown }),
365
385
  streaming ? /* @__PURE__ */ jsx11("button", { type: "button", className: "ss-w-send ss-w-stop", "aria-label": "Stop response", onClick: onStop, children: /* @__PURE__ */ jsx11("span", { className: "ss-w-stop-glyph" }) }) : /* @__PURE__ */ jsx11("button", { type: "button", className: "ss-w-send", "aria-label": "Send message", onClick: submit, children: /* @__PURE__ */ jsx11(SendIcon, {}) })
366
386
  ] });
367
387
  }
368
388
 
369
389
  // src/components/ChatPanel.tsx
370
- import { jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
390
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
391
+ var FULLSCREEN_QUERY = "(max-width: 639px)";
392
+ function useIsModal() {
393
+ const [modal, setModal] = useState3(false);
394
+ useEffect2(() => {
395
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
396
+ const mql = window.matchMedia(FULLSCREEN_QUERY);
397
+ const sync = () => setModal(mql.matches);
398
+ sync();
399
+ mql.addEventListener("change", sync);
400
+ return () => mql.removeEventListener("change", sync);
401
+ }, []);
402
+ return modal;
403
+ }
371
404
  function ChatPanel({ controller, ui }) {
372
405
  const { state, close, stop, send } = controller;
406
+ const isModal = useIsModal();
373
407
  useEffect2(() => {
374
408
  const onKey = (e) => {
375
409
  if (e.key === "Escape") close();
@@ -377,7 +411,7 @@ function ChatPanel({ controller, ui }) {
377
411
  window.addEventListener("keydown", onKey);
378
412
  return () => window.removeEventListener("keydown", onKey);
379
413
  }, [close]);
380
- return /* @__PURE__ */ jsxs8("div", { className: "ss-w-panel", role: "dialog", "aria-modal": "true", "aria-label": "SensibleStats chat", children: [
414
+ return /* @__PURE__ */ jsxs9("div", { className: "ss-w-panel", role: "dialog", "aria-modal": isModal, "aria-label": "SensibleStats chat", children: [
381
415
  /* @__PURE__ */ jsx12(ChatHeader, { onClose: close }),
382
416
  /* @__PURE__ */ jsx12(MessageList, { controller, ui }),
383
417
  /* @__PURE__ */ jsx12(ChatInput, { placeholder: ui.placeholder, streaming: state.isStreaming, onSend: (t) => send(t), onStop: stop })
@@ -385,10 +419,10 @@ function ChatPanel({ controller, ui }) {
385
419
  }
386
420
 
387
421
  // src/styles/css.generated.ts
388
- var WIDGET_CSS = '.ss-w-root { all: revert; }\n.ss-w-root, .ss-w-root * { box-sizing: border-box; margin: 0; padding: 0; }\n.ss-w-root {\n --ss-w-accent: #17936a;\n --ss-w-ink: #14181d; --ss-w-faint: #6b7580; --ss-w-line: #e4e8ec;\n --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;\n --ss-w-radius: 16px; --ss-w-offset-x: 20px; --ss-w-offset-y: 20px;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;\n font-size: 13px; line-height: 1.5; color: var(--ss-w-ink);\n}\n@media (prefers-color-scheme: dark) {\n .ss-w-root[data-ss-w-theme="auto"] {\n --ss-w-accent: #37c891; --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;\n --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;\n }\n}\n.ss-w-root[data-ss-w-theme="dark"] {\n --ss-w-accent: #37c891; --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;\n --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;\n}\n.ss-w-root[data-ss-w-theme="light"] {\n --ss-w-accent: #17936a; --ss-w-ink: #14181d; --ss-w-faint: #6b7580;\n --ss-w-line: #e4e8ec; --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;\n}\n\n/* launcher */\n.ss-w-fab-wrap { position: fixed; bottom: var(--ss-w-offset-y); z-index: 2147483000; display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }\n.ss-w-root[data-ss-w-pos="right"] .ss-w-fab-wrap { right: var(--ss-w-offset-x); }\n.ss-w-root[data-ss-w-pos="left"] .ss-w-fab-wrap { left: var(--ss-w-offset-x); align-items: flex-start; }\n.ss-w-fab { width: 56px; height: 56px; border: none; border-radius: 50%; background: var(--ss-w-nav); color: #fff; cursor: pointer; display: grid; place-items: center; box-shadow: 0 6px 20px rgba(0,0,0,.28); }\n.ss-w-fab-mark { font-size: 30px; }\n.ss-w-greeting { max-width: 220px; background: var(--ss-w-panel); color: var(--ss-w-ink); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 9px 12px; box-shadow: 0 4px 14px rgba(0,0,0,.14); }\n\n/* panel */\n.ss-w-panel { position: fixed; bottom: calc(var(--ss-w-offset-y) + 72px); z-index: 2147483000; display: flex; flex-direction: column; width: min(400px, calc(100vw - 40px)); height: min(620px, calc(100vh - 120px)); background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: var(--ss-w-radius); overflow: hidden; box-shadow: 0 12px 40px rgba(0,0,0,.24); }\n.ss-w-root[data-ss-w-pos="right"] .ss-w-panel { right: var(--ss-w-offset-x); }\n.ss-w-root[data-ss-w-pos="left"] .ss-w-panel { left: var(--ss-w-offset-x); }\n@media (max-width: 639px) {\n .ss-w-panel { inset: 0; width: 100vw; height: 100vh; border: none; border-radius: 0; }\n}\n.ss-w-header { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 1px solid var(--ss-w-line); }\n.ss-w-header-mark { font-size: 18px; }\n.ss-w-header-title { font-weight: 660; }\n.ss-w-close { margin-left: auto; background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 16px; display: grid; place-items: center; }\n\n/* messages */\n.ss-w-list { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 12px; }\n.ss-w-msg { display: flex; }\n.ss-w-user { justify-content: flex-end; }\n.ss-w-user .ss-w-bubble { background: color-mix(in srgb, var(--ss-w-accent) 14%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 30%, var(--ss-w-line)); border-radius: 14px; padding: 8px 12px; max-width: 80%; }\n.ss-w-bot { flex-direction: column; gap: 8px; }\n.ss-w-status { font-size: 11px; color: var(--ss-w-faint); font-style: italic; }\n.ss-w-error { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #c0392b; }\n.ss-w-retry { background: none; border: 1px solid currentColor; border-radius: 10px; padding: 2px 8px; cursor: pointer; color: inherit; }\n\n/* markdown */\n.ss-w-md { display: flex; flex-direction: column; gap: 8px; }\n.ss-w-md :where(p, ul, ol, table, pre, blockquote, h1, h2, h3, h4) { margin: 0; }\n.ss-w-md > *:first-child { margin-top: 0; }\n.ss-w-md > *:last-child { margin-bottom: 0; }\n.ss-w-md-ul { list-style: disc outside; padding-left: 18px; }\n.ss-w-md-ol { list-style: decimal outside; padding-left: 18px; }\n.ss-w-md-ul li::marker, .ss-w-md-ol li::marker { color: var(--ss-w-accent); }\n.ss-w-md-link { color: var(--ss-w-accent); text-decoration: underline; }\n.ss-w-table-wrap { overflow-x: auto; }\n.ss-w-md-table { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }\n.ss-w-md-table td, .ss-w-md-table th { border: 1px solid var(--ss-w-line); padding: 4px 8px; text-align: left; }\n.ss-w-md-table tr:nth-child(even) { background: var(--ss-w-bubble); }\n.ss-w-md-code { background: var(--ss-w-bubble); border-radius: 5px; padding: 1px 5px; font-family: ui-monospace, monospace; }\n\n/* entity cards */\n.ss-w-cards { display: flex; flex-direction: column; gap: 7px; }\n.ss-w-ecard { display: flex; align-items: center; gap: 11px; width: 100%; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 11px; text-align: left; cursor: pointer; }\n.ss-w-thumb { width: 40px; height: 40px; flex: none; border-radius: 50%; display: grid; place-items: center; font-size: 13px; font-weight: 700; color: #fff; background-color: var(--ss-w-nav); background-size: cover; background-position: center; }\n.ss-w-thumb.ss-w-team { border-radius: 10px; }\n.ss-w-meta { display: flex; flex-direction: column; min-width: 0; }\n.ss-w-nm { font-weight: 660; }\n.ss-w-sub { font-size: 11px; color: var(--ss-w-faint); }\n.ss-w-stat { margin-left: auto; text-align: center; }\n.ss-w-stat b { display: block; font-variant-numeric: tabular-nums; }\n.ss-w-stat span { font-size: 9px; text-transform: uppercase; color: var(--ss-w-faint); }\n.ss-w-chev { color: var(--ss-w-faint); font-size: 16px; display: grid; place-items: center; }\n\n/* action buttons */\n.ss-w-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }\n.ss-w-abtn { font-size: 12px; font-weight: 560; color: var(--ss-w-accent); background: var(--ss-w-panel); border: 1px solid var(--ss-w-accent); border-radius: 16px; padding: 6px 12px; cursor: pointer; }\n.ss-w-abtn-shimmer { width: 140px; height: 30px; border-radius: 16px; background: linear-gradient(90deg, var(--ss-w-bubble), var(--ss-w-line), var(--ss-w-bubble)); background-size: 200% 100%; animation: ss-w-shimmer 1.2s infinite; }\n@keyframes ss-w-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }\n\n/* intent chips */\n.ss-w-chips { display: flex; flex-wrap: wrap; gap: 6px; }\n.ss-w-chip { font-size: 11.5px; font-weight: 550; color: var(--ss-w-ink); background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 14px; padding: 5px 11px; cursor: pointer; }\n\n/* input */\n.ss-w-input { display: flex; align-items: flex-end; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--ss-w-line); }\n.ss-w-textarea { flex: 1; resize: none; border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 10px; font: inherit; color: inherit; background: var(--ss-w-panel); max-height: 96px; }\n.ss-w-send { width: 36px; height: 36px; flex: none; border: none; border-radius: 50%; background: var(--ss-w-accent); color: #fff; cursor: pointer; display: grid; place-items: center; font-size: 16px; }\n.ss-w-stop-glyph { width: 11px; height: 11px; background: currentColor; border-radius: 2px; }\n\n@media (prefers-reduced-motion: reduce) {\n .ss-w-abtn-shimmer { animation: none; }\n}\n';
422
+ var WIDGET_CSS = '.ss-w-root { all: revert; }\n.ss-w-root, .ss-w-root * { box-sizing: border-box; margin: 0; padding: 0; }\n.ss-w-root {\n --ss-w-accent: #17936a;\n --ss-w-ink: #14181d; --ss-w-faint: #6b7580; --ss-w-line: #e4e8ec;\n --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;\n --ss-w-radius: 16px; --ss-w-offset-x: 20px; --ss-w-offset-y: 20px;\n font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;\n font-size: 13px; line-height: 1.5; color: var(--ss-w-ink);\n}\n@media (prefers-color-scheme: dark) {\n .ss-w-root[data-ss-w-theme="auto"] {\n --ss-w-accent: #37c891; --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;\n --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;\n }\n}\n.ss-w-root[data-ss-w-theme="dark"] {\n --ss-w-accent: #37c891; --ss-w-ink: #e8edf2; --ss-w-faint: #8b95a1;\n --ss-w-line: #2a3441; --ss-w-panel: #141b24; --ss-w-bubble: #1e2732; --ss-w-nav: #0a0f16;\n}\n.ss-w-root[data-ss-w-theme="light"] {\n --ss-w-accent: #17936a; --ss-w-ink: #14181d; --ss-w-faint: #6b7580;\n --ss-w-line: #e4e8ec; --ss-w-panel: #ffffff; --ss-w-bubble: #f3f5f7; --ss-w-nav: #0e1622;\n}\n\n/* launcher */\n.ss-w-fab-wrap { position: fixed; bottom: var(--ss-w-offset-y); z-index: 2147483000; display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }\n.ss-w-root[data-ss-w-pos="right"] .ss-w-fab-wrap { right: var(--ss-w-offset-x); }\n.ss-w-root[data-ss-w-pos="left"] .ss-w-fab-wrap { left: var(--ss-w-offset-x); align-items: flex-start; }\n.ss-w-fab { width: 56px; height: 56px; border: none; border-radius: 50%; background: var(--ss-w-nav); color: #fff; cursor: pointer; display: grid; place-items: center; box-shadow: 0 6px 20px rgba(0,0,0,.28); }\n.ss-w-fab-mark { font-size: 30px; }\n.ss-w-greeting { max-width: 220px; background: var(--ss-w-panel); color: var(--ss-w-ink); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 9px 12px; box-shadow: 0 4px 14px rgba(0,0,0,.14); }\n\n/* panel */\n.ss-w-panel { position: fixed; bottom: calc(var(--ss-w-offset-y) + 72px); z-index: 2147483000; display: flex; flex-direction: column; width: min(400px, calc(100vw - 40px)); height: min(620px, calc(100vh - 120px)); background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: var(--ss-w-radius); overflow: hidden; box-shadow: 0 12px 40px rgba(0,0,0,.24); }\n.ss-w-root[data-ss-w-pos="right"] .ss-w-panel { right: var(--ss-w-offset-x); }\n.ss-w-root[data-ss-w-pos="left"] .ss-w-panel { left: var(--ss-w-offset-x); }\n@media (max-width: 639px) {\n .ss-w-panel { inset: 0; width: 100vw; height: 100vh; border: none; border-radius: 0; }\n}\n.ss-w-header { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 1px solid var(--ss-w-line); }\n.ss-w-header-mark { font-size: 18px; }\n.ss-w-header-title { font-weight: 660; }\n.ss-w-close { margin-left: auto; background: none; border: none; color: var(--ss-w-faint); cursor: pointer; font-size: 16px; display: grid; place-items: center; }\n\n/* messages */\n.ss-w-list { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 12px; }\n.ss-w-msg { display: flex; }\n.ss-w-user { justify-content: flex-end; }\n.ss-w-user .ss-w-bubble { background: color-mix(in srgb, var(--ss-w-accent) 14%, var(--ss-w-panel)); border: 1px solid color-mix(in srgb, var(--ss-w-accent) 30%, var(--ss-w-line)); border-radius: 14px; padding: 8px 12px; max-width: 80%; }\n.ss-w-bot { flex-direction: column; gap: 8px; }\n.ss-w-status { font-size: 11px; color: var(--ss-w-faint); font-style: italic; }\n.ss-w-error { display: flex; align-items: center; gap: 8px; font-size: 12px; color: #c0392b; }\n.ss-w-retry { background: none; border: 1px solid currentColor; border-radius: 10px; padding: 2px 8px; cursor: pointer; color: inherit; }\n\n/* markdown */\n.ss-w-md { display: flex; flex-direction: column; gap: 8px; }\n.ss-w-md :where(p, ul, ol, table, pre, blockquote, h1, h2, h3, h4) { margin: 0; }\n.ss-w-md > *:first-child { margin-top: 0; }\n.ss-w-md > *:last-child { margin-bottom: 0; }\n.ss-w-md-ul { list-style: disc outside; padding-left: 18px; }\n.ss-w-md-ol { list-style: decimal outside; padding-left: 18px; }\n.ss-w-md-ul li::marker, .ss-w-md-ol li::marker { color: var(--ss-w-accent); }\n.ss-w-md-link { color: var(--ss-w-accent); text-decoration: underline; }\n.ss-w-table-wrap { overflow-x: auto; max-width: 100%; -webkit-overflow-scrolling: touch; }\n.ss-w-md-table { border-collapse: collapse; width: max-content; min-width: 100%; font-variant-numeric: tabular-nums; }\n.ss-w-md-table td, .ss-w-md-table th { border: 1px solid var(--ss-w-line); padding: 4px 8px; text-align: left; white-space: nowrap; }\n.ss-w-md-table th { background: color-mix(in srgb, var(--ss-w-accent) 12%, var(--ss-w-panel)); font-weight: 700; }\n.ss-w-md-table tbody tr:nth-child(even) { background: var(--ss-w-bubble); }\n.ss-w-md-code { background: var(--ss-w-bubble); border-radius: 5px; padding: 1px 5px; font-family: ui-monospace, monospace; }\n\n/* entity cards */\n.ss-w-cards { display: flex; flex-direction: column; gap: 7px; }\n.ss-w-ecard { display: flex; align-items: center; gap: 11px; width: 100%; background: var(--ss-w-panel); border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 11px; text-align: left; cursor: pointer; }\n.ss-w-thumb { width: 40px; height: 40px; flex: none; border-radius: 50%; display: grid; place-items: center; font-size: 13px; font-weight: 700; color: #fff; background-color: var(--ss-w-nav); background-size: cover; background-position: center; }\n.ss-w-thumb.ss-w-team { border-radius: 10px; }\n.ss-w-ecard[data-kind="player"] .ss-w-thumb { background-color: var(--ss-w-accent); }\n.ss-w-meta { display: flex; flex-direction: column; min-width: 0; }\n.ss-w-kind { font-size: 8.5px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--ss-w-faint); line-height: 1.3; }\n.ss-w-nm { font-weight: 660; }\n.ss-w-sub { font-size: 11px; color: var(--ss-w-faint); }\n.ss-w-stat { margin-left: auto; text-align: center; }\n.ss-w-stat b { display: block; font-variant-numeric: tabular-nums; }\n.ss-w-stat span { font-size: 9px; text-transform: uppercase; color: var(--ss-w-faint); }\n.ss-w-chev { color: var(--ss-w-faint); font-size: 16px; display: grid; place-items: center; }\n.ss-w-cards-more { align-self: flex-start; font-size: 11.5px; font-weight: 560; color: var(--ss-w-accent); background: none; border: none; cursor: pointer; padding: 3px 2px; }\n.ss-w-cards-more:hover:not(:disabled) { text-decoration: underline; }\n.ss-w-ecard:disabled, .ss-w-cards-more:disabled { opacity: .5; cursor: default; }\n\n/* action buttons */\n.ss-w-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }\n.ss-w-abtn { font-size: 12px; font-weight: 560; color: var(--ss-w-accent); background: var(--ss-w-panel); border: 1px solid var(--ss-w-accent); border-radius: 16px; padding: 6px 12px; cursor: pointer; }\n.ss-w-abtn:disabled { opacity: .5; cursor: default; }\n.ss-w-abtn-shimmer { width: 140px; height: 30px; border-radius: 16px; background: linear-gradient(90deg, var(--ss-w-bubble), var(--ss-w-line), var(--ss-w-bubble)); background-size: 200% 100%; animation: ss-w-shimmer 1.2s infinite; }\n@keyframes ss-w-shimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }\n\n/* intent chips */\n.ss-w-chips { display: flex; flex-wrap: wrap; gap: 6px; }\n.ss-w-chip { font-size: 11.5px; font-weight: 550; color: var(--ss-w-ink); background: var(--ss-w-bubble); border: 1px solid var(--ss-w-line); border-radius: 14px; padding: 5px 11px; cursor: pointer; }\n\n/* input */\n.ss-w-input { display: flex; align-items: flex-end; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--ss-w-line); }\n.ss-w-textarea { flex: 1; resize: none; border: 1px solid var(--ss-w-line); border-radius: 12px; padding: 8px 10px; font: inherit; color: inherit; background: var(--ss-w-panel); max-height: 96px; }\n.ss-w-textarea:disabled { opacity: .55; cursor: not-allowed; }\n.ss-w-send { width: 36px; height: 36px; flex: none; border: none; border-radius: 50%; background: var(--ss-w-accent); color: #fff; cursor: pointer; display: grid; place-items: center; font-size: 16px; }\n.ss-w-stop-glyph { width: 11px; height: 11px; background: currentColor; border-radius: 2px; }\n\n@media (prefers-reduced-motion: reduce) {\n .ss-w-abtn-shimmer { animation: none; }\n}\n';
389
423
 
390
424
  // src/StatsWidget.tsx
391
- import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
425
+ import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
392
426
  function buildClient(props) {
393
427
  try {
394
428
  if ("client" in props && props.client) {
@@ -424,7 +458,7 @@ function StatsWidget(props) {
424
458
  }, [built, ui.injectStyles]);
425
459
  if (!built) return null;
426
460
  const attrs = themeAttrs(ui);
427
- return /* @__PURE__ */ jsxs9("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: [
461
+ return /* @__PURE__ */ jsxs10("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: [
428
462
  controller.state.isOpen ? /* @__PURE__ */ jsx13(ChatPanel, { controller, ui }) : null,
429
463
  /* @__PURE__ */ jsx13(ChatFab, { label: ui.launcherLabel, open: controller.state.isOpen, greeting: ui.greeting, onToggle: controller.toggle })
430
464
  ] });