@moldable-ai/ui 0.2.1 → 0.2.3

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 (64) hide show
  1. package/dist/components/chat/chat-message.d.ts +4 -0
  2. package/dist/components/chat/chat-message.d.ts.map +1 -1
  3. package/dist/components/chat/chat-message.js +64 -10
  4. package/dist/components/chat/chat-panel.d.ts +20 -1
  5. package/dist/components/chat/chat-panel.d.ts.map +1 -1
  6. package/dist/components/chat/chat-panel.js +20 -5
  7. package/dist/components/chat/index.d.ts +4 -1
  8. package/dist/components/chat/index.d.ts.map +1 -1
  9. package/dist/components/chat/index.js +4 -1
  10. package/dist/components/chat/tool-approval-context.d.ts +21 -0
  11. package/dist/components/chat/tool-approval-context.d.ts.map +1 -0
  12. package/dist/components/chat/tool-approval-context.js +19 -0
  13. package/dist/components/chat/tool-approval.d.ts +85 -0
  14. package/dist/components/chat/tool-approval.d.ts.map +1 -0
  15. package/dist/components/chat/tool-approval.js +80 -0
  16. package/dist/components/chat/tool-handlers.d.ts +21 -0
  17. package/dist/components/chat/tool-handlers.d.ts.map +1 -1
  18. package/dist/components/chat/tool-handlers.js +147 -35
  19. package/dist/components/chat/tool-progress-context.d.ts +27 -0
  20. package/dist/components/chat/tool-progress-context.d.ts.map +1 -0
  21. package/dist/components/chat/tool-progress-context.js +26 -0
  22. package/dist/components/ui/button.d.ts +3 -2
  23. package/dist/components/ui/button.d.ts.map +1 -1
  24. package/dist/components/ui/button.js +6 -5
  25. package/dist/components/ui/card.d.ts +7 -8
  26. package/dist/components/ui/card.d.ts.map +1 -1
  27. package/dist/components/ui/card.js +14 -23
  28. package/dist/components/ui/checkbox.js +1 -1
  29. package/dist/components/ui/collapsible.d.ts +3 -3
  30. package/dist/components/ui/collapsible.d.ts.map +1 -1
  31. package/dist/components/ui/collapsible.js +3 -11
  32. package/dist/components/ui/command.d.ts +15 -15
  33. package/dist/components/ui/command.d.ts.map +1 -1
  34. package/dist/components/ui/command.js +24 -29
  35. package/dist/components/ui/dialog.d.ts +17 -13
  36. package/dist/components/ui/dialog.d.ts.map +1 -1
  37. package/dist/components/ui/dialog.js +19 -32
  38. package/dist/components/ui/dropdown-menu.js +6 -6
  39. package/dist/components/ui/input.d.ts +2 -1
  40. package/dist/components/ui/input.d.ts.map +1 -1
  41. package/dist/components/ui/input.js +5 -4
  42. package/dist/components/ui/label.d.ts +1 -1
  43. package/dist/components/ui/label.d.ts.map +1 -1
  44. package/dist/components/ui/label.js +3 -3
  45. package/dist/components/ui/scroll-area.d.ts +2 -2
  46. package/dist/components/ui/scroll-area.d.ts.map +1 -1
  47. package/dist/components/ui/scroll-area.js +7 -9
  48. package/dist/components/ui/select.d.ts +11 -13
  49. package/dist/components/ui/select.d.ts.map +1 -1
  50. package/dist/components/ui/select.js +23 -35
  51. package/dist/components/ui/switch.d.ts +2 -2
  52. package/dist/components/ui/switch.d.ts.map +1 -1
  53. package/dist/components/ui/switch.js +4 -4
  54. package/dist/components/ui/textarea.d.ts +2 -1
  55. package/dist/components/ui/textarea.d.ts.map +1 -1
  56. package/dist/components/ui/textarea.js +5 -4
  57. package/dist/components/ui/tooltip.js +1 -1
  58. package/dist/index.d.ts +1 -1
  59. package/dist/index.d.ts.map +1 -1
  60. package/dist/index.js +1 -1
  61. package/dist/lib/commands.d.ts +37 -0
  62. package/dist/lib/commands.d.ts.map +1 -1
  63. package/dist/lib/commands.js +85 -0
  64. package/package.json +1 -1
@@ -11,6 +11,10 @@ export type ChatMessagePart = {
11
11
  state: string;
12
12
  args?: unknown;
13
13
  output?: unknown;
14
+ approval?: {
15
+ id: string;
16
+ approved?: boolean;
17
+ };
14
18
  };
15
19
  export type ChatMessage = {
16
20
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"chat-message.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-message.tsx"],"names":[],"mappings":"AAsBA,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IACE,IAAI,EAAE,iBAAiB,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,CAAA;AAEL,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAA;IACrC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;CAC1B,CAAA;AAED,KAAK,YAAY,GAAG;IAClB,OAAO,EAAE,WAAW,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAcD,iBAAS,WAAW,CAAC,EACnB,OAAO,EACP,MAAc,EACd,WAAmB,GACpB,EAAE,YAAY,2CA2Xd;AAED,eAAO,MAAM,OAAO,yDAAoB,CAAA;AAExC,wBAAgB,eAAe,4CAiB9B"}
1
+ {"version":3,"file":"chat-message.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-message.tsx"],"names":[],"mappings":"AAgCA,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IACE,IAAI,EAAE,iBAAiB,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB,QAAQ,CAAC,EAAE;QACT,EAAE,EAAE,MAAM,CAAA;QACV,QAAQ,CAAC,EAAE,OAAO,CAAA;KACnB,CAAA;CACF,CAAA;AAEL,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAA;IACrC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;CAC1B,CAAA;AAED,KAAK,YAAY,GAAG;IAClB,OAAO,EAAE,WAAW,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,CAAA;AAcD,iBAAS,WAAW,CAAC,EACnB,OAAO,EACP,MAAc,EACd,WAAmB,GACpB,EAAE,YAAY,2CA8bd;AAED,eAAO,MAAM,OAAO,yDAAoB,CAAA;AAExC,wBAAgB,eAAe,4CAiB9B"}
@@ -6,10 +6,19 @@ import { cn } from '../../lib/utils';
6
6
  import { Markdown } from '../markdown';
7
7
  import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from '../ui/collapsible';
8
8
  import { ThinkingTimeline, ThinkingTimelineMarker, } from './thinking-timeline';
9
+ import { ToolApproval, ToolApprovalHeader, ToolApprovalRejected, } from './tool-approval';
10
+ import { useToolApprovalResponse } from './tool-approval-context';
9
11
  import { getToolHandler } from './tool-handlers';
12
+ import { useToolProgress } from './tool-progress-context';
10
13
  import { AnimatePresence, motion } from 'framer-motion';
11
14
  const DEFAULT_ACTIONS_LABEL = 'Thinking';
15
+ // OpenRouter sends these for encrypted reasoning msgs
16
+ const stripRedacted = (text) => text.replace(/\[REDACTED\]/g, '').trim();
12
17
  function PureMessage({ message, isLast = false, isStreaming = false, }) {
18
+ // Get tool progress for streaming stdout/stderr
19
+ const toolProgress = useToolProgress();
20
+ // Get approval response handler
21
+ const onApprovalResponse = useToolApprovalResponse();
13
22
  // Track open state for each thinking group independently
14
23
  const [thinkingGroupStates, setThinkingGroupStates] = useState(new Map());
15
24
  // Process parts chronologically, grouping only reasoning into thinking groups
@@ -69,13 +78,51 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
69
78
  if (toolHandler.inline) {
70
79
  // Close any open thinking group first
71
80
  closeThinkingGroup();
81
+ // Check for approval states
82
+ const isApprovalRequested = part.state === 'approval-requested';
83
+ const isApprovalResponded = part.state === 'approval-responded' ||
84
+ part.state === 'output-denied';
85
+ const wasRejected = part.approval?.approved === false;
86
+ const wasApproved = part.approval?.approved === true;
72
87
  // Render inline tool
88
+ // Also treat approval-responded (when approved) as loading until output arrives
73
89
  const isToolLoading = part.state === 'partial-call' ||
74
90
  part.state === 'call' ||
75
- part.state === 'pending';
76
- const toolContent = isToolLoading
77
- ? toolHandler.renderLoading?.(part.args) || (_jsx("div", { className: "text-muted-foreground text-xs", children: toolHandler.loadingLabel }))
78
- : toolHandler.renderOutput(part.output, part.toolCallId || key);
91
+ part.state === 'pending' ||
92
+ (isApprovalResponded && wasApproved && part.output === undefined);
93
+ // Check for streaming progress (stdout/stderr from command execution)
94
+ const progress = part.toolCallId
95
+ ? toolProgress[part.toolCallId]
96
+ : undefined;
97
+ const hasStreamingOutput = progress && (progress.stdout || progress.stderr);
98
+ let toolContent;
99
+ // Handle approval request state
100
+ if (isApprovalRequested &&
101
+ toolHandler.renderApproval &&
102
+ part.approval?.id &&
103
+ onApprovalResponse) {
104
+ toolContent = toolHandler.renderApproval({
105
+ approvalId: part.approval.id,
106
+ toolCallId: part.toolCallId || key,
107
+ args: part.args,
108
+ }, onApprovalResponse);
109
+ }
110
+ else if (isApprovalResponded && wasRejected) {
111
+ // Show rejection message
112
+ toolContent = (_jsx(ToolApproval, { state: "output-denied", approved: false, children: _jsx(ToolApprovalHeader, { children: _jsx(ToolApprovalRejected, { children: "Command execution was rejected" }) }) }));
113
+ }
114
+ else if (hasStreamingOutput && toolHandler.renderStreaming) {
115
+ // Show streaming output (live stdout/stderr)
116
+ toolContent = toolHandler.renderStreaming(part.args, progress);
117
+ }
118
+ else if (isToolLoading) {
119
+ // Show loading state (arguments streaming or waiting)
120
+ toolContent = toolHandler.renderLoading?.(part.args) || (_jsx("div", { className: "text-muted-foreground text-xs", children: toolHandler.loadingLabel }));
121
+ }
122
+ else {
123
+ // Show final output
124
+ toolContent = toolHandler.renderOutput(part.output, part.toolCallId || key);
125
+ }
79
126
  items.push({
80
127
  type: 'inline-tool',
81
128
  id: key,
@@ -110,15 +157,15 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
110
157
  }
111
158
  if (part.type === 'reasoning') {
112
159
  const full = part.text ?? '';
113
- const trimmedFull = full.trim();
114
- if (trimmedFull.length === 0) {
160
+ const cleaned = stripRedacted(full);
161
+ if (cleaned.length === 0) {
115
162
  continue;
116
163
  }
117
164
  const group = ensureThinkingGroup();
118
165
  // Extract title from markdown **title** or use first line
119
- const markdownTitleMatch = full.match(/\*\*([^*]+)\*\*/);
166
+ const markdownTitleMatch = cleaned.match(/\*\*([^*]+)\*\*/);
120
167
  const candidateTitle = markdownTitleMatch?.[1];
121
- const trimmed = trimmedFull;
168
+ const trimmed = cleaned;
122
169
  const firstLine = trimmed.split(/\n+/)[0];
123
170
  const extractedTitle = candidateTitle || firstLine?.slice(0, 50) || DEFAULT_ACTIONS_LABEL;
124
171
  group.title = extractedTitle;
@@ -148,7 +195,13 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
148
195
  contentItems: items,
149
196
  hasFinalAssistantText: state.hasText,
150
197
  };
151
- }, [message.id, message.parts, message.content]);
198
+ }, [
199
+ message.id,
200
+ message.parts,
201
+ message.content,
202
+ toolProgress,
203
+ onApprovalResponse,
204
+ ]);
152
205
  // Determine if last thinking group is streaming
153
206
  const lastThinkingGroup = contentItems
154
207
  .filter((item) => item.type === 'thinking')
@@ -236,8 +289,9 @@ function PureMessage({ message, isLast = false, isStreaming = false, }) {
236
289
  return (_jsx("div", { className: "w-full min-w-0", children: item.content }, item.id));
237
290
  }
238
291
  if (item.type === 'text' && item.textPart) {
239
- return (_jsx("div", { className: cn('flex flex-col gap-4', {
292
+ return (_jsx("div", { className: cn('flex min-w-0 flex-col gap-4', {
240
293
  'bg-secondary text-secondary-foreground w-fit rounded-2xl px-4 py-2.5': message.role === 'user',
294
+ 'w-full': message.role === 'assistant',
241
295
  }), children: _jsx(Markdown, { markdown: item.textPart.text, proseSize: "sm", className: cn('[&_.prose]:leading-relaxed [&_.prose]:text-current', '[&_.prose_pre]:bg-background/80 [&_.prose_code]:text-current', '[&_.prose_p:last-child]:mb-0 [&_.prose_p]:mb-2', message.role === 'user' &&
242
296
  '[&_.prose_pre]:bg-secondary-foreground/20') }) }, item.id));
243
297
  }
@@ -3,7 +3,18 @@ import type { ChatMessage } from './chat-message';
3
3
  import { type ConversationMeta } from './conversation-history';
4
4
  import { type ModelOption } from './model-selector';
5
5
  import { type ReasoningEffortOption } from './reasoning-effort-selector';
6
+ import { type ApprovalResponseHandler } from './tool-approval-context';
6
7
  type ChatStatus = 'ready' | 'submitted' | 'streaming' | 'error';
8
+ /**
9
+ * Progress data for a running tool (command execution with streaming stdout/stderr)
10
+ */
11
+ export interface ToolProgressData {
12
+ toolCallId: string;
13
+ command: string;
14
+ stdout: string;
15
+ stderr: string;
16
+ status: 'running' | 'complete';
17
+ }
7
18
  export interface ChatPanelProps {
8
19
  /** Chat messages */
9
20
  messages: ChatMessage[];
@@ -47,6 +58,10 @@ export interface ChatPanelProps {
47
58
  isExpanded: boolean;
48
59
  /** Toggle expanded state */
49
60
  onExpandedChange: (expanded: boolean) => void;
61
+ /** Whether the panel is minimized (hidden below fold with only flap visible) */
62
+ isMinimized?: boolean;
63
+ /** Toggle minimized state */
64
+ onMinimizedChange?: (minimized: boolean) => void;
50
65
  /** Custom class name */
51
66
  className?: string;
52
67
  /** Error from chat request */
@@ -55,10 +70,14 @@ export interface ChatPanelProps {
55
70
  missingApiKey?: boolean;
56
71
  /** Callback to trigger API key setup */
57
72
  onAddApiKey?: () => void;
73
+ /** Progress data for running tools (streaming stdout/stderr) */
74
+ toolProgress?: Record<string, ToolProgressData>;
75
+ /** Callback for tool approval responses */
76
+ onApprovalResponse?: ApprovalResponseHandler;
58
77
  }
59
78
  /**
60
79
  * Floating chat panel with model selector
61
80
  */
62
- export declare function ChatPanel({ messages, status, input, onInputChange, onSubmit, onStop, onNewChat, models, selectedModel, onModelChange, reasoningEffortOptions, selectedReasoningEffort, onReasoningEffortChange, conversations, currentConversationId, onSelectConversation, onDeleteConversation, placeholder, welcomeMessage, isExpanded, onExpandedChange, className, error, missingApiKey, onAddApiKey, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
81
+ export declare function ChatPanel({ messages, status, input, onInputChange, onSubmit, onStop, onNewChat, models, selectedModel, onModelChange, reasoningEffortOptions, selectedReasoningEffort, onReasoningEffortChange, conversations, currentConversationId, onSelectConversation, onDeleteConversation, placeholder, welcomeMessage, isExpanded, onExpandedChange, isMinimized, onMinimizedChange, className, error, missingApiKey, onAddApiKey, toolProgress, onApprovalResponse, }: ChatPanelProps): import("react/jsx-runtime").JSX.Element;
63
82
  export {};
64
83
  //# sourceMappingURL=chat-panel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat-panel.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-panel.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,SAAS,EAKf,MAAM,OAAO,CAAA;AAWd,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,KAAK,WAAW,EAAiB,MAAM,kBAAkB,CAAA;AAClE,OAAO,EACL,KAAK,qBAAqB,EAE3B,MAAM,6BAA6B,CAAA;AAGpC,KAAK,UAAU,GAAG,OAAO,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,CAAA;AAE/D,MAAM,WAAW,cAAc;IAC7B,oBAAoB;IACpB,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,0BAA0B;IAC1B,MAAM,EAAE,UAAU,CAAA;IAClB,kBAAkB;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,0BAA0B;IAC1B,aAAa,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,mBAAmB,CAAC,KAAK,IAAI,CAAA;IAC5D,yBAAyB;IACzB,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,eAAe,CAAC,KAAK,IAAI,CAAA;IAClD,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,sBAAsB;IACtB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,uBAAuB;IACvB,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,wBAAwB;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,0BAA0B;IAC1B,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,kEAAkE;IAClE,sBAAsB,CAAC,EAAE,qBAAqB,EAAE,CAAA;IAChD,gCAAgC;IAChC,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,qCAAqC;IACrC,uBAAuB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,2BAA2B;IAC3B,aAAa,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAClC,8BAA8B;IAC9B,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,oCAAoC;IACpC,oBAAoB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C,mCAAmC;IACnC,oBAAoB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,6CAA6C;IAC7C,cAAc,CAAC,EAAE,SAAS,CAAA;IAC1B,oCAAoC;IACpC,UAAU,EAAE,OAAO,CAAA;IACnB,4BAA4B;IAC5B,gBAAgB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAC7C,wBAAwB;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,CAAA;IACpB,mCAAmC;IACnC,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;CACzB;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,MAAM,EACN,KAAK,EACL,aAAa,EACb,QAAQ,EACR,MAAM,EACN,SAAS,EACT,MAAM,EACN,aAAa,EACb,aAAa,EACb,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,WAA+B,EAC/B,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,SAAS,EACT,KAAK,EACL,aAAa,EACb,WAAW,GACZ,EAAE,cAAc,2CAgThB"}
1
+ {"version":3,"file":"chat-panel.d.ts","sourceRoot":"","sources":["../../../src/components/chat/chat-panel.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,SAAS,EAKf,MAAM,OAAO,CAAA;AAWd,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjD,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,KAAK,WAAW,EAAiB,MAAM,kBAAkB,CAAA;AAClE,OAAO,EACL,KAAK,qBAAqB,EAE3B,MAAM,6BAA6B,CAAA;AACpC,OAAO,EACL,KAAK,uBAAuB,EAE7B,MAAM,yBAAyB,CAAA;AAIhC,KAAK,UAAU,GAAG,OAAO,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,CAAA;AAE/D;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,SAAS,GAAG,UAAU,CAAA;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,oBAAoB;IACpB,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,0BAA0B;IAC1B,MAAM,EAAE,UAAU,CAAA;IAClB,kBAAkB;IAClB,KAAK,EAAE,MAAM,CAAA;IACb,0BAA0B;IAC1B,aAAa,EAAE,CAAC,CAAC,EAAE,WAAW,CAAC,mBAAmB,CAAC,KAAK,IAAI,CAAA;IAC5D,yBAAyB;IACzB,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,eAAe,CAAC,KAAK,IAAI,CAAA;IAClD,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,sBAAsB;IACtB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,uBAAuB;IACvB,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,wBAAwB;IACxB,aAAa,EAAE,MAAM,CAAA;IACrB,0BAA0B;IAC1B,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,kEAAkE;IAClE,sBAAsB,CAAC,EAAE,qBAAqB,EAAE,CAAA;IAChD,gCAAgC;IAChC,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAChC,qCAAqC;IACrC,uBAAuB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAClD,2BAA2B;IAC3B,aAAa,CAAC,EAAE,gBAAgB,EAAE,CAAA;IAClC,8BAA8B;IAC9B,qBAAqB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrC,oCAAoC;IACpC,oBAAoB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C,mCAAmC;IACnC,oBAAoB,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,6CAA6C;IAC7C,cAAc,CAAC,EAAE,SAAS,CAAA;IAC1B,oCAAoC;IACpC,UAAU,EAAE,OAAO,CAAA;IACnB,4BAA4B;IAC5B,gBAAgB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAC7C,gFAAgF;IAChF,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,6BAA6B;IAC7B,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAA;IAChD,wBAAwB;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,CAAA;IACpB,mCAAmC;IACnC,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;IAC/C,2CAA2C;IAC3C,kBAAkB,CAAC,EAAE,uBAAuB,CAAA;CAC7C;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,MAAM,EACN,KAAK,EACL,aAAa,EACb,QAAQ,EACR,MAAM,EACN,SAAS,EACT,MAAM,EACN,aAAa,EACb,aAAa,EACb,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,WAA+B,EAC/B,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,WAAmB,EACnB,iBAAiB,EACjB,SAAS,EACT,KAAK,EACL,aAAa,EACb,WAAW,EACX,YAAiB,EACjB,kBAAkB,GACnB,EAAE,cAAc,2CAiWhB"}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { AlertCircle, Minimize2, Plus } from 'lucide-react';
3
+ import { AlertCircle, ChevronDown, MessageCircle, Plus } from 'lucide-react';
4
4
  import { useCallback, useEffect, useRef, useState, } from 'react';
5
5
  import { cn } from '../../lib/utils';
6
6
  import { Button } from '../ui/button';
@@ -11,11 +11,13 @@ import { Messages } from './chat-messages';
11
11
  import { ConversationHistory, } from './conversation-history';
12
12
  import { ModelSelector } from './model-selector';
13
13
  import { ReasoningEffortSelector, } from './reasoning-effort-selector';
14
+ import { ToolApprovalProvider, } from './tool-approval-context';
15
+ import { ToolProgressProvider } from './tool-progress-context';
14
16
  import { AnimatePresence, motion } from 'framer-motion';
15
17
  /**
16
18
  * Floating chat panel with model selector
17
19
  */
18
- export function ChatPanel({ messages, status, input, onInputChange, onSubmit, onStop, onNewChat, models, selectedModel, onModelChange, reasoningEffortOptions, selectedReasoningEffort, onReasoningEffortChange, conversations, currentConversationId, onSelectConversation, onDeleteConversation, placeholder = 'Ask anything...', welcomeMessage, isExpanded, onExpandedChange, className, error, missingApiKey, onAddApiKey, }) {
20
+ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, onStop, onNewChat, models, selectedModel, onModelChange, reasoningEffortOptions, selectedReasoningEffort, onReasoningEffortChange, conversations, currentConversationId, onSelectConversation, onDeleteConversation, placeholder = 'Ask anything...', welcomeMessage, isExpanded, onExpandedChange, isMinimized = false, onMinimizedChange, className, error, missingApiKey, onAddApiKey, toolProgress = {}, onApprovalResponse, }) {
19
21
  const inputRef = useRef(null);
20
22
  const messagesEndRef = useRef(null);
21
23
  const scrollAreaRef = useRef(null);
@@ -87,9 +89,22 @@ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, on
87
89
  document.addEventListener('keydown', handleKeyDown);
88
90
  return () => document.removeEventListener('keydown', handleKeyDown);
89
91
  }, [isResponding, isExpanded, handleNewChat]);
90
- return (_jsxs(_Fragment, { children: [_jsx(AnimatePresence, { children: isExpanded && (_jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.2 }, className: "fixed inset-0 z-40 bg-black/20", onClick: () => onExpandedChange(false) })) }), _jsx(motion.div, { initial: false, animate: {
92
+ // Handle clicking the minimized flap
93
+ const handleFlapClick = useCallback(() => {
94
+ onMinimizedChange?.(false);
95
+ }, [onMinimizedChange]);
96
+ // Handle minimize button - now goes to minimized state
97
+ const handleMinimize = useCallback(() => {
98
+ if (isExpanded) {
99
+ onExpandedChange(false);
100
+ }
101
+ onMinimizedChange?.(true);
102
+ }, [isExpanded, onExpandedChange, onMinimizedChange]);
103
+ return (_jsxs(_Fragment, { children: [_jsx(AnimatePresence, { children: isExpanded && !isMinimized && (_jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, transition: { duration: 0.2 }, className: "fixed inset-0 z-40 bg-black/20", onClick: () => onExpandedChange(false) })) }), _jsx(AnimatePresence, { children: isMinimized && (_jsx(motion.div, { initial: { y: 20, opacity: 0 }, animate: { y: 0, opacity: 1 }, exit: { y: 20, opacity: 0 }, transition: { type: 'tween', duration: 0.2, ease: 'easeOut' }, className: "fixed bottom-0 left-[calc(50%+var(--sidebar-width,0px)/2)] z-50 -translate-x-1/2", children: _jsxs("button", { onClick: handleFlapClick, className: cn('bg-background/95 hover:bg-background group flex cursor-pointer items-center gap-2 rounded-t-xl border border-b-0 px-4 py-2 shadow-lg backdrop-blur transition-colors', 'text-muted-foreground hover:text-foreground'), children: [_jsx(MessageCircle, { className: "size-4" }), _jsx("span", { className: "text-sm font-medium", children: "Chat" }), _jsx("span", { className: "bg-muted text-muted-foreground group-hover:bg-primary/10 group-hover:text-primary rounded px-1.5 py-0.5 text-xs transition-colors", children: "\u2318M" })] }) })) }), _jsx(motion.div, { initial: false, animate: {
91
104
  width: isExpanded ? 'min(90vw, 640px)' : '400px',
92
- }, transition: { type: 'tween', duration: 0.2, ease: 'easeOut' }, className: cn('pointer-events-none fixed bottom-6 left-[calc(50%+var(--sidebar-width,0px)/2)] z-50 flex -translate-x-1/2 justify-center', className), children: _jsxs("div", { className: cn('bg-background pointer-events-auto relative w-full border shadow-lg', 'supports-[backdrop-filter]:bg-background/95 backdrop-blur', 'rounded-4xl', isExpanded ? 'overflow-hidden' : 'overflow-visible'), children: [!isExpanded && (_jsx(motion.div, { className: "rounded-4xl pointer-events-none absolute inset-0 z-0", animate: {
105
+ y: isMinimized ? 'calc(100% + 24px)' : 0,
106
+ opacity: isMinimized ? 0 : 1,
107
+ }, transition: { type: 'tween', duration: 0.25, ease: 'easeOut' }, className: cn('pointer-events-none fixed bottom-6 left-[calc(50%+var(--sidebar-width,0px)/2)] z-50 flex -translate-x-1/2 justify-center', isMinimized && 'pointer-events-none', className), children: _jsxs("div", { className: cn('bg-background pointer-events-auto relative w-full border shadow-lg', 'supports-[backdrop-filter]:bg-background/95 backdrop-blur', 'rounded-4xl', isExpanded ? 'overflow-hidden' : 'overflow-visible'), children: [!isExpanded && (_jsx(motion.div, { className: "rounded-4xl pointer-events-none absolute inset-0 z-0", animate: {
93
108
  boxShadow: [
94
109
  '0 0 0px 0px color-mix(in oklch, var(--primary) 0%, #ff8800 00%)',
95
110
  '0 0 20px 0px color-mix(in oklch, var(--primary) 20%, #ff8800 30%)',
@@ -107,7 +122,7 @@ export function ChatPanel({ messages, status, input, onInputChange, onSubmit, on
107
122
  selectedReasoningEffort &&
108
123
  onReasoningEffortChange && (_jsx(ReasoningEffortSelector, { options: reasoningEffortOptions, selectedEffort: selectedReasoningEffort, onEffortChange: onReasoningEffortChange, disabled: isResponding }))] }), _jsxs("div", { className: "flex items-center gap-1", children: [conversations &&
109
124
  conversations.length > 0 &&
110
- onSelectConversation && (_jsx(ConversationHistory, { conversations: conversations, currentConversationId: currentConversationId, onSelect: onSelectConversation, onDelete: onDeleteConversation, disabled: isResponding })), _jsxs(TooltipProvider, { children: [messages.length > 0 && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: handleNewChat, disabled: isResponding, className: "text-muted-foreground hover:text-foreground", children: _jsx(Plus, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "New chat" }) })] })), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: () => onExpandedChange(false), className: "text-muted-foreground hover:text-foreground", children: _jsx(Minimize2, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "Minimize" }) })] })] })] })] }), _jsx(ScrollArea, { ref: scrollAreaRef, className: "min-w-0 flex-1 px-2", children: _jsxs("div", { className: "min-w-0 space-y-4 py-4", children: [messages.length === 0 && welcomeMessage && (_jsx("div", { className: "bg-muted/50 text-muted-foreground mx-2 rounded-2xl p-4 text-sm", children: welcomeMessage })), _jsx(Messages, { messages: messages, status: status }), (missingApiKey ||
125
+ onSelectConversation && (_jsx(ConversationHistory, { conversations: conversations, currentConversationId: currentConversationId, onSelect: onSelectConversation, onDelete: onDeleteConversation, disabled: isResponding })), _jsxs(TooltipProvider, { children: [messages.length > 0 && (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: handleNewChat, disabled: isResponding, className: "text-muted-foreground hover:text-foreground", children: _jsx(Plus, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "New chat" }) })] })), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { variant: "ghost", size: "icon-sm", onClick: handleMinimize, className: "text-muted-foreground hover:text-foreground", children: _jsx(ChevronDown, { className: "size-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("p", { children: "Minimize" }) })] })] })] })] }), _jsx(ScrollArea, { ref: scrollAreaRef, className: "min-w-0 flex-1 px-2", children: _jsxs("div", { className: "min-w-0 max-w-full space-y-4 overflow-hidden py-4", children: [messages.length === 0 && welcomeMessage && (_jsx("div", { className: "bg-muted/50 text-muted-foreground mx-2 rounded-2xl p-4 text-sm", children: welcomeMessage })), _jsx(ToolProgressProvider, { value: toolProgress, children: _jsx(ToolApprovalProvider, { onApprovalResponse: onApprovalResponse ?? null, children: _jsx(Messages, { messages: messages, status: status }) }) }), (missingApiKey ||
111
126
  (error &&
112
127
  error.message?.includes('API_KEY not configured'))) && (_jsxs("div", { className: "border-primary/30 bg-primary/5 mx-2 flex flex-col items-center gap-3 rounded-lg border p-4 text-center", children: [_jsx("div", { className: "bg-primary/10 flex size-10 items-center justify-center rounded-full", children: _jsx(AlertCircle, { className: "text-primary size-5" }) }), _jsxs("div", { children: [_jsx("p", { className: "text-foreground font-medium", children: "API key required" }), _jsx("p", { className: "text-muted-foreground mt-1 text-sm", children: "Add an API key to start chatting with AI" })] }), onAddApiKey && (_jsx(Button, { onClick: onAddApiKey, size: "sm", className: "cursor-pointer", children: "Add API key" }))] })), error &&
113
128
  status === 'error' &&
@@ -4,7 +4,10 @@ export { Messages } from './chat-messages';
4
4
  export { ModelSelector, type ModelOption } from './model-selector';
5
5
  export { ReasoningEffortSelector, type ReasoningEffortOption, } from './reasoning-effort-selector';
6
6
  export { ConversationHistory, type ConversationMeta, } from './conversation-history';
7
- export { ChatPanel, type ChatPanelProps } from './chat-panel';
7
+ export { ChatPanel, type ChatPanelProps, type ToolProgressData, } from './chat-panel';
8
8
  export { ThinkingTimeline, ThinkingTimelineMarker, type ThinkingTimelineItem, type ThinkingTimelineProps, } from './thinking-timeline';
9
9
  export { getToolHandler, DEFAULT_TOOL_HANDLERS, type ToolHandler, } from './tool-handlers';
10
+ export { ToolProgressProvider, useToolProgress, useToolCallProgress, } from './tool-progress-context';
11
+ export { ToolApprovalProvider, useToolApprovalResponse, type ApprovalResponseHandler, } from './tool-approval-context';
12
+ export { ToolApproval, ToolApprovalHeader, ToolApprovalRequest, ToolApprovalAccepted, ToolApprovalRejected, ToolApprovalActions, ToolApprovalAction, type ToolApprovalState, } from './tool-approval';
10
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EACL,OAAO,EACP,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAClE,OAAO,EACL,uBAAuB,EACvB,KAAK,qBAAqB,GAC3B,MAAM,6BAA6B,CAAA;AACpC,OAAO,EACL,mBAAmB,EACnB,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7D,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,KAAK,WAAW,GACjB,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AACxC,OAAO,EACL,OAAO,EACP,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAClE,OAAO,EACL,uBAAuB,EACvB,KAAK,qBAAqB,GAC3B,MAAM,6BAA6B,CAAA;AACpC,OAAO,EACL,mBAAmB,EACnB,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,SAAS,EACT,KAAK,cAAc,EACnB,KAAK,gBAAgB,GACtB,MAAM,cAAc,CAAA;AACrB,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,KAAK,WAAW,GACjB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,oBAAoB,EACpB,eAAe,EACf,mBAAmB,GACpB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,KAAK,uBAAuB,GAC7B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAA"}
@@ -4,6 +4,9 @@ export { Messages } from './chat-messages';
4
4
  export { ModelSelector } from './model-selector';
5
5
  export { ReasoningEffortSelector, } from './reasoning-effort-selector';
6
6
  export { ConversationHistory, } from './conversation-history';
7
- export { ChatPanel } from './chat-panel';
7
+ export { ChatPanel, } from './chat-panel';
8
8
  export { ThinkingTimeline, ThinkingTimelineMarker, } from './thinking-timeline';
9
9
  export { getToolHandler, DEFAULT_TOOL_HANDLERS, } from './tool-handlers';
10
+ export { ToolProgressProvider, useToolProgress, useToolCallProgress, } from './tool-progress-context';
11
+ export { ToolApprovalProvider, useToolApprovalResponse, } from './tool-approval-context';
12
+ export { ToolApproval, ToolApprovalHeader, ToolApprovalRequest, ToolApprovalAccepted, ToolApprovalRejected, ToolApprovalActions, ToolApprovalAction, } from './tool-approval';
@@ -0,0 +1,21 @@
1
+ import { type ReactNode } from 'react';
2
+ /**
3
+ * Callback type for responding to tool approval requests
4
+ */
5
+ export type ApprovalResponseHandler = (params: {
6
+ approvalId: string;
7
+ approved: boolean;
8
+ reason?: string;
9
+ }) => void;
10
+ /**
11
+ * Provider for tool approval handling
12
+ */
13
+ export declare function ToolApprovalProvider({ onApprovalResponse, children, }: {
14
+ onApprovalResponse: ApprovalResponseHandler | null;
15
+ children: ReactNode;
16
+ }): import("react/jsx-runtime").JSX.Element;
17
+ /**
18
+ * Hook to get the approval response handler
19
+ */
20
+ export declare function useToolApprovalResponse(): ApprovalResponseHandler | null;
21
+ //# sourceMappingURL=tool-approval-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-approval-context.d.ts","sourceRoot":"","sources":["../../../src/components/chat/tool-approval-context.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAA6B,MAAM,OAAO,CAAA;AAEjE;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,MAAM,EAAE;IAC7C,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,KAAK,IAAI,CAAA;AAOV;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,kBAAkB,EAClB,QAAQ,GACT,EAAE;IACD,kBAAkB,EAAE,uBAAuB,GAAG,IAAI,CAAA;IAClD,QAAQ,EAAE,SAAS,CAAA;CACpB,2CAMA;AAED;;GAEG;AACH,wBAAgB,uBAAuB,IAAI,uBAAuB,GAAG,IAAI,CAExE"}
@@ -0,0 +1,19 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, useContext } from 'react';
4
+ /**
5
+ * Context for tool approval handling
6
+ */
7
+ const ToolApprovalContext = createContext(null);
8
+ /**
9
+ * Provider for tool approval handling
10
+ */
11
+ export function ToolApprovalProvider({ onApprovalResponse, children, }) {
12
+ return (_jsx(ToolApprovalContext.Provider, { value: onApprovalResponse, children: children }));
13
+ }
14
+ /**
15
+ * Hook to get the approval response handler
16
+ */
17
+ export function useToolApprovalResponse() {
18
+ return useContext(ToolApprovalContext);
19
+ }
@@ -0,0 +1,85 @@
1
+ import * as React from 'react';
2
+ import { Button } from '../ui/button';
3
+ /**
4
+ * State of the tool approval
5
+ */
6
+ export type ToolApprovalState = 'approval-requested' | 'approval-responded' | 'output-available' | 'output-denied';
7
+ /**
8
+ * Root component for tool approval UI
9
+ */
10
+ interface ToolApprovalProps {
11
+ children: React.ReactNode;
12
+ /** Current state of the approval */
13
+ state: ToolApprovalState;
14
+ /** Whether the user approved (for responded/output states) */
15
+ approved?: boolean;
16
+ className?: string;
17
+ }
18
+ export declare function ToolApproval({ children, state, approved, className, }: ToolApprovalProps): import("react/jsx-runtime").JSX.Element;
19
+ /**
20
+ * Header section with icon and title
21
+ */
22
+ interface ToolApprovalHeaderProps {
23
+ children: React.ReactNode;
24
+ className?: string;
25
+ }
26
+ export declare function ToolApprovalHeader({ children, className, }: ToolApprovalHeaderProps): import("react/jsx-runtime").JSX.Element;
27
+ /**
28
+ * Content shown only when approval is being requested
29
+ */
30
+ interface ToolApprovalRequestProps {
31
+ children: React.ReactNode;
32
+ className?: string;
33
+ }
34
+ export declare function ToolApprovalRequest({ children, className, }: ToolApprovalRequestProps): import("react/jsx-runtime").JSX.Element | null;
35
+ /**
36
+ * Expandable help section explaining why approval is needed
37
+ */
38
+ interface ToolApprovalHelpProps {
39
+ children: React.ReactNode;
40
+ className?: string;
41
+ }
42
+ export declare function ToolApprovalHelp({ children, className, }: ToolApprovalHelpProps): import("react/jsx-runtime").JSX.Element | null;
43
+ /**
44
+ * Pre-built help content for sandboxing explanation
45
+ */
46
+ export declare function ToolApprovalSandboxHelp({ className }: {
47
+ className?: string;
48
+ }): import("react/jsx-runtime").JSX.Element;
49
+ /**
50
+ * Pre-built help content for dangerous command explanation
51
+ */
52
+ export declare function ToolApprovalDangerousHelp({ className, }: {
53
+ className?: string;
54
+ }): import("react/jsx-runtime").JSX.Element;
55
+ /**
56
+ * Content shown when approval was granted
57
+ */
58
+ interface ToolApprovalAcceptedProps {
59
+ children?: React.ReactNode;
60
+ className?: string;
61
+ }
62
+ export declare function ToolApprovalAccepted({ children, className, }: ToolApprovalAcceptedProps): import("react/jsx-runtime").JSX.Element | null;
63
+ /**
64
+ * Content shown when approval was rejected
65
+ */
66
+ interface ToolApprovalRejectedProps {
67
+ children?: React.ReactNode;
68
+ className?: string;
69
+ }
70
+ export declare function ToolApprovalRejected({ children, className, }: ToolApprovalRejectedProps): import("react/jsx-runtime").JSX.Element | null;
71
+ /**
72
+ * Actions container (approve/reject buttons)
73
+ */
74
+ interface ToolApprovalActionsProps {
75
+ children: React.ReactNode;
76
+ className?: string;
77
+ }
78
+ export declare function ToolApprovalActions({ children, className, }: ToolApprovalActionsProps): import("react/jsx-runtime").JSX.Element | null;
79
+ /**
80
+ * Individual action button
81
+ */
82
+ type ToolApprovalActionProps = React.ComponentProps<typeof Button>;
83
+ export declare function ToolApprovalAction({ className, size, ...props }: ToolApprovalActionProps): import("react/jsx-runtime").JSX.Element;
84
+ export {};
85
+ //# sourceMappingURL=tool-approval.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-approval.d.ts","sourceRoot":"","sources":["../../../src/components/chat/tool-approval.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAErC;;GAEG;AACH,MAAM,MAAM,iBAAiB,GACzB,oBAAoB,GACpB,oBAAoB,GACpB,kBAAkB,GAClB,eAAe,CAAA;AAenB;;GAEG;AACH,UAAU,iBAAiB;IACzB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,oCAAoC;IACpC,KAAK,EAAE,iBAAiB,CAAA;IACxB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,SAAS,GACV,EAAE,iBAAiB,2CAanB;AAED;;GAEG;AACH,UAAU,uBAAuB;IAC/B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,SAAS,GACV,EAAE,uBAAuB,2CAqBzB;AAED;;GAEG;AACH,UAAU,wBAAwB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,SAAS,GACV,EAAE,wBAAwB,kDAQ1B;AAED;;GAEG;AACH,UAAU,qBAAqB;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,SAAS,GACV,EAAE,qBAAqB,kDAkCvB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,2CAc5E;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,SAAS,GACV,EAAE;IACD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,2CAaA;AAED;;GAEG;AACH,UAAU,yBAAyB;IACjC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,SAAS,GACV,EAAE,yBAAyB,kDAqB3B;AAED;;GAEG;AACH,UAAU,yBAAyB;IACjC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,SAAS,GACV,EAAE,yBAAyB,kDAuB3B;AAED;;GAEG;AACH,UAAU,wBAAwB;IAChC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,SAAS,GACV,EAAE,wBAAwB,kDAkB1B;AAED;;GAEG;AACH,KAAK,uBAAuB,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,CAAA;AAElE,wBAAgB,kBAAkB,CAAC,EACjC,SAAS,EACT,IAAW,EACX,GAAG,KAAK,EACT,EAAE,uBAAuB,2CAIzB"}
@@ -0,0 +1,80 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Check, ChevronDown, HelpCircle, ShieldAlert, X } from 'lucide-react';
4
+ import * as React from 'react';
5
+ import { cn } from '../../lib/utils';
6
+ import { Button } from '../ui/button';
7
+ const ToolApprovalContext = React.createContext({
8
+ state: 'approval-requested',
9
+ });
10
+ function useToolApprovalContext() {
11
+ return React.useContext(ToolApprovalContext);
12
+ }
13
+ export function ToolApproval({ children, state, approved, className, }) {
14
+ return (_jsx(ToolApprovalContext.Provider, { value: { state, approved }, children: _jsx("div", { className: cn('bg-card border-border my-2 overflow-hidden rounded-lg border', className), children: children }) }));
15
+ }
16
+ export function ToolApprovalHeader({ children, className, }) {
17
+ const { state, approved } = useToolApprovalContext();
18
+ // Show different icons based on state
19
+ const icon = state === 'approval-requested' ? (_jsx(ShieldAlert, { className: "size-3.5 shrink-0 text-amber-500" })) : approved ? (_jsx(Check, { className: "text-success size-3.5 shrink-0" })) : (_jsx(X, { className: "text-destructive size-3.5 shrink-0" }));
20
+ return (_jsxs("div", { className: cn('bg-muted/50 flex items-start gap-2 px-3 py-2', className), children: [_jsx("div", { className: "mt-0.5", children: icon }), _jsx("div", { className: "min-w-0 flex-1", children: children })] }));
21
+ }
22
+ export function ToolApprovalRequest({ children, className, }) {
23
+ const { state } = useToolApprovalContext();
24
+ if (state !== 'approval-requested') {
25
+ return null;
26
+ }
27
+ return _jsx("div", { className: cn('text-xs', className), children: children });
28
+ }
29
+ export function ToolApprovalHelp({ children, className, }) {
30
+ const { state } = useToolApprovalContext();
31
+ const [isOpen, setIsOpen] = React.useState(false);
32
+ // Only show when approval is being requested
33
+ if (state !== 'approval-requested') {
34
+ return null;
35
+ }
36
+ return (_jsxs("div", { className: cn('', className), children: [_jsxs("button", { type: "button", onClick: () => setIsOpen(!isOpen), className: "text-muted-foreground hover:text-foreground flex cursor-pointer items-center gap-1 text-[10px] transition-colors", children: [_jsx(HelpCircle, { className: "size-2.5" }), _jsx("span", { children: "What does this mean?" }), _jsx(ChevronDown, { className: cn('size-2.5 transition-transform', isOpen && 'rotate-180') })] }), isOpen && (_jsx("div", { className: "bg-muted/30 mt-1.5 rounded-md px-2 py-1.5", children: _jsx("p", { className: "text-muted-foreground text-[10px] leading-relaxed", children: children }) }))] }));
37
+ }
38
+ /**
39
+ * Pre-built help content for sandboxing explanation
40
+ */
41
+ export function ToolApprovalSandboxHelp({ className }) {
42
+ return (_jsxs(ToolApprovalHelp, { className: className, children: [_jsx("strong", { className: "text-foreground", children: "Sandboxing" }), " restricts commands from accessing the network and writing to sensitive locations. Package managers like", ' ', _jsx("code", { className: "bg-muted rounded px-1 py-0.5", children: "pnpm install" }), " need network access to download packages, so they run unsandboxed.", _jsxs("strong", { className: "text-foreground", children: [' ', "Only approve commands you trust."] })] }));
43
+ }
44
+ /**
45
+ * Pre-built help content for dangerous command explanation
46
+ */
47
+ export function ToolApprovalDangerousHelp({ className, }) {
48
+ return (_jsxs(ToolApprovalHelp, { className: className, children: [_jsx("strong", { className: "text-foreground", children: "Dangerous commands" }), " can make irreversible changes to your system, like deleting files recursively or modifying permissions. These commands are flagged based on common patterns (rm -rf, sudo, etc.) to give you a chance to review before execution.", _jsxs("strong", { className: "text-foreground", children: [' ', "Only approve if you understand what the command does."] })] }));
49
+ }
50
+ export function ToolApprovalAccepted({ children, className, }) {
51
+ const { state, approved } = useToolApprovalContext();
52
+ // Show for responded/output states where approved is true
53
+ const shouldShow = (state === 'approval-responded' || state === 'output-available') && approved;
54
+ if (!shouldShow) {
55
+ return null;
56
+ }
57
+ return (_jsx("div", { className: cn('text-success flex items-center gap-1.5 text-xs', className), children: children ?? 'Approved' }));
58
+ }
59
+ export function ToolApprovalRejected({ children, className, }) {
60
+ const { state, approved } = useToolApprovalContext();
61
+ // Show for output-denied or responded with approved=false
62
+ const shouldShow = state === 'output-denied' ||
63
+ ((state === 'approval-responded' || state === 'output-available') &&
64
+ approved === false);
65
+ if (!shouldShow) {
66
+ return null;
67
+ }
68
+ return (_jsx("div", { className: cn('text-destructive flex items-center gap-1.5 text-xs', className), children: children ?? 'Rejected' }));
69
+ }
70
+ export function ToolApprovalActions({ children, className, }) {
71
+ const { state } = useToolApprovalContext();
72
+ // Only show actions when approval is being requested
73
+ if (state !== 'approval-requested') {
74
+ return null;
75
+ }
76
+ return (_jsx("div", { className: cn('border-border flex items-center justify-end gap-2 border-t px-3 py-2', className), children: children }));
77
+ }
78
+ export function ToolApprovalAction({ className, size = 'sm', ...props }) {
79
+ return (_jsx(Button, { className: cn('h-7 text-xs', className), size: size, ...props }));
80
+ }
@@ -1,5 +1,24 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import { ThinkingTimelineMarker } from './thinking-timeline';
3
+ import { type ApprovalResponseHandler } from './tool-approval-context';
4
+ /**
5
+ * Progress data for streaming tool execution (e.g., command stdout/stderr)
6
+ */
7
+ export interface ToolStreamingProgress {
8
+ toolCallId: string;
9
+ command?: string;
10
+ stdout: string;
11
+ stderr: string;
12
+ status: 'running' | 'complete';
13
+ }
14
+ /**
15
+ * Approval info passed to renderApproval
16
+ */
17
+ export interface ToolApprovalInfo {
18
+ approvalId: string;
19
+ toolCallId: string;
20
+ args: unknown;
21
+ }
3
22
  /**
4
23
  * Tool handler definition
5
24
  */
@@ -9,6 +28,8 @@ export type ToolHandler = {
9
28
  inline?: boolean;
10
29
  renderOutput: (output: unknown, toolCallId: string) => ReactNode;
11
30
  renderLoading?: (args?: unknown) => ReactNode;
31
+ renderStreaming?: (args: unknown, progress: ToolStreamingProgress) => ReactNode;
32
+ renderApproval?: (approval: ToolApprovalInfo, onRespond: ApprovalResponseHandler) => ReactNode;
12
33
  };
13
34
  /**
14
35
  * Default tool handlers for Moldable tools
@@ -1 +1 @@
1
- {"version":3,"file":"tool-handlers.d.ts","sourceRoot":"","sources":["../../../src/components/chat/tool-handlers.tsx"],"names":[],"mappings":"AAmBA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAEhD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAE5D;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IAExB,YAAY,EAAE,MAAM,CAAA;IAEpB,MAAM,CAAC,EAAE,sBAAsB,CAAA;IAE/B,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,SAAS,CAAA;IAGhE,aAAa,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,KAAK,SAAS,CAAA;CAC9C,CAAA;AA0MD;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAm+B7D,CAAA;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAgC5D"}
1
+ {"version":3,"file":"tool-handlers.d.ts","sourceRoot":"","sources":["../../../src/components/chat/tool-handlers.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAEhD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAU5D,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,yBAAyB,CAAA;AAEtE;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,SAAS,GAAG,UAAU,CAAA;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,OAAO,CAAA;CACd;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IAExB,YAAY,EAAE,MAAM,CAAA;IAEpB,MAAM,CAAC,EAAE,sBAAsB,CAAA;IAE/B,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB,YAAY,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,SAAS,CAAA;IAGhE,aAAa,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,OAAO,KAAK,SAAS,CAAA;IAG7C,eAAe,CAAC,EAAE,CAChB,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,qBAAqB,KAC5B,SAAS,CAAA;IAGd,cAAc,CAAC,EAAE,CACf,QAAQ,EAAE,gBAAgB,EAC1B,SAAS,EAAE,uBAAuB,KAC/B,SAAS,CAAA;CACf,CAAA;AAkOD;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAk1C7D,CAAA;AAoBD;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAmC5D"}