@contentgrowth/llm-service 0.8.9 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ui/react/components/index.cjs +128 -141
- package/dist/ui/react/components/index.cjs.map +1 -1
- package/dist/ui/react/components/index.d.cts +1 -0
- package/dist/ui/react/components/index.d.ts +1 -0
- package/dist/ui/react/components/index.js +131 -144
- package/dist/ui/react/components/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -57,7 +57,7 @@ var MessageBubble = ({
|
|
|
57
57
|
)
|
|
58
58
|
] }) });
|
|
59
59
|
}
|
|
60
|
-
return /* @__PURE__ */ jsxs("div", { className: `flex items-start gap-3 my-
|
|
60
|
+
return /* @__PURE__ */ jsxs("div", { className: `flex items-start gap-3 my-1 ${isUser ? "justify-end" : "justify-start"}`, children: [
|
|
61
61
|
!isUser && /* @__PURE__ */ jsx3("div", { className: "flex-shrink-0 h-8 w-8 rounded-full bg-blue-500 flex items-center justify-center text-white", children: /* @__PURE__ */ jsx3(SparklesIcon, { className: "h-5 w-5" }) }),
|
|
62
62
|
/* @__PURE__ */ jsxs(
|
|
63
63
|
"div",
|
|
@@ -148,8 +148,8 @@ function ChatHeader({
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
// src/ui/react/components/ChatInputArea.tsx
|
|
151
|
-
import { useState as useState3, useRef as useRef3, useImperativeHandle, forwardRef, useEffect as useEffect3, useCallback as useCallback3 } from "react";
|
|
152
|
-
import { StopIcon, PaperAirplaneIcon } from "@heroicons/react/24/outline";
|
|
151
|
+
import { useState as useState3, useRef as useRef3, useImperativeHandle, forwardRef, useEffect as useEffect3, useCallback as useCallback3, useLayoutEffect } from "react";
|
|
152
|
+
import { MicrophoneIcon, StopIcon, PaperAirplaneIcon } from "@heroicons/react/24/outline";
|
|
153
153
|
|
|
154
154
|
// src/ui/react/hooks/useSpeechRecognition.ts
|
|
155
155
|
import { useState, useEffect, useCallback, useRef } from "react";
|
|
@@ -277,12 +277,23 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
|
|
|
277
277
|
import { useState as useState2, useRef as useRef2, useCallback as useCallback2 } from "react";
|
|
278
278
|
var useAudioRecorder = (onStop) => {
|
|
279
279
|
const [isRecording, setIsRecording] = useState2(false);
|
|
280
|
+
const [isSimulated, setIsSimulated] = useState2(false);
|
|
280
281
|
const [blob, setBlob] = useState2(null);
|
|
281
282
|
const [error, setError] = useState2(null);
|
|
282
283
|
const mediaRecorderRef = useRef2(null);
|
|
283
284
|
const chunksRef = useRef2([]);
|
|
284
285
|
const start = useCallback2(async () => {
|
|
285
286
|
try {
|
|
287
|
+
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
|
288
|
+
if (process.env.NODE_ENV === "development") {
|
|
289
|
+
console.warn("[useAudioRecorder] MediaDevices not available. Entering simulation mode...");
|
|
290
|
+
setIsRecording(true);
|
|
291
|
+
setIsSimulated(true);
|
|
292
|
+
setError(null);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
throw new Error("Media devices not available. Ensure you are using HTTPS or localhost.");
|
|
296
|
+
}
|
|
286
297
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
287
298
|
const mediaRecorder = new MediaRecorder(stream);
|
|
288
299
|
mediaRecorderRef.current = mediaRecorder;
|
|
@@ -310,12 +321,21 @@ var useAudioRecorder = (onStop) => {
|
|
|
310
321
|
}
|
|
311
322
|
}, [onStop]);
|
|
312
323
|
const stop = useCallback2(() => {
|
|
324
|
+
if (isSimulated) {
|
|
325
|
+
setIsRecording(false);
|
|
326
|
+
setIsSimulated(false);
|
|
327
|
+
const simulatedBlob = new Blob(["simulated speech"], { type: "audio/simulated" });
|
|
328
|
+
setBlob(simulatedBlob);
|
|
329
|
+
if (onStop) onStop(simulatedBlob);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
313
332
|
if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") {
|
|
314
333
|
mediaRecorderRef.current.stop();
|
|
315
334
|
}
|
|
316
|
-
}, []);
|
|
335
|
+
}, [isSimulated, onStop]);
|
|
317
336
|
return {
|
|
318
337
|
isRecording,
|
|
338
|
+
isSimulated,
|
|
319
339
|
start,
|
|
320
340
|
stop,
|
|
321
341
|
blob,
|
|
@@ -345,7 +365,7 @@ function useProactiveResize(textareaRef, measurementRef, value, disabled) {
|
|
|
345
365
|
}
|
|
346
366
|
|
|
347
367
|
// src/ui/react/components/ChatInputArea.tsx
|
|
348
|
-
import {
|
|
368
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
349
369
|
var ChatInputArea = forwardRef(({
|
|
350
370
|
onSubmit,
|
|
351
371
|
isSending,
|
|
@@ -364,21 +384,24 @@ var ChatInputArea = forwardRef(({
|
|
|
364
384
|
var _a, _b, _c, _d;
|
|
365
385
|
const [internalMessage, setInternalMessage] = useState3("");
|
|
366
386
|
const [voiceTrigger, setVoiceTrigger] = useState3(null);
|
|
367
|
-
const [
|
|
387
|
+
const [isTranscribing, setIsTranscribing] = useState3(false);
|
|
388
|
+
const [voiceError, setVoiceError] = useState3(null);
|
|
368
389
|
const [isFocused, setIsFocused] = useState3(false);
|
|
369
390
|
const textareaRef = useRef3(null);
|
|
370
391
|
const measurementRef = useRef3(null);
|
|
371
|
-
const
|
|
372
|
-
useEffect3(() => {
|
|
373
|
-
var _a2;
|
|
374
|
-
if (inputMode === "voice") {
|
|
375
|
-
(_a2 = voiceContainerRef.current) == null ? void 0 : _a2.focus();
|
|
376
|
-
}
|
|
377
|
-
}, [inputMode]);
|
|
392
|
+
const pendingSelectionRef = useRef3(null);
|
|
378
393
|
const isControlled = value !== void 0;
|
|
379
394
|
const message = isControlled ? value : internalMessage;
|
|
380
395
|
const messageRef = useRef3(message);
|
|
381
396
|
messageRef.current = message;
|
|
397
|
+
useLayoutEffect(() => {
|
|
398
|
+
if (pendingSelectionRef.current && textareaRef.current) {
|
|
399
|
+
const { start, end } = pendingSelectionRef.current;
|
|
400
|
+
textareaRef.current.focus();
|
|
401
|
+
textareaRef.current.setSelectionRange(start, end);
|
|
402
|
+
pendingSelectionRef.current = null;
|
|
403
|
+
}
|
|
404
|
+
}, [message]);
|
|
382
405
|
const onChangeRef = useRef3(onChange);
|
|
383
406
|
useEffect3(() => {
|
|
384
407
|
onChangeRef.current = onChange;
|
|
@@ -391,6 +414,7 @@ var ChatInputArea = forwardRef(({
|
|
|
391
414
|
voiceConfigRef.current = voiceConfig;
|
|
392
415
|
}, [voiceConfig]);
|
|
393
416
|
const triggerChange = useCallback3((newValue) => {
|
|
417
|
+
setVoiceError(null);
|
|
394
418
|
if (isControlled && onChangeRef.current) {
|
|
395
419
|
const syntheticEvent = {
|
|
396
420
|
target: { value: newValue },
|
|
@@ -402,34 +426,30 @@ var ChatInputArea = forwardRef(({
|
|
|
402
426
|
}
|
|
403
427
|
}, [isControlled]);
|
|
404
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));
|
|
405
|
-
useProactiveResize(textareaRef, measurementRef, message, isInputDisabled || !!voiceTrigger
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
e.preventDefault();
|
|
413
|
-
e.stopPropagation();
|
|
414
|
-
if (voiceTrigger === "click") return;
|
|
415
|
-
if (!e.repeat && !voiceTrigger) {
|
|
416
|
-
startRecording("space");
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
const handleVoiceKeyUp = (e) => {
|
|
420
|
-
if (inputMode !== "voice" || isInputDisabled) return;
|
|
421
|
-
if (e.code === "Space") {
|
|
422
|
-
if (voiceTrigger === "space") {
|
|
423
|
-
e.preventDefault();
|
|
424
|
-
stopRecording();
|
|
425
|
-
}
|
|
429
|
+
useProactiveResize(textareaRef, measurementRef, message, isInputDisabled || !!voiceTrigger);
|
|
430
|
+
const insertTextAtCursor = useCallback3((text) => {
|
|
431
|
+
const textarea = textareaRef.current;
|
|
432
|
+
const currentVal = messageRef.current || "";
|
|
433
|
+
if (!textarea) {
|
|
434
|
+
triggerChange(currentVal + (currentVal ? " " : "") + text);
|
|
435
|
+
return;
|
|
426
436
|
}
|
|
427
|
-
|
|
437
|
+
const start = textarea.selectionStart;
|
|
438
|
+
const end = textarea.selectionEnd;
|
|
439
|
+
const before = currentVal.substring(0, start);
|
|
440
|
+
const after = currentVal.substring(end);
|
|
441
|
+
const prefix = start > 0 && !/\s$/.test(before) ? " " : "";
|
|
442
|
+
const newText = before + prefix + text + after;
|
|
443
|
+
const selectionStart = start + prefix.length;
|
|
444
|
+
const selectionEnd = selectionStart + text.length;
|
|
445
|
+
pendingSelectionRef.current = { start: selectionStart, end: selectionEnd };
|
|
446
|
+
triggerChange(newText);
|
|
447
|
+
}, [triggerChange]);
|
|
428
448
|
const handleVoiceResult = useCallback3((text, isFinal) => {
|
|
429
449
|
if (isFinal) {
|
|
430
|
-
|
|
450
|
+
insertTextAtCursor(text);
|
|
431
451
|
}
|
|
432
|
-
}, []);
|
|
452
|
+
}, [insertTextAtCursor]);
|
|
433
453
|
const handleVoiceEnd = useCallback3(() => {
|
|
434
454
|
var _a2, _b2;
|
|
435
455
|
setVoiceTrigger(null);
|
|
@@ -439,24 +459,34 @@ var ChatInputArea = forwardRef(({
|
|
|
439
459
|
const customRecorder = useAudioRecorder(async (blob) => {
|
|
440
460
|
var _a2, _b2, _c2;
|
|
441
461
|
setVoiceTrigger(null);
|
|
462
|
+
setIsTranscribing(true);
|
|
463
|
+
setVoiceError(null);
|
|
442
464
|
(_b2 = (_a2 = voiceConfigRef.current) == null ? void 0 : _a2.onVoiceEnd) == null ? void 0 : _b2.call(_a2);
|
|
465
|
+
if (blob.type === "audio/simulated") {
|
|
466
|
+
console.log("[ChatInputArea] Handling simulated audio capture");
|
|
467
|
+
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
468
|
+
insertTextAtCursor("This is a simulated transcription for development testing.");
|
|
469
|
+
setIsTranscribing(false);
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
443
472
|
if ((_c2 = voiceConfigRef.current) == null ? void 0 : _c2.onAudioCapture) {
|
|
444
473
|
try {
|
|
445
474
|
const text = await voiceConfigRef.current.onAudioCapture(blob);
|
|
446
|
-
if (text)
|
|
475
|
+
if (text) insertTextAtCursor(text);
|
|
447
476
|
} catch (e) {
|
|
448
477
|
console.error("[ChatInputArea] Audio capture failed", e);
|
|
478
|
+
setVoiceError(e.message || "Transcription failed");
|
|
479
|
+
} finally {
|
|
480
|
+
setIsTranscribing(false);
|
|
449
481
|
}
|
|
482
|
+
} else {
|
|
483
|
+
setIsTranscribing(false);
|
|
450
484
|
}
|
|
451
485
|
});
|
|
452
486
|
useImperativeHandle(ref, () => ({
|
|
453
487
|
focus: () => {
|
|
454
|
-
var _a2
|
|
455
|
-
|
|
456
|
-
(_a2 = voiceContainerRef.current) == null ? void 0 : _a2.focus();
|
|
457
|
-
} else {
|
|
458
|
-
(_b2 = textareaRef.current) == null ? void 0 : _b2.focus();
|
|
459
|
-
}
|
|
488
|
+
var _a2;
|
|
489
|
+
(_a2 = textareaRef.current) == null ? void 0 : _a2.focus();
|
|
460
490
|
},
|
|
461
491
|
setValue: (newValue) => {
|
|
462
492
|
triggerChange(newValue);
|
|
@@ -487,8 +517,9 @@ var ChatInputArea = forwardRef(({
|
|
|
487
517
|
};
|
|
488
518
|
const startRecording = async (trigger) => {
|
|
489
519
|
var _a2;
|
|
490
|
-
if (voiceTrigger) return;
|
|
520
|
+
if (voiceTrigger || isTranscribing) return;
|
|
491
521
|
setVoiceTrigger(trigger);
|
|
522
|
+
setVoiceError(null);
|
|
492
523
|
(_a2 = voiceConfig == null ? void 0 : voiceConfig.onVoiceStart) == null ? void 0 : _a2.call(voiceConfig);
|
|
493
524
|
if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "native") {
|
|
494
525
|
if (!nativeSpeech.isSupported) {
|
|
@@ -500,6 +531,10 @@ var ChatInputArea = forwardRef(({
|
|
|
500
531
|
} else {
|
|
501
532
|
await customRecorder.start();
|
|
502
533
|
}
|
|
534
|
+
setTimeout(() => {
|
|
535
|
+
var _a3;
|
|
536
|
+
return (_a3 = textareaRef.current) == null ? void 0 : _a3.focus();
|
|
537
|
+
}, 0);
|
|
503
538
|
};
|
|
504
539
|
const stopRecording = () => {
|
|
505
540
|
if (!voiceTrigger) return;
|
|
@@ -512,9 +547,7 @@ var ChatInputArea = forwardRef(({
|
|
|
512
547
|
const getPlaceholder = () => {
|
|
513
548
|
if (placeholder) return placeholder;
|
|
514
549
|
if (voiceTrigger) return "Listening...";
|
|
515
|
-
if (currentTask == null ? void 0 : currentTask.complete)
|
|
516
|
-
return "Task completed!";
|
|
517
|
-
}
|
|
550
|
+
if (currentTask == null ? void 0 : currentTask.complete) return "Task completed!";
|
|
518
551
|
if ((lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.interactive) && (lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.interactiveData) && !(lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.isResponseSubmitted)) {
|
|
519
552
|
const interactiveType = lastInteractiveMessage.interactiveData.function;
|
|
520
553
|
switch (interactiveType) {
|
|
@@ -543,85 +576,61 @@ var ChatInputArea = forwardRef(({
|
|
|
543
576
|
{
|
|
544
577
|
type: "button",
|
|
545
578
|
onClick: () => {
|
|
546
|
-
if (
|
|
579
|
+
if (voiceTrigger) {
|
|
547
580
|
stopRecording();
|
|
581
|
+
} else {
|
|
582
|
+
startRecording("click");
|
|
548
583
|
}
|
|
549
|
-
setInputMode((prev) => prev === "text" ? "voice" : "text");
|
|
550
584
|
},
|
|
551
|
-
className:
|
|
552
|
-
title:
|
|
553
|
-
children:
|
|
554
|
-
// Voice Icon (Waveform)
|
|
555
|
-
/* @__PURE__ */ jsx5("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__ */ jsx5("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" }) })
|
|
556
|
-
) : (
|
|
557
|
-
// Keyboard Icon (Filled)
|
|
558
|
-
/* @__PURE__ */ jsx5("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__ */ jsx5("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" }) })
|
|
559
|
-
)
|
|
585
|
+
className: `mb-1 p-2 rounded-full transition-all duration-300 flex-shrink-0 border ${voiceTrigger ? "text-white border-orange-400 bg-orange-500 scale-110 shadow-lg animate-pulse" : "text-gray-500 border-gray-300 bg-white hover:text-gray-700 hover:bg-gray-100"}`,
|
|
586
|
+
title: voiceTrigger ? "Stop Recording" : "Start Voice Input",
|
|
587
|
+
children: /* @__PURE__ */ jsx5(MicrophoneIcon, { className: "w-5 h-5" })
|
|
560
588
|
}
|
|
561
589
|
),
|
|
562
590
|
/* @__PURE__ */ jsxs3(
|
|
563
591
|
"div",
|
|
564
592
|
{
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
onKeyDown: handleVoiceKeyDown,
|
|
568
|
-
onKeyUp: handleVoiceKeyUp,
|
|
569
|
-
onFocus: () => setIsFocused(true),
|
|
570
|
-
onBlur: () => setIsFocused(false),
|
|
571
|
-
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",
|
|
593
|
+
tabIndex: -1,
|
|
594
|
+
className: `flex-1 flex items-center border border-gray-300 rounded-lg overflow-hidden outline-none bg-white min-h-[42px] mb-1 transition-all ${voiceTrigger ? "ring-2 ring-orange-100 border-orange-300" : "focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-blue-500"}`,
|
|
572
595
|
children: [
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
"textarea",
|
|
584
|
-
{
|
|
585
|
-
ref: textareaRef,
|
|
586
|
-
value: message,
|
|
587
|
-
onChange: (e) => {
|
|
588
|
-
if (isControlled && onChange) {
|
|
589
|
-
onChange(e);
|
|
590
|
-
} else {
|
|
591
|
-
setInternalMessage(e.target.value);
|
|
592
|
-
}
|
|
593
|
-
},
|
|
594
|
-
onKeyDown: handleKeyDown,
|
|
595
|
-
placeholder: getPlaceholder(),
|
|
596
|
-
disabled: isInputDisabled || !!voiceTrigger,
|
|
597
|
-
rows: 1,
|
|
598
|
-
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" : ""}`
|
|
599
|
-
}
|
|
600
|
-
)
|
|
601
|
-
] }),
|
|
602
|
-
inputMode === "voice" && /* @__PURE__ */ jsx5("div", { className: "flex-grow flex flex-col justify-center items-center p-1 relative", children: /* @__PURE__ */ jsx5(
|
|
603
|
-
"button",
|
|
596
|
+
/* @__PURE__ */ jsx5(
|
|
597
|
+
"span",
|
|
598
|
+
{
|
|
599
|
+
ref: measurementRef,
|
|
600
|
+
className: "absolute invisible whitespace-pre-wrap p-0 m-0 text-gray-700 leading-6",
|
|
601
|
+
style: { fontSize: "1rem" }
|
|
602
|
+
}
|
|
603
|
+
),
|
|
604
|
+
/* @__PURE__ */ jsx5(
|
|
605
|
+
"textarea",
|
|
604
606
|
{
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
607
|
+
ref: textareaRef,
|
|
608
|
+
value: message,
|
|
609
|
+
onChange: (e) => {
|
|
610
|
+
if (isControlled && onChange) {
|
|
611
|
+
onChange(e);
|
|
612
|
+
} else {
|
|
613
|
+
setInternalMessage(e.target.value);
|
|
611
614
|
}
|
|
612
615
|
},
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
616
|
+
onKeyDown: handleKeyDown,
|
|
617
|
+
onFocus: () => {
|
|
618
|
+
setIsFocused(true);
|
|
619
|
+
setVoiceError(null);
|
|
620
|
+
},
|
|
621
|
+
onBlur: () => setIsFocused(false),
|
|
622
|
+
placeholder: getPlaceholder(),
|
|
623
|
+
disabled: isInputDisabled,
|
|
624
|
+
readOnly: !!voiceTrigger || isTranscribing,
|
|
625
|
+
rows: 1,
|
|
626
|
+
className: `flex-grow px-4 py-2 outline-none text-gray-700 placeholder-gray-500 resize-none leading-6 w-full ${isInputDisabled ? "bg-gray-100 cursor-not-allowed" : "bg-transparent"} ${voiceTrigger || isTranscribing ? "cursor-default" : ""}`
|
|
622
627
|
}
|
|
623
|
-
)
|
|
624
|
-
|
|
628
|
+
),
|
|
629
|
+
isTranscribing && /* @__PURE__ */ jsx5("div", { className: "flex-shrink-0 animate-spin mr-2", children: /* @__PURE__ */ jsxs3("svg", { className: "w-4 h-4 text-orange-500", viewBox: "0 0 24 24", children: [
|
|
630
|
+
/* @__PURE__ */ jsx5("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4", fill: "none" }),
|
|
631
|
+
/* @__PURE__ */ jsx5("path", { className: "opacity-75", fill: "currentColor", 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" })
|
|
632
|
+
] }) }),
|
|
633
|
+
/* @__PURE__ */ jsxs3("div", { className: "relative mx-2 flex-shrink-0", children: [
|
|
625
634
|
isSending && /* @__PURE__ */ jsx5("div", { className: "absolute -inset-1", children: /* @__PURE__ */ jsxs3(
|
|
626
635
|
"svg",
|
|
627
636
|
{
|
|
@@ -664,24 +673,10 @@ var ChatInputArea = forwardRef(({
|
|
|
664
673
|
handleSubmit();
|
|
665
674
|
}
|
|
666
675
|
},
|
|
667
|
-
disabled: (currentTask == null ? void 0 : currentTask.complete) || isSending && !onStop || isInputDisabled
|
|
676
|
+
disabled: (currentTask == null ? void 0 : currentTask.complete) || isSending && !onStop || isInputDisabled,
|
|
668
677
|
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"}`,
|
|
669
678
|
title: isSending && onStop ? "Stop generating" : "Send message",
|
|
670
|
-
children: isSending ? onStop ? /* @__PURE__ */ jsx5(StopIcon, { className: "h-5 w-5" }) : (
|
|
671
|
-
// AND we show the overlay spinner outside?
|
|
672
|
-
// Actually `ChatInput.tsx` lines 117-140 are `isLoading && (...)`. It is always shown when loading.
|
|
673
|
-
// So we have a spinner ring AROUND the button (absolute -inset-1).
|
|
674
|
-
// AND potentially a spinner INSIDE the button if no onStop?
|
|
675
|
-
// In my case, I will stick to:
|
|
676
|
-
// If onStop: Show StopIcon. Button is Red.
|
|
677
|
-
// If !onStop: Show Spinner inside? Or just let the outer ring do the work?
|
|
678
|
-
// Legacy `Spinner` component usage inside button suggests double spinner if we are not careful.
|
|
679
|
-
// But usually `onStop` is provided for streaming.
|
|
680
|
-
// If I look at the screenshot, it shows a RED button (with stop icon) and a BLUE ring around it.
|
|
681
|
-
// That matches: Red button (bg-red-500) + Blue Spinner Ring (text-blue-500).
|
|
682
|
-
// So I will replicate that structure.
|
|
683
|
-
/* @__PURE__ */ jsx5(StopIcon, { className: "h-5 w-5" })
|
|
684
|
-
) : /* @__PURE__ */ jsx5(PaperAirplaneIcon, { className: "h-5 w-5" })
|
|
679
|
+
children: isSending ? onStop ? /* @__PURE__ */ jsx5(StopIcon, { className: "h-5 w-5" }) : /* @__PURE__ */ jsx5("div", { className: "w-5 h-5" }) : /* @__PURE__ */ jsx5(PaperAirplaneIcon, { className: "h-5 w-5" })
|
|
685
680
|
}
|
|
686
681
|
)
|
|
687
682
|
] })
|
|
@@ -690,18 +685,10 @@ var ChatInputArea = forwardRef(({
|
|
|
690
685
|
)
|
|
691
686
|
] }),
|
|
692
687
|
inputHint && /* @__PURE__ */ jsx5("div", { className: "text-sm text-red-500 bg-red-50 py-1 px-4 rounded-lg mt-1", children: inputHint }),
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
className: "text-[10px] text-gray-400 font-medium ml-12 text-center -mt-1 mb-1 cursor-pointer hover:text-gray-600 transition-colors",
|
|
698
|
-
onClick: () => {
|
|
699
|
-
var _a2;
|
|
700
|
-
return (_a2 = voiceContainerRef.current) == null ? void 0 : _a2.focus();
|
|
701
|
-
},
|
|
702
|
-
children: isFocused ? "Click to talk or hold space to talk" : "Tap to talk or click here to focus and push space to talk"
|
|
703
|
-
}
|
|
704
|
-
)
|
|
688
|
+
/* @__PURE__ */ jsx5("div", { className: "ml-[46px] mb-2 mt-0.5 min-h-[0.75rem]", style: { marginLeft: "48px" }, children: /* @__PURE__ */ jsx5("p", { className: `text-[10px] leading-tight transition-all duration-200 ${voiceError ? "text-red-500" : voiceTrigger || isTranscribing ? "text-orange-600 font-medium" : "text-gray-400"}`, children: voiceError ? /* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-1 font-semibold italic", children: [
|
|
689
|
+
"Error: ",
|
|
690
|
+
voiceError
|
|
691
|
+
] }) : isTranscribing ? "Transcribing, please wait..." : voiceTrigger ? "Listening... tap mic icon again to stop" : hintText || (voiceConfig ? "Type in text or tap mic icon to talk" : "Type your message...") }) })
|
|
705
692
|
] });
|
|
706
693
|
});
|
|
707
694
|
ChatInputArea.displayName = "ChatInputArea";
|
|
@@ -1224,7 +1211,7 @@ var ChatMessageList = ({
|
|
|
1224
1211
|
"div",
|
|
1225
1212
|
{
|
|
1226
1213
|
ref: chatContainerRef,
|
|
1227
|
-
className: "flex-1 overflow-y-auto p-4 space-y-
|
|
1214
|
+
className: "flex-1 overflow-y-auto p-4 space-y-8 bg-gray-50",
|
|
1228
1215
|
children: [
|
|
1229
1216
|
chatHistory.length === 0 && !isProcessing && /* @__PURE__ */ jsxs8("div", { className: "text-center py-8", children: [
|
|
1230
1217
|
/* @__PURE__ */ jsx11("h3", { className: "text-lg font-medium text-gray-700 mb-2", children: "How can I help you today?" }),
|
|
@@ -1280,7 +1267,7 @@ var ChatMessageList = ({
|
|
|
1280
1267
|
"div",
|
|
1281
1268
|
{
|
|
1282
1269
|
ref: processingIndicatorRef,
|
|
1283
|
-
className: "flex justify-start",
|
|
1270
|
+
className: "flex justify-start my-4",
|
|
1284
1271
|
children: /* @__PURE__ */ jsx11("div", { className: "bg-white text-gray-800 border border-gray-200 rounded-lg px-4 py-2 max-w-[85%]", children: /* @__PURE__ */ jsxs8("div", { className: "flex items-center", children: [
|
|
1285
1272
|
/* @__PURE__ */ jsx11("span", { className: "text-sm", children: processingHint }),
|
|
1286
1273
|
/* @__PURE__ */ jsxs8("span", { className: "ml-2 flex space-x-1", children: [
|