@contentgrowth/llm-service 0.8.3 → 0.8.5

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.
@@ -0,0 +1,1474 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // src/ui/react/components/index.ts
30
+ var components_exports = {};
31
+ __export(components_exports, {
32
+ ChatBubble: () => ChatBubble,
33
+ ChatConfigProvider: () => ChatConfigProvider,
34
+ ChatHeader: () => ChatHeader,
35
+ ChatInputArea: () => ChatInputArea,
36
+ ChatMessageList: () => ChatMessageList,
37
+ ConnectionStatus: () => ConnectionStatus,
38
+ MessageBubble: () => MessageBubble,
39
+ Spinner: () => Spinner,
40
+ createWhisperVoiceConfig: () => createWhisperVoiceConfig,
41
+ transcribeAudio: () => transcribeAudio,
42
+ useAudioRecorder: () => useAudioRecorder,
43
+ useChatConfig: () => useChatConfig,
44
+ useProactiveResize: () => useProactiveResize,
45
+ useSpeechRecognition: () => useSpeechRecognition
46
+ });
47
+ module.exports = __toCommonJS(components_exports);
48
+
49
+ // src/ui/react/components/ChatBubble.tsx
50
+ var import_jsx_runtime = require("react/jsx-runtime");
51
+ function ChatBubble({ onClick }) {
52
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
53
+ "button",
54
+ {
55
+ onClick,
56
+ className: "bg-blue-500 hover:bg-blue-600 text-white rounded-full w-12 h-12 flex items-center justify-center shadow-lg animate-pulse",
57
+ "aria-label": "Open chat assistant",
58
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-6 w-6", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z" }) })
59
+ }
60
+ );
61
+ }
62
+
63
+ // src/ui/react/components/MessageBubble.tsx
64
+ var import_react_markdown = __toESM(require("react-markdown"), 1);
65
+ var import_remark_gfm = __toESM(require("remark-gfm"), 1);
66
+ var import_solid = require("@heroicons/react/24/solid");
67
+
68
+ // src/ui/react/context/ChatConfigContext.tsx
69
+ var import_react = require("react");
70
+ var import_jsx_runtime2 = require("react/jsx-runtime");
71
+ var ChatConfigContext = (0, import_react.createContext)({});
72
+ var ChatConfigProvider = ({ config, children }) => {
73
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChatConfigContext.Provider, { value: config, children });
74
+ };
75
+ var useChatConfig = () => (0, import_react.useContext)(ChatConfigContext);
76
+
77
+ // src/ui/react/components/MessageBubble.tsx
78
+ var import_jsx_runtime3 = require("react/jsx-runtime");
79
+ var MessageBubble = ({
80
+ message,
81
+ isUser,
82
+ userAvatarUrl,
83
+ onViewChanges
84
+ }) => {
85
+ const { user } = useChatConfig();
86
+ const finalAvatarUrl = userAvatarUrl || (user == null ? void 0 : user.avatarUrl);
87
+ const isSystem = message.role === "system";
88
+ if (isSystem) {
89
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "text-center my-4", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "text-xs text-gray-500 italic bg-gray-100 px-3 py-1 rounded-full", children: [
90
+ message.content,
91
+ message.diff && onViewChanges && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
92
+ "button",
93
+ {
94
+ onClick: () => onViewChanges(message.diff),
95
+ className: "ml-2 font-semibold text-blue-600 hover:underline focus:outline-none",
96
+ children: "View Changes"
97
+ }
98
+ )
99
+ ] }) });
100
+ }
101
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `flex items-start gap-3 my-4 ${isUser ? "justify-end" : "justify-start"}`, children: [
102
+ !isUser && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex-shrink-0 h-8 w-8 rounded-full bg-blue-500 flex items-center justify-center text-white", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_solid.SparklesIcon, { className: "h-5 w-5" }) }),
103
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
104
+ "div",
105
+ {
106
+ className: `max-w-[85%] px-4 py-3 rounded-2xl shadow-sm overflow-hidden ${isUser ? "bg-blue-500 text-white rounded-br-none" : "bg-gray-100 text-gray-800 rounded-bl-none"}`,
107
+ children: [
108
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: `text-sm ${!isUser ? "prose prose-sm max-w-none" : ""}`, children: isUser ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "whitespace-pre-wrap", children: message.content }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
109
+ import_react_markdown.default,
110
+ {
111
+ remarkPlugins: [import_remark_gfm.default],
112
+ components: {
113
+ p: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "mb-2 last:mb-0", ...props }),
114
+ ul: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ul", { className: "list-disc ml-4 mb-2", ...props }),
115
+ ol: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ol", { className: "list-decimal ml-4 mb-2", ...props }),
116
+ li: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { className: "mb-0.5", ...props }),
117
+ a: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("a", { className: "text-blue-600 hover:underline", ...props }),
118
+ code: ({ node, ...props }) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("code", { className: "bg-gray-200 rounded px-1 py-0.5 text-xs font-mono", ...props })
119
+ },
120
+ children: message.content
121
+ }
122
+ ) }),
123
+ message.role === "assistant" && message.diff && onViewChanges && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
124
+ "button",
125
+ {
126
+ onClick: () => onViewChanges(message.diff),
127
+ className: "mt-2 text-sm text-blue-500 hover:underline block",
128
+ children: "View Changes"
129
+ }
130
+ ),
131
+ message.actionResult && message.actionResult.type === "link" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "mt-2 pt-2 border-t border-gray-300", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
132
+ "a",
133
+ {
134
+ href: message.actionResult.url,
135
+ target: message.actionResult.target || "_blank",
136
+ rel: "noopener noreferrer",
137
+ onClick: (e) => {
138
+ var _a;
139
+ if (((_a = message.actionResult) == null ? void 0 : _a.target) && message.actionResult.target !== "_blank") {
140
+ e.preventDefault();
141
+ window.open(message.actionResult.url, message.actionResult.target);
142
+ }
143
+ },
144
+ className: `inline-flex items-center gap-1 text-sm font-medium hover:underline ${isUser ? "text-blue-100 hover:text-white" : "text-blue-600 hover:text-blue-800"}`,
145
+ children: [
146
+ message.actionResult.label,
147
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { className: "w-3.5 h-3.5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" }) })
148
+ ]
149
+ }
150
+ ) })
151
+ ]
152
+ }
153
+ ),
154
+ isUser && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex-shrink-0 h-8 w-8 rounded-full bg-gray-200 overflow-hidden flex items-center justify-center text-gray-500", children: finalAvatarUrl ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
155
+ "img",
156
+ {
157
+ className: "h-full w-full object-cover",
158
+ src: finalAvatarUrl,
159
+ alt: "User",
160
+ onError: (e) => {
161
+ var _a;
162
+ e.target.style.display = "none";
163
+ (_a = e.target.parentElement) == null ? void 0 : _a.classList.remove("bg-gray-200");
164
+ }
165
+ }
166
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-5 h-5", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { fillRule: "evenodd", d: "M7.5 6a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM3.751 20.105a8.25 8.25 0 0 1 16.498 0 .75.75 0 0 1-.437.695A18.683 18.683 0 0 1 12 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 0 1-.437-.695Z", clipRule: "evenodd" }) }) })
167
+ ] });
168
+ };
169
+
170
+ // src/ui/react/components/ChatHeader.tsx
171
+ var import_jsx_runtime4 = require("react/jsx-runtime");
172
+ function ChatHeader({
173
+ contextTitle,
174
+ currentTask,
175
+ onClose
176
+ }) {
177
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "bg-blue-500 text-white px-4 py-4 flex justify-between items-center", children: [
178
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h3", { className: "font-medium text-lg", children: currentTask ? `DoveText Virtual Assistant (${currentTask.currentStep}/${currentTask.steps})` : `DoveText Virtual Assistant (${contextTitle})` }),
179
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
180
+ "button",
181
+ {
182
+ onClick: onClose,
183
+ className: "bg-blue-600 hover:bg-blue-700 text-white rounded-full p-1.5 flex items-center justify-center transition-colors cursor-pointer",
184
+ "aria-label": "Close chat",
185
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", className: "h-5 w-5", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { fillRule: "evenodd", d: "M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z", clipRule: "evenodd" }) })
186
+ }
187
+ )
188
+ ] });
189
+ }
190
+
191
+ // src/ui/react/components/ChatInputArea.tsx
192
+ var import_react5 = require("react");
193
+ var import_outline = require("@heroicons/react/24/outline");
194
+
195
+ // src/ui/react/hooks/useSpeechRecognition.ts
196
+ var import_react2 = require("react");
197
+ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
198
+ const [isListening, setIsListening] = (0, import_react2.useState)(false);
199
+ const [transcript, setTranscript] = (0, import_react2.useState)("");
200
+ const [error, setError] = (0, import_react2.useState)(null);
201
+ const [isSupported, setIsSupported] = (0, import_react2.useState)(false);
202
+ const recognitionRef = (0, import_react2.useRef)(null);
203
+ const isSimulatingRef = (0, import_react2.useRef)(false);
204
+ const simulationTimeoutRef = (0, import_react2.useRef)(null);
205
+ (0, import_react2.useEffect)(() => {
206
+ if (typeof window !== "undefined") {
207
+ const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
208
+ if (SpeechRecognition) {
209
+ setIsSupported(true);
210
+ const recognition = new SpeechRecognition();
211
+ recognition.continuous = true;
212
+ recognition.interimResults = true;
213
+ recognition.lang = language;
214
+ recognition.onstart = () => {
215
+ setIsListening(true);
216
+ setError(null);
217
+ };
218
+ recognition.onend = () => {
219
+ if (isSimulatingRef.current) return;
220
+ setIsListening(false);
221
+ if (onEnd) onEnd();
222
+ };
223
+ recognition.onresult = (event) => {
224
+ let interimTranscript = "";
225
+ let finalTranscript = "";
226
+ for (let i = event.results.length - 1; i < event.results.length; ++i) {
227
+ const result = event.results[i];
228
+ if (result.isFinal) {
229
+ finalTranscript += result[0].transcript;
230
+ if (onResult) onResult(finalTranscript, true);
231
+ } else {
232
+ interimTranscript += result[0].transcript;
233
+ if (onResult) onResult(interimTranscript, false);
234
+ }
235
+ }
236
+ setTranscript((prev) => prev + finalTranscript);
237
+ };
238
+ recognition.onerror = (event) => {
239
+ if (event.error === "not-allowed" && process.env.NODE_ENV === "development") {
240
+ console.warn("Speech recognition blocked. Simulating input for development...");
241
+ isSimulatingRef.current = true;
242
+ setError(null);
243
+ setIsListening(true);
244
+ simulationTimeoutRef.current = setTimeout(() => {
245
+ const mockText = "This is a simulated voice input for testing.";
246
+ setTranscript((prev) => prev + (prev ? " " : "") + mockText);
247
+ if (onResult) onResult(mockText, true);
248
+ isSimulatingRef.current = false;
249
+ setIsListening(false);
250
+ if (onEnd) onEnd();
251
+ simulationTimeoutRef.current = null;
252
+ }, 3e3);
253
+ return;
254
+ }
255
+ console.error("Speech recognition error", event.error);
256
+ setError(event.error);
257
+ setIsListening(false);
258
+ };
259
+ recognitionRef.current = recognition;
260
+ }
261
+ }
262
+ return () => {
263
+ if (isSimulatingRef.current && simulationTimeoutRef.current) {
264
+ clearTimeout(simulationTimeoutRef.current);
265
+ simulationTimeoutRef.current = null;
266
+ }
267
+ recognitionRef.current.stop();
268
+ };
269
+ }, [onResult, onEnd, language]);
270
+ const start = (0, import_react2.useCallback)(() => {
271
+ if (recognitionRef.current && !isListening) {
272
+ try {
273
+ setTranscript("");
274
+ recognitionRef.current.start();
275
+ } catch (e) {
276
+ console.error("Failed to start speech recognition:", e);
277
+ }
278
+ }
279
+ }, [isListening]);
280
+ const stop = (0, import_react2.useCallback)(() => {
281
+ if (isSimulatingRef.current) {
282
+ if (simulationTimeoutRef.current) {
283
+ clearTimeout(simulationTimeoutRef.current);
284
+ simulationTimeoutRef.current = null;
285
+ }
286
+ const mockText = "This is a simulated voice input for testing.";
287
+ setTranscript((prev) => prev + (prev ? " " : "") + mockText);
288
+ if (onResult) onResult(mockText, true);
289
+ isSimulatingRef.current = false;
290
+ setIsListening(false);
291
+ if (onEnd) onEnd();
292
+ return;
293
+ }
294
+ if (recognitionRef.current && isListening) {
295
+ recognitionRef.current.stop();
296
+ }
297
+ }, [isListening, onResult, onEnd]);
298
+ const resetTranscript = (0, import_react2.useCallback)(() => {
299
+ setTranscript("");
300
+ }, []);
301
+ return {
302
+ isListening,
303
+ transcript,
304
+ start,
305
+ stop,
306
+ resetTranscript,
307
+ isSupported,
308
+ error
309
+ };
310
+ };
311
+
312
+ // src/ui/react/hooks/useAudioRecorder.ts
313
+ var import_react3 = require("react");
314
+ var useAudioRecorder = (onStop) => {
315
+ const [isRecording, setIsRecording] = (0, import_react3.useState)(false);
316
+ const [blob, setBlob] = (0, import_react3.useState)(null);
317
+ const [error, setError] = (0, import_react3.useState)(null);
318
+ const mediaRecorderRef = (0, import_react3.useRef)(null);
319
+ const chunksRef = (0, import_react3.useRef)([]);
320
+ const start = (0, import_react3.useCallback)(async () => {
321
+ try {
322
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
323
+ const mediaRecorder = new MediaRecorder(stream);
324
+ mediaRecorderRef.current = mediaRecorder;
325
+ chunksRef.current = [];
326
+ mediaRecorder.ondataavailable = (e) => {
327
+ if (e.data.size > 0) {
328
+ chunksRef.current.push(e.data);
329
+ }
330
+ };
331
+ mediaRecorder.onstop = () => {
332
+ const audioBlob = new Blob(chunksRef.current, { type: "audio/webm" });
333
+ setBlob(audioBlob);
334
+ setIsRecording(false);
335
+ if (onStop) onStop(audioBlob);
336
+ stream.getTracks().forEach((track) => track.stop());
337
+ };
338
+ mediaRecorder.start();
339
+ setIsRecording(true);
340
+ setError(null);
341
+ } catch (e) {
342
+ console.error("Failed to start audio recording:", e);
343
+ setError(e.message || "Microphone access denied");
344
+ }
345
+ }, [onStop]);
346
+ const stop = (0, import_react3.useCallback)(() => {
347
+ if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") {
348
+ mediaRecorderRef.current.stop();
349
+ }
350
+ }, []);
351
+ return {
352
+ isRecording,
353
+ start,
354
+ stop,
355
+ blob,
356
+ error
357
+ };
358
+ };
359
+
360
+ // src/ui/react/hooks/useProactiveResize.ts
361
+ var import_react4 = require("react");
362
+ function useProactiveResize(textareaRef, measurementRef, value, disabled) {
363
+ (0, import_react4.useEffect)(() => {
364
+ if (!textareaRef.current || !measurementRef.current) return;
365
+ const styles = window.getComputedStyle(textareaRef.current);
366
+ measurementRef.current.style.width = styles.width;
367
+ measurementRef.current.style.font = styles.font;
368
+ measurementRef.current.style.padding = styles.padding;
369
+ measurementRef.current.style.border = styles.border;
370
+ measurementRef.current.style.boxSizing = styles.boxSizing;
371
+ measurementRef.current.style.whiteSpace = "pre-wrap";
372
+ measurementRef.current.style.visibility = "hidden";
373
+ measurementRef.current.style.position = "absolute";
374
+ measurementRef.current.style.pointerEvents = "none";
375
+ measurementRef.current.textContent = value + "\u200B";
376
+ const scrollHeight = measurementRef.current.offsetHeight;
377
+ textareaRef.current.style.height = `${scrollHeight}px`;
378
+ }, [value, disabled, textareaRef, measurementRef]);
379
+ }
380
+
381
+ // src/ui/react/components/ChatInputArea.tsx
382
+ var import_jsx_runtime5 = require("react/jsx-runtime");
383
+ var ChatInputArea = (0, import_react5.forwardRef)(({
384
+ onSubmit,
385
+ isSending,
386
+ showInputForm,
387
+ currentTask,
388
+ lastInteractiveMessage,
389
+ voiceConfig: propVoiceConfig,
390
+ // Rename to distinguish from context
391
+ onStop,
392
+ hintText,
393
+ placeholder,
394
+ value,
395
+ onChange,
396
+ defaultInputMode = "text"
397
+ }, ref) => {
398
+ var _a, _b, _c, _d;
399
+ const [internalMessage, setInternalMessage] = (0, import_react5.useState)("");
400
+ const [voiceTrigger, setVoiceTrigger] = (0, import_react5.useState)(null);
401
+ const [inputMode, setInputMode] = (0, import_react5.useState)(defaultInputMode);
402
+ const [isFocused, setIsFocused] = (0, import_react5.useState)(false);
403
+ const textareaRef = (0, import_react5.useRef)(null);
404
+ const measurementRef = (0, import_react5.useRef)(null);
405
+ const voiceContainerRef = (0, import_react5.useRef)(null);
406
+ (0, import_react5.useEffect)(() => {
407
+ var _a2;
408
+ if (inputMode === "voice") {
409
+ (_a2 = voiceContainerRef.current) == null ? void 0 : _a2.focus();
410
+ }
411
+ }, [inputMode]);
412
+ const isControlled = value !== void 0;
413
+ const message = isControlled ? value : internalMessage;
414
+ const { voice: globalVoice } = useChatConfig();
415
+ const isVoiceEnabled = (_a = globalVoice == null ? void 0 : globalVoice.enabled) != null ? _a : !!propVoiceConfig;
416
+ const voiceConfig = isVoiceEnabled ? propVoiceConfig || (globalVoice == null ? void 0 : globalVoice.config) : void 0;
417
+ const triggerChange = (newValue) => {
418
+ if (isControlled && onChange && textareaRef.current) {
419
+ const syntheticEvent = {
420
+ target: { value: newValue },
421
+ currentTarget: { value: newValue }
422
+ };
423
+ onChange(syntheticEvent);
424
+ } else {
425
+ setInternalMessage(newValue);
426
+ }
427
+ };
428
+ const isInputDisabled = (currentTask == null ? void 0 : currentTask.complete) || (lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.interactive) && (((_b = lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.interactiveData) == null ? void 0 : _b.function) === "form" && !(lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.isResponseSubmitted) || ((_c = lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.interactiveData) == null ? void 0 : _c.function) === "confirm" && !(lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.isResponseSubmitted));
429
+ useProactiveResize(textareaRef, measurementRef, message, isInputDisabled || !!voiceTrigger || inputMode === "voice");
430
+ const handleVoiceKeyDown = (e) => {
431
+ if (inputMode !== "voice" || isInputDisabled) return;
432
+ if (e.code !== "Space") return;
433
+ const activeElement = document.activeElement;
434
+ const isInputActive = activeElement && (activeElement.tagName === "INPUT" || activeElement.tagName === "TEXTAREA" || activeElement instanceof HTMLElement && activeElement.isContentEditable);
435
+ if (isInputActive) return;
436
+ e.preventDefault();
437
+ e.stopPropagation();
438
+ if (voiceTrigger === "click") return;
439
+ if (!e.repeat && !voiceTrigger) {
440
+ startRecording("space");
441
+ }
442
+ };
443
+ const handleVoiceKeyUp = (e) => {
444
+ if (inputMode !== "voice" || isInputDisabled) return;
445
+ if (e.code === "Space") {
446
+ if (voiceTrigger === "space") {
447
+ e.preventDefault();
448
+ stopRecording();
449
+ }
450
+ }
451
+ };
452
+ const nativeSpeech = useSpeechRecognition((text) => {
453
+ triggerChange(message + (message ? " " : "") + text);
454
+ }, () => {
455
+ var _a2;
456
+ setVoiceTrigger(null);
457
+ (_a2 = voiceConfig == null ? void 0 : voiceConfig.onVoiceEnd) == null ? void 0 : _a2.call(voiceConfig);
458
+ }, voiceConfig == null ? void 0 : voiceConfig.language);
459
+ const customRecorder = useAudioRecorder(async (blob) => {
460
+ var _a2;
461
+ setVoiceTrigger(null);
462
+ (_a2 = voiceConfig == null ? void 0 : voiceConfig.onVoiceEnd) == null ? void 0 : _a2.call(voiceConfig);
463
+ if (voiceConfig == null ? void 0 : voiceConfig.onAudioCapture) {
464
+ try {
465
+ const text = await voiceConfig.onAudioCapture(blob);
466
+ if (text) triggerChange(message + (message ? " " : "") + text);
467
+ } catch (e) {
468
+ console.error("Audio capture failed", e);
469
+ }
470
+ }
471
+ });
472
+ (0, import_react5.useImperativeHandle)(ref, () => ({
473
+ focus: () => {
474
+ var _a2, _b2;
475
+ if (inputMode === "voice") {
476
+ (_a2 = voiceContainerRef.current) == null ? void 0 : _a2.focus();
477
+ } else {
478
+ (_b2 = textareaRef.current) == null ? void 0 : _b2.focus();
479
+ }
480
+ },
481
+ setValue: (newValue) => {
482
+ triggerChange(newValue);
483
+ }
484
+ }));
485
+ const handleSubmit = (e) => {
486
+ if (e) e.preventDefault();
487
+ if (!message.trim()) {
488
+ setTimeout(() => {
489
+ var _a2;
490
+ return (_a2 = textareaRef.current) == null ? void 0 : _a2.focus();
491
+ }, 0);
492
+ return;
493
+ }
494
+ const currentMessage = message;
495
+ triggerChange("");
496
+ setTimeout(() => {
497
+ var _a2;
498
+ return (_a2 = textareaRef.current) == null ? void 0 : _a2.focus();
499
+ }, 0);
500
+ onSubmit(currentMessage);
501
+ };
502
+ const handleKeyDown = (e) => {
503
+ if (e.key === "Enter" && !e.shiftKey && !e.metaKey && !e.altKey) {
504
+ e.preventDefault();
505
+ handleSubmit();
506
+ }
507
+ };
508
+ const startRecording = async (trigger) => {
509
+ var _a2;
510
+ if (voiceTrigger) return;
511
+ setVoiceTrigger(trigger);
512
+ (_a2 = voiceConfig == null ? void 0 : voiceConfig.onVoiceStart) == null ? void 0 : _a2.call(voiceConfig);
513
+ if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "native") {
514
+ if (!nativeSpeech.isSupported) {
515
+ alert("Speech recognition is not supported in this browser.");
516
+ setVoiceTrigger(null);
517
+ return;
518
+ }
519
+ nativeSpeech.start();
520
+ } else {
521
+ await customRecorder.start();
522
+ }
523
+ };
524
+ const stopRecording = () => {
525
+ if (!voiceTrigger) return;
526
+ if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "native") {
527
+ nativeSpeech.stop();
528
+ } else {
529
+ customRecorder.stop();
530
+ }
531
+ };
532
+ const getPlaceholder = () => {
533
+ if (placeholder) return placeholder;
534
+ if (voiceTrigger) return "Listening...";
535
+ if (currentTask == null ? void 0 : currentTask.complete) {
536
+ return "Task completed!";
537
+ }
538
+ if ((lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.interactive) && (lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.interactiveData) && !(lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.isResponseSubmitted)) {
539
+ const interactiveType = lastInteractiveMessage.interactiveData.function;
540
+ switch (interactiveType) {
541
+ case "chat":
542
+ return "Type your response...";
543
+ case "confirm":
544
+ return "Select Yes or No, or type your response...";
545
+ case "select":
546
+ return "Choose an option or type your response...";
547
+ case "form":
548
+ return "Fill out the form...";
549
+ default:
550
+ return "Type your response...";
551
+ }
552
+ }
553
+ return currentTask ? "Continue your conversation..." : "Ask me anything...";
554
+ };
555
+ const inputHint = !isInputDisabled && !(lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.interactive) ? null : ((_d = lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.interactiveData) == null ? void 0 : _d.function) === "form" ? "Please provide information by completing above form" : null;
556
+ if (!showInputForm) {
557
+ return null;
558
+ }
559
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col w-full", children: [
560
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2", children: [
561
+ voiceConfig && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
562
+ "button",
563
+ {
564
+ type: "button",
565
+ onClick: () => {
566
+ if (inputMode === "voice" && voiceTrigger) {
567
+ stopRecording();
568
+ }
569
+ setInputMode((prev) => prev === "text" ? "voice" : "text");
570
+ },
571
+ className: "mb-1 p-2 text-gray-500 hover:text-gray-700 hover:bg-gray-100 rounded-full transition-colors flex-shrink-0 border border-gray-300 bg-white",
572
+ title: inputMode === "text" ? "Switch to Voice" : "Switch to Keyboard",
573
+ children: inputMode === "text" ? (
574
+ // Voice Icon (Waveform)
575
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-5 h-5 text-gray-600", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { d: "M11.25 4.532A.75.75 0 0 1 12 5.25v13.5a.75.75 0 0 1-1.5 0V5.25a.75.75 0 0 1 .75-.718ZM7.5 8.25a.75.75 0 0 1 .75.75v5.25a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm9 0a.75.75 0 0 1 .75.75v5.25a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75ZM3.75 10.5a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5a.75.75 0 0 1 .75-.75Zm16.5 0a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-1.5 0v-1.5a.75.75 0 0 1 .75-.75Z" }) })
576
+ ) : (
577
+ // Keyboard Icon (Filled)
578
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-5 h-5 text-gray-600", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("path", { fillRule: "evenodd", d: "M3 6a3 3 0 0 1 3-3h12a3 3 0 0 1 3 3v12a3 3 0 0 1-3 3H6a3 3 0 0 1-3-3V6Zm4.5 3a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-.75.75h-1.5a.75.75 0 0 1-.75-.75V9Zm6 0a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-.75.75h-1.5a.75.75 0 0 1-.75-.75V9Zm6 0a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-.75.75h-1.5a.75.75 0 0 1-.75-.75V9Zm-12 4.5a.75.75 0 0 1 .75-.75h1.5a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-.75.75h-1.5a.75.75 0 0 1-.75-.75v-1.5Zm6 0a.75.75 0 0 1 .75-.75h6.75a.75.75 0 0 1 .75.75v1.5a.75.75 0 0 1-.75.75h-6.75a.75.75 0 0 1-.75-.75v-1.5Z", clipRule: "evenodd" }) })
579
+ )
580
+ }
581
+ ),
582
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
583
+ "div",
584
+ {
585
+ ref: voiceContainerRef,
586
+ tabIndex: inputMode === "voice" ? 0 : -1,
587
+ onKeyDown: handleVoiceKeyDown,
588
+ onKeyUp: handleVoiceKeyUp,
589
+ onFocus: () => setIsFocused(true),
590
+ onBlur: () => setIsFocused(false),
591
+ className: "flex-1 flex items-center border border-gray-300 rounded-lg overflow-hidden outline-none focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-blue-500 bg-white min-h-[42px] mb-1",
592
+ children: [
593
+ inputMode === "text" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
594
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
595
+ "span",
596
+ {
597
+ ref: measurementRef,
598
+ className: "absolute invisible whitespace-pre-wrap p-0 m-0 text-gray-700 leading-6",
599
+ style: { fontSize: "1rem" }
600
+ }
601
+ ),
602
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
603
+ "textarea",
604
+ {
605
+ ref: textareaRef,
606
+ value: message,
607
+ onChange: (e) => {
608
+ if (isControlled && onChange) {
609
+ onChange(e);
610
+ } else {
611
+ setInternalMessage(e.target.value);
612
+ }
613
+ },
614
+ onKeyDown: handleKeyDown,
615
+ placeholder: getPlaceholder(),
616
+ disabled: isInputDisabled || !!voiceTrigger,
617
+ rows: 1,
618
+ className: `flex-grow px-4 py-2 outline-none text-gray-700 placeholder-gray-500 disabled:bg-gray-100 resize-none leading-6 w-full ${isInputDisabled ? "cursor-not-allowed" : ""}`
619
+ }
620
+ )
621
+ ] }),
622
+ inputMode === "voice" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex-grow flex flex-col justify-center items-center p-1 relative", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
623
+ "button",
624
+ {
625
+ type: "button",
626
+ onClick: () => {
627
+ if (voiceTrigger === "click") {
628
+ stopRecording();
629
+ } else if (!voiceTrigger) {
630
+ startRecording("click");
631
+ }
632
+ },
633
+ disabled: isInputDisabled || voiceTrigger === "space",
634
+ className: `w-full py-2 text-center font-medium rounded-md transition-all select-none flex items-center justify-center gap-2 ${voiceTrigger ? "bg-red-50 text-red-600 animate-pulse border border-red-200" : "bg-gray-50 text-gray-700 hover:bg-gray-100"} ${voiceTrigger === "space" ? "opacity-90 cursor-default" : ""}`,
635
+ children: voiceTrigger ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
636
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-2 h-2 rounded-full bg-red-500 animate-ping mr-2" }),
637
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
638
+ "Listening... ",
639
+ voiceTrigger === "space" ? "(Release Space to send)" : "Tap to send"
640
+ ] })
641
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Tap to Talk" })
642
+ }
643
+ ) }),
644
+ (inputMode === "text" || isSending) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative mx-2 flex-shrink-0", children: [
645
+ isSending && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "absolute -inset-1", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
646
+ "svg",
647
+ {
648
+ className: "animate-spin h-full w-full text-blue-500 opacity-75",
649
+ xmlns: "http://www.w3.org/2000/svg",
650
+ fill: "none",
651
+ viewBox: "0 0 24 24",
652
+ children: [
653
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
654
+ "circle",
655
+ {
656
+ className: "opacity-25",
657
+ cx: "12",
658
+ cy: "12",
659
+ r: "10",
660
+ stroke: "currentColor",
661
+ strokeWidth: "4"
662
+ }
663
+ ),
664
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
665
+ "path",
666
+ {
667
+ className: "opacity-75",
668
+ fill: "currentColor",
669
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
670
+ }
671
+ )
672
+ ]
673
+ }
674
+ ) }),
675
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
676
+ "button",
677
+ {
678
+ type: "button",
679
+ onClick: (e) => {
680
+ if (isSending && onStop) {
681
+ e.preventDefault();
682
+ onStop();
683
+ } else {
684
+ handleSubmit();
685
+ }
686
+ },
687
+ disabled: (currentTask == null ? void 0 : currentTask.complete) || isSending && !onStop || isInputDisabled || !!voiceTrigger,
688
+ className: `relative z-10 text-white rounded-full p-2 transition-colors duration-200 disabled:bg-gray-400 disabled:cursor-not-allowed ${isSending && onStop ? "bg-red-500 hover:bg-red-600" : "bg-blue-600 hover:bg-blue-700"}`,
689
+ title: isSending && onStop ? "Stop generating" : "Send message",
690
+ children: isSending ? onStop ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_outline.StopIcon, { className: "h-5 w-5" }) : (
691
+ // AND we show the overlay spinner outside?
692
+ // Actually `ChatInput.tsx` lines 117-140 are `isLoading && (...)`. It is always shown when loading.
693
+ // So we have a spinner ring AROUND the button (absolute -inset-1).
694
+ // AND potentially a spinner INSIDE the button if no onStop?
695
+ // In my case, I will stick to:
696
+ // If onStop: Show StopIcon. Button is Red.
697
+ // If !onStop: Show Spinner inside? Or just let the outer ring do the work?
698
+ // Legacy `Spinner` component usage inside button suggests double spinner if we are not careful.
699
+ // But usually `onStop` is provided for streaming.
700
+ // If I look at the screenshot, it shows a RED button (with stop icon) and a BLUE ring around it.
701
+ // That matches: Red button (bg-red-500) + Blue Spinner Ring (text-blue-500).
702
+ // So I will replicate that structure.
703
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_outline.StopIcon, { className: "h-5 w-5" })
704
+ ) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_outline.PaperAirplaneIcon, { className: "h-5 w-5" })
705
+ }
706
+ )
707
+ ] })
708
+ ]
709
+ }
710
+ )
711
+ ] }),
712
+ inputHint && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-sm text-red-500 bg-red-50 py-1 px-4 rounded-lg mt-1", children: inputHint }),
713
+ hintText && inputMode === "text" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-xs text-gray-500 ml-12 mb-2 mt-1", children: hintText }),
714
+ inputMode === "voice" && !voiceTrigger && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
715
+ "p",
716
+ {
717
+ className: "text-[10px] text-gray-400 font-medium ml-12 text-center -mt-1 mb-1 cursor-pointer hover:text-gray-600 transition-colors",
718
+ onClick: () => {
719
+ var _a2;
720
+ return (_a2 = voiceContainerRef.current) == null ? void 0 : _a2.focus();
721
+ },
722
+ children: isFocused ? "Click to talk or hold space to talk" : "Tap to talk or click here to focus and push space to talk"
723
+ }
724
+ )
725
+ ] });
726
+ });
727
+ ChatInputArea.displayName = "ChatInputArea";
728
+
729
+ // src/ui/react/components/ChatMessageList.tsx
730
+ var import_react9 = require("react");
731
+
732
+ // src/ui/react/components/interactive/ConfirmInteraction.tsx
733
+ var import_react6 = require("react");
734
+ var import_jsx_runtime6 = require("react/jsx-runtime");
735
+ var ConfirmInteraction = ({
736
+ parameters,
737
+ onResponse,
738
+ isResponseSubmitted
739
+ }) => {
740
+ const [selectedOption, setSelectedOption] = (0, import_react6.useState)(null);
741
+ const params = parameters;
742
+ const { yesPrompt, noPrompt } = params;
743
+ console.log("[ConfirmInteraction] Parameters:", params);
744
+ const handleOptionClick = (value, buttonText) => {
745
+ if (isResponseSubmitted) return;
746
+ setSelectedOption(buttonText);
747
+ onResponse(value);
748
+ };
749
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mt-2 mb-4", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex space-x-2", children: [
750
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
751
+ "button",
752
+ {
753
+ onClick: () => handleOptionClick(true, yesPrompt),
754
+ disabled: isResponseSubmitted,
755
+ className: `px-4 py-2 rounded-md text-sm transition-colors ${isResponseSubmitted ? selectedOption === yesPrompt ? "bg-blue-500 text-white" : "bg-gray-200 text-gray-500 cursor-not-allowed" : "bg-blue-100 text-blue-700 hover:bg-blue-200"}`,
756
+ children: yesPrompt
757
+ }
758
+ ),
759
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
760
+ "button",
761
+ {
762
+ onClick: () => handleOptionClick(false, noPrompt),
763
+ disabled: isResponseSubmitted,
764
+ className: `px-4 py-2 rounded-md text-sm transition-colors ${isResponseSubmitted ? selectedOption === noPrompt ? "bg-blue-500 text-white" : "bg-gray-200 text-gray-500 cursor-not-allowed" : "bg-gray-100 text-gray-700 hover:bg-gray-200"}`,
765
+ children: noPrompt
766
+ }
767
+ )
768
+ ] }) });
769
+ };
770
+ var ConfirmInteraction_default = ConfirmInteraction;
771
+
772
+ // src/ui/react/components/interactive/SelectInteraction.tsx
773
+ var import_react7 = require("react");
774
+ var import_jsx_runtime7 = require("react/jsx-runtime");
775
+ var SelectInteraction = ({
776
+ parameters,
777
+ onResponse,
778
+ isResponseSubmitted,
779
+ message
780
+ }) => {
781
+ const [selectedOption, setSelectedOption] = (0, import_react7.useState)(null);
782
+ const [customOption, setCustomOption] = (0, import_react7.useState)(null);
783
+ const params = parameters;
784
+ const { question, options, placeholder } = params;
785
+ (0, import_react7.useEffect)(() => {
786
+ if (isResponseSubmitted && (message == null ? void 0 : message.responseValue)) {
787
+ const responseValueStr = String(message.responseValue);
788
+ setSelectedOption(responseValueStr);
789
+ if (!options.includes(responseValueStr)) {
790
+ setCustomOption(responseValueStr);
791
+ }
792
+ }
793
+ }, [isResponseSubmitted, message, options]);
794
+ const handleOptionClick = (option) => {
795
+ if (isResponseSubmitted) return;
796
+ setSelectedOption(option);
797
+ setCustomOption(null);
798
+ onResponse(option);
799
+ };
800
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mt-2 mb-4", children: [
801
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "flex flex-wrap gap-2", children: options.map((option, index) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
802
+ "button",
803
+ {
804
+ onClick: () => handleOptionClick(option),
805
+ disabled: isResponseSubmitted,
806
+ className: `px-4 py-2 rounded-md text-sm transition-colors ${isResponseSubmitted ? selectedOption === option ? "bg-blue-500 text-white" : "bg-gray-200 text-gray-500 cursor-not-allowed" : "bg-blue-100 text-blue-700 hover:bg-blue-200"}`,
807
+ children: option
808
+ },
809
+ index
810
+ )) }),
811
+ customOption && isResponseSubmitted && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mt-2 text-sm text-amber-600 bg-amber-50 p-2 rounded", children: [
812
+ "User provided custom option: ",
813
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "font-semibold", children: [
814
+ '"',
815
+ customOption,
816
+ '"'
817
+ ] })
818
+ ] })
819
+ ] });
820
+ };
821
+ var SelectInteraction_default = SelectInteraction;
822
+
823
+ // src/ui/react/components/interactive/FormInteraction.tsx
824
+ var import_react8 = require("react");
825
+ var import_lucide_react = require("lucide-react");
826
+ var import_jsx_runtime8 = require("react/jsx-runtime");
827
+ var FormInteraction = ({
828
+ parameters,
829
+ onResponse,
830
+ isResponseSubmitted,
831
+ submittedValues
832
+ }) => {
833
+ const [isModalOpen, setIsModalOpen] = (0, import_react8.useState)(false);
834
+ const [formValues, setFormValues] = (0, import_react8.useState)({});
835
+ const [isExpanded, setIsExpanded] = (0, import_react8.useState)(false);
836
+ const [parsedFields, setParsedFields] = (0, import_react8.useState)([]);
837
+ const formButtonsRef = (0, import_react8.useRef)(null);
838
+ const parseParameters = () => {
839
+ const { prompt, description, submitText = "Submit", cancelText = "Cancel" } = parameters;
840
+ let fieldsArray = [];
841
+ if (parameters.fields) {
842
+ if (Array.isArray(parameters.fields)) {
843
+ fieldsArray = parameters.fields.map((field) => {
844
+ if (typeof field === "string") {
845
+ try {
846
+ const fieldData = JSON.parse(field);
847
+ const name = fieldData.name;
848
+ return {
849
+ name,
850
+ label: fieldData.label || name,
851
+ type: fieldData.type || "string",
852
+ required: fieldData.required || false,
853
+ options: fieldData.options || [],
854
+ placeholder: fieldData.description || fieldData.label || name,
855
+ defaultValue: fieldData.defaultValue
856
+ };
857
+ } catch (error) {
858
+ console.error(`Error parsing field:`, error);
859
+ return null;
860
+ }
861
+ }
862
+ return field;
863
+ }).filter((field) => field !== null);
864
+ } else if (typeof parameters.fields === "object") {
865
+ fieldsArray = Object.entries(parameters.fields).map(([name, fieldDataStr]) => {
866
+ let fieldData = {};
867
+ try {
868
+ if (typeof fieldDataStr === "string") {
869
+ fieldData = JSON.parse(fieldDataStr);
870
+ } else {
871
+ fieldData = fieldDataStr;
872
+ }
873
+ } catch (error) {
874
+ console.error(`Error parsing field data for ${name}:`, error);
875
+ }
876
+ return {
877
+ name,
878
+ label: fieldData.label || name,
879
+ type: fieldData.type || "string",
880
+ required: fieldData.required || false,
881
+ options: fieldData.options || [],
882
+ placeholder: fieldData.description || fieldData.label || name,
883
+ defaultValue: fieldData.defaultValue
884
+ };
885
+ });
886
+ console.log("Field arrays: ", fieldsArray);
887
+ }
888
+ }
889
+ return {
890
+ title: prompt || "Please fill out this form",
891
+ description,
892
+ fields: fieldsArray,
893
+ submitText,
894
+ cancelText
895
+ };
896
+ };
897
+ const params = parseParameters();
898
+ (0, import_react8.useEffect)(() => {
899
+ const processedParams = parseParameters();
900
+ setParsedFields(processedParams.fields);
901
+ const initialValues = {};
902
+ processedParams.fields.forEach((field) => {
903
+ if (field.defaultValue !== void 0) {
904
+ initialValues[field.name] = field.defaultValue;
905
+ } else if (field.type === "checkbox") {
906
+ initialValues[field.name] = false;
907
+ } else {
908
+ initialValues[field.name] = "";
909
+ }
910
+ });
911
+ setFormValues(initialValues);
912
+ if (!isResponseSubmitted) {
913
+ setIsModalOpen(true);
914
+ }
915
+ }, [parameters, isResponseSubmitted]);
916
+ (0, import_react8.useEffect)(() => {
917
+ if (isModalOpen && formButtonsRef.current) {
918
+ setTimeout(() => {
919
+ var _a;
920
+ (_a = formButtonsRef.current) == null ? void 0 : _a.scrollIntoView({ behavior: "smooth", block: "center" });
921
+ }, 100);
922
+ }
923
+ }, [isModalOpen]);
924
+ const handleInputChange = (field, value) => {
925
+ setFormValues((prev) => ({
926
+ ...prev,
927
+ [field.name]: value
928
+ }));
929
+ };
930
+ const handleSubmit = (e) => {
931
+ e.preventDefault();
932
+ onResponse(formValues);
933
+ setIsModalOpen(false);
934
+ };
935
+ const handleCancel = () => {
936
+ onResponse({});
937
+ setIsModalOpen(false);
938
+ };
939
+ const renderField = (field) => {
940
+ const { name, label, type, required, options, placeholder } = field;
941
+ switch (type) {
942
+ case "string":
943
+ case "email":
944
+ case "number":
945
+ case "password":
946
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-4 flex items-center space-x-4", children: [
947
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { htmlFor: name, className: "text-sm font-medium text-gray-700 min-w-[120px]", children: [
948
+ label,
949
+ required && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-red-500", children: "*" })
950
+ ] }),
951
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
952
+ "input",
953
+ {
954
+ type: type === "string" ? "text" : type,
955
+ id: name,
956
+ name,
957
+ value: formValues[name] || "",
958
+ onChange: (e) => handleInputChange(field, e.target.value),
959
+ placeholder,
960
+ required,
961
+ disabled: isResponseSubmitted,
962
+ className: "flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
963
+ }
964
+ )
965
+ ] }, name);
966
+ case "textarea":
967
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-4 flex items-start space-x-4", children: [
968
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { htmlFor: name, className: "text-sm font-medium text-gray-700 min-w-[120px] pt-2", children: [
969
+ label,
970
+ required && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-red-500", children: "*" })
971
+ ] }),
972
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
973
+ "textarea",
974
+ {
975
+ id: name,
976
+ name,
977
+ value: formValues[name] || "",
978
+ onChange: (e) => handleInputChange(field, e.target.value),
979
+ placeholder,
980
+ required,
981
+ disabled: isResponseSubmitted,
982
+ rows: 4,
983
+ className: "flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
984
+ }
985
+ )
986
+ ] }, name);
987
+ case "select":
988
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-4 flex items-center space-x-4", children: [
989
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { htmlFor: name, className: "text-sm font-medium text-gray-700 min-w-[120px]", children: [
990
+ label,
991
+ required && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-red-500", children: "*" })
992
+ ] }),
993
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
994
+ "select",
995
+ {
996
+ id: name,
997
+ name,
998
+ value: formValues[name] || "",
999
+ onChange: (e) => handleInputChange(field, e.target.value),
1000
+ required,
1001
+ disabled: isResponseSubmitted,
1002
+ className: "flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500",
1003
+ children: [
1004
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: "", disabled: true, children: placeholder || "Select an option" }),
1005
+ options == null ? void 0 : options.map((option, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("option", { value: option, children: option }, index))
1006
+ ]
1007
+ }
1008
+ )
1009
+ ] }, name);
1010
+ case "checkbox":
1011
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-4 flex items-center space-x-4", children: [
1012
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "min-w-[120px]" }),
1013
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center", children: [
1014
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1015
+ "input",
1016
+ {
1017
+ type: "checkbox",
1018
+ id: name,
1019
+ name,
1020
+ checked: !!formValues[name],
1021
+ onChange: (e) => handleInputChange(field, e.target.checked),
1022
+ required,
1023
+ disabled: isResponseSubmitted,
1024
+ className: "h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
1025
+ }
1026
+ ),
1027
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("label", { htmlFor: name, className: "ml-2 text-sm text-gray-700", children: [
1028
+ label,
1029
+ required && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-red-500", children: "*" })
1030
+ ] })
1031
+ ] })
1032
+ ] }, name);
1033
+ case "radio":
1034
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-4 flex space-x-4", children: [
1035
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "text-sm font-medium text-gray-700 min-w-[120px] pt-2", children: [
1036
+ label,
1037
+ required && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-red-500", children: "*" })
1038
+ ] }),
1039
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex-1 space-y-2", children: options == null ? void 0 : options.map((option, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center", children: [
1040
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1041
+ "input",
1042
+ {
1043
+ id: `${name}-${index}`,
1044
+ name,
1045
+ type: "radio",
1046
+ value: option,
1047
+ checked: formValues[name] === option,
1048
+ onChange: () => handleInputChange(field, option),
1049
+ required,
1050
+ disabled: isResponseSubmitted,
1051
+ className: "h-4 w-4 text-blue-600 border-gray-300 focus:ring-blue-500"
1052
+ }
1053
+ ),
1054
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("label", { htmlFor: `${name}-${index}`, className: "ml-2 text-sm text-gray-700", children: option })
1055
+ ] }, index)) })
1056
+ ] }, name);
1057
+ default:
1058
+ return null;
1059
+ }
1060
+ };
1061
+ if (isResponseSubmitted) {
1062
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mt-2 bg-gray-50 p-3 rounded-md border border-gray-200", children: [
1063
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex justify-between items-center cursor-pointer", onClick: () => setIsExpanded(!isExpanded), children: [
1064
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "font-medium text-gray-700", children: [
1065
+ isExpanded ? "Hide" : "Show",
1066
+ " Form Responses"
1067
+ ] }),
1068
+ isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react.ChevronUpIcon, { className: "h-5 w-5 text-gray-500" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react.ChevronDownIcon, { className: "h-5 w-5 text-gray-500" })
1069
+ ] }),
1070
+ isExpanded && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mt-2 space-y-2", children: parsedFields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex", children: [
1071
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-sm font-medium text-gray-600 mr-2", children: [
1072
+ field.label,
1073
+ ":"
1074
+ ] }),
1075
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-sm text-gray-800", children: typeof (submittedValues == null ? void 0 : submittedValues[field.name]) === "boolean" ? submittedValues[field.name] ? "Yes" : "No" : (submittedValues == null ? void 0 : submittedValues[field.name]) || "Not provided" })
1076
+ ] }, field.name)) })
1077
+ ] });
1078
+ }
1079
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "mt-2", children: isModalOpen && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "p-4", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("form", { onSubmit: handleSubmit, className: "mt-4", children: [
1080
+ parsedFields.map((field) => renderField(field)),
1081
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { ref: formButtonsRef, className: "flex justify-end mt-4 space-x-2", children: [
1082
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1083
+ "button",
1084
+ {
1085
+ type: "button",
1086
+ onClick: handleCancel,
1087
+ disabled: isResponseSubmitted,
1088
+ className: "px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 border border-gray-300 rounded-md hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-500",
1089
+ children: params.cancelText
1090
+ }
1091
+ ),
1092
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1093
+ "button",
1094
+ {
1095
+ type: "submit",
1096
+ disabled: isResponseSubmitted,
1097
+ className: "px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500",
1098
+ children: params.submitText
1099
+ }
1100
+ )
1101
+ ] })
1102
+ ] }) }) });
1103
+ };
1104
+ var FormInteraction_default = FormInteraction;
1105
+
1106
+ // src/ui/react/components/interactive/PresentInteraction.tsx
1107
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1108
+ var ReactMarkdown2;
1109
+ try {
1110
+ ReactMarkdown2 = require("react-markdown");
1111
+ } catch (error) {
1112
+ ReactMarkdown2 = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "whitespace-pre-wrap", children });
1113
+ }
1114
+ var PresentInteraction = ({
1115
+ parameters
1116
+ }) => {
1117
+ const params = parameters;
1118
+ const { title, content, format = "text", level = "info" } = params;
1119
+ const getLevelStyles = () => {
1120
+ switch (level) {
1121
+ case "error":
1122
+ return {
1123
+ container: "bg-red-50 border-red-100",
1124
+ title: "text-red-800",
1125
+ content: "text-red-700"
1126
+ };
1127
+ case "warn":
1128
+ case "warning":
1129
+ return {
1130
+ container: "bg-amber-50 border-amber-100",
1131
+ title: "text-amber-800",
1132
+ content: "text-amber-700"
1133
+ };
1134
+ case "success":
1135
+ return {
1136
+ container: "bg-green-50 border-green-100",
1137
+ title: "text-green-800",
1138
+ content: "text-green-700"
1139
+ };
1140
+ case "info":
1141
+ default:
1142
+ return {
1143
+ container: "bg-blue-50 border-blue-100",
1144
+ title: "text-blue-800",
1145
+ content: "text-blue-700"
1146
+ };
1147
+ }
1148
+ };
1149
+ const styles = getLevelStyles();
1150
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: `mt-2 mb-4 p-4 ${styles.container} border rounded-md`, children: [
1151
+ title && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `font-medium ${styles.title} mb-2`, children: title }),
1152
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: `text-sm ${styles.content}`, children: format === "markdown" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ReactMarkdown2, { className: "prose prose-sm max-w-none", children: content }) : format === "html" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { dangerouslySetInnerHTML: { __html: content } }) : /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "whitespace-pre-wrap", children: content }) })
1153
+ ] });
1154
+ };
1155
+ var PresentInteraction_default = PresentInteraction;
1156
+
1157
+ // src/ui/react/components/interactive/InteractiveMessageHandler.tsx
1158
+ var import_jsx_runtime10 = require("react/jsx-runtime");
1159
+ var InteractiveMessageHandler = ({
1160
+ message,
1161
+ onResponse,
1162
+ isResponseSubmitted,
1163
+ parentMessage
1164
+ }) => {
1165
+ const { function: functionType, parameters } = message;
1166
+ const submittedResponse = parentMessage == null ? void 0 : parentMessage.responseValue;
1167
+ switch (functionType) {
1168
+ case "confirm":
1169
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1170
+ ConfirmInteraction_default,
1171
+ {
1172
+ parameters,
1173
+ onResponse,
1174
+ isResponseSubmitted
1175
+ }
1176
+ );
1177
+ case "select":
1178
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1179
+ SelectInteraction_default,
1180
+ {
1181
+ parameters,
1182
+ onResponse,
1183
+ isResponseSubmitted,
1184
+ message: parentMessage
1185
+ }
1186
+ );
1187
+ case "form":
1188
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1189
+ FormInteraction_default,
1190
+ {
1191
+ parameters,
1192
+ onResponse,
1193
+ isResponseSubmitted,
1194
+ submittedValues: submittedResponse
1195
+ }
1196
+ );
1197
+ case "present":
1198
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
1199
+ PresentInteraction_default,
1200
+ {
1201
+ parameters
1202
+ }
1203
+ );
1204
+ case "chat":
1205
+ return null;
1206
+ default:
1207
+ console.warn(`Unknown interactive function type: ${functionType}`);
1208
+ return null;
1209
+ }
1210
+ };
1211
+ var InteractiveMessageHandler_default = InteractiveMessageHandler;
1212
+
1213
+ // src/ui/react/components/ChatMessageList.tsx
1214
+ var import_outline2 = require("@heroicons/react/24/outline");
1215
+ var import_jsx_runtime11 = require("react/jsx-runtime");
1216
+ var ChatMessageList = ({
1217
+ chatHistory,
1218
+ isProcessing,
1219
+ processingHint,
1220
+ currentTask,
1221
+ getContextExample,
1222
+ onInteractiveResponse
1223
+ }) => {
1224
+ const chatContainerRef = (0, import_react9.useRef)(null);
1225
+ const lastMessageRef = (0, import_react9.useRef)(null);
1226
+ const processingIndicatorRef = (0, import_react9.useRef)(null);
1227
+ (0, import_react9.useEffect)(() => {
1228
+ if (isProcessing && processingIndicatorRef.current) {
1229
+ processingIndicatorRef.current.scrollIntoView({ behavior: "smooth" });
1230
+ return;
1231
+ }
1232
+ if (lastMessageRef.current) {
1233
+ lastMessageRef.current.scrollIntoView({ behavior: "smooth" });
1234
+ } else if (chatContainerRef.current) {
1235
+ chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight;
1236
+ }
1237
+ }, [chatHistory, isProcessing]);
1238
+ const handleInteractiveResponse = (messageId, response) => {
1239
+ if (onInteractiveResponse) {
1240
+ onInteractiveResponse(messageId, response);
1241
+ }
1242
+ };
1243
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1244
+ "div",
1245
+ {
1246
+ ref: chatContainerRef,
1247
+ className: "flex-1 overflow-y-auto p-4 space-y-4 bg-gray-50",
1248
+ children: [
1249
+ chatHistory.length === 0 && !isProcessing && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "text-center py-8", children: [
1250
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { className: "text-lg font-medium text-gray-700 mb-2", children: "How can I help you today?" }),
1251
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { className: "text-sm text-gray-500 mb-4", children: [
1252
+ "Try asking me something like ",
1253
+ getContextExample()
1254
+ ] })
1255
+ ] }),
1256
+ chatHistory.map((message, index) => {
1257
+ const isLastMessage = index === chatHistory.length - 1;
1258
+ const isUser = message.role === "user";
1259
+ const isError = message.role === "error";
1260
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
1261
+ "div",
1262
+ {
1263
+ ref: isLastMessage ? lastMessageRef : void 0,
1264
+ className: `flex flex-col w-full ${isUser ? "items-end" : "items-start"}`,
1265
+ children: [
1266
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "text-xs text-gray-400 mb-1 px-1", children: message.timestamp ? `${isUser ? "Sent" : "Received"} at ${new Date(message.timestamp).toLocaleString()}` : "" }),
1267
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-full", children: message.interactive && message.interactiveData ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: `max-w-[85%] ${isUser ? "ml-auto" : "mr-auto"}`, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1268
+ InteractiveMessageHandler_default,
1269
+ {
1270
+ message: message.interactiveData,
1271
+ onResponse: (response) => {
1272
+ const messageId = message.id || `interactive-${index}-${Date.now()}`;
1273
+ message.responseValue = response;
1274
+ handleInteractiveResponse(messageId, response);
1275
+ },
1276
+ isResponseSubmitted: !!message.isResponseSubmitted,
1277
+ parentMessage: message
1278
+ }
1279
+ ) }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1280
+ MessageBubble,
1281
+ {
1282
+ message,
1283
+ isUser
1284
+ }
1285
+ ) }),
1286
+ isUser && message.interactive && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center mt-1 space-x-1 text-xs text-gray-500 italic", children: [
1287
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_outline2.InformationCircleIcon, { className: "h-3 w-3 text-blue-400" }),
1288
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { children: [
1289
+ message.request === "form" && "Response to form submission",
1290
+ message.request === "select" && "Response to selection prompt",
1291
+ message.request === "confirm" && "Response to confirmation prompt"
1292
+ ] })
1293
+ ] })
1294
+ ]
1295
+ },
1296
+ message.id || `message-${index}`
1297
+ );
1298
+ }),
1299
+ isProcessing && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1300
+ "div",
1301
+ {
1302
+ ref: processingIndicatorRef,
1303
+ className: "flex justify-start",
1304
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "bg-white text-gray-800 border border-gray-200 rounded-lg px-4 py-2 max-w-[85%]", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center", children: [
1305
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-sm", children: processingHint }),
1306
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ml-2 flex space-x-1", children: [
1307
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "h-2 w-2 bg-gray-400 rounded-full animate-bounce", style: { animationDelay: "0ms" } }),
1308
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "h-2 w-2 bg-gray-400 rounded-full animate-bounce", style: { animationDelay: "150ms" } }),
1309
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "h-2 w-2 bg-gray-400 rounded-full animate-bounce", style: { animationDelay: "300ms" } })
1310
+ ] })
1311
+ ] }) })
1312
+ }
1313
+ ),
1314
+ currentTask && !currentTask.complete && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "mt-4 bg-blue-50 border border-blue-100 rounded-lg p-3", children: [
1315
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "text-sm text-blue-800 mb-1", children: [
1316
+ "Task in progress: Step ",
1317
+ currentTask.currentStep,
1318
+ " of ",
1319
+ currentTask.steps
1320
+ ] }),
1321
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "w-full bg-blue-200 rounded-full h-2", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
1322
+ "div",
1323
+ {
1324
+ className: "bg-blue-500 h-2 rounded-full",
1325
+ style: { width: `${currentTask.currentStep / currentTask.steps * 100}%` }
1326
+ }
1327
+ ) })
1328
+ ] })
1329
+ ]
1330
+ }
1331
+ );
1332
+ };
1333
+
1334
+ // src/ui/react/components/ConnectionStatus.tsx
1335
+ var import_lucide_react2 = require("lucide-react");
1336
+ var import_jsx_runtime12 = require("react/jsx-runtime");
1337
+ var ConnectionStatus = ({
1338
+ connectionStatus,
1339
+ onReconnect
1340
+ }) => {
1341
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "bg-amber-100 connection-status flex items-center justify-between px-4 py-1 text-xs", children: [
1342
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "text-gray-700 font-medium", children: "You are talking with your personal Content Strategist" }),
1343
+ connectionStatus === "connected" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center text-green-600", children: [
1344
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react2.Wifi, { className: "w-3 h-3 mr-1" }),
1345
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "Connected" })
1346
+ ] }),
1347
+ connectionStatus === "reconnecting" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center text-amber-600", children: [
1348
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react2.RefreshCw, { className: "w-3 h-3 mr-1 animate-spin" }),
1349
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "Reconnecting..." })
1350
+ ] }),
1351
+ connectionStatus === "disconnected" && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "flex items-center gap-2", children: [
1352
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "text-red-600 flex items-center", children: [
1353
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react2.WifiOff, { className: "w-3 h-3 mr-1" }),
1354
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "Disconnected" })
1355
+ ] }),
1356
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
1357
+ "button",
1358
+ {
1359
+ onClick: onReconnect,
1360
+ className: "text-blue-600 hover:text-blue-800 flex items-center",
1361
+ children: [
1362
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react2.RefreshCw, { className: "w-3 h-3 mr-1" }),
1363
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: "Reconnect" })
1364
+ ]
1365
+ }
1366
+ )
1367
+ ] })
1368
+ ] });
1369
+ };
1370
+
1371
+ // src/ui/react/components/Spinner.tsx
1372
+ var import_jsx_runtime13 = require("react/jsx-runtime");
1373
+ var Spinner = ({
1374
+ size = "md",
1375
+ className = "",
1376
+ color = "text-blue-600"
1377
+ }) => {
1378
+ const sizeClasses = {
1379
+ sm: "h-4 w-4",
1380
+ md: "h-6 w-6",
1381
+ lg: "h-8 w-8",
1382
+ xl: "h-12 w-12"
1383
+ };
1384
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
1385
+ "svg",
1386
+ {
1387
+ className: `animate-spin ${sizeClasses[size]} ${color} ${className}`,
1388
+ xmlns: "http://www.w3.org/2000/svg",
1389
+ fill: "none",
1390
+ viewBox: "0 0 24 24",
1391
+ children: [
1392
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1393
+ "circle",
1394
+ {
1395
+ className: "opacity-25",
1396
+ cx: "12",
1397
+ cy: "12",
1398
+ r: "10",
1399
+ stroke: "currentColor",
1400
+ strokeWidth: "4"
1401
+ }
1402
+ ),
1403
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
1404
+ "path",
1405
+ {
1406
+ className: "opacity-75",
1407
+ fill: "currentColor",
1408
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
1409
+ }
1410
+ )
1411
+ ]
1412
+ }
1413
+ );
1414
+ };
1415
+
1416
+ // src/ui/react/services/whisper-client.ts
1417
+ async function transcribeAudio(audioBlob, config) {
1418
+ const formData = new FormData();
1419
+ formData.append("audio", audioBlob, "recording.webm");
1420
+ if (config.language) {
1421
+ formData.append("language", config.language);
1422
+ }
1423
+ try {
1424
+ const response = await fetch(config.endpoint, {
1425
+ method: "POST",
1426
+ body: formData,
1427
+ headers: config.headers || {},
1428
+ credentials: "same-origin"
1429
+ // Ensure cookies are sent for same-domain requests
1430
+ });
1431
+ if (!response.ok) {
1432
+ const errorText = await response.text();
1433
+ throw new Error(`Transcription failed (${response.status}): ${errorText}`);
1434
+ }
1435
+ const data = await response.json();
1436
+ return data.text;
1437
+ } catch (error) {
1438
+ console.error("Transcription client error:", error);
1439
+ throw error;
1440
+ }
1441
+ }
1442
+
1443
+ // src/ui/react/utils/voice-utils.ts
1444
+ function createWhisperVoiceConfig(config, callbacks) {
1445
+ return {
1446
+ mode: "custom",
1447
+ onVoiceStart: callbacks == null ? void 0 : callbacks.onVoiceStart,
1448
+ onVoiceEnd: callbacks == null ? void 0 : callbacks.onVoiceEnd,
1449
+ /**
1450
+ * The core handler: takes the recorded blob and sends it to the server.
1451
+ */
1452
+ onAudioCapture: async (blob) => {
1453
+ return await transcribeAudio(blob, config);
1454
+ }
1455
+ };
1456
+ }
1457
+ // Annotate the CommonJS export names for ESM import in node:
1458
+ 0 && (module.exports = {
1459
+ ChatBubble,
1460
+ ChatConfigProvider,
1461
+ ChatHeader,
1462
+ ChatInputArea,
1463
+ ChatMessageList,
1464
+ ConnectionStatus,
1465
+ MessageBubble,
1466
+ Spinner,
1467
+ createWhisperVoiceConfig,
1468
+ transcribeAudio,
1469
+ useAudioRecorder,
1470
+ useChatConfig,
1471
+ useProactiveResize,
1472
+ useSpeechRecognition
1473
+ });
1474
+ //# sourceMappingURL=index.cjs.map