@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
|
@@ -98,7 +98,7 @@ var MessageBubble = ({
|
|
|
98
98
|
)
|
|
99
99
|
] }) });
|
|
100
100
|
}
|
|
101
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `flex items-start gap-3 my-
|
|
101
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `flex items-start gap-3 my-1 ${isUser ? "justify-end" : "justify-start"}`, children: [
|
|
102
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
103
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
104
104
|
"div",
|
|
@@ -318,12 +318,23 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
|
|
|
318
318
|
var import_react3 = require("react");
|
|
319
319
|
var useAudioRecorder = (onStop) => {
|
|
320
320
|
const [isRecording, setIsRecording] = (0, import_react3.useState)(false);
|
|
321
|
+
const [isSimulated, setIsSimulated] = (0, import_react3.useState)(false);
|
|
321
322
|
const [blob, setBlob] = (0, import_react3.useState)(null);
|
|
322
323
|
const [error, setError] = (0, import_react3.useState)(null);
|
|
323
324
|
const mediaRecorderRef = (0, import_react3.useRef)(null);
|
|
324
325
|
const chunksRef = (0, import_react3.useRef)([]);
|
|
325
326
|
const start = (0, import_react3.useCallback)(async () => {
|
|
326
327
|
try {
|
|
328
|
+
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
|
329
|
+
if (process.env.NODE_ENV === "development") {
|
|
330
|
+
console.warn("[useAudioRecorder] MediaDevices not available. Entering simulation mode...");
|
|
331
|
+
setIsRecording(true);
|
|
332
|
+
setIsSimulated(true);
|
|
333
|
+
setError(null);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
throw new Error("Media devices not available. Ensure you are using HTTPS or localhost.");
|
|
337
|
+
}
|
|
327
338
|
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
328
339
|
const mediaRecorder = new MediaRecorder(stream);
|
|
329
340
|
mediaRecorderRef.current = mediaRecorder;
|
|
@@ -351,12 +362,21 @@ var useAudioRecorder = (onStop) => {
|
|
|
351
362
|
}
|
|
352
363
|
}, [onStop]);
|
|
353
364
|
const stop = (0, import_react3.useCallback)(() => {
|
|
365
|
+
if (isSimulated) {
|
|
366
|
+
setIsRecording(false);
|
|
367
|
+
setIsSimulated(false);
|
|
368
|
+
const simulatedBlob = new Blob(["simulated speech"], { type: "audio/simulated" });
|
|
369
|
+
setBlob(simulatedBlob);
|
|
370
|
+
if (onStop) onStop(simulatedBlob);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
354
373
|
if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") {
|
|
355
374
|
mediaRecorderRef.current.stop();
|
|
356
375
|
}
|
|
357
|
-
}, []);
|
|
376
|
+
}, [isSimulated, onStop]);
|
|
358
377
|
return {
|
|
359
378
|
isRecording,
|
|
379
|
+
isSimulated,
|
|
360
380
|
start,
|
|
361
381
|
stop,
|
|
362
382
|
blob,
|
|
@@ -405,21 +425,24 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
405
425
|
var _a, _b, _c, _d;
|
|
406
426
|
const [internalMessage, setInternalMessage] = (0, import_react5.useState)("");
|
|
407
427
|
const [voiceTrigger, setVoiceTrigger] = (0, import_react5.useState)(null);
|
|
408
|
-
const [
|
|
428
|
+
const [isTranscribing, setIsTranscribing] = (0, import_react5.useState)(false);
|
|
429
|
+
const [voiceError, setVoiceError] = (0, import_react5.useState)(null);
|
|
409
430
|
const [isFocused, setIsFocused] = (0, import_react5.useState)(false);
|
|
410
431
|
const textareaRef = (0, import_react5.useRef)(null);
|
|
411
432
|
const measurementRef = (0, import_react5.useRef)(null);
|
|
412
|
-
const
|
|
413
|
-
(0, import_react5.useEffect)(() => {
|
|
414
|
-
var _a2;
|
|
415
|
-
if (inputMode === "voice") {
|
|
416
|
-
(_a2 = voiceContainerRef.current) == null ? void 0 : _a2.focus();
|
|
417
|
-
}
|
|
418
|
-
}, [inputMode]);
|
|
433
|
+
const pendingSelectionRef = (0, import_react5.useRef)(null);
|
|
419
434
|
const isControlled = value !== void 0;
|
|
420
435
|
const message = isControlled ? value : internalMessage;
|
|
421
436
|
const messageRef = (0, import_react5.useRef)(message);
|
|
422
437
|
messageRef.current = message;
|
|
438
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
439
|
+
if (pendingSelectionRef.current && textareaRef.current) {
|
|
440
|
+
const { start, end } = pendingSelectionRef.current;
|
|
441
|
+
textareaRef.current.focus();
|
|
442
|
+
textareaRef.current.setSelectionRange(start, end);
|
|
443
|
+
pendingSelectionRef.current = null;
|
|
444
|
+
}
|
|
445
|
+
}, [message]);
|
|
423
446
|
const onChangeRef = (0, import_react5.useRef)(onChange);
|
|
424
447
|
(0, import_react5.useEffect)(() => {
|
|
425
448
|
onChangeRef.current = onChange;
|
|
@@ -432,6 +455,7 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
432
455
|
voiceConfigRef.current = voiceConfig;
|
|
433
456
|
}, [voiceConfig]);
|
|
434
457
|
const triggerChange = (0, import_react5.useCallback)((newValue) => {
|
|
458
|
+
setVoiceError(null);
|
|
435
459
|
if (isControlled && onChangeRef.current) {
|
|
436
460
|
const syntheticEvent = {
|
|
437
461
|
target: { value: newValue },
|
|
@@ -443,34 +467,30 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
443
467
|
}
|
|
444
468
|
}, [isControlled]);
|
|
445
469
|
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));
|
|
446
|
-
useProactiveResize(textareaRef, measurementRef, message, isInputDisabled || !!voiceTrigger
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
e.preventDefault();
|
|
454
|
-
e.stopPropagation();
|
|
455
|
-
if (voiceTrigger === "click") return;
|
|
456
|
-
if (!e.repeat && !voiceTrigger) {
|
|
457
|
-
startRecording("space");
|
|
458
|
-
}
|
|
459
|
-
};
|
|
460
|
-
const handleVoiceKeyUp = (e) => {
|
|
461
|
-
if (inputMode !== "voice" || isInputDisabled) return;
|
|
462
|
-
if (e.code === "Space") {
|
|
463
|
-
if (voiceTrigger === "space") {
|
|
464
|
-
e.preventDefault();
|
|
465
|
-
stopRecording();
|
|
466
|
-
}
|
|
470
|
+
useProactiveResize(textareaRef, measurementRef, message, isInputDisabled || !!voiceTrigger);
|
|
471
|
+
const insertTextAtCursor = (0, import_react5.useCallback)((text) => {
|
|
472
|
+
const textarea = textareaRef.current;
|
|
473
|
+
const currentVal = messageRef.current || "";
|
|
474
|
+
if (!textarea) {
|
|
475
|
+
triggerChange(currentVal + (currentVal ? " " : "") + text);
|
|
476
|
+
return;
|
|
467
477
|
}
|
|
468
|
-
|
|
478
|
+
const start = textarea.selectionStart;
|
|
479
|
+
const end = textarea.selectionEnd;
|
|
480
|
+
const before = currentVal.substring(0, start);
|
|
481
|
+
const after = currentVal.substring(end);
|
|
482
|
+
const prefix = start > 0 && !/\s$/.test(before) ? " " : "";
|
|
483
|
+
const newText = before + prefix + text + after;
|
|
484
|
+
const selectionStart = start + prefix.length;
|
|
485
|
+
const selectionEnd = selectionStart + text.length;
|
|
486
|
+
pendingSelectionRef.current = { start: selectionStart, end: selectionEnd };
|
|
487
|
+
triggerChange(newText);
|
|
488
|
+
}, [triggerChange]);
|
|
469
489
|
const handleVoiceResult = (0, import_react5.useCallback)((text, isFinal) => {
|
|
470
490
|
if (isFinal) {
|
|
471
|
-
|
|
491
|
+
insertTextAtCursor(text);
|
|
472
492
|
}
|
|
473
|
-
}, []);
|
|
493
|
+
}, [insertTextAtCursor]);
|
|
474
494
|
const handleVoiceEnd = (0, import_react5.useCallback)(() => {
|
|
475
495
|
var _a2, _b2;
|
|
476
496
|
setVoiceTrigger(null);
|
|
@@ -480,24 +500,34 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
480
500
|
const customRecorder = useAudioRecorder(async (blob) => {
|
|
481
501
|
var _a2, _b2, _c2;
|
|
482
502
|
setVoiceTrigger(null);
|
|
503
|
+
setIsTranscribing(true);
|
|
504
|
+
setVoiceError(null);
|
|
483
505
|
(_b2 = (_a2 = voiceConfigRef.current) == null ? void 0 : _a2.onVoiceEnd) == null ? void 0 : _b2.call(_a2);
|
|
506
|
+
if (blob.type === "audio/simulated") {
|
|
507
|
+
console.log("[ChatInputArea] Handling simulated audio capture");
|
|
508
|
+
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
509
|
+
insertTextAtCursor("This is a simulated transcription for development testing.");
|
|
510
|
+
setIsTranscribing(false);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
484
513
|
if ((_c2 = voiceConfigRef.current) == null ? void 0 : _c2.onAudioCapture) {
|
|
485
514
|
try {
|
|
486
515
|
const text = await voiceConfigRef.current.onAudioCapture(blob);
|
|
487
|
-
if (text)
|
|
516
|
+
if (text) insertTextAtCursor(text);
|
|
488
517
|
} catch (e) {
|
|
489
518
|
console.error("[ChatInputArea] Audio capture failed", e);
|
|
519
|
+
setVoiceError(e.message || "Transcription failed");
|
|
520
|
+
} finally {
|
|
521
|
+
setIsTranscribing(false);
|
|
490
522
|
}
|
|
523
|
+
} else {
|
|
524
|
+
setIsTranscribing(false);
|
|
491
525
|
}
|
|
492
526
|
});
|
|
493
527
|
(0, import_react5.useImperativeHandle)(ref, () => ({
|
|
494
528
|
focus: () => {
|
|
495
|
-
var _a2
|
|
496
|
-
|
|
497
|
-
(_a2 = voiceContainerRef.current) == null ? void 0 : _a2.focus();
|
|
498
|
-
} else {
|
|
499
|
-
(_b2 = textareaRef.current) == null ? void 0 : _b2.focus();
|
|
500
|
-
}
|
|
529
|
+
var _a2;
|
|
530
|
+
(_a2 = textareaRef.current) == null ? void 0 : _a2.focus();
|
|
501
531
|
},
|
|
502
532
|
setValue: (newValue) => {
|
|
503
533
|
triggerChange(newValue);
|
|
@@ -528,8 +558,9 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
528
558
|
};
|
|
529
559
|
const startRecording = async (trigger) => {
|
|
530
560
|
var _a2;
|
|
531
|
-
if (voiceTrigger) return;
|
|
561
|
+
if (voiceTrigger || isTranscribing) return;
|
|
532
562
|
setVoiceTrigger(trigger);
|
|
563
|
+
setVoiceError(null);
|
|
533
564
|
(_a2 = voiceConfig == null ? void 0 : voiceConfig.onVoiceStart) == null ? void 0 : _a2.call(voiceConfig);
|
|
534
565
|
if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "native") {
|
|
535
566
|
if (!nativeSpeech.isSupported) {
|
|
@@ -541,6 +572,10 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
541
572
|
} else {
|
|
542
573
|
await customRecorder.start();
|
|
543
574
|
}
|
|
575
|
+
setTimeout(() => {
|
|
576
|
+
var _a3;
|
|
577
|
+
return (_a3 = textareaRef.current) == null ? void 0 : _a3.focus();
|
|
578
|
+
}, 0);
|
|
544
579
|
};
|
|
545
580
|
const stopRecording = () => {
|
|
546
581
|
if (!voiceTrigger) return;
|
|
@@ -553,9 +588,7 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
553
588
|
const getPlaceholder = () => {
|
|
554
589
|
if (placeholder) return placeholder;
|
|
555
590
|
if (voiceTrigger) return "Listening...";
|
|
556
|
-
if (currentTask == null ? void 0 : currentTask.complete)
|
|
557
|
-
return "Task completed!";
|
|
558
|
-
}
|
|
591
|
+
if (currentTask == null ? void 0 : currentTask.complete) return "Task completed!";
|
|
559
592
|
if ((lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.interactive) && (lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.interactiveData) && !(lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.isResponseSubmitted)) {
|
|
560
593
|
const interactiveType = lastInteractiveMessage.interactiveData.function;
|
|
561
594
|
switch (interactiveType) {
|
|
@@ -584,85 +617,61 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
584
617
|
{
|
|
585
618
|
type: "button",
|
|
586
619
|
onClick: () => {
|
|
587
|
-
if (
|
|
620
|
+
if (voiceTrigger) {
|
|
588
621
|
stopRecording();
|
|
622
|
+
} else {
|
|
623
|
+
startRecording("click");
|
|
589
624
|
}
|
|
590
|
-
setInputMode((prev) => prev === "text" ? "voice" : "text");
|
|
591
625
|
},
|
|
592
|
-
className:
|
|
593
|
-
title:
|
|
594
|
-
children:
|
|
595
|
-
// Voice Icon (Waveform)
|
|
596
|
-
/* @__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" }) })
|
|
597
|
-
) : (
|
|
598
|
-
// Keyboard Icon (Filled)
|
|
599
|
-
/* @__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" }) })
|
|
600
|
-
)
|
|
626
|
+
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"}`,
|
|
627
|
+
title: voiceTrigger ? "Stop Recording" : "Start Voice Input",
|
|
628
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_outline.MicrophoneIcon, { className: "w-5 h-5" })
|
|
601
629
|
}
|
|
602
630
|
),
|
|
603
631
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
604
632
|
"div",
|
|
605
633
|
{
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
onKeyDown: handleVoiceKeyDown,
|
|
609
|
-
onKeyUp: handleVoiceKeyUp,
|
|
610
|
-
onFocus: () => setIsFocused(true),
|
|
611
|
-
onBlur: () => setIsFocused(false),
|
|
612
|
-
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",
|
|
634
|
+
tabIndex: -1,
|
|
635
|
+
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"}`,
|
|
613
636
|
children: [
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
"textarea",
|
|
625
|
-
{
|
|
626
|
-
ref: textareaRef,
|
|
627
|
-
value: message,
|
|
628
|
-
onChange: (e) => {
|
|
629
|
-
if (isControlled && onChange) {
|
|
630
|
-
onChange(e);
|
|
631
|
-
} else {
|
|
632
|
-
setInternalMessage(e.target.value);
|
|
633
|
-
}
|
|
634
|
-
},
|
|
635
|
-
onKeyDown: handleKeyDown,
|
|
636
|
-
placeholder: getPlaceholder(),
|
|
637
|
-
disabled: isInputDisabled || !!voiceTrigger,
|
|
638
|
-
rows: 1,
|
|
639
|
-
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" : ""}`
|
|
640
|
-
}
|
|
641
|
-
)
|
|
642
|
-
] }),
|
|
643
|
-
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)(
|
|
644
|
-
"button",
|
|
637
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
638
|
+
"span",
|
|
639
|
+
{
|
|
640
|
+
ref: measurementRef,
|
|
641
|
+
className: "absolute invisible whitespace-pre-wrap p-0 m-0 text-gray-700 leading-6",
|
|
642
|
+
style: { fontSize: "1rem" }
|
|
643
|
+
}
|
|
644
|
+
),
|
|
645
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
646
|
+
"textarea",
|
|
645
647
|
{
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
648
|
+
ref: textareaRef,
|
|
649
|
+
value: message,
|
|
650
|
+
onChange: (e) => {
|
|
651
|
+
if (isControlled && onChange) {
|
|
652
|
+
onChange(e);
|
|
653
|
+
} else {
|
|
654
|
+
setInternalMessage(e.target.value);
|
|
652
655
|
}
|
|
653
656
|
},
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
657
|
+
onKeyDown: handleKeyDown,
|
|
658
|
+
onFocus: () => {
|
|
659
|
+
setIsFocused(true);
|
|
660
|
+
setVoiceError(null);
|
|
661
|
+
},
|
|
662
|
+
onBlur: () => setIsFocused(false),
|
|
663
|
+
placeholder: getPlaceholder(),
|
|
664
|
+
disabled: isInputDisabled,
|
|
665
|
+
readOnly: !!voiceTrigger || isTranscribing,
|
|
666
|
+
rows: 1,
|
|
667
|
+
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" : ""}`
|
|
663
668
|
}
|
|
664
|
-
)
|
|
665
|
-
(
|
|
669
|
+
),
|
|
670
|
+
isTranscribing && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "flex-shrink-0 animate-spin mr-2", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { className: "w-4 h-4 text-orange-500", viewBox: "0 0 24 24", children: [
|
|
671
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4", fill: "none" }),
|
|
672
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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" })
|
|
673
|
+
] }) }),
|
|
674
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative mx-2 flex-shrink-0", children: [
|
|
666
675
|
isSending && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "absolute -inset-1", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
667
676
|
"svg",
|
|
668
677
|
{
|
|
@@ -705,24 +714,10 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
705
714
|
handleSubmit();
|
|
706
715
|
}
|
|
707
716
|
},
|
|
708
|
-
disabled: (currentTask == null ? void 0 : currentTask.complete) || isSending && !onStop || isInputDisabled
|
|
717
|
+
disabled: (currentTask == null ? void 0 : currentTask.complete) || isSending && !onStop || isInputDisabled,
|
|
709
718
|
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"}`,
|
|
710
719
|
title: isSending && onStop ? "Stop generating" : "Send message",
|
|
711
|
-
children: isSending ? onStop ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_outline.StopIcon, { className: "h-5 w-5" }) : (
|
|
712
|
-
// AND we show the overlay spinner outside?
|
|
713
|
-
// Actually `ChatInput.tsx` lines 117-140 are `isLoading && (...)`. It is always shown when loading.
|
|
714
|
-
// So we have a spinner ring AROUND the button (absolute -inset-1).
|
|
715
|
-
// AND potentially a spinner INSIDE the button if no onStop?
|
|
716
|
-
// In my case, I will stick to:
|
|
717
|
-
// If onStop: Show StopIcon. Button is Red.
|
|
718
|
-
// If !onStop: Show Spinner inside? Or just let the outer ring do the work?
|
|
719
|
-
// Legacy `Spinner` component usage inside button suggests double spinner if we are not careful.
|
|
720
|
-
// But usually `onStop` is provided for streaming.
|
|
721
|
-
// If I look at the screenshot, it shows a RED button (with stop icon) and a BLUE ring around it.
|
|
722
|
-
// That matches: Red button (bg-red-500) + Blue Spinner Ring (text-blue-500).
|
|
723
|
-
// So I will replicate that structure.
|
|
724
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_outline.StopIcon, { className: "h-5 w-5" })
|
|
725
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_outline.PaperAirplaneIcon, { className: "h-5 w-5" })
|
|
720
|
+
children: isSending ? onStop ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_outline.StopIcon, { className: "h-5 w-5" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_outline.PaperAirplaneIcon, { className: "h-5 w-5" })
|
|
726
721
|
}
|
|
727
722
|
)
|
|
728
723
|
] })
|
|
@@ -731,18 +726,10 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
731
726
|
)
|
|
732
727
|
] }),
|
|
733
728
|
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 }),
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
className: "text-[10px] text-gray-400 font-medium ml-12 text-center -mt-1 mb-1 cursor-pointer hover:text-gray-600 transition-colors",
|
|
739
|
-
onClick: () => {
|
|
740
|
-
var _a2;
|
|
741
|
-
return (_a2 = voiceContainerRef.current) == null ? void 0 : _a2.focus();
|
|
742
|
-
},
|
|
743
|
-
children: isFocused ? "Click to talk or hold space to talk" : "Tap to talk or click here to focus and push space to talk"
|
|
744
|
-
}
|
|
745
|
-
)
|
|
729
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ml-[46px] mb-2 mt-0.5 min-h-[0.75rem]", style: { marginLeft: "48px" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "flex items-center gap-1 font-semibold italic", children: [
|
|
730
|
+
"Error: ",
|
|
731
|
+
voiceError
|
|
732
|
+
] }) : 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...") }) })
|
|
746
733
|
] });
|
|
747
734
|
});
|
|
748
735
|
ChatInputArea.displayName = "ChatInputArea";
|
|
@@ -1265,7 +1252,7 @@ var ChatMessageList = ({
|
|
|
1265
1252
|
"div",
|
|
1266
1253
|
{
|
|
1267
1254
|
ref: chatContainerRef,
|
|
1268
|
-
className: "flex-1 overflow-y-auto p-4 space-y-
|
|
1255
|
+
className: "flex-1 overflow-y-auto p-4 space-y-8 bg-gray-50",
|
|
1269
1256
|
children: [
|
|
1270
1257
|
chatHistory.length === 0 && !isProcessing && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "text-center py-8", children: [
|
|
1271
1258
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("h3", { className: "text-lg font-medium text-gray-700 mb-2", children: "How can I help you today?" }),
|
|
@@ -1321,7 +1308,7 @@ var ChatMessageList = ({
|
|
|
1321
1308
|
"div",
|
|
1322
1309
|
{
|
|
1323
1310
|
ref: processingIndicatorRef,
|
|
1324
|
-
className: "flex justify-start",
|
|
1311
|
+
className: "flex justify-start my-4",
|
|
1325
1312
|
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: [
|
|
1326
1313
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-sm", children: processingHint }),
|
|
1327
1314
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "ml-2 flex space-x-1", children: [
|