@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.
@@ -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 voiceContainerRef = (0, import_react5.useRef)(null);
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 || inputMode === "voice");
447
- const handleVoiceKeyDown = (e) => {
448
- if (inputMode !== "voice" || isInputDisabled) return;
449
- if (e.code !== "Space") return;
450
- const activeElement = document.activeElement;
451
- const isInputActive = activeElement && (activeElement.tagName === "INPUT" || activeElement.tagName === "TEXTAREA" || activeElement instanceof HTMLElement && activeElement.isContentEditable);
452
- if (isInputActive) return;
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
- triggerChange(messageRef.current + (messageRef.current ? " " : "") + text);
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) triggerChange(messageRef.current + (messageRef.current ? " " : "") + 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, _b2;
496
- if (inputMode === "voice") {
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 (inputMode === "voice" && voiceTrigger) {
607
+ if (voiceTrigger) {
588
608
  stopRecording();
609
+ } else {
610
+ startRecording("click");
589
611
  }
590
- setInputMode((prev) => prev === "text" ? "voice" : "text");
591
612
  },
592
- 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",
593
- title: inputMode === "text" ? "Switch to Voice" : "Switch to Keyboard",
594
- children: inputMode === "text" ? (
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
- ref: voiceContainerRef,
607
- tabIndex: inputMode === "voice" ? 0 : -1,
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
- inputMode === "text" && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
615
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
616
- "span",
617
- {
618
- ref: measurementRef,
619
- className: "absolute invisible whitespace-pre-wrap p-0 m-0 text-gray-700 leading-6",
620
- style: { fontSize: "1rem" }
621
- }
622
- ),
623
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
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
- type: "button",
647
- onClick: () => {
648
- if (voiceTrigger === "click") {
649
- stopRecording();
650
- } else if (!voiceTrigger) {
651
- startRecording("click");
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
- disabled: isInputDisabled || voiceTrigger === "space",
655
- 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" : ""}`,
656
- children: voiceTrigger ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
657
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "w-2 h-2 rounded-full bg-red-500 animate-ping mr-2" }),
658
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { children: [
659
- "Listening... ",
660
- voiceTrigger === "space" ? "(Release Space to send)" : "Tap to send"
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
- (inputMode === "text" || isSending) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative mx-2 flex-shrink-0", children: [
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 || !!voiceTrigger,
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
- hintText && inputMode === "text" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: "text-xs text-gray-500 ml-12 mb-2 mt-1", children: hintText }),
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";