@sensiblestats/widget-react 0.79.0 → 0.80.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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # @sensiblestats/widget-react
2
2
 
3
+ ## 0.80.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Show the brand's limit message: LimitError for limit refusals, the limit_notice capability, and a status line that replaces the composer while the assistant is paused.
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies
12
+ - @sensiblestats/widget-sdk@0.51.0
13
+
3
14
  ## 0.79.0
4
15
 
5
16
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -38,7 +38,7 @@ module.exports = __toCommonJS(index_exports);
38
38
 
39
39
  // src/StatsWidget.tsx
40
40
  var import_react57 = require("react");
41
- var import_widget_sdk5 = require("@sensiblestats/widget-sdk");
41
+ var import_widget_sdk6 = require("@sensiblestats/widget-sdk");
42
42
 
43
43
  // src/kickoff.ts
44
44
  function formatBettingKickoff(kickoffUtc, ui, locale, now = /* @__PURE__ */ new Date()) {
@@ -68,6 +68,9 @@ var STRINGS = {
68
68
  dismissGreeting: "Dismiss",
69
69
  somethingWentWrong: "Something went wrong.",
70
70
  retry: "Retry",
71
+ limitDailyCap: "You've asked all your questions for today. You can ask again tomorrow.",
72
+ limitQuotaExhausted: "The assistant can't take new questions right now. Please try again later.",
73
+ limitAssistantPaused: "The assistant isn't available right now. Please try again later.",
71
74
  showLess: "Show less",
72
75
  showMore: (n) => `Show ${n} more`,
73
76
  allMarkets: (n) => `All markets (${n})`,
@@ -417,6 +420,9 @@ var STRINGS = {
417
420
  dismissGreeting: "Kapat",
418
421
  somethingWentWrong: "Bir \u015Feyler ters gitti.",
419
422
  retry: "Tekrar dene",
423
+ limitDailyCap: "Bug\xFCnl\xFCk sorular\u0131n\u0131 tamamlad\u0131n. Yar\u0131n yeniden sorabilirsin.",
424
+ limitQuotaExhausted: "Asistan \u015Fu an yeni soru alam\u0131yor. L\xFCtfen daha sonra tekrar dene.",
425
+ limitAssistantPaused: "Asistan \u015Fu an kullan\u0131lam\u0131yor. L\xFCtfen daha sonra tekrar dene.",
420
426
  showLess: "Daha az g\xF6ster",
421
427
  showMore: (n) => `${n} tane daha g\xF6ster`,
422
428
  allMarkets: (n) => `T\xFCm bahisler (${n})`,
@@ -795,6 +801,9 @@ function normalizeUi(cfg) {
795
801
  dismissGreeting: strings.dismissGreeting,
796
802
  somethingWentWrong: strings.somethingWentWrong,
797
803
  retry: strings.retry,
804
+ limitDailyCap: strings.limitDailyCap,
805
+ limitQuotaExhausted: strings.limitQuotaExhausted,
806
+ limitAssistantPaused: strings.limitAssistantPaused,
798
807
  showLess: strings.showLess,
799
808
  showMore: strings.showMore,
800
809
  allMarkets: strings.allMarkets,
@@ -1738,10 +1747,55 @@ function themeAttrs(ui) {
1738
1747
 
1739
1748
  // src/useChatStream.ts
1740
1749
  var import_react3 = require("react");
1741
- var import_widget_sdk3 = require("@sensiblestats/widget-sdk");
1750
+ var import_widget_sdk4 = require("@sensiblestats/widget-sdk");
1742
1751
 
1743
1752
  // src/reducer.ts
1753
+ var import_widget_sdk3 = require("@sensiblestats/widget-sdk");
1754
+
1755
+ // src/limit.ts
1744
1756
  var import_widget_sdk2 = require("@sensiblestats/widget-sdk");
1757
+ var LIMIT_TIMER_MAX_MS = 60 * 60 * 1e3;
1758
+ var ZONED = /(?:Z|[+-]\d{2}:?\d{2})$/i;
1759
+ function build(code, message, resetsAt, retryAfterSeconds) {
1760
+ return {
1761
+ code,
1762
+ paused: code === "assistant_paused",
1763
+ message: message?.trim() || null,
1764
+ resetsAt: resetsAt ?? null,
1765
+ retryAfterSeconds: typeof retryAfterSeconds === "number" && Number.isFinite(retryAfterSeconds) ? retryAfterSeconds : null
1766
+ };
1767
+ }
1768
+ function limitFromEvent(data) {
1769
+ return (0, import_widget_sdk2.isLimitCode)(data.code) ? build(data.code, data.message, data.resetsAt, data.retryAfterSeconds) : null;
1770
+ }
1771
+ function limitFromError(error) {
1772
+ return error instanceof import_widget_sdk2.LimitError ? build(error.limitCode, error.limitMessage, error.resetsAt, error.retryAfter) : null;
1773
+ }
1774
+ function isLimitNotice(data) {
1775
+ return data.terminal === false && (0, import_widget_sdk2.isLimitCode)(data.code);
1776
+ }
1777
+ function isPaused(limit) {
1778
+ return limit?.paused === true;
1779
+ }
1780
+ function limitText(limit, ui) {
1781
+ if (limit.message) return limit.message;
1782
+ switch (limit.code) {
1783
+ case "daily_cap":
1784
+ return ui.limitDailyCap;
1785
+ case "quota_exhausted":
1786
+ return ui.limitQuotaExhausted;
1787
+ case "assistant_paused":
1788
+ return ui.limitAssistantPaused;
1789
+ }
1790
+ }
1791
+ function limitDelayMs(limit, nowMs) {
1792
+ const retry = limit.retryAfterSeconds;
1793
+ if (retry != null && Number.isFinite(retry) && retry >= 0) return Math.min(retry * 1e3, LIMIT_TIMER_MAX_MS);
1794
+ const at = limit.resetsAt ? Date.parse(ZONED.test(limit.resetsAt) ? limit.resetsAt : `${limit.resetsAt}Z`) : Number.NaN;
1795
+ return Number.isNaN(at) ? null : Math.min(Math.max(at - nowMs, 0), LIMIT_TIMER_MAX_MS);
1796
+ }
1797
+
1798
+ // src/reducer.ts
1745
1799
  var POPUP_MAX = 160;
1746
1800
  function markAwaitingLiveUpdate(messages, held = []) {
1747
1801
  if (!messages.some((m) => m.role === "assistant" && m.marketOdds.some((vm) => vm.live != null))) return messages;
@@ -1759,12 +1813,18 @@ function markAwaitingLiveUpdate(messages, held = []) {
1759
1813
  });
1760
1814
  }
1761
1815
  function initialState(starters) {
1762
- return { isOpen: false, messages: [], isStreaming: false, status: null, actionsLoading: false, starters, configuredStarters: starters, error: null, lastUserInput: null, lastDisplayText: null, popupText: null, conversationId: null };
1816
+ return { isOpen: false, messages: [], isStreaming: false, status: null, actionsLoading: false, starters, configuredStarters: starters, error: null, lastUserInput: null, lastDisplayText: null, popupText: null, conversationId: null, limit: null };
1763
1817
  }
1764
1818
  function activeTurn(state) {
1765
1819
  const last = state.messages[state.messages.length - 1];
1766
1820
  return last && last.role === "assistant" && !last.done ? last : null;
1767
1821
  }
1822
+ function settleRefused(state, limit) {
1823
+ return { ...state, limit, error: null, isStreaming: false, status: null, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, done: true })) };
1824
+ }
1825
+ function isPlainInput(input) {
1826
+ return !!input && !input.actionRef && !input.actionId && !input.grounding && !input.preset;
1827
+ }
1768
1828
  function mapActive(state, fn) {
1769
1829
  const last = state.messages[state.messages.length - 1];
1770
1830
  if (!last || last.role !== "assistant" || last.done) return state.messages;
@@ -1804,6 +1864,7 @@ function applyEvent(state, event) {
1804
1864
  case "turn_complete": {
1805
1865
  const active = activeTurn(state);
1806
1866
  const greetingLine = event.data.clubFanDashboard?.greeting?.trim() || (active?.text.trim() ?? "");
1867
+ const clearsLimit = !active?.isGreeting && isPlainInput(state.lastUserInput);
1807
1868
  const popupText = active?.isGreeting && !state.isOpen && greetingLine ? greetingLine.slice(0, POPUP_MAX) : state.popupText;
1808
1869
  return {
1809
1870
  ...state,
@@ -1811,6 +1872,7 @@ function applyEvent(state, event) {
1811
1872
  status: null,
1812
1873
  actionsLoading: false,
1813
1874
  popupText,
1875
+ limit: clearsLimit ? null : state.limit,
1814
1876
  conversationId: event.data.conversationId ?? state.conversationId,
1815
1877
  messages: mapActive(state, (t) => ({
1816
1878
  ...t,
@@ -1821,6 +1883,9 @@ function applyEvent(state, event) {
1821
1883
  };
1822
1884
  }
1823
1885
  case "error": {
1886
+ const limit = limitFromEvent(event.data);
1887
+ if (limit && event.data.terminal === false) return { ...state, limit };
1888
+ if (limit) return settleRefused(state, limit);
1824
1889
  const suppress = activeTurn(state)?.isGreeting === true;
1825
1890
  return { ...state, error: suppress ? state.error : event.data, isStreaming: false, status: null, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, done: true })) };
1826
1891
  }
@@ -1845,7 +1910,7 @@ function reducer(state, action) {
1845
1910
  case "DISMISS_POPUP":
1846
1911
  return { ...state, popupText: null };
1847
1912
  case "RESET":
1848
- return { ...initialState(state.configuredStarters), isOpen: state.isOpen };
1913
+ return { ...initialState(state.configuredStarters), isOpen: state.isOpen, limit: state.limit };
1849
1914
  case "RESTORE":
1850
1915
  return {
1851
1916
  ...state,
@@ -1859,8 +1924,13 @@ function reducer(state, action) {
1859
1924
  };
1860
1925
  case "EVENT":
1861
1926
  return applyEvent(state, action.event);
1862
- case "STREAM_ERROR":
1927
+ case "STREAM_ERROR": {
1928
+ const limit = limitFromError(action.error);
1929
+ if (limit) return settleRefused(state, limit);
1863
1930
  return { ...state, error: action.error, isStreaming: false, status: null, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, done: true })) };
1931
+ }
1932
+ case "LIMIT_EXPIRED":
1933
+ return { ...state, limit: null };
1864
1934
  case "STOP":
1865
1935
  return { ...state, isStreaming: false, status: null, actionsLoading: false, messages: mapActive(state, (t) => ({ ...t, done: true })) };
1866
1936
  case "UPDATE_LIVE_CARD": {
@@ -2033,7 +2103,7 @@ function clearConversation(storageKey) {
2033
2103
  }
2034
2104
 
2035
2105
  // src/presets.ts
2036
- var CLIENT_CAPABILITIES = ["price_presets", "price_presets_open"];
2106
+ var CLIENT_CAPABILITIES = ["price_presets", "price_presets_open", "limit_notice"];
2037
2107
  var PRICE_TILE_PREFIX = "goal-preset-";
2038
2108
  function isPriceTile(tile) {
2039
2109
  const id = tile.signal?.entityId;
@@ -2131,11 +2201,11 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
2131
2201
  try {
2132
2202
  for await (const event of handle) {
2133
2203
  dispatch({ kind: "EVENT", event });
2134
- if (event.type === "turn_complete" || event.type === "error") clearFallbackTimer();
2204
+ if (event.type === "turn_complete" || event.type === "error" && !isLimitNotice(event.data)) clearFallbackTimer();
2135
2205
  }
2136
2206
  } catch (err) {
2137
2207
  if (handleRef.current === handle) clearFallbackTimer();
2138
- if (err instanceof import_widget_sdk3.WidgetSdkError) onError(err);
2208
+ if (err instanceof import_widget_sdk4.WidgetSdkError) onError(err);
2139
2209
  else if (err?.name === "AbortError") {
2140
2210
  } else throw err;
2141
2211
  } finally {
@@ -2144,6 +2214,7 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
2144
2214
  })();
2145
2215
  }, [clearFallbackTimer]);
2146
2216
  const run = (0, import_react3.useCallback)((input, displayText) => {
2217
+ if (isPaused(stateRef.current.limit)) return;
2147
2218
  handleRef.current?.cancel();
2148
2219
  clearFallbackTimer();
2149
2220
  dispatch({ kind: "USER_SENT", input, userId: nextId(), assistantId: nextId(), displayText });
@@ -2151,7 +2222,7 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
2151
2222
  handleRef.current = handle;
2152
2223
  armFallbackTimer(() => {
2153
2224
  dispatch({ kind: "STOP" });
2154
- dispatch({ kind: "STREAM_ERROR", error: new import_widget_sdk3.WidgetSdkError("No response received in time", "timeout") });
2225
+ dispatch({ kind: "STREAM_ERROR", error: new import_widget_sdk4.WidgetSdkError("No response received in time", "timeout") });
2155
2226
  });
2156
2227
  consume(handle, (err) => dispatch({ kind: "STREAM_ERROR", error: err }));
2157
2228
  }, [conversation, consume, clearFallbackTimer, armFallbackTimer]);
@@ -2191,6 +2262,13 @@ function useChatStream(conversation, starterTexts, greetingMessage, storageKey)
2191
2262
  greetedRef.current = true;
2192
2263
  fireGreeting();
2193
2264
  }, [fireGreeting]);
2265
+ (0, import_react3.useEffect)(() => {
2266
+ if (!state.limit) return;
2267
+ const delay = limitDelayMs(state.limit, Date.now());
2268
+ if (delay === null) return;
2269
+ const timer = setTimeout(() => dispatch({ kind: "LIMIT_EXPIRED" }), delay);
2270
+ return () => clearTimeout(timer);
2271
+ }, [state.limit]);
2194
2272
  (0, import_react3.useEffect)(() => {
2195
2273
  if (boot.stale && storageKey) clearConversation(storageKey);
2196
2274
  }, []);
@@ -3533,7 +3611,7 @@ var import_react20 = require("react");
3533
3611
 
3534
3612
  // src/slipAdd.ts
3535
3613
  var import_react18 = require("react");
3536
- var import_widget_sdk4 = require("@sensiblestats/widget-sdk");
3614
+ var import_widget_sdk5 = require("@sensiblestats/widget-sdk");
3537
3615
 
3538
3616
  // src/slipStateContext.ts
3539
3617
  var import_react17 = require("react");
@@ -3562,7 +3640,7 @@ function useSlipAdd(slip, ui) {
3562
3640
  }
3563
3641
  try {
3564
3642
  const raw = await withTimeout(Promise.resolve(handler(selection)));
3565
- if (raw != null && !(0, import_widget_sdk4.isAddToSlipResult)(raw)) throw new Error("malformed add-to-slip result");
3643
+ if (raw != null && !(0, import_widget_sdk5.isAddToSlipResult)(raw)) throw new Error("malformed add-to-slip result");
3566
3644
  const result = raw ?? void 0;
3567
3645
  if (marketDeveloperName !== void 0) {
3568
3646
  log?.log({
@@ -5381,7 +5459,7 @@ function MessageList({ controller, ui, slip, onDashboardTap, monitor }) {
5381
5459
  ChatMessage,
5382
5460
  {
5383
5461
  message: m,
5384
- disabled: state.isStreaming,
5462
+ disabled: state.isStreaming || isPaused(state.limit),
5385
5463
  onAct: (a) => send(a.message, { actionId: a.actionId, actionRef: a.actionRef, displayText: a.label }),
5386
5464
  onTap: (tile, index) => {
5387
5465
  const displayText = tileDisplayText(tile);
@@ -5401,7 +5479,7 @@ function MessageList({ controller, ui, slip, onDashboardTap, monitor }) {
5401
5479
  /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { children: "message" in state.error ? state.error.message : ui.somethingWentWrong }),
5402
5480
  /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("button", { type: "button", className: "ss-w-retry", onClick: () => controller.retry(), children: ui.retry })
5403
5481
  ] }) : null,
5404
- !hasUserMessage && !hasDashboard ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
5482
+ !hasUserMessage && !hasDashboard && !isPaused(state.limit) ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(IntentChips, { chips: state.starters, onPick: (t) => send(t) }) : null,
5405
5483
  /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { ref: endRef, className: "ss-w-turn-spacer", "aria-hidden": "true" })
5406
5484
  ]
5407
5485
  }
@@ -9374,6 +9452,7 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
9374
9452
  return () => window.removeEventListener("keydown", onKey);
9375
9453
  }, [close]);
9376
9454
  const handleSend = (text) => {
9455
+ typedSendRef.current = true;
9377
9456
  monitor?.setActiveTab("chat");
9378
9457
  send(text);
9379
9458
  };
@@ -9389,6 +9468,8 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
9389
9468
  const dialRef = (0, import_react56.useRef)(null);
9390
9469
  const dialButtonRef = (0, import_react56.useRef)(null);
9391
9470
  const inputRef = (0, import_react56.useRef)(null);
9471
+ const limitRef = (0, import_react56.useRef)(null);
9472
+ const typedSendRef = (0, import_react56.useRef)(false);
9392
9473
  const focusAfterCloseRef = (0, import_react56.useRef)(null);
9393
9474
  const [tool, setTool] = (0, import_react56.useState)(null);
9394
9475
  const [toolSeen, setToolSeen] = (0, import_react56.useState)(readToolSeen);
@@ -9401,7 +9482,16 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
9401
9482
  activeTab: monitor?.activeTab ?? "chat"
9402
9483
  });
9403
9484
  const { fold } = dial;
9404
- const showDial = !!presetGrid && onChatTab && !tool;
9485
+ const paused = isPaused(state.limit);
9486
+ const showDial = !!presetGrid && onChatTab && !tool && !paused;
9487
+ (0, import_react56.useEffect)(() => {
9488
+ if (paused) setTool(null);
9489
+ }, [paused]);
9490
+ (0, import_react56.useEffect)(() => {
9491
+ if (!paused) return;
9492
+ if (typedSendRef.current) limitRef.current?.focus();
9493
+ typedSendRef.current = false;
9494
+ }, [paused]);
9405
9495
  const openTool = (0, import_react56.useCallback)((next) => {
9406
9496
  fold();
9407
9497
  markToolSeen();
@@ -9410,8 +9500,8 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
9410
9500
  }, [fold]);
9411
9501
  const onPill = (0, import_react56.useCallback)((kind) => openTool(kind === "numbers" ? { kind: "numbers" } : { kind: "combo" }), [openTool]);
9412
9502
  const toolsValue = (0, import_react56.useMemo)(
9413
- () => presetGrid ? { openBuilder: (total, legs) => openTool({ kind: "combo", total, legs }), changeLabel: ui.presets.changeCombo } : null,
9414
- [presetGrid, openTool, ui.presets.changeCombo]
9503
+ () => presetGrid && !paused ? { openBuilder: (total, legs) => openTool({ kind: "combo", total, legs }), changeLabel: ui.presets.changeCombo } : null,
9504
+ [presetGrid, paused, openTool, ui.presets.changeCombo]
9415
9505
  );
9416
9506
  const closeTool = (0, import_react56.useCallback)(() => {
9417
9507
  focusAfterCloseRef.current = "dial";
@@ -9479,10 +9569,10 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
9479
9569
  buttonRef: dialButtonRef
9480
9570
  }
9481
9571
  ) : null,
9482
- tool && presetGrid && onChatTab ? tool.kind === "numbers" ? /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(SetNumbersTool, { grid: presetGrid, strings: ui.presets, streaming: state.isStreaming, onSend: sendAsk, onClose: closeTool }) : /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(BuildComboTool, { grid: presetGrid, strings: ui.presets, streaming: state.isStreaming, formatKickoff, initialTotal: tool.total, initialLegs: tool.legs, onSend: sendAsk, onClose: closeTool }) : null
9572
+ tool && presetGrid && onChatTab && !paused ? tool.kind === "numbers" ? /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(SetNumbersTool, { grid: presetGrid, strings: ui.presets, streaming: state.isStreaming, onSend: sendAsk, onClose: closeTool }) : /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(BuildComboTool, { grid: presetGrid, strings: ui.presets, streaming: state.isStreaming, formatKickoff, initialTotal: tool.total, initialLegs: tool.legs, onSend: sendAsk, onClose: closeTool }) : null
9483
9573
  ] }),
9484
9574
  monitor && discovery && monitor.activeTab === "today" && /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(TodayPanel, { discovery, ui, monitor }),
9485
- monitor && monitor.activeTab === "following" && /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(MatchesPanel, { monitor, ui, slip, onAsk: handleAsk }),
9575
+ monitor && monitor.activeTab === "following" && /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(MatchesPanel, { monitor, ui, slip, onAsk: paused ? void 0 : handleAsk }),
9486
9576
  monitor && monitor.activeTab === "odds" && /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(OddsPanel, { monitor, ui, slip }),
9487
9577
  monitor && monitor.activeTab === "games" && gamesResult && /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
9488
9578
  GamesPanel,
@@ -9508,20 +9598,23 @@ function ChatPanel({ controller, ui, slip, onDashboardTap, monitor, discovery, g
9508
9598
  ui
9509
9599
  }
9510
9600
  ),
9511
- (!monitor || monitor.activeTab !== "games" && monitor.activeTab !== "leaderboard") && /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
9512
- ChatInput,
9513
- {
9514
- ref: inputRef,
9515
- placeholder: ui.placeholder,
9516
- streaming: state.isStreaming,
9517
- onSend: handleSend,
9518
- onStop: stop,
9519
- stopLabel: ui.stopResponse,
9520
- sendLabel: ui.sendMessage,
9521
- disabled: !!tool,
9522
- onType: fold
9523
- }
9524
- )
9601
+ (!monitor || monitor.activeTab !== "games" && monitor.activeTab !== "leaderboard") && /* @__PURE__ */ (0, import_jsx_runtime95.jsxs)(import_jsx_runtime95.Fragment, { children: [
9602
+ /* @__PURE__ */ (0, import_jsx_runtime95.jsx)("div", { ref: limitRef, className: "ss-w-limit", role: "status", tabIndex: -1, children: state.limit ? limitText(state.limit, ui) : null }),
9603
+ paused ? null : /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
9604
+ ChatInput,
9605
+ {
9606
+ ref: inputRef,
9607
+ placeholder: ui.placeholder,
9608
+ streaming: state.isStreaming,
9609
+ onSend: handleSend,
9610
+ onStop: stop,
9611
+ stopLabel: ui.stopResponse,
9612
+ sendLabel: ui.sendMessage,
9613
+ disabled: !!tool,
9614
+ onType: fold
9615
+ }
9616
+ )
9617
+ ] })
9525
9618
  ] });
9526
9619
  }
9527
9620
 
@@ -10119,6 +10212,10 @@ var WIDGET_CSS = `.ss-w-root { all: revert; }
10119
10212
 
10120
10213
  /* input */
10121
10214
  .ss-w-input { display: flex; align-items: flex-end; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--ss-w-line); }
10215
+ .ss-w-limit { padding: 10px 12px; border-top: 1px solid var(--ss-w-line); font-size: 13px; line-height: 1.4; color: var(--ss-w-ink); overflow-wrap: anywhere; }
10216
+ .ss-w-limit:empty { padding: 0; border-top: 0; }
10217
+ .ss-w-limit:focus { outline: none; }
10218
+ .ss-w-limit:not(:empty) + .ss-w-input { border-top: none; }
10122
10219
  .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; }
10123
10220
  .ss-w-textarea:disabled { opacity: .55; cursor: not-allowed; }
10124
10221
  /* iOS Safari zooms the page when a focused field's font-size is < 16px. Bump it
@@ -11217,7 +11314,7 @@ function buildClient(props) {
11217
11314
  return { client: props.client, conversation: props.client.conversation() };
11218
11315
  }
11219
11316
  const cfg = props.config;
11220
- const client = new import_widget_sdk5.StatsWidgetClient(toClientOptions(cfg));
11317
+ const client = new import_widget_sdk6.StatsWidgetClient(toClientOptions(cfg));
11221
11318
  return { client, conversation: client.conversation(toUserContext(cfg)) };
11222
11319
  } catch (err) {
11223
11320
  console.error("[StatsWidget] failed to initialize:", err instanceof Error ? err.message : err);