@agentforge-io/chat-sdk 2.4.0-dev.10 → 2.4.0-dev.11

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/react.d.ts CHANGED
@@ -205,6 +205,19 @@ export interface ChatWidgetHandle {
205
205
  * the textarea hasn't mounted yet or is disabled.
206
206
  */
207
207
  focus(): void;
208
+ /**
209
+ * Warm up the underlying session — fires the initial
210
+ * `GET /agent` (and optionally a `resumeConversation` lookup) so
211
+ * the first `send()` doesn't pay the round-trip. Idempotent:
212
+ * subsequent calls join the same in-flight promise.
213
+ *
214
+ * Hosts call this when the visitor signals INTENT to chat (e.g.
215
+ * tapping a fake-composer pill that opens a drawer) so the
216
+ * session boot overlaps with the visitor finding the keyboard
217
+ * and tapping out their first message. Safe no-op if the session
218
+ * is already past `loading`.
219
+ */
220
+ warmup(): void;
208
221
  }
209
222
  /**
210
223
  * Drop-in chat widget. Owns its own `ChatSession` and re-renders on every
package/dist/react.js CHANGED
@@ -448,6 +448,14 @@ function ChatWidget(props) {
448
448
  return;
449
449
  el.focus({ preventScroll: true });
450
450
  },
451
+ warmup: () => {
452
+ // session.start() is idempotent — joins the in-flight
453
+ // promise if one exists, resolves immediately if start
454
+ // already completed.
455
+ if (!session)
456
+ return;
457
+ void session.start();
458
+ },
451
459
  };
452
460
  return () => {
453
461
  // Drop the handle on unmount so a stale ref can't fire send
@@ -553,7 +561,9 @@ function ChatWidget(props) {
553
561
  // before `handleSend` runs, the button then disables
554
562
  // (sendDisabled flips true on status change), focus
555
563
  // jumps to <body>, and the on-screen keyboard collapses.
556
- onPointerDown: (e) => e.preventDefault(), onClick: handleSend, disabled: sendDisabled, "aria-label": "Send message", children: (0, jsx_runtime_1.jsx)(SendIcon, {}) })] }), !bare && (0, jsx_runtime_1.jsx)("div", { className: "af-footer", children: "Powered by AgentForge" })] })] }));
564
+ onPointerDown: (e) => e.preventDefault(), onClick: handleSend, disabled: sendDisabled, "aria-label": status === 'sending' || status === 'streaming'
565
+ ? 'Sending message'
566
+ : 'Send message', children: status === 'sending' || status === 'streaming' ? ((0, jsx_runtime_1.jsx)(SpinnerIcon, {})) : ((0, jsx_runtime_1.jsx)(SendIcon, {})) })] }), !bare && (0, jsx_runtime_1.jsx)("div", { className: "af-footer", children: "Powered by AgentForge" })] })] }));
557
567
  }
558
568
  function MessageBubble({ message, session, readOnly, onDecision, onContinue, bare = false, showAvatar = false, avatarTheme, avatarName, avatarAgentId, speakerLabel, }) {
559
569
  const kind = message.metadata?.kind;
@@ -726,6 +736,11 @@ function CloseIcon() {
726
736
  function SendIcon() {
727
737
  return ((0, jsx_runtime_1.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": true, children: [(0, jsx_runtime_1.jsx)("line", { x1: "22", y1: "2", x2: "11", y2: "13" }), (0, jsx_runtime_1.jsx)("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })] }));
728
738
  }
739
+ function SpinnerIcon() {
740
+ // Inline SVG spinner — no external dep. Stroke-dasharray
741
+ // arc rotates via the CSS keyframes injected by WIDGET_CSS.
742
+ return ((0, jsx_runtime_1.jsxs)("svg", { viewBox: "0 0 24 24", fill: "none", className: "af-spinner", "aria-hidden": true, children: [(0, jsx_runtime_1.jsx)("circle", { cx: "12", cy: "12", r: "9", stroke: "currentColor", strokeOpacity: "0.25", strokeWidth: "2.5" }), (0, jsx_runtime_1.jsx)("path", { d: "M21 12a9 9 0 0 0-9-9", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round" })] }));
743
+ }
729
744
  // Stylesheet kept verbatim from the standalone widget.js so the React
730
745
  // component is visually indistinguishable from the script-injected one.
731
746
  const WIDGET_CSS = `
@@ -869,6 +884,8 @@ const WIDGET_CSS = `
869
884
  .af-send:active:not(:disabled) { transform: translateY(0); }
870
885
  .af-send:disabled { opacity: 0.5; cursor: not-allowed; }
871
886
  .af-send svg { width: 16px; height: 16px; }
887
+ .af-send .af-spinner { animation: af-spin 720ms linear infinite; }
888
+ @keyframes af-spin { to { transform: rotate(360deg); } }
872
889
  .af-error { padding: 10px 16px; font-size: 12px; color: #b91c1c; background: #fef2f2; border-top: 1px solid #fee2e2; }
873
890
  .af-footer { padding: 6px 12px; font-size: 10px; color: var(--af-muted); text-align: center; background: var(--af-bubble-bg); border-top: 1px solid var(--af-border); }
874
891
  /* Approval and blocked bubbles. Amber for needs-decision, red for
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge-io/chat-sdk",
3
- "version": "2.4.0-dev.10",
3
+ "version": "2.4.0-dev.11",
4
4
  "description": "Framework-free chat session SDK for AgentForge public chat tokens. Headless — no DOM. Drop into any frontend (React, Vue, Svelte, vanilla) and listen for events.",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",