@optilogic/chat 1.0.0-beta.6 → 1.0.0-beta.7

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.d.ts CHANGED
@@ -40,6 +40,37 @@ interface MemoryItem {
40
40
  content: string;
41
41
  timestamp: number;
42
42
  }
43
+ /**
44
+ * Status update information from the agent
45
+ */
46
+ interface StatusItem {
47
+ id: string;
48
+ message: string;
49
+ timestamp: number;
50
+ /** Optional agent name if in multi-agent scenario */
51
+ agent?: string;
52
+ }
53
+ /**
54
+ * A single step in structured thinking content
55
+ */
56
+ interface ThinkingStep {
57
+ /** Unique identifier for the step */
58
+ id: string;
59
+ /** Label/title shown in the collapsible header */
60
+ label: string;
61
+ /** Content of the thinking step */
62
+ content: string;
63
+ /** Nesting depth (0 = root level, 1 = first indent, etc.) */
64
+ depth: number;
65
+ /** Whether this step should start collapsed (default: false) */
66
+ isCollapsed?: boolean;
67
+ }
68
+ /**
69
+ * Union type for thinking content
70
+ * - string: plain text (backward compatible)
71
+ * - ThinkingStep[]: structured with collapsible sub-sections
72
+ */
73
+ type ThinkingContent = string | ThinkingStep[];
43
74
  /**
44
75
  * State shape for the agent response component
45
76
  */
@@ -48,12 +79,16 @@ interface AgentResponseState {
48
79
  status: AgentResponseStatus;
49
80
  /** Accumulated thinking/reasoning text */
50
81
  thinking: string;
82
+ /** Structured thinking steps (if provided, takes precedence over thinking string) */
83
+ thinkingSteps?: ThinkingStep[];
51
84
  /** Tool calls made during processing */
52
85
  toolCalls: ToolCall[];
53
86
  /** Knowledge items retrieved */
54
87
  knowledge: KnowledgeItem[];
55
88
  /** Memory items accessed */
56
89
  memory: MemoryItem[];
90
+ /** Status updates from the agent */
91
+ statusUpdates: StatusItem[];
57
92
  /** Final response text */
58
93
  response: string;
59
94
  /** Timestamp when first thinking message was received (for timer) */
@@ -67,7 +102,7 @@ interface AgentResponseState {
67
102
  * WebSocket message payload for agent responses
68
103
  */
69
104
  interface AgentMessage {
70
- type: "status" | "thinking" | "tool_call" | "knowledge" | "memory" | "response";
105
+ type: "status" | "thinking" | "tool_call" | "knowledge" | "memory" | "response" | "status_update";
71
106
  /** Message content - for simple string payloads */
72
107
  message?: string;
73
108
  /** Alternative content field */
@@ -92,6 +127,20 @@ interface AgentMessage {
92
127
  type: string;
93
128
  content: string;
94
129
  };
130
+ /** For status_update messages */
131
+ statusUpdate?: {
132
+ id: string;
133
+ message: string;
134
+ agent?: string;
135
+ };
136
+ /** For structured thinking step messages */
137
+ thinkingStep?: {
138
+ id?: string;
139
+ label: string;
140
+ content: string;
141
+ depth?: number;
142
+ isCollapsed?: boolean;
143
+ };
95
144
  }
96
145
  /**
97
146
  * Generic websocket message wrapper type
@@ -142,6 +191,17 @@ interface AgentResponseProps extends React.HTMLAttributes<HTMLDivElement> {
142
191
  * />
143
192
  */
144
193
  renderMarkdown?: (content: string) => React.ReactNode;
194
+ /**
195
+ * Custom markdown renderer for the thinking content.
196
+ * If not provided, the thinking will be rendered as plain preformatted text.
197
+ *
198
+ * @example
199
+ * <AgentResponse
200
+ * state={state}
201
+ * renderThinkingMarkdown={(content) => <MyMarkdownRenderer content={content} />}
202
+ * />
203
+ */
204
+ renderThinkingMarkdown?: (content: string) => React.ReactNode;
145
205
  }
146
206
  /**
147
207
  * AgentResponse Component
@@ -190,6 +250,8 @@ interface ActivityIndicatorsProps extends React.HTMLAttributes<HTMLDivElement> {
190
250
  knowledge: KnowledgeItem[];
191
251
  /** Memory items to display */
192
252
  memory: MemoryItem[];
253
+ /** Status updates to display */
254
+ statusUpdates?: StatusItem[];
193
255
  }
194
256
  /**
195
257
  * ActivityIndicators Component
@@ -225,6 +287,8 @@ interface MetadataRowProps extends React.HTMLAttributes<HTMLDivElement> {
225
287
  knowledge: KnowledgeItem[];
226
288
  /** Memory items to display */
227
289
  memory: MemoryItem[];
290
+ /** Status updates to display */
291
+ statusUpdates?: StatusItem[];
228
292
  /** Current response status */
229
293
  status: AgentResponseStatus;
230
294
  /** Elapsed time in seconds */
@@ -253,22 +317,40 @@ declare const MetadataRow: React.ForwardRefExoticComponent<MetadataRowProps & Re
253
317
  /**
254
318
  * Thinking Section Component
255
319
  *
256
- * Collapsible section for displaying agent thinking/reasoning content
320
+ * Collapsible section for displaying agent thinking/reasoning content.
321
+ * Supports both plain text and structured collapsible sub-sections.
257
322
  */
258
323
 
259
- interface ThinkingSectionProps extends React.HTMLAttributes<HTMLDivElement> {
260
- /** The thinking content to display */
261
- content: string;
324
+ interface ThinkingSectionProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "content"> {
325
+ /** The thinking content to display (string or structured steps) */
326
+ content: string | ThinkingStep[];
262
327
  /** Whether the section is expanded */
263
328
  isExpanded: boolean;
329
+ /**
330
+ * Custom markdown renderer for the thinking content.
331
+ * If not provided, the content will be rendered as plain preformatted text.
332
+ */
333
+ renderMarkdown?: (content: string) => React.ReactNode;
264
334
  }
265
335
  /**
266
336
  * ThinkingSection Component
267
337
  *
268
338
  * Displays the agent's thinking/reasoning content in a collapsible panel.
339
+ * Supports both plain text content and structured collapsible sub-sections.
269
340
  *
270
341
  * @example
342
+ * // Plain text content
271
343
  * <ThinkingSection content={state.thinking} isExpanded={isExpanded} />
344
+ *
345
+ * @example
346
+ * // Structured content with sub-sections
347
+ * <ThinkingSection
348
+ * content={[
349
+ * { id: "1", label: "Analysis", content: "...", depth: 0 },
350
+ * { id: "2", label: "Sub-analysis", content: "...", depth: 1 },
351
+ * ]}
352
+ * isExpanded={isExpanded}
353
+ * />
272
354
  */
273
355
  declare const ThinkingSection: React.ForwardRefExoticComponent<ThinkingSectionProps & React.RefAttributes<HTMLDivElement>>;
274
356
 
@@ -421,6 +503,16 @@ interface UserPromptInputProps extends Omit<React.HTMLAttributes<HTMLDivElement>
421
503
  disabled?: boolean;
422
504
  /** Whether a submission is in progress (shows loading state) */
423
505
  isSubmitting?: boolean;
506
+ /** Called when user clicks Stop during submission */
507
+ onStop?: () => void;
508
+ /** Whether to disable input while submitting (default: true) */
509
+ disableWhileSubmitting?: boolean;
510
+ /** Auto-focus the editor when mounted (handles Slate initialization timing) */
511
+ autoFocus?: boolean;
512
+ /** Refocus the input after submission completes (default: false) */
513
+ refocusAfterSubmit?: boolean;
514
+ /** Called when the editor is fully initialized and ready for interaction */
515
+ onReady?: () => void;
424
516
  /** Minimum number of rows for the editor */
425
517
  minRows?: number;
426
518
  /** Maximum number of rows before scrolling */
@@ -466,4 +558,4 @@ interface UserPromptInputRef {
466
558
  */
467
559
  declare const UserPromptInput: React.ForwardRefExoticComponent<UserPromptInputProps & React.RefAttributes<UserPromptInputRef>>;
468
560
 
469
- export { ActionBar, type ActionBarProps, ActivityIndicators, type ActivityIndicatorsProps, type AgentMessage, AgentResponse, type AgentResponseProps, type AgentResponseState, type AgentResponseStatus, type FeedbackValue, type GenericWebSocketMessage, type KnowledgeItem, type MemoryItem, MetadataRow, type MetadataRowProps, ThinkingSection, type ThinkingSectionProps, type ToolCall, type UseAgentResponseAccumulatorOptions, type UseAgentResponseAccumulatorReturn, type UseThinkingTimerOptions, UserPrompt, UserPromptInput, type UserPromptInputProps, type UserPromptInputRef, type UserPromptProps, formatTime, formatTotalTime, initialAgentResponseState, useAgentResponseAccumulator, useThinkingTimer };
561
+ export { ActionBar, type ActionBarProps, ActivityIndicators, type ActivityIndicatorsProps, type AgentMessage, AgentResponse, type AgentResponseProps, type AgentResponseState, type AgentResponseStatus, type FeedbackValue, type GenericWebSocketMessage, type KnowledgeItem, type MemoryItem, MetadataRow, type MetadataRowProps, type StatusItem, type ThinkingContent, ThinkingSection, type ThinkingSectionProps, type ThinkingStep, type ToolCall, type UseAgentResponseAccumulatorOptions, type UseAgentResponseAccumulatorReturn, type UseThinkingTimerOptions, UserPrompt, UserPromptInput, type UserPromptInputProps, type UserPromptInputRef, type UserPromptProps, formatTime, formatTotalTime, initialAgentResponseState, useAgentResponseAccumulator, useThinkingTimer };
package/dist/index.js CHANGED
@@ -1,16 +1,36 @@
1
1
  import * as React7 from 'react';
2
2
  import { useState, useCallback, useMemo, useEffect } from 'react';
3
3
  import { cn, Popover, PopoverTrigger, PopoverContent, IconButton, LoadingSpinner } from '@optilogic/core';
4
- import { Wrench, Book, HardDrive, Check, Copy, ThumbsUp, ThumbsDown, Loader2, Send, ChevronUp, ChevronDown } from 'lucide-react';
4
+ import { Activity, Wrench, Book, HardDrive, Check, Copy, ThumbsUp, ThumbsDown, Square, Loader2, Send, ChevronUp, ChevronDown, ChevronRight } from 'lucide-react';
5
5
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
6
  import { SlateEditor, Text } from '@optilogic/editor';
7
7
 
8
8
  // src/components/agent-response/AgentResponse.tsx
9
9
  var ActivityIndicators = React7.forwardRef(
10
- ({ toolCalls, knowledge, memory, className, ...props }, ref) => {
11
- const hasAnyActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0;
10
+ ({ toolCalls, knowledge, memory, statusUpdates = [], className, ...props }, ref) => {
11
+ const hasAnyActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0 || statusUpdates.length > 0;
12
12
  if (!hasAnyActivity) return null;
13
13
  return /* @__PURE__ */ jsxs("div", { ref, className: cn("flex items-center gap-2", className), ...props, children: [
14
+ statusUpdates.length > 0 && /* @__PURE__ */ jsxs(Popover, { children: [
15
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
16
+ "button",
17
+ {
18
+ className: "flex items-center gap-1 text-muted-foreground hover:text-foreground transition-colors",
19
+ onClick: (e) => e.stopPropagation(),
20
+ children: [
21
+ /* @__PURE__ */ jsx(Activity, { className: "w-3.5 h-3.5" }),
22
+ /* @__PURE__ */ jsx("span", { className: "text-xs", children: statusUpdates.length })
23
+ ]
24
+ }
25
+ ) }),
26
+ /* @__PURE__ */ jsx(PopoverContent, { className: "w-80", children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
27
+ /* @__PURE__ */ jsx("h4", { className: "font-medium text-sm", children: "Status Updates" }),
28
+ /* @__PURE__ */ jsx("div", { className: "space-y-2 max-h-60 overflow-auto", children: statusUpdates.map((item) => /* @__PURE__ */ jsxs("div", { className: "p-2 bg-muted rounded text-xs", children: [
29
+ item.agent && /* @__PURE__ */ jsx("div", { className: "font-medium text-muted-foreground", children: item.agent }),
30
+ /* @__PURE__ */ jsx("div", { className: item.agent ? "mt-1" : "", children: item.message })
31
+ ] }, item.id)) })
32
+ ] }) })
33
+ ] }),
14
34
  toolCalls.length > 0 && /* @__PURE__ */ jsxs(Popover, { children: [
15
35
  /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
16
36
  "button",
@@ -113,6 +133,7 @@ var MetadataRow = React7.forwardRef(
113
133
  toolCalls,
114
134
  knowledge,
115
135
  memory,
136
+ statusUpdates = [],
116
137
  status,
117
138
  elapsedTime,
118
139
  className,
@@ -120,7 +141,7 @@ var MetadataRow = React7.forwardRef(
120
141
  }, ref) => {
121
142
  const isProcessing = status === "processing";
122
143
  const isComplete = status === "complete";
123
- const hasActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0;
144
+ const hasActivity = toolCalls.length > 0 || knowledge.length > 0 || memory.length > 0 || statusUpdates.length > 0;
124
145
  const renderLeftContent = () => {
125
146
  if (hasThinking) {
126
147
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
@@ -160,7 +181,8 @@ var MetadataRow = React7.forwardRef(
160
181
  {
161
182
  toolCalls,
162
183
  knowledge,
163
- memory
184
+ memory,
185
+ statusUpdates
164
186
  }
165
187
  )
166
188
  ]
@@ -169,18 +191,55 @@ var MetadataRow = React7.forwardRef(
169
191
  }
170
192
  );
171
193
  MetadataRow.displayName = "MetadataRow";
194
+ var ThinkingStepItem = ({ step, renderMarkdown }) => {
195
+ const [isCollapsed, setIsCollapsed] = useState(step.isCollapsed ?? false);
196
+ const toggleCollapse = useCallback(() => {
197
+ setIsCollapsed((prev) => !prev);
198
+ }, []);
199
+ const indentPadding = step.depth * 16;
200
+ return /* @__PURE__ */ jsxs("div", { className: "border-b border-border/50 last:border-b-0", children: [
201
+ /* @__PURE__ */ jsxs(
202
+ "button",
203
+ {
204
+ onClick: toggleCollapse,
205
+ className: "w-full flex items-center gap-1.5 py-1.5 px-2 hover:bg-muted/50 transition-colors text-left",
206
+ style: { paddingLeft: `${indentPadding + 8}px` },
207
+ children: [
208
+ isCollapsed ? /* @__PURE__ */ jsx(ChevronRight, { className: "w-3 h-3 text-muted-foreground flex-shrink-0" }) : /* @__PURE__ */ jsx(ChevronDown, { className: "w-3 h-3 text-muted-foreground flex-shrink-0" }),
209
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground/80", children: step.label })
210
+ ]
211
+ }
212
+ ),
213
+ !isCollapsed && /* @__PURE__ */ jsx(
214
+ "div",
215
+ {
216
+ className: "pb-2 px-2",
217
+ style: { paddingLeft: `${indentPadding + 28}px` },
218
+ children: renderMarkdown ? /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: renderMarkdown(step.content) }) : /* @__PURE__ */ jsx("pre", { className: "text-xs text-muted-foreground whitespace-pre-wrap font-mono", children: step.content })
219
+ }
220
+ )
221
+ ] });
222
+ };
172
223
  var ThinkingSection = React7.forwardRef(
173
- ({ content, isExpanded, className, ...props }, ref) => {
174
- if (!isExpanded || !content) {
224
+ ({ content, isExpanded, renderMarkdown, className, ...props }, ref) => {
225
+ if (!isExpanded || !content || Array.isArray(content) && content.length === 0) {
175
226
  return null;
176
227
  }
228
+ const isStructured = Array.isArray(content);
177
229
  return /* @__PURE__ */ jsx(
178
230
  "div",
179
231
  {
180
232
  ref,
181
233
  className: cn("px-3 pb-3 border-t border-border", className),
182
234
  ...props,
183
- children: /* @__PURE__ */ jsx("div", { className: "mt-2 max-h-[200px] overflow-y-auto", children: /* @__PURE__ */ jsx("pre", { className: "text-xs text-muted-foreground whitespace-pre-wrap font-mono", children: content }) })
235
+ children: /* @__PURE__ */ jsx("div", { className: "mt-2 max-h-[200px] overflow-y-auto", children: isStructured ? /* @__PURE__ */ jsx("div", { className: "space-y-0", children: content.map((step) => /* @__PURE__ */ jsx(
236
+ ThinkingStepItem,
237
+ {
238
+ step,
239
+ renderMarkdown
240
+ },
241
+ step.id
242
+ )) }) : renderMarkdown ? /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: renderMarkdown(content) }) : /* @__PURE__ */ jsx("pre", { className: "text-xs text-muted-foreground whitespace-pre-wrap font-mono", children: content }) })
184
243
  }
185
244
  );
186
245
  }
@@ -309,6 +368,7 @@ var initialAgentResponseState = {
309
368
  toolCalls: [],
310
369
  knowledge: [],
311
370
  memory: [],
371
+ statusUpdates: [],
312
372
  response: "",
313
373
  thinkingStartTime: null,
314
374
  responseCompleteTime: null,
@@ -343,6 +403,23 @@ function useAgentResponseAccumulator(options) {
343
403
  }
344
404
  return { ...prev, status: newStatus };
345
405
  case "thinking": {
406
+ if (payload.thinkingStep) {
407
+ const newStep = {
408
+ id: payload.thinkingStep.id || `step-${Date.now()}`,
409
+ label: payload.thinkingStep.label,
410
+ content: payload.thinkingStep.content,
411
+ depth: payload.thinkingStep.depth ?? 0,
412
+ isCollapsed: payload.thinkingStep.isCollapsed
413
+ };
414
+ const thinkingStartTime2 = prev.thinkingStartTime ?? Date.now();
415
+ return {
416
+ ...prev,
417
+ status: newStatus,
418
+ thinkingSteps: [...prev.thinkingSteps || [], newStep],
419
+ thinkingStartTime: thinkingStartTime2,
420
+ firstMessageTime
421
+ };
422
+ }
346
423
  const newThinking = payload.message || payload.content || "";
347
424
  const separator = prev.thinking && newThinking ? "\n\n" : "";
348
425
  const thinkingStartTime = prev.thinkingStartTime ?? (newThinking ? Date.now() : null);
@@ -416,6 +493,24 @@ function useAgentResponseAccumulator(options) {
416
493
  responseCompleteTime: Date.now(),
417
494
  firstMessageTime: prev.firstMessageTime ?? Date.now()
418
495
  };
496
+ case "status_update": {
497
+ const statusMessage = payload.message || payload.statusUpdate?.message;
498
+ if (statusMessage) {
499
+ const newStatusItem = {
500
+ id: payload.statusUpdate?.id || `status-${Date.now()}`,
501
+ message: statusMessage,
502
+ agent: payload.statusUpdate?.agent,
503
+ timestamp: Date.now()
504
+ };
505
+ return {
506
+ ...prev,
507
+ status: newStatus,
508
+ statusUpdates: [...prev.statusUpdates, newStatusItem],
509
+ firstMessageTime
510
+ };
511
+ }
512
+ return { ...prev, status: newStatus, firstMessageTime };
513
+ }
419
514
  default:
420
515
  return { ...prev, status: newStatus, firstMessageTime };
421
516
  }
@@ -441,6 +536,7 @@ var AgentResponse = React7.forwardRef(
441
536
  onThinkingExpandedChange,
442
537
  actionsVisible = "hover",
443
538
  renderMarkdown,
539
+ renderThinkingMarkdown,
444
540
  className,
445
541
  ...props
446
542
  }, ref) => {
@@ -465,8 +561,9 @@ var AgentResponse = React7.forwardRef(
465
561
  if (!state.firstMessageTime || !state.responseCompleteTime) return 0;
466
562
  return (state.responseCompleteTime - state.firstMessageTime) / 1e3;
467
563
  }, [state.firstMessageTime, state.responseCompleteTime]);
468
- const hasAnyContent = state.thinking || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.response;
469
- const showMetadataRow = state.thinking || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.status === "processing";
564
+ const hasThinkingContent = !!state.thinking || state.thinkingSteps && state.thinkingSteps.length > 0 || false;
565
+ const hasAnyContent = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || state.response;
566
+ const showMetadataRow = hasThinkingContent || state.toolCalls.length > 0 || state.knowledge.length > 0 || state.memory.length > 0 || state.statusUpdates.length > 0 || state.status === "processing";
470
567
  const showActionBar = state.status === "complete" && state.response;
471
568
  const isActionBarVisible = actionsVisible === true || actionsVisible === "hover" && isHovered;
472
569
  if (!hasAnyContent) {
@@ -486,12 +583,13 @@ var AgentResponse = React7.forwardRef(
486
583
  /* @__PURE__ */ jsx(
487
584
  MetadataRow,
488
585
  {
489
- hasThinking: !!state.thinking,
586
+ hasThinking: hasThinkingContent,
490
587
  isExpanded: thinkingExpanded,
491
588
  onToggle: toggleThinking,
492
589
  toolCalls: state.toolCalls,
493
590
  knowledge: state.knowledge,
494
591
  memory: state.memory,
592
+ statusUpdates: state.statusUpdates,
495
593
  status: state.status,
496
594
  elapsedTime
497
595
  }
@@ -499,8 +597,9 @@ var AgentResponse = React7.forwardRef(
499
597
  /* @__PURE__ */ jsx(
500
598
  ThinkingSection,
501
599
  {
502
- content: state.thinking,
503
- isExpanded: thinkingExpanded
600
+ content: state.thinkingSteps && state.thinkingSteps.length > 0 ? state.thinkingSteps : state.thinking,
601
+ isExpanded: thinkingExpanded,
602
+ renderMarkdown: renderThinkingMarkdown
504
603
  }
505
604
  )
506
605
  ] }),
@@ -640,6 +739,11 @@ var UserPromptInput = React7.forwardRef(
640
739
  placeholder = "Type your message...",
641
740
  disabled = false,
642
741
  isSubmitting = false,
742
+ onStop,
743
+ disableWhileSubmitting = true,
744
+ autoFocus = false,
745
+ refocusAfterSubmit = false,
746
+ onReady,
643
747
  minRows = 1,
644
748
  maxRows = 6,
645
749
  renderActions,
@@ -651,13 +755,52 @@ var UserPromptInput = React7.forwardRef(
651
755
  }, ref) => {
652
756
  const editorRef = React7.useRef(null);
653
757
  const [internalValue, setInternalValue] = React7.useState(value);
758
+ const prevIsSubmitting = React7.useRef(isSubmitting);
759
+ const hasEmittedReady = React7.useRef(false);
654
760
  React7.useEffect(() => {
655
761
  setInternalValue(value);
656
762
  }, [value]);
763
+ React7.useEffect(() => {
764
+ if (autoFocus) {
765
+ requestAnimationFrame(() => {
766
+ requestAnimationFrame(() => {
767
+ editorRef.current?.focus();
768
+ });
769
+ });
770
+ }
771
+ }, [autoFocus]);
772
+ React7.useEffect(() => {
773
+ if (!hasEmittedReady.current && onReady) {
774
+ requestAnimationFrame(() => {
775
+ requestAnimationFrame(() => {
776
+ hasEmittedReady.current = true;
777
+ onReady();
778
+ });
779
+ });
780
+ }
781
+ }, [onReady]);
782
+ React7.useEffect(() => {
783
+ if (refocusAfterSubmit && prevIsSubmitting.current && !isSubmitting) {
784
+ requestAnimationFrame(() => {
785
+ editorRef.current?.focus();
786
+ });
787
+ }
788
+ prevIsSubmitting.current = isSubmitting;
789
+ }, [isSubmitting, refocusAfterSubmit]);
657
790
  React7.useImperativeHandle(
658
791
  ref,
659
792
  () => ({
660
- focus: () => editorRef.current?.focus(),
793
+ focus: () => {
794
+ try {
795
+ editorRef.current?.focus();
796
+ } catch {
797
+ requestAnimationFrame(() => {
798
+ requestAnimationFrame(() => {
799
+ editorRef.current?.focus();
800
+ });
801
+ });
802
+ }
803
+ },
661
804
  clear: () => {
662
805
  editorRef.current?.clear();
663
806
  setInternalValue("");
@@ -711,7 +854,7 @@ var UserPromptInput = React7.forwardRef(
711
854
  onSubmit: handleSubmit,
712
855
  clearOnSubmit: false,
713
856
  placeholder,
714
- disabled: disabled || isSubmitting,
857
+ disabled: disabled || disableWhileSubmitting && isSubmitting,
715
858
  enableTags,
716
859
  onTagCreate,
717
860
  onTagDelete,
@@ -723,7 +866,16 @@ var UserPromptInput = React7.forwardRef(
723
866
  ) }),
724
867
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pl-2 pr-1 pb-1 pt-1", children: [
725
868
  /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: renderActions?.() }),
726
- /* @__PURE__ */ jsx(
869
+ isSubmitting && onStop ? /* @__PURE__ */ jsx(
870
+ IconButton,
871
+ {
872
+ icon: /* @__PURE__ */ jsx(Square, {}),
873
+ variant: "filled",
874
+ size: "sm",
875
+ "aria-label": "Stop",
876
+ onClick: onStop
877
+ }
878
+ ) : /* @__PURE__ */ jsx(
727
879
  IconButton,
728
880
  {
729
881
  icon: isSubmitting ? /* @__PURE__ */ jsx(Loader2, { className: "animate-spin" }) : /* @__PURE__ */ jsx(Send, {}),