@agentforge-io/chat-sdk 2.4.0-dev.0 → 2.4.0-dev.1

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.
Files changed (2) hide show
  1. package/dist/react.js +36 -1
  2. package/package.json +1 -1
package/dist/react.js CHANGED
@@ -385,6 +385,28 @@ function ChatWidget(props) {
385
385
  hasInteractedRef.current = true;
386
386
  setDraft('');
387
387
  void session.send(text);
388
+ // Mobile UX: when the send button disables (sendDisabled flips
389
+ // true the moment status goes to 'sending'), focus jumps to
390
+ // <body> and the on-screen keyboard collapses. Re-anchor focus
391
+ // on the textarea synchronously so the visitor keeps typing
392
+ // their next message without re-tapping. We schedule one rAF
393
+ // to win the race against React's status flip + button disable
394
+ // (a single tick is enough — mobile browsers haven't decided
395
+ // to dismiss the keyboard yet).
396
+ if (typeof window !== 'undefined') {
397
+ const el = inputRef.current;
398
+ if (el) {
399
+ el.focus({ preventScroll: true });
400
+ // Belt-and-braces: re-focus in the next frame in case
401
+ // React's render happens AFTER our synchronous focus call
402
+ // and steals it via the button-disable side effect.
403
+ window.requestAnimationFrame(() => {
404
+ if (document.activeElement !== el) {
405
+ el.focus({ preventScroll: true });
406
+ }
407
+ });
408
+ }
409
+ }
388
410
  }, [session, draft]);
389
411
  const onKeyDown = (0, react_1.useCallback)((e) => {
390
412
  if (e.key === 'Enter' && !e.shiftKey) {
@@ -531,7 +553,20 @@ function ChatWidget(props) {
531
553
  onShortcutClick(text, i);
532
554
  else
533
555
  setDraft(text);
534
- }, children: text }, `${i}-${text}`))) })), (0, jsx_runtime_1.jsxs)("div", { className: "af-input-row", children: [composerLeftSlot && ((0, jsx_runtime_1.jsx)("div", { className: "af-input-left", children: composerLeftSlot })), (0, jsx_runtime_1.jsx)("textarea", { ref: inputRef, className: "af-input", value: draft, onChange: (e) => setDraft(e.target.value), onKeyDown: onKeyDown, placeholder: inputPlaceholder ?? 'Type a message…', rows: 1, disabled: status === 'ended' || status === 'loading' || status === 'idle' }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "af-send", 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" })] })] }));
556
+ }, children: text }, `${i}-${text}`))) })), (0, jsx_runtime_1.jsxs)("div", { className: "af-input-row", children: [composerLeftSlot && ((0, jsx_runtime_1.jsx)("div", { className: "af-input-left", children: composerLeftSlot })), (0, jsx_runtime_1.jsx)("textarea", { ref: inputRef, className: "af-input", value: draft, onChange: (e) => setDraft(e.target.value), onKeyDown: onKeyDown, placeholder: inputPlaceholder ?? 'Type a message…', rows: 1,
557
+ // The textarea stays editable while the agent is
558
+ // streaming so the visitor can compose their next
559
+ // message without waiting. Only block when the
560
+ // conversation has actually ended OR before the
561
+ // session is ready at all.
562
+ disabled: status === 'ended' || status === 'loading' || status === 'idle' }), (0, jsx_runtime_1.jsx)("button", { type: "button", className: "af-send",
563
+ // `onMouseDown preventDefault` prevents the button from
564
+ // stealing focus from the textarea when the visitor
565
+ // taps Send. Without this, the focus shifts to the
566
+ // button just before `handleSend` runs, the button
567
+ // then disables (sendDisabled flips true), focus jumps
568
+ // to <body>, and on mobile the keyboard collapses.
569
+ onMouseDown: (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" })] })] }));
535
570
  }
536
571
  function MessageBubble({ message, session, readOnly, onDecision, onContinue, bare = false, showAvatar = false, avatarTheme, avatarName, avatarAgentId, speakerLabel, }) {
537
572
  const kind = message.metadata?.kind;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge-io/chat-sdk",
3
- "version": "2.4.0-dev.0",
3
+ "version": "2.4.0-dev.1",
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",