@contentgrowth/llm-service 0.8.9 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ui/react/components/index.cjs +101 -137
- 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 +104 -140
- package/dist/ui/react/components/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -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,22 @@ 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 [inputMode, setInputMode] = (0, import_react5.useState)(defaultInputMode);
|
|
409
428
|
const [isFocused, setIsFocused] = (0, import_react5.useState)(false);
|
|
410
429
|
const textareaRef = (0, import_react5.useRef)(null);
|
|
411
430
|
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]);
|
|
431
|
+
const pendingSelectionRef = (0, import_react5.useRef)(null);
|
|
419
432
|
const isControlled = value !== void 0;
|
|
420
433
|
const message = isControlled ? value : internalMessage;
|
|
421
434
|
const messageRef = (0, import_react5.useRef)(message);
|
|
422
435
|
messageRef.current = message;
|
|
436
|
+
(0, import_react5.useLayoutEffect)(() => {
|
|
437
|
+
if (pendingSelectionRef.current && textareaRef.current) {
|
|
438
|
+
const { start, end } = pendingSelectionRef.current;
|
|
439
|
+
textareaRef.current.focus();
|
|
440
|
+
textareaRef.current.setSelectionRange(start, end);
|
|
441
|
+
pendingSelectionRef.current = null;
|
|
442
|
+
}
|
|
443
|
+
}, [message]);
|
|
423
444
|
const onChangeRef = (0, import_react5.useRef)(onChange);
|
|
424
445
|
(0, import_react5.useEffect)(() => {
|
|
425
446
|
onChangeRef.current = onChange;
|
|
@@ -443,34 +464,30 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
443
464
|
}
|
|
444
465
|
}, [isControlled]);
|
|
445
466
|
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
|
-
}
|
|
467
|
+
useProactiveResize(textareaRef, measurementRef, message, isInputDisabled || !!voiceTrigger);
|
|
468
|
+
const insertTextAtCursor = (0, import_react5.useCallback)((text) => {
|
|
469
|
+
const textarea = textareaRef.current;
|
|
470
|
+
const currentVal = messageRef.current || "";
|
|
471
|
+
if (!textarea) {
|
|
472
|
+
triggerChange(currentVal + (currentVal ? " " : "") + text);
|
|
473
|
+
return;
|
|
467
474
|
}
|
|
468
|
-
|
|
475
|
+
const start = textarea.selectionStart;
|
|
476
|
+
const end = textarea.selectionEnd;
|
|
477
|
+
const before = currentVal.substring(0, start);
|
|
478
|
+
const after = currentVal.substring(end);
|
|
479
|
+
const prefix = start > 0 && !/\s$/.test(before) ? " " : "";
|
|
480
|
+
const newText = before + prefix + text + after;
|
|
481
|
+
const selectionStart = start + prefix.length;
|
|
482
|
+
const selectionEnd = selectionStart + text.length;
|
|
483
|
+
pendingSelectionRef.current = { start: selectionStart, end: selectionEnd };
|
|
484
|
+
triggerChange(newText);
|
|
485
|
+
}, [triggerChange]);
|
|
469
486
|
const handleVoiceResult = (0, import_react5.useCallback)((text, isFinal) => {
|
|
470
487
|
if (isFinal) {
|
|
471
|
-
|
|
488
|
+
insertTextAtCursor(text);
|
|
472
489
|
}
|
|
473
|
-
}, []);
|
|
490
|
+
}, [insertTextAtCursor]);
|
|
474
491
|
const handleVoiceEnd = (0, import_react5.useCallback)(() => {
|
|
475
492
|
var _a2, _b2;
|
|
476
493
|
setVoiceTrigger(null);
|
|
@@ -481,10 +498,15 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
481
498
|
var _a2, _b2, _c2;
|
|
482
499
|
setVoiceTrigger(null);
|
|
483
500
|
(_b2 = (_a2 = voiceConfigRef.current) == null ? void 0 : _a2.onVoiceEnd) == null ? void 0 : _b2.call(_a2);
|
|
501
|
+
if (blob.type === "audio/simulated") {
|
|
502
|
+
console.log("[ChatInputArea] Handling simulated audio capture");
|
|
503
|
+
insertTextAtCursor("This is a simulated transcription for development testing.");
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
484
506
|
if ((_c2 = voiceConfigRef.current) == null ? void 0 : _c2.onAudioCapture) {
|
|
485
507
|
try {
|
|
486
508
|
const text = await voiceConfigRef.current.onAudioCapture(blob);
|
|
487
|
-
if (text)
|
|
509
|
+
if (text) insertTextAtCursor(text);
|
|
488
510
|
} catch (e) {
|
|
489
511
|
console.error("[ChatInputArea] Audio capture failed", e);
|
|
490
512
|
}
|
|
@@ -492,12 +514,8 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
492
514
|
});
|
|
493
515
|
(0, import_react5.useImperativeHandle)(ref, () => ({
|
|
494
516
|
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
|
-
}
|
|
517
|
+
var _a2;
|
|
518
|
+
(_a2 = textareaRef.current) == null ? void 0 : _a2.focus();
|
|
501
519
|
},
|
|
502
520
|
setValue: (newValue) => {
|
|
503
521
|
triggerChange(newValue);
|
|
@@ -541,6 +559,10 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
541
559
|
} else {
|
|
542
560
|
await customRecorder.start();
|
|
543
561
|
}
|
|
562
|
+
setTimeout(() => {
|
|
563
|
+
var _a3;
|
|
564
|
+
return (_a3 = textareaRef.current) == null ? void 0 : _a3.focus();
|
|
565
|
+
}, 0);
|
|
544
566
|
};
|
|
545
567
|
const stopRecording = () => {
|
|
546
568
|
if (!voiceTrigger) return;
|
|
@@ -553,9 +575,7 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
553
575
|
const getPlaceholder = () => {
|
|
554
576
|
if (placeholder) return placeholder;
|
|
555
577
|
if (voiceTrigger) return "Listening...";
|
|
556
|
-
if (currentTask == null ? void 0 : currentTask.complete)
|
|
557
|
-
return "Task completed!";
|
|
558
|
-
}
|
|
578
|
+
if (currentTask == null ? void 0 : currentTask.complete) return "Task completed!";
|
|
559
579
|
if ((lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.interactive) && (lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.interactiveData) && !(lastInteractiveMessage == null ? void 0 : lastInteractiveMessage.isResponseSubmitted)) {
|
|
560
580
|
const interactiveType = lastInteractiveMessage.interactiveData.function;
|
|
561
581
|
switch (interactiveType) {
|
|
@@ -584,85 +604,54 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
584
604
|
{
|
|
585
605
|
type: "button",
|
|
586
606
|
onClick: () => {
|
|
587
|
-
if (
|
|
607
|
+
if (voiceTrigger) {
|
|
588
608
|
stopRecording();
|
|
609
|
+
} else {
|
|
610
|
+
startRecording("click");
|
|
589
611
|
}
|
|
590
|
-
setInputMode((prev) => prev === "text" ? "voice" : "text");
|
|
591
612
|
},
|
|
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
|
-
)
|
|
613
|
+
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"}`,
|
|
614
|
+
title: voiceTrigger ? "Stop Recording" : "Start Voice Input",
|
|
615
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_outline.MicrophoneIcon, { className: "w-5 h-5" })
|
|
601
616
|
}
|
|
602
617
|
),
|
|
603
618
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
604
619
|
"div",
|
|
605
620
|
{
|
|
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",
|
|
621
|
+
tabIndex: -1,
|
|
622
|
+
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
623
|
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",
|
|
624
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
625
|
+
"span",
|
|
626
|
+
{
|
|
627
|
+
ref: measurementRef,
|
|
628
|
+
className: "absolute invisible whitespace-pre-wrap p-0 m-0 text-gray-700 leading-6",
|
|
629
|
+
style: { fontSize: "1rem" }
|
|
630
|
+
}
|
|
631
|
+
),
|
|
632
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
633
|
+
"textarea",
|
|
645
634
|
{
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
635
|
+
ref: textareaRef,
|
|
636
|
+
value: message,
|
|
637
|
+
onChange: (e) => {
|
|
638
|
+
if (isControlled && onChange) {
|
|
639
|
+
onChange(e);
|
|
640
|
+
} else {
|
|
641
|
+
setInternalMessage(e.target.value);
|
|
652
642
|
}
|
|
653
643
|
},
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Tap to Talk" })
|
|
644
|
+
onKeyDown: handleKeyDown,
|
|
645
|
+
onFocus: () => setIsFocused(true),
|
|
646
|
+
onBlur: () => setIsFocused(false),
|
|
647
|
+
placeholder: getPlaceholder(),
|
|
648
|
+
disabled: isInputDisabled,
|
|
649
|
+
readOnly: !!voiceTrigger,
|
|
650
|
+
rows: 1,
|
|
651
|
+
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 ? "cursor-default" : ""}`
|
|
663
652
|
}
|
|
664
|
-
)
|
|
665
|
-
|
|
653
|
+
),
|
|
654
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative mx-2 flex-shrink-0", children: [
|
|
666
655
|
isSending && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "absolute -inset-1", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
667
656
|
"svg",
|
|
668
657
|
{
|
|
@@ -705,24 +694,10 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
705
694
|
handleSubmit();
|
|
706
695
|
}
|
|
707
696
|
},
|
|
708
|
-
disabled: (currentTask == null ? void 0 : currentTask.complete) || isSending && !onStop || isInputDisabled
|
|
697
|
+
disabled: (currentTask == null ? void 0 : currentTask.complete) || isSending && !onStop || isInputDisabled,
|
|
709
698
|
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
699
|
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" })
|
|
700
|
+
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
701
|
}
|
|
727
702
|
)
|
|
728
703
|
] })
|
|
@@ -731,18 +706,7 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
|
|
|
731
706
|
)
|
|
732
707
|
] }),
|
|
733
708
|
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
|
-
inputMode === "voice" && !voiceTrigger && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
736
|
-
"p",
|
|
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
|
-
)
|
|
709
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "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-colors duration-200 ${voiceTrigger ? "text-orange-600 font-medium" : "text-gray-400"}`, children: voiceTrigger ? "Listening... tap mic icon again to stop" : hintText || (voiceConfig ? "Type in text or tap mic icon to talk" : "Type your message...") }) })
|
|
746
710
|
] });
|
|
747
711
|
});
|
|
748
712
|
ChatInputArea.displayName = "ChatInputArea";
|