@sensiblestats/widget-react 0.2.1 → 0.3.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
@@ -114,8 +114,7 @@ function toCardVM(card) {
114
114
  name: card.canonicalName,
115
115
  image: str(card.imagePath) ?? str(card.imageUrl) ?? str(card.image_url) ?? str(card.logoUrl) ?? str(card.logo),
116
116
  sub: str(card.subtitle) ?? str(card.teamName) ?? str(card.leagueName) ?? str(card.position),
117
- stats: readStats(card),
118
- query: card.canonicalName
117
+ stats: readStats(card)
119
118
  };
120
119
  }
121
120
  function toActionVM(action) {
@@ -288,9 +287,6 @@ function SendIcon() {
288
287
  function CloseIcon() {
289
288
  return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fill: "none", d: "M18 6L6 18M6 6l12 12", stroke: "currentColor", strokeWidth: "2" }) });
290
289
  }
291
- function ChevronIcon() {
292
- return /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "1em", height: "1em", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fill: "none", stroke: "currentColor", strokeWidth: "2", d: "M9 6l6 6-6 6" }) });
293
- }
294
290
 
295
291
  // src/components/GreetingBubble.tsx
296
292
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
@@ -349,13 +345,13 @@ var MAX_VISIBLE = 3;
349
345
  function initials(name) {
350
346
  return name.split(/\s+/).slice(0, 2).map((w) => w[0] ?? "").join("").toUpperCase();
351
347
  }
352
- function EntityCardList({ cards, disabled, onSelect, showLessLabel = "Show less", showMoreLabel = (n) => `Show ${n} more` }) {
348
+ function EntityCardList({ cards, disabled, showLessLabel = "Show less", showMoreLabel = (n) => `Show ${n} more` }) {
353
349
  const [expanded, setExpanded] = useState(false);
354
350
  if (cards.length === 0) return null;
355
351
  const visible = expanded ? cards : cards.slice(0, MAX_VISIBLE);
356
352
  const overflow = cards.length - MAX_VISIBLE;
357
353
  return /* @__PURE__ */ jsxs4("div", { className: "ss-w-cards", children: [
358
- visible.map((c) => /* @__PURE__ */ jsxs4("button", { type: "button", className: "ss-w-ecard", "data-kind": c.kind, disabled, onClick: () => onSelect(c.query), children: [
354
+ visible.map((c) => /* @__PURE__ */ jsxs4("div", { className: "ss-w-ecard", "data-kind": c.kind, children: [
359
355
  /* @__PURE__ */ jsx6(
360
356
  "span",
361
357
  {
@@ -372,8 +368,7 @@ function EntityCardList({ cards, disabled, onSelect, showLessLabel = "Show less"
372
368
  c.stats.map((s) => /* @__PURE__ */ jsxs4("span", { className: "ss-w-stat", children: [
373
369
  /* @__PURE__ */ jsx6("b", { children: s.value }),
374
370
  /* @__PURE__ */ jsx6("span", { children: s.label })
375
- ] }, s.label)),
376
- /* @__PURE__ */ jsx6("span", { className: "ss-w-chev", children: /* @__PURE__ */ jsx6(ChevronIcon, {}) })
371
+ ] }, s.label))
377
372
  ] }, c.id)),
378
373
  overflow > 0 ? /* @__PURE__ */ jsx6("button", { type: "button", className: "ss-w-cards-more", disabled, onClick: () => setExpanded((v) => !v), children: expanded ? showLessLabel : showMoreLabel(overflow) }) : null
379
374
  ] });
@@ -394,13 +389,13 @@ function ActionButtons({ actions, loading, disabled, onAct }) {
394
389
 
395
390
  // src/components/ChatMessage.tsx
396
391
  import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
397
- function ChatMessage({ message, disabled, onAct, onSelect, ui }) {
392
+ function ChatMessage({ message, disabled, onAct, ui }) {
398
393
  if (message.role === "user") {
399
394
  return /* @__PURE__ */ jsx8("div", { className: "ss-w-msg ss-w-user", children: /* @__PURE__ */ jsx8("div", { className: "ss-w-bubble", children: message.text }) });
400
395
  }
401
396
  return /* @__PURE__ */ jsxs6("div", { className: "ss-w-msg ss-w-bot", children: [
402
397
  message.text ? /* @__PURE__ */ jsx8(Markdown, { text: message.text }) : null,
403
- /* @__PURE__ */ jsx8(EntityCardList, { cards: message.cards, disabled, onSelect, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
398
+ /* @__PURE__ */ jsx8(EntityCardList, { cards: message.cards, disabled, showLessLabel: ui?.showLess, showMoreLabel: ui?.showMore }),
404
399
  /* @__PURE__ */ jsx8(ActionButtons, { actions: message.actions, loading: false, disabled, onAct })
405
400
  ] });
406
401
  }
@@ -412,37 +407,55 @@ function IntentChips({ chips, onPick }) {
412
407
  return /* @__PURE__ */ jsx9("div", { className: "ss-w-chips", children: chips.map((c, i) => /* @__PURE__ */ jsx9("button", { type: "button", className: "ss-w-chip", onClick: () => onPick(c.text), children: c.text }, `${c.text}-${i}`)) });
413
408
  }
414
409
 
410
+ // src/scroll.ts
411
+ var PIN_THRESHOLD = 48;
412
+ function isPinnedToBottom(el, threshold = PIN_THRESHOLD) {
413
+ return el.scrollHeight - el.scrollTop - el.clientHeight <= threshold;
414
+ }
415
+
415
416
  // src/components/MessageList.tsx
416
417
  import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
417
418
  function MessageList({ controller, ui }) {
418
419
  const { state, send } = controller;
420
+ const listRef = useRef2(null);
419
421
  const endRef = useRef2(null);
422
+ const pinnedRef = useRef2(true);
420
423
  useEffect2(() => {
421
- endRef.current?.scrollIntoView({ block: "end" });
424
+ if (pinnedRef.current) endRef.current?.scrollIntoView({ block: "end" });
422
425
  }, [state.messages, state.status]);
423
426
  const empty = state.messages.length === 0;
424
- return /* @__PURE__ */ jsxs7("div", { className: "ss-w-list", "aria-live": "polite", children: [
425
- state.messages.map((m) => /* @__PURE__ */ jsx10(
426
- ChatMessage,
427
- {
428
- message: m,
429
- disabled: state.isStreaming,
430
- onAct: (a) => send(a.message, { actionId: a.actionId, displayText: a.label }),
431
- onSelect: (q) => send(q),
432
- ui
427
+ return /* @__PURE__ */ jsxs7(
428
+ "div",
429
+ {
430
+ className: "ss-w-list",
431
+ "aria-live": "polite",
432
+ ref: listRef,
433
+ onScroll: () => {
434
+ if (listRef.current) pinnedRef.current = isPinnedToBottom(listRef.current);
433
435
  },
434
- m.id
435
- )),
436
- state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx10(ActionButtons, { actions: [], loading: true, onAct: () => {
437
- } }) : null,
438
- state.status ? /* @__PURE__ */ jsx10("div", { className: "ss-w-status", children: state.status }) : null,
439
- state.error ? /* @__PURE__ */ jsxs7("div", { className: "ss-w-error", role: "alert", children: [
440
- /* @__PURE__ */ jsx10("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
441
- /* @__PURE__ */ jsx10("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
442
- ] }) : null,
443
- empty ? /* @__PURE__ */ jsx10(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
444
- /* @__PURE__ */ jsx10("div", { ref: endRef })
445
- ] });
436
+ children: [
437
+ state.messages.map((m) => /* @__PURE__ */ jsx10(
438
+ ChatMessage,
439
+ {
440
+ message: m,
441
+ disabled: state.isStreaming,
442
+ onAct: (a) => send(a.message, { actionId: a.actionId, displayText: a.label }),
443
+ ui
444
+ },
445
+ m.id
446
+ )),
447
+ state.isStreaming && state.actionsLoading ? /* @__PURE__ */ jsx10(ActionButtons, { actions: [], loading: true, onAct: () => {
448
+ } }) : null,
449
+ state.status ? /* @__PURE__ */ jsx10("div", { className: "ss-w-status", children: state.status }) : null,
450
+ state.error ? /* @__PURE__ */ jsxs7("div", { className: "ss-w-error", role: "alert", children: [
451
+ /* @__PURE__ */ jsx10("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
452
+ /* @__PURE__ */ jsx10("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
453
+ ] }) : null,
454
+ empty ? /* @__PURE__ */ jsx10(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
455
+ /* @__PURE__ */ jsx10("div", { ref: endRef })
456
+ ]
457
+ }
458
+ );
446
459
  }
447
460
 
448
461
  // src/components/ChatInput.tsx
@@ -580,7 +593,7 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
580
593
 
581
594
  /* entity cards */
582
595
  .ss-w-cards { display: flex; flex-direction: column; gap: 7px; }
583
- .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; }
596
+ .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; }
584
597
  .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; }
585
598
  .ss-w-thumb.ss-w-team { border-radius: 10px; }
586
599
  .ss-w-ecard[data-kind="player"] .ss-w-thumb { background-color: var(--ss-w-accent); }
@@ -591,10 +604,9 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
591
604
  .ss-w-stat { margin-left: auto; text-align: center; }
592
605
  .ss-w-stat b { display: block; font-variant-numeric: tabular-nums; }
593
606
  .ss-w-stat span { font-size: 9px; text-transform: uppercase; color: var(--ss-w-faint); }
594
- .ss-w-chev { color: var(--ss-w-faint); font-size: 16px; display: grid; place-items: center; }
595
607
  .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; }
596
608
  .ss-w-cards-more:hover:not(:disabled) { text-decoration: underline; }
597
- .ss-w-ecard:disabled, .ss-w-cards-more:disabled { opacity: .5; cursor: default; }
609
+ .ss-w-cards-more:disabled { opacity: .5; cursor: default; }
598
610
 
599
611
  /* action buttons */
600
612
  .ss-w-actions { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
@@ -611,6 +623,11 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
611
623
  .ss-w-input { display: flex; align-items: flex-end; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--ss-w-line); }
612
624
  .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; }
613
625
  .ss-w-textarea:disabled { opacity: .55; cursor: not-allowed; }
626
+ /* iOS Safari zooms the page when a focused field's font-size is < 16px. Bump it
627
+ on touch devices only, so the desktop 13px design is unchanged. */
628
+ @media (pointer: coarse) {
629
+ .ss-w-textarea { font-size: 16px; }
630
+ }
614
631
  .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; }
615
632
  .ss-w-stop-glyph { width: 11px; height: 11px; background: currentColor; border-radius: 2px; }
616
633