@devicai/ui 0.4.0 → 0.6.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/README.md CHANGED
@@ -5,8 +5,11 @@ React component library for integrating Devic AI assistants into your applicatio
5
5
  ## Features
6
6
 
7
7
  - **ChatDrawer** - A ready-to-use chat drawer component
8
+ - **AICommandBar** - A spotlight-style command bar for quick AI interactions
9
+ - **AIGenerationButton** - A button for triggering AI generation with modal, tooltip, or direct modes
8
10
  - **useDevicChat** - Hook for building custom chat UIs
9
11
  - **Model Interface Protocol** - Support for client-side tool execution
12
+ - **Message Feedback** - Built-in thumbs up/down feedback with comments
10
13
  - **CSS Variables** - Easy theming with CSS custom properties
11
14
  - **TypeScript** - Full type definitions included
12
15
  - **React 17+** - Compatible with React 17 and above
@@ -152,6 +155,232 @@ A complete chat drawer component.
152
155
  />
153
156
  ```
154
157
 
158
+ ### AICommandBar
159
+
160
+ A floating command bar (similar to Spotlight/Command Palette) for quick AI interactions.
161
+
162
+ ```tsx
163
+ import { AICommandBar } from '@devicai/ui';
164
+
165
+ <AICommandBar
166
+ assistantId="my-assistant"
167
+ options={{
168
+ shortcut: 'cmd+k', // Keyboard shortcut to open
169
+ placeholder: 'Ask AI...',
170
+ position: 'fixed', // 'inline' | 'fixed'
171
+ fixedPlacement: { bottom: 20, right: 20 },
172
+ showResultCard: true, // Show response in a card
173
+ showShortcutHint: true, // Show shortcut badge
174
+
175
+ // Commands (slash commands)
176
+ commands: [
177
+ {
178
+ keyword: 'summarize',
179
+ description: 'Summarize content',
180
+ message: 'Please summarize this page.',
181
+ icon: <SummarizeIcon />,
182
+ },
183
+ ],
184
+
185
+ // History
186
+ enableHistory: true,
187
+ maxHistoryItems: 50,
188
+
189
+ // Theming
190
+ backgroundColor: '#ffffff',
191
+ textColor: '#1f2937',
192
+ borderColor: '#e5e7eb',
193
+ borderRadius: 12,
194
+ }}
195
+
196
+ // Callbacks
197
+ onResponse={({ message, toolCalls, chatUid }) => {}}
198
+ onSubmit={(message) => {}}
199
+ onToolCall={(toolName, params) => {}}
200
+ onError={(error) => {}}
201
+ onOpen={() => {}}
202
+ onClose={() => {}}
203
+
204
+ // Integration with ChatDrawer
205
+ onExecute="openDrawer" // 'callback' | 'openDrawer'
206
+ chatDrawerRef={drawerRef} // Required when onExecute="openDrawer"
207
+
208
+ // Controlled mode
209
+ isVisible={true}
210
+ onVisibilityChange={(visible) => {}}
211
+ />
212
+ ```
213
+
214
+ #### AICommandBar with ChatDrawer Integration
215
+
216
+ ```tsx
217
+ import { useRef } from 'react';
218
+ import { AICommandBar, ChatDrawer, ChatDrawerHandle } from '@devicai/ui';
219
+
220
+ function App() {
221
+ const drawerRef = useRef<ChatDrawerHandle>(null);
222
+
223
+ return (
224
+ <>
225
+ <AICommandBar
226
+ assistantId="my-assistant"
227
+ onExecute="openDrawer"
228
+ chatDrawerRef={drawerRef}
229
+ options={{
230
+ shortcut: 'cmd+k',
231
+ showResultCard: false,
232
+ }}
233
+ />
234
+ <ChatDrawer ref={drawerRef} assistantId="my-assistant" />
235
+ </>
236
+ );
237
+ }
238
+ ```
239
+
240
+ #### AICommandBar Handle (ref methods)
241
+
242
+ ```tsx
243
+ const commandBarRef = useRef<AICommandBarHandle>(null);
244
+
245
+ // Methods available via ref
246
+ commandBarRef.current?.open();
247
+ commandBarRef.current?.close();
248
+ commandBarRef.current?.toggle();
249
+ commandBarRef.current?.focus();
250
+ commandBarRef.current?.submit('Hello!');
251
+ commandBarRef.current?.reset();
252
+ ```
253
+
254
+ ### AIGenerationButton
255
+
256
+ A button component for triggering AI generation with three interaction modes: direct, modal, or tooltip.
257
+
258
+ ```tsx
259
+ import { AIGenerationButton } from '@devicai/ui';
260
+
261
+ // Modal mode (default) - opens a modal for user input
262
+ <AIGenerationButton
263
+ assistantId="my-assistant"
264
+ options={{
265
+ mode: 'modal',
266
+ modalTitle: 'Generate with AI',
267
+ modalDescription: 'Describe what you want to generate.',
268
+ placeholder: 'E.g., Create a product description...',
269
+ confirmText: 'Generate',
270
+ cancelText: 'Cancel',
271
+ }}
272
+ onResponse={({ message, toolCalls }) => {
273
+ console.log('Generated:', message.content.message);
274
+ }}
275
+ />
276
+
277
+ // Direct mode - sends predefined prompt immediately
278
+ <AIGenerationButton
279
+ assistantId="my-assistant"
280
+ options={{
281
+ mode: 'direct',
282
+ prompt: 'Generate a summary of this content',
283
+ label: 'Summarize',
284
+ loadingLabel: 'Summarizing...',
285
+ }}
286
+ onResponse={({ message }) => setSummary(message.content.message)}
287
+ />
288
+
289
+ // Tooltip mode - shows inline input
290
+ <AIGenerationButton
291
+ assistantId="my-assistant"
292
+ options={{
293
+ mode: 'tooltip',
294
+ tooltipPlacement: 'bottom', // 'top' | 'bottom' | 'left' | 'right'
295
+ tooltipWidth: 350,
296
+ }}
297
+ onResponse={handleGeneration}
298
+ />
299
+ ```
300
+
301
+ #### AIGenerationButton Options
302
+
303
+ ```tsx
304
+ <AIGenerationButton
305
+ assistantId="my-assistant"
306
+ options={{
307
+ // Mode
308
+ mode: 'modal', // 'direct' | 'modal' | 'tooltip'
309
+ prompt: 'Predefined prompt', // Required for direct mode
310
+
311
+ // Labels
312
+ label: 'Generate with AI',
313
+ loadingLabel: 'Generating...',
314
+ placeholder: 'Describe what you want...',
315
+ modalTitle: 'Generate with AI',
316
+ modalDescription: 'Optional description',
317
+ confirmText: 'Generate',
318
+ cancelText: 'Cancel',
319
+
320
+ // Button styling
321
+ variant: 'primary', // 'primary' | 'secondary' | 'outline' | 'ghost'
322
+ size: 'medium', // 'small' | 'medium' | 'large'
323
+ icon: <CustomIcon />, // Custom icon
324
+ hideIcon: false,
325
+ hideLabel: false, // Icon-only button
326
+
327
+ // Tooltip options
328
+ tooltipPlacement: 'top',
329
+ tooltipWidth: 300,
330
+
331
+ // Tool call display
332
+ toolRenderers: {
333
+ search_docs: (input, output) => (
334
+ <div>Found {output.count} results</div>
335
+ ),
336
+ },
337
+ toolIcons: {
338
+ search_docs: <SearchIcon />,
339
+ },
340
+ processingMessage: 'Processing...',
341
+
342
+ // Theming
343
+ color: '#3b82f6',
344
+ backgroundColor: '#ffffff',
345
+ textColor: '#1f2937',
346
+ borderColor: '#e5e7eb',
347
+ borderRadius: 8,
348
+ zIndex: 10000,
349
+ }}
350
+
351
+ // Callbacks
352
+ onResponse={({ message, toolCalls, chatUid }) => {}}
353
+ onBeforeSend={(prompt) => modifiedPrompt} // Modify prompt before sending
354
+ onError={(error) => {}}
355
+ onStart={() => {}}
356
+ onOpen={() => {}}
357
+ onClose={() => {}}
358
+
359
+ // Other props
360
+ modelInterfaceTools={[...]}
361
+ tenantId="tenant-123"
362
+ tenantMetadata={{ userId: '456' }}
363
+ disabled={false}
364
+ />
365
+ ```
366
+
367
+ #### AIGenerationButton Handle (ref methods)
368
+
369
+ ```tsx
370
+ const buttonRef = useRef<AIGenerationButtonHandle>(null);
371
+
372
+ // Trigger generation programmatically
373
+ const result = await buttonRef.current?.generate('Custom prompt');
374
+
375
+ // Open/close modal or tooltip
376
+ buttonRef.current?.open();
377
+ buttonRef.current?.close();
378
+ buttonRef.current?.reset();
379
+
380
+ // Check processing state
381
+ if (buttonRef.current?.isProcessing) { ... }
382
+ ```
383
+
155
384
  ## Hooks
156
385
 
157
386
  ### useDevicChat
@@ -186,6 +415,74 @@ const {
186
415
  });
187
416
  ```
188
417
 
418
+ ### useAICommandBar
419
+
420
+ Hook for building custom command bar UIs.
421
+
422
+ ```tsx
423
+ import { useAICommandBar } from '@devicai/ui';
424
+
425
+ const {
426
+ isVisible, // boolean
427
+ open, // () => void
428
+ close, // () => void
429
+ toggle, // () => void
430
+ inputValue, // string
431
+ setInputValue, // (value: string) => void
432
+ inputRef, // RefObject<HTMLInputElement>
433
+ focus, // () => void
434
+ isProcessing, // boolean
435
+ currentToolSummary, // string | null
436
+ toolCalls, // ToolCallSummary[]
437
+ result, // CommandBarResult | null
438
+ error, // Error | null
439
+ history, // string[]
440
+ showingHistory, // boolean
441
+ showingCommands, // boolean
442
+ filteredCommands, // AICommandBarCommand[]
443
+ submit, // (message?: string) => Promise<void>
444
+ reset, // () => void
445
+ handleKeyDown, // (e: KeyboardEvent) => void
446
+ } = useAICommandBar({
447
+ assistantId: 'my-assistant',
448
+ options: { shortcut: 'cmd+k' },
449
+ onResponse: (result) => {},
450
+ onError: (error) => {},
451
+ });
452
+ ```
453
+
454
+ ### useAIGenerationButton
455
+
456
+ Hook for building custom generation button UIs.
457
+
458
+ ```tsx
459
+ import { useAIGenerationButton } from '@devicai/ui';
460
+
461
+ const {
462
+ isOpen, // boolean - modal/tooltip open state
463
+ isProcessing, // boolean
464
+ inputValue, // string
465
+ setInputValue, // (value: string) => void
466
+ error, // Error | null
467
+ result, // GenerationResult | null
468
+ toolCalls, // ToolCallSummary[]
469
+ currentToolSummary, // string | null
470
+ inputRef, // RefObject<HTMLTextAreaElement>
471
+ open, // () => void
472
+ close, // () => void
473
+ generate, // (prompt?: string) => Promise<GenerationResult | null>
474
+ reset, // () => void
475
+ handleKeyDown, // (e: KeyboardEvent) => void
476
+ } = useAIGenerationButton({
477
+ assistantId: 'my-assistant',
478
+ options: { mode: 'modal' },
479
+ onResponse: (result) => {},
480
+ onBeforeSend: (prompt) => prompt,
481
+ onError: (error) => {},
482
+ onStart: () => {},
483
+ });
484
+ ```
485
+
189
486
  ### useModelInterface
190
487
 
191
488
  Hook for implementing the Model Interface Protocol.
@@ -346,14 +643,35 @@ All types are exported:
346
643
 
347
644
  ```tsx
348
645
  import type {
646
+ // Chat types
349
647
  ChatMessage,
350
648
  ChatFile,
649
+ ChatDrawerOptions,
650
+ ChatDrawerHandle,
651
+
652
+ // AICommandBar types
653
+ AICommandBarOptions,
654
+ AICommandBarHandle,
655
+ AICommandBarCommand,
656
+ CommandBarResult,
657
+ ToolCallSummary,
658
+
659
+ // AIGenerationButton types
660
+ AIGenerationButtonOptions,
661
+ AIGenerationButtonHandle,
662
+ AIGenerationButtonMode,
663
+ GenerationResult,
664
+
665
+ // Tool types
351
666
  ModelInterfaceTool,
352
667
  ModelInterfaceToolSchema,
353
668
  ToolCall,
354
669
  ToolCallResponse,
670
+
671
+ // API types
355
672
  RealtimeChatHistory,
356
- ChatDrawerOptions,
673
+
674
+ // Hook types
357
675
  UseDevicChatOptions,
358
676
  } from '@devicai/ui';
359
677
  ```
@@ -0,0 +1,254 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var React = require('react');
5
+ var useAIGenerationButton = require('./useAIGenerationButton.js');
6
+
7
+ const DEFAULT_OPTIONS = {
8
+ mode: 'modal',
9
+ prompt: '',
10
+ placeholder: 'Describe what you want to generate...',
11
+ modalTitle: 'Generate with AI',
12
+ modalDescription: '',
13
+ confirmText: 'Generate',
14
+ cancelText: 'Cancel',
15
+ tooltipPlacement: 'top',
16
+ tooltipWidth: 300,
17
+ variant: 'primary',
18
+ size: 'medium',
19
+ icon: undefined,
20
+ hideIcon: false,
21
+ label: 'Generate with AI',
22
+ hideLabel: false,
23
+ loadingLabel: 'Generating...',
24
+ color: '#3b82f6',
25
+ backgroundColor: '',
26
+ textColor: '',
27
+ borderColor: '',
28
+ borderRadius: 8,
29
+ fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
30
+ fontSize: 14,
31
+ zIndex: 10000,
32
+ animationDuration: 200,
33
+ toolRenderers: undefined,
34
+ toolIcons: undefined,
35
+ processingMessage: 'Processing...',
36
+ };
37
+ /**
38
+ * AIGenerationButton component - a button that triggers AI generation with configurable interaction modes
39
+ *
40
+ * @example
41
+ * ```tsx
42
+ * // Direct mode - sends predefined prompt on click
43
+ * <AIGenerationButton
44
+ * assistantId="my-assistant"
45
+ * options={{
46
+ * mode: 'direct',
47
+ * prompt: 'Generate a summary of the current page',
48
+ * label: 'Summarize',
49
+ * }}
50
+ * onResponse={({ message }) => console.log(message.content)}
51
+ * />
52
+ *
53
+ * // Modal mode - opens modal for user input
54
+ * <AIGenerationButton
55
+ * assistantId="my-assistant"
56
+ * options={{
57
+ * mode: 'modal',
58
+ * modalTitle: 'Generate Content',
59
+ * placeholder: 'Describe what you want...',
60
+ * }}
61
+ * onResponse={({ message }) => setContent(message.content.message)}
62
+ * />
63
+ *
64
+ * // Tooltip mode - quick inline input
65
+ * <AIGenerationButton
66
+ * assistantId="my-assistant"
67
+ * options={{
68
+ * mode: 'tooltip',
69
+ * tooltipPlacement: 'bottom',
70
+ * }}
71
+ * onResponse={handleGeneration}
72
+ * />
73
+ * ```
74
+ */
75
+ const AIGenerationButton = React.forwardRef(function AIGenerationButton(props, ref) {
76
+ const { assistantId, apiKey, baseUrl, tenantId, tenantMetadata, options = {}, modelInterfaceTools, onResponse, onBeforeSend, onError, onStart, onOpen, onClose, disabled, className, containerClassName, children, theme, } = props;
77
+ const mergedOptions = React.useMemo(() => ({ ...DEFAULT_OPTIONS, ...options }), [options]);
78
+ const hook = useAIGenerationButton.useAIGenerationButton({
79
+ assistantId,
80
+ apiKey,
81
+ baseUrl,
82
+ tenantId,
83
+ tenantMetadata,
84
+ options: mergedOptions,
85
+ modelInterfaceTools,
86
+ onResponse,
87
+ onBeforeSend,
88
+ onError,
89
+ onStart,
90
+ onOpen,
91
+ onClose,
92
+ disabled,
93
+ });
94
+ // Expose handle
95
+ React.useImperativeHandle(ref, () => ({
96
+ generate: hook.generate,
97
+ open: hook.open,
98
+ close: hook.close,
99
+ reset: hook.reset,
100
+ isProcessing: hook.isProcessing,
101
+ }));
102
+ // Container ref for theming
103
+ const containerRef = React.useRef(null);
104
+ const tooltipRef = React.useRef(null);
105
+ // Apply CSS variables for theming
106
+ React.useEffect(() => {
107
+ const el = containerRef.current;
108
+ if (!el)
109
+ return;
110
+ const vars = [
111
+ ['--devic-gen-primary', mergedOptions.color],
112
+ ['----devic-gen-bg', mergedOptions.backgroundColor || undefined],
113
+ ['--devic-gen-text', mergedOptions.textColor || undefined],
114
+ ['--devic-gen-border', mergedOptions.borderColor || undefined],
115
+ ['--devic-gen-font-family', mergedOptions.fontFamily],
116
+ ['--devic-gen-font-size', typeof mergedOptions.fontSize === 'number' ? `${mergedOptions.fontSize}px` : mergedOptions.fontSize],
117
+ ['--devic-gen-radius', typeof mergedOptions.borderRadius === 'number' ? `${mergedOptions.borderRadius}px` : mergedOptions.borderRadius],
118
+ ['--devic-gen-z-index', String(mergedOptions.zIndex)],
119
+ ['--devic-gen-animation-duration', `${mergedOptions.animationDuration}ms`],
120
+ ];
121
+ // Apply theme from parent if provided
122
+ if (theme) {
123
+ if (theme.backgroundColor)
124
+ vars.push(['--devic-gen-modal-bg', theme.backgroundColor]);
125
+ if (theme.textColor)
126
+ vars.push(['--devic-gen-modal-text', theme.textColor]);
127
+ if (theme.borderColor)
128
+ vars.push(['--devic-gen-modal-border', theme.borderColor]);
129
+ if (theme.primaryColor)
130
+ vars.push(['--devic-gen-primary', theme.primaryColor]);
131
+ }
132
+ for (const [name, value] of vars) {
133
+ if (value) {
134
+ el.style.setProperty(name, value);
135
+ }
136
+ else {
137
+ el.style.removeProperty(name);
138
+ }
139
+ }
140
+ }, [mergedOptions, theme]);
141
+ // Click outside to close tooltip
142
+ React.useEffect(() => {
143
+ if (!hook.isOpen || mergedOptions.mode !== 'tooltip')
144
+ return;
145
+ const handleClickOutside = (e) => {
146
+ const tooltip = tooltipRef.current;
147
+ const container = containerRef.current;
148
+ if (tooltip &&
149
+ container &&
150
+ !tooltip.contains(e.target) &&
151
+ !container.contains(e.target)) {
152
+ hook.close();
153
+ }
154
+ };
155
+ document.addEventListener('mousedown', handleClickOutside);
156
+ return () => document.removeEventListener('mousedown', handleClickOutside);
157
+ }, [hook.isOpen, mergedOptions.mode, hook.close]);
158
+ // Handle button click
159
+ const handleButtonClick = React.useCallback(() => {
160
+ if (disabled || hook.isProcessing)
161
+ return;
162
+ if (mergedOptions.mode === 'direct') {
163
+ hook.generate();
164
+ }
165
+ else {
166
+ hook.open();
167
+ }
168
+ }, [disabled, hook.isProcessing, mergedOptions.mode, hook.generate, hook.open]);
169
+ // Handle confirm in modal/tooltip
170
+ const handleConfirm = React.useCallback(() => {
171
+ hook.generate();
172
+ }, [hook.generate]);
173
+ // Tooltip position styles
174
+ const tooltipPositionStyle = React.useMemo(() => {
175
+ const placement = mergedOptions.tooltipPlacement;
176
+ const width = typeof mergedOptions.tooltipWidth === 'number'
177
+ ? `${mergedOptions.tooltipWidth}px`
178
+ : mergedOptions.tooltipWidth;
179
+ const baseStyle = {
180
+ width,
181
+ position: 'absolute',
182
+ zIndex: mergedOptions.zIndex,
183
+ };
184
+ switch (placement) {
185
+ case 'top':
186
+ return { ...baseStyle, bottom: '100%', left: '50%', transform: 'translateX(-50%)', marginBottom: '8px' };
187
+ case 'bottom':
188
+ return { ...baseStyle, top: '100%', left: '50%', transform: 'translateX(-50%)', marginTop: '8px' };
189
+ case 'left':
190
+ return { ...baseStyle, right: '100%', top: '50%', transform: 'translateY(-50%)', marginRight: '8px' };
191
+ case 'right':
192
+ return { ...baseStyle, left: '100%', top: '50%', transform: 'translateY(-50%)', marginLeft: '8px' };
193
+ default:
194
+ return baseStyle;
195
+ }
196
+ }, [mergedOptions.tooltipPlacement, mergedOptions.tooltipWidth, mergedOptions.zIndex]);
197
+ // Render button content
198
+ // Only show loading state on button for direct mode (modal/tooltip have their own loading)
199
+ const showButtonLoading = hook.isProcessing && mergedOptions.mode === 'direct';
200
+ const renderButtonContent = () => {
201
+ if (children) {
202
+ return children;
203
+ }
204
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [!mergedOptions.hideIcon && (jsxRuntime.jsx("span", { className: "devic-gen-button-icon", children: showButtonLoading ? (jsxRuntime.jsx("span", { className: "devic-gen-spinner" })) : (mergedOptions.icon || jsxRuntime.jsx(SparklesIcon, {})) })), !mergedOptions.hideLabel && (jsxRuntime.jsx("span", { className: "devic-gen-button-label", children: showButtonLoading ? mergedOptions.loadingLabel : mergedOptions.label }))] }));
205
+ };
206
+ // Render tool calls display
207
+ const renderToolCalls = () => {
208
+ if (!hook.isProcessing || hook.toolCalls.length === 0) {
209
+ return null;
210
+ }
211
+ return (jsxRuntime.jsx("div", { className: "devic-gen-tool-calls", children: hook.toolCalls.map((tc) => {
212
+ // Check for custom renderer
213
+ const customRenderer = mergedOptions.toolRenderers?.[tc.name];
214
+ if (customRenderer && tc.status === 'completed') {
215
+ return (jsxRuntime.jsx("div", { className: "devic-gen-tool-item devic-gen-tool-custom", children: customRenderer(tc.input, tc.output) }, tc.id));
216
+ }
217
+ // Use custom icon or default
218
+ const customIcon = mergedOptions.toolIcons?.[tc.name];
219
+ const isExecuting = tc.status === 'executing';
220
+ return (jsxRuntime.jsxs("div", { className: "devic-gen-tool-item", "data-status": tc.status, children: [jsxRuntime.jsx("span", { className: "devic-gen-tool-icon", children: isExecuting ? (jsxRuntime.jsx("span", { className: "devic-gen-spinner devic-gen-spinner-small" })) : customIcon ? (customIcon) : (jsxRuntime.jsx(CheckIcon, {})) }), jsxRuntime.jsx("span", { className: "devic-gen-tool-name", children: tc.summary || tc.name })] }, tc.id));
221
+ }) }));
222
+ };
223
+ // Render processing status
224
+ const renderProcessingStatus = () => {
225
+ if (!hook.isProcessing)
226
+ return null;
227
+ return (jsxRuntime.jsxs("div", { className: "devic-gen-processing-status", children: [jsxRuntime.jsx("span", { className: "devic-gen-spinner devic-gen-spinner-small" }), jsxRuntime.jsx("span", { className: "devic-gen-processing-text", children: hook.currentToolSummary || mergedOptions.processingMessage })] }));
228
+ };
229
+ return (jsxRuntime.jsxs("div", { ref: containerRef, className: `devic-gen-container ${containerClassName || ''}`, style: { position: 'relative', display: 'inline-block' }, children: [jsxRuntime.jsx("button", { type: "button", className: `devic-gen-button ${className || ''}`, "data-variant": mergedOptions.variant, "data-size": mergedOptions.size, "data-processing": showButtonLoading, onClick: handleButtonClick, disabled: disabled || showButtonLoading, children: renderButtonContent() }), mergedOptions.mode === 'tooltip' && hook.isOpen && (jsxRuntime.jsxs("div", { ref: tooltipRef, className: "devic-gen-tooltip", style: tooltipPositionStyle, "data-placement": mergedOptions.tooltipPlacement, children: [renderToolCalls(), hook.isProcessing && hook.toolCalls.length === 0 && renderProcessingStatus(), jsxRuntime.jsx("textarea", { ref: hook.inputRef, className: "devic-gen-input", placeholder: mergedOptions.placeholder, value: hook.inputValue, onChange: (e) => hook.setInputValue(e.target.value), onKeyDown: hook.handleKeyDown, rows: 3, disabled: hook.isProcessing }), hook.error && (jsxRuntime.jsx("div", { className: "devic-gen-error", children: hook.error.message })), jsxRuntime.jsxs("div", { className: "devic-gen-tooltip-actions", children: [jsxRuntime.jsx("button", { type: "button", className: "devic-gen-tooltip-cancel", onClick: hook.close, disabled: hook.isProcessing, children: mergedOptions.cancelText }), jsxRuntime.jsx("button", { type: "button", className: "devic-gen-tooltip-confirm", onClick: handleConfirm, disabled: hook.isProcessing || !hook.inputValue.trim(), children: hook.isProcessing ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "devic-gen-spinner devic-gen-spinner-small" }), mergedOptions.loadingLabel] })) : (mergedOptions.confirmText) })] })] })), mergedOptions.mode === 'modal' && hook.isOpen && (jsxRuntime.jsx("div", { className: "devic-gen-modal-overlay", onClick: (e) => {
230
+ if (e.target === e.currentTarget && !hook.isProcessing)
231
+ hook.close();
232
+ }, children: jsxRuntime.jsxs("div", { className: "devic-gen-modal", children: [jsxRuntime.jsxs("div", { className: "devic-gen-modal-header", children: [jsxRuntime.jsx("h3", { className: "devic-gen-modal-title", children: mergedOptions.modalTitle }), jsxRuntime.jsx("button", { type: "button", className: "devic-gen-modal-close", onClick: hook.close, "aria-label": "Close", disabled: hook.isProcessing, children: jsxRuntime.jsx(CloseIcon, {}) })] }), mergedOptions.modalDescription && (jsxRuntime.jsx("p", { className: "devic-gen-modal-description", children: mergedOptions.modalDescription })), jsxRuntime.jsxs("div", { className: "devic-gen-modal-body", children: [renderToolCalls(), hook.isProcessing && hook.toolCalls.length === 0 && renderProcessingStatus(), jsxRuntime.jsx("textarea", { ref: hook.inputRef, className: "devic-gen-input", placeholder: mergedOptions.placeholder, value: hook.inputValue, onChange: (e) => hook.setInputValue(e.target.value), onKeyDown: hook.handleKeyDown, rows: 4, disabled: hook.isProcessing }), hook.error && (jsxRuntime.jsx("div", { className: "devic-gen-error", children: hook.error.message }))] }), jsxRuntime.jsxs("div", { className: "devic-gen-modal-footer", children: [jsxRuntime.jsx("button", { type: "button", className: "devic-gen-modal-cancel", onClick: hook.close, disabled: hook.isProcessing, children: mergedOptions.cancelText }), jsxRuntime.jsx("button", { type: "button", className: "devic-gen-modal-confirm", onClick: handleConfirm, disabled: hook.isProcessing || !hook.inputValue.trim(), children: hook.isProcessing ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { className: "devic-gen-spinner devic-gen-spinner-small" }), mergedOptions.loadingLabel] })) : (mergedOptions.confirmText) })] })] }) }))] }));
233
+ });
234
+ /**
235
+ * Default sparkles icon
236
+ */
237
+ function SparklesIcon() {
238
+ return (jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsxRuntime.jsx("path", { d: "M10 2L11.5 8.5L18 10L11.5 11.5L10 18L8.5 11.5L2 10L8.5 8.5L10 2Z", fill: "currentColor", opacity: "0.9" }), jsxRuntime.jsx("path", { d: "M16 3L16.5 5L18.5 5.5L16.5 6L16 8L15.5 6L13.5 5.5L15.5 5L16 3Z", fill: "currentColor", opacity: "0.6" }), jsxRuntime.jsx("circle", { cx: "4", cy: "15", r: "1", fill: "currentColor", opacity: "0.4" })] }));
239
+ }
240
+ /**
241
+ * Close icon
242
+ */
243
+ function CloseIcon() {
244
+ return (jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }));
245
+ }
246
+ /**
247
+ * Check icon for completed tool calls
248
+ */
249
+ function CheckIcon() {
250
+ return (jsxRuntime.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "20 6 9 17 4 12" }) }));
251
+ }
252
+
253
+ exports.AIGenerationButton = AIGenerationButton;
254
+ //# sourceMappingURL=AIGenerationButton.js.map