@apteva/apteva-kit 0.1.112 → 0.1.114

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/index.mjs CHANGED
@@ -4,7 +4,7 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
5
 
6
6
  // src/components/Chat/Chat.tsx
7
- import { useState as useState9, useEffect as useEffect8, useRef as useRef9, useMemo as useMemo2, useCallback as useCallback3, forwardRef, useImperativeHandle } from "react";
7
+ import { useState as useState9, useEffect as useEffect9, useRef as useRef9, useMemo as useMemo2, useCallback as useCallback4, forwardRef, useImperativeHandle } from "react";
8
8
 
9
9
  // src/components/Chat/MessageList.tsx
10
10
  import { useEffect as useEffect7, useRef as useRef6 } from "react";
@@ -3336,9 +3336,13 @@ function MessageList({
3336
3336
  }
3337
3337
 
3338
3338
  // src/components/Chat/Composer.tsx
3339
- import { useState as useState6, useRef as useRef7 } from "react";
3339
+ import { useState as useState6, useEffect as useEffect8, useCallback as useCallback3, useRef as useRef7 } from "react";
3340
3340
  import { Fragment as Fragment4, jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
3341
- function Composer({ onSendMessage, placeholder = "Type a message...", disabled = false, isLoading = false, onStop, onFileUpload, onSwitchMode }) {
3341
+ var getSpeechRecognition = () => {
3342
+ if (typeof window === "undefined") return null;
3343
+ return window.SpeechRecognition || window.webkitSpeechRecognition || null;
3344
+ };
3345
+ function Composer({ onSendMessage, placeholder = "Type a message...", disabled = false, isLoading = false, onStop, onFileUpload, onSwitchMode, speechToText }) {
3342
3346
  const [text, setText] = useState6("");
3343
3347
  const [showMenu, setShowMenu] = useState6(false);
3344
3348
  const [pendingFiles, setPendingFiles] = useState6([]);
@@ -3347,6 +3351,190 @@ function Composer({ onSendMessage, placeholder = "Type a message...", disabled =
3347
3351
  const textareaRef = useRef7(null);
3348
3352
  const fileInputRef = useRef7(null);
3349
3353
  const menuButtonRef = useRef7(null);
3354
+ const [isRecording, setIsRecording] = useState6(false);
3355
+ const [recordingTime, setRecordingTime] = useState6(0);
3356
+ const [transcriptFlash, setTranscriptFlash] = useState6(null);
3357
+ const recognitionRef = useRef7(null);
3358
+ const mediaStreamRef = useRef7(null);
3359
+ const audioContextRef = useRef7(null);
3360
+ const analyserRef = useRef7(null);
3361
+ const canvasRef = useRef7(null);
3362
+ const animFrameRef = useRef7(0);
3363
+ const silenceTimerRef = useRef7(null);
3364
+ const recordingTimerRef = useRef7(null);
3365
+ const finalTranscriptRef = useRef7("");
3366
+ const manualStopRef = useRef7(false);
3367
+ const sttConfig = speechToText ? typeof speechToText === "object" ? speechToText : {} : null;
3368
+ const sttSupported = !!sttConfig && !!getSpeechRecognition();
3369
+ const silenceTimeout = sttConfig?.silenceTimeout ?? 2e3;
3370
+ const autoSend = sttConfig?.autoSend !== false;
3371
+ useEffect8(() => {
3372
+ return () => {
3373
+ stopRecording(true);
3374
+ };
3375
+ }, []);
3376
+ const drawWaveform = useCallback3(() => {
3377
+ const canvas = canvasRef.current;
3378
+ const analyser = analyserRef.current;
3379
+ if (!canvas || !analyser) return;
3380
+ const ctx = canvas.getContext("2d");
3381
+ if (!ctx) return;
3382
+ const bufferLength = analyser.frequencyBinCount;
3383
+ const dataArray = new Uint8Array(bufferLength);
3384
+ const draw = () => {
3385
+ animFrameRef.current = requestAnimationFrame(draw);
3386
+ analyser.getByteTimeDomainData(dataArray);
3387
+ const { width, height } = canvas;
3388
+ ctx.clearRect(0, 0, width, height);
3389
+ const barCount = 32;
3390
+ const barWidth = Math.max(2, (width - (barCount - 1) * 2) / barCount);
3391
+ const gap = 2;
3392
+ const samplesPerBar = Math.floor(bufferLength / barCount);
3393
+ for (let i = 0; i < barCount; i++) {
3394
+ let sum = 0;
3395
+ for (let j = 0; j < samplesPerBar; j++) {
3396
+ const val = dataArray[i * samplesPerBar + j] - 128;
3397
+ sum += Math.abs(val);
3398
+ }
3399
+ const avg = sum / samplesPerBar;
3400
+ const barHeight = Math.max(3, avg / 128 * height * 2.5);
3401
+ const x = i * (barWidth + gap);
3402
+ const y = (height - barHeight) / 2;
3403
+ ctx.fillStyle = "#3b82f6";
3404
+ ctx.beginPath();
3405
+ ctx.roundRect(x, y, barWidth, barHeight, barWidth / 2);
3406
+ ctx.fill();
3407
+ }
3408
+ };
3409
+ draw();
3410
+ }, []);
3411
+ const startRecording = useCallback3(async () => {
3412
+ const SpeechRecognitionCtor = getSpeechRecognition();
3413
+ if (!SpeechRecognitionCtor) return;
3414
+ try {
3415
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
3416
+ mediaStreamRef.current = stream;
3417
+ const audioCtx = new AudioContext();
3418
+ audioContextRef.current = audioCtx;
3419
+ const source = audioCtx.createMediaStreamSource(stream);
3420
+ const analyser = audioCtx.createAnalyser();
3421
+ analyser.fftSize = 256;
3422
+ source.connect(analyser);
3423
+ analyserRef.current = analyser;
3424
+ const recognition = new SpeechRecognitionCtor();
3425
+ recognition.continuous = true;
3426
+ recognition.interimResults = true;
3427
+ recognition.lang = sttConfig?.language || navigator.language || "en-US";
3428
+ recognitionRef.current = recognition;
3429
+ finalTranscriptRef.current = "";
3430
+ manualStopRef.current = false;
3431
+ recognition.onresult = (event) => {
3432
+ let final = "";
3433
+ let interim = "";
3434
+ for (let i = 0; i < event.results.length; i++) {
3435
+ const result = event.results[i];
3436
+ if (result.isFinal) {
3437
+ final += result[0].transcript;
3438
+ } else {
3439
+ interim += result[0].transcript;
3440
+ }
3441
+ }
3442
+ finalTranscriptRef.current = final;
3443
+ if (silenceTimerRef.current) {
3444
+ clearTimeout(silenceTimerRef.current);
3445
+ }
3446
+ silenceTimerRef.current = setTimeout(() => {
3447
+ stopRecording(false);
3448
+ }, silenceTimeout);
3449
+ };
3450
+ recognition.onerror = (event) => {
3451
+ if (event.error !== "aborted") {
3452
+ console.warn("Speech recognition error:", event.error);
3453
+ }
3454
+ stopRecording(true);
3455
+ };
3456
+ recognition.onend = () => {
3457
+ if (!manualStopRef.current && isRecording) {
3458
+ finishRecording();
3459
+ }
3460
+ };
3461
+ recognition.start();
3462
+ setIsRecording(true);
3463
+ setRecordingTime(0);
3464
+ recordingTimerRef.current = setInterval(() => {
3465
+ setRecordingTime((t) => t + 1);
3466
+ }, 1e3);
3467
+ requestAnimationFrame(() => drawWaveform());
3468
+ silenceTimerRef.current = setTimeout(() => {
3469
+ stopRecording(false);
3470
+ }, silenceTimeout + 1e3);
3471
+ } catch (err) {
3472
+ console.warn("Microphone access denied or error:", err);
3473
+ setFileError("Microphone access denied");
3474
+ setTimeout(() => setFileError(null), 3e3);
3475
+ }
3476
+ }, [sttConfig?.language, silenceTimeout, drawWaveform]);
3477
+ const finishRecording = useCallback3(() => {
3478
+ const transcript = finalTranscriptRef.current.trim();
3479
+ setIsRecording(false);
3480
+ setRecordingTime(0);
3481
+ if (transcript) {
3482
+ if (autoSend) {
3483
+ setTranscriptFlash(transcript);
3484
+ setTimeout(() => {
3485
+ setTranscriptFlash(null);
3486
+ onSendMessage(transcript);
3487
+ }, 600);
3488
+ } else {
3489
+ setText((prev) => prev ? `${prev} ${transcript}` : transcript);
3490
+ }
3491
+ }
3492
+ }, [autoSend, onSendMessage]);
3493
+ const stopRecording = useCallback3((isCleanupOnly) => {
3494
+ manualStopRef.current = true;
3495
+ if (silenceTimerRef.current) {
3496
+ clearTimeout(silenceTimerRef.current);
3497
+ silenceTimerRef.current = null;
3498
+ }
3499
+ if (recordingTimerRef.current) {
3500
+ clearInterval(recordingTimerRef.current);
3501
+ recordingTimerRef.current = null;
3502
+ }
3503
+ if (animFrameRef.current) {
3504
+ cancelAnimationFrame(animFrameRef.current);
3505
+ animFrameRef.current = 0;
3506
+ }
3507
+ if (recognitionRef.current) {
3508
+ try {
3509
+ recognitionRef.current.stop();
3510
+ } catch (_e) {
3511
+ }
3512
+ recognitionRef.current = null;
3513
+ }
3514
+ if (mediaStreamRef.current) {
3515
+ mediaStreamRef.current.getTracks().forEach((t) => t.stop());
3516
+ mediaStreamRef.current = null;
3517
+ }
3518
+ if (audioContextRef.current) {
3519
+ try {
3520
+ audioContextRef.current.close();
3521
+ } catch (_e) {
3522
+ }
3523
+ audioContextRef.current = null;
3524
+ }
3525
+ analyserRef.current = null;
3526
+ if (!isCleanupOnly) {
3527
+ finishRecording();
3528
+ } else {
3529
+ setIsRecording(false);
3530
+ setRecordingTime(0);
3531
+ }
3532
+ }, [finishRecording]);
3533
+ const formatTime = (seconds) => {
3534
+ const m = Math.floor(seconds / 60);
3535
+ const s = seconds % 60;
3536
+ return `${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
3537
+ };
3350
3538
  const handleKeyDown = (e) => {
3351
3539
  if (e.key === "Enter" && !e.shiftKey) {
3352
3540
  e.preventDefault();
@@ -3429,12 +3617,17 @@ function Composer({ onSendMessage, placeholder = "Type a message...", disabled =
3429
3617
  }
3430
3618
  return /* @__PURE__ */ jsx23("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx23("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" }) });
3431
3619
  };
3620
+ const hasMic = sttSupported && !isRecording;
3621
+ const gridCols = hasMic ? "auto 1fr auto auto" : "auto 1fr auto";
3622
+ const gridAreas = isRecording ? '"plus waveform waveform stop"' : isMultiLine ? hasMic ? '"textarea textarea textarea textarea" "plus . mic send"' : '"textarea textarea textarea" "plus . send"' : hasMic ? '"plus textarea mic send"' : '"plus textarea send"';
3623
+ const gridColsRecording = "auto 1fr auto";
3432
3624
  return /* @__PURE__ */ jsxs17("div", { className: "px-4 py-3 relative", children: [
3433
3625
  fileError && /* @__PURE__ */ jsx23("div", { className: "apteva-file-error", children: /* @__PURE__ */ jsxs17("div", { className: "apteva-file-error-content", children: [
3434
3626
  /* @__PURE__ */ jsx23("svg", { fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx23("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" }) }),
3435
3627
  /* @__PURE__ */ jsx23("span", { children: fileError })
3436
3628
  ] }) }),
3437
- pendingFiles.length > 0 && /* @__PURE__ */ jsx23("div", { className: "apteva-file-preview", children: pendingFiles.map((pf, index) => /* @__PURE__ */ jsxs17("div", { className: "apteva-file-item", children: [
3629
+ transcriptFlash && /* @__PURE__ */ jsx23("div", { className: "apteva-transcript-flash", children: /* @__PURE__ */ jsx23("span", { children: transcriptFlash }) }),
3630
+ pendingFiles.length > 0 && !isRecording && /* @__PURE__ */ jsx23("div", { className: "apteva-file-preview", children: pendingFiles.map((pf, index) => /* @__PURE__ */ jsxs17("div", { className: "apteva-file-item", children: [
3438
3631
  pf.preview ? /* @__PURE__ */ jsx23("img", { src: pf.preview, alt: pf.file.name, className: "apteva-file-thumb" }) : /* @__PURE__ */ jsx23("div", { className: "apteva-file-icon", children: getFileIcon(pf.file.type) }),
3439
3632
  /* @__PURE__ */ jsxs17("div", { className: "apteva-file-info", children: [
3440
3633
  /* @__PURE__ */ jsx23("span", { className: "apteva-file-name", children: pf.file.name }),
@@ -3455,12 +3648,12 @@ function Composer({ onSendMessage, placeholder = "Type a message...", disabled =
3455
3648
  {
3456
3649
  className: "apteva-composer",
3457
3650
  style: {
3458
- gridTemplateColumns: "auto 1fr auto",
3459
- gridTemplateAreas: isMultiLine ? '"textarea textarea textarea" "plus . send"' : '"plus textarea send"',
3651
+ gridTemplateColumns: isRecording ? gridColsRecording : gridCols,
3652
+ gridTemplateAreas: isRecording ? '"plus waveform stop"' : gridAreas,
3460
3653
  alignItems: "end"
3461
3654
  },
3462
3655
  children: [
3463
- /* @__PURE__ */ jsxs17("div", { className: "relative flex-shrink-0 self-end", style: { gridArea: "plus" }, children: [
3656
+ /* @__PURE__ */ jsx23("div", { className: "relative flex-shrink-0 self-end", style: { gridArea: "plus" }, children: isRecording ? /* @__PURE__ */ jsx23("div", { className: "apteva-composer-rec-dot", title: "Recording...", children: /* @__PURE__ */ jsx23("span", {}) }) : /* @__PURE__ */ jsxs17(Fragment4, { children: [
3464
3657
  /* @__PURE__ */ jsx23(
3465
3658
  "button",
3466
3659
  {
@@ -3514,39 +3707,77 @@ function Composer({ onSendMessage, placeholder = "Type a message...", disabled =
3514
3707
  }
3515
3708
  )
3516
3709
  ] })
3517
- ] }),
3518
- /* @__PURE__ */ jsx23(
3519
- "textarea",
3520
- {
3521
- ref: textareaRef,
3522
- value: text,
3523
- onChange: handleChange,
3524
- onKeyDown: handleKeyDown,
3525
- placeholder,
3526
- disabled,
3527
- className: "apteva-composer-textarea resize-none bg-transparent border-none focus:outline-none !text-neutral-900 dark:!text-neutral-100 placeholder-neutral-400 dark:placeholder-neutral-500 py-1 disabled:opacity-50 disabled:cursor-not-allowed overflow-y-auto max-h-[200px]",
3528
- style: { gridArea: "textarea" },
3529
- rows: 1
3530
- }
3531
- ),
3532
- /* @__PURE__ */ jsx23("div", { className: "self-end", style: { gridArea: "send" }, children: isLoading && onStop ? /* @__PURE__ */ jsx23(
3533
- "button",
3534
- {
3535
- onClick: onStop,
3536
- className: "apteva-composer-stop-btn",
3537
- title: "Stop generation",
3538
- children: /* @__PURE__ */ jsx23("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx23("rect", { x: "2", y: "2", width: "10", height: "10", rx: "1", fill: "currentColor" }) })
3539
- }
3540
- ) : /* @__PURE__ */ jsx23(
3541
- "button",
3542
- {
3543
- onClick: handleSend,
3544
- disabled: !text.trim() && pendingFiles.length === 0 || disabled,
3545
- className: "apteva-composer-send-btn w-8 h-8 rounded-lg flex items-center justify-center font-bold transition-all flex-shrink-0 border border-neutral-300 dark:border-neutral-600 bg-white dark:bg-neutral-800 !text-neutral-700 dark:!text-neutral-300 hover:bg-neutral-50 dark:hover:bg-neutral-700 disabled:opacity-30 disabled:cursor-not-allowed !text-lg",
3546
- title: "Send message",
3547
- children: /* @__PURE__ */ jsx23("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx23("path", { d: "M8 3L8 13M8 3L4 7M8 3L12 7", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
3548
- }
3549
- ) })
3710
+ ] }) }),
3711
+ isRecording ? /* @__PURE__ */ jsxs17(Fragment4, { children: [
3712
+ /* @__PURE__ */ jsxs17("div", { className: "apteva-composer-waveform", style: { gridArea: "waveform" }, children: [
3713
+ /* @__PURE__ */ jsx23(
3714
+ "canvas",
3715
+ {
3716
+ ref: canvasRef,
3717
+ width: 300,
3718
+ height: 36,
3719
+ className: "apteva-composer-waveform-canvas"
3720
+ }
3721
+ ),
3722
+ /* @__PURE__ */ jsx23("span", { className: "apteva-composer-recording-timer", children: formatTime(recordingTime) })
3723
+ ] }),
3724
+ /* @__PURE__ */ jsx23("div", { className: "self-end", style: { gridArea: "stop" }, children: /* @__PURE__ */ jsx23(
3725
+ "button",
3726
+ {
3727
+ onClick: () => stopRecording(false),
3728
+ className: "apteva-composer-stop-btn",
3729
+ title: "Stop recording",
3730
+ children: /* @__PURE__ */ jsx23("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx23("rect", { x: "2", y: "2", width: "10", height: "10", rx: "1", fill: "currentColor" }) })
3731
+ }
3732
+ ) })
3733
+ ] }) : /* @__PURE__ */ jsxs17(Fragment4, { children: [
3734
+ /* @__PURE__ */ jsx23(
3735
+ "textarea",
3736
+ {
3737
+ ref: textareaRef,
3738
+ value: text,
3739
+ onChange: handleChange,
3740
+ onKeyDown: handleKeyDown,
3741
+ placeholder,
3742
+ disabled,
3743
+ className: "apteva-composer-textarea resize-none bg-transparent border-none focus:outline-none !text-neutral-900 dark:!text-neutral-100 placeholder-neutral-400 dark:placeholder-neutral-500 py-1 disabled:opacity-50 disabled:cursor-not-allowed overflow-y-auto max-h-[200px]",
3744
+ style: { gridArea: "textarea" },
3745
+ rows: 1
3746
+ }
3747
+ ),
3748
+ sttSupported && /* @__PURE__ */ jsx23("div", { className: "self-end", style: { gridArea: "mic" }, children: /* @__PURE__ */ jsx23(
3749
+ "button",
3750
+ {
3751
+ onClick: startRecording,
3752
+ disabled: disabled || isLoading,
3753
+ className: "apteva-composer-mic-btn",
3754
+ title: "Voice input",
3755
+ children: /* @__PURE__ */ jsxs17("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
3756
+ /* @__PURE__ */ jsx23("path", { d: "M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z", fill: "currentColor" }),
3757
+ /* @__PURE__ */ jsx23("path", { d: "M19 10v2a7 7 0 01-14 0v-2", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }),
3758
+ /* @__PURE__ */ jsx23("path", { d: "M12 19v4M8 23h8", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
3759
+ ] })
3760
+ }
3761
+ ) }),
3762
+ /* @__PURE__ */ jsx23("div", { className: "self-end", style: { gridArea: "send" }, children: isLoading && onStop ? /* @__PURE__ */ jsx23(
3763
+ "button",
3764
+ {
3765
+ onClick: onStop,
3766
+ className: "apteva-composer-stop-btn",
3767
+ title: "Stop generation",
3768
+ children: /* @__PURE__ */ jsx23("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx23("rect", { x: "2", y: "2", width: "10", height: "10", rx: "1", fill: "currentColor" }) })
3769
+ }
3770
+ ) : /* @__PURE__ */ jsx23(
3771
+ "button",
3772
+ {
3773
+ onClick: handleSend,
3774
+ disabled: !text.trim() && pendingFiles.length === 0 || disabled,
3775
+ className: "apteva-composer-send-btn w-8 h-8 rounded-lg flex items-center justify-center font-bold transition-all flex-shrink-0 border border-neutral-300 dark:border-neutral-600 bg-white dark:bg-neutral-800 !text-neutral-700 dark:!text-neutral-300 hover:bg-neutral-50 dark:hover:bg-neutral-700 disabled:opacity-30 disabled:cursor-not-allowed !text-lg",
3776
+ title: "Send message",
3777
+ children: /* @__PURE__ */ jsx23("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ jsx23("path", { d: "M8 3L8 13M8 3L4 7M8 3L12 7", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
3778
+ }
3779
+ ) })
3780
+ ] })
3550
3781
  ]
3551
3782
  }
3552
3783
  ),
@@ -4145,6 +4376,8 @@ var Chat = forwardRef(function Chat2({
4145
4376
  availableWidgets,
4146
4377
  compactWidgetContext = false,
4147
4378
  onWidgetRender,
4379
+ // Speech to text
4380
+ speechToText,
4148
4381
  className
4149
4382
  }, ref) {
4150
4383
  const [messages, setMessages] = useState9(initialMessages);
@@ -4166,7 +4399,7 @@ var Chat = forwardRef(function Chat2({
4166
4399
  const [showSettingsMenu, setShowSettingsMenu] = useState9(false);
4167
4400
  const fileInputRef = useRef9(null);
4168
4401
  const [persistentWidgets, setPersistentWidgets] = useState9(/* @__PURE__ */ new Map());
4169
- const updatePersistentWidgets = useCallback3((msgs) => {
4402
+ const updatePersistentWidgets = useCallback4((msgs) => {
4170
4403
  setPersistentWidgets((prev) => {
4171
4404
  const next = new Map(prev);
4172
4405
  let changed = false;
@@ -4184,7 +4417,7 @@ var Chat = forwardRef(function Chat2({
4184
4417
  return changed ? next : prev;
4185
4418
  });
4186
4419
  }, []);
4187
- useEffect8(() => {
4420
+ useEffect9(() => {
4188
4421
  updatePersistentWidgets(messages);
4189
4422
  }, [messages, updatePersistentWidgets]);
4190
4423
  const persistentWidgetList = useMemo2(() => Array.from(persistentWidgets.values()), [persistentWidgets]);
@@ -4210,7 +4443,7 @@ var Chat = forwardRef(function Chat2({
4210
4443
  return context ? `${context}
4211
4444
  ${widgetContext}` : widgetContext;
4212
4445
  }, [context, enableWidgets, availableWidgets, compactWidgetContext]);
4213
- useEffect8(() => {
4446
+ useEffect9(() => {
4214
4447
  if (apiUrl || apiKey) {
4215
4448
  aptevaClient.configure({
4216
4449
  ...apiUrl && { apiUrl },
@@ -4218,15 +4451,15 @@ ${widgetContext}` : widgetContext;
4218
4451
  });
4219
4452
  }
4220
4453
  }, [apiUrl, apiKey]);
4221
- useEffect8(() => {
4454
+ useEffect9(() => {
4222
4455
  if (threadId) {
4223
4456
  onThreadChange?.(threadId);
4224
4457
  }
4225
4458
  }, [threadId, onThreadChange]);
4226
- useEffect8(() => {
4459
+ useEffect9(() => {
4227
4460
  setInternalPlanMode(planMode);
4228
4461
  }, [planMode]);
4229
- useEffect8(() => {
4462
+ useEffect9(() => {
4230
4463
  const handleClickOutside = (event) => {
4231
4464
  const target = event.target;
4232
4465
  if (showSettingsMenu && !target.closest(".settings-menu-container")) {
@@ -4246,7 +4479,7 @@ ${widgetContext}` : widgetContext;
4246
4479
  }
4247
4480
  };
4248
4481
  const defaultPlaceholder = mode === "chat" ? "Type a message..." : "Enter your command...";
4249
- const handleWidgetAction = useCallback3((action) => {
4482
+ const handleWidgetAction = useCallback4((action) => {
4250
4483
  onAction?.(action);
4251
4484
  if (action.type === "submit" && action.payload?.formData) {
4252
4485
  const formData = action.payload.formData;
@@ -4800,7 +5033,8 @@ ${planToExecute}`;
4800
5033
  isLoading,
4801
5034
  onStop: handleStop,
4802
5035
  onFileUpload,
4803
- onSwitchMode: showModeToggle ? () => handleModeChange("command") : void 0
5036
+ onSwitchMode: showModeToggle ? () => handleModeChange("command") : void 0,
5037
+ speechToText
4804
5038
  }
4805
5039
  )
4806
5040
  ] }),
@@ -4847,7 +5081,7 @@ import { useState as useState10 } from "react";
4847
5081
  import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
4848
5082
 
4849
5083
  // src/components/Command/Command.tsx
4850
- import React, { useState as useState11, useEffect as useEffect9 } from "react";
5084
+ import React, { useState as useState11, useEffect as useEffect10 } from "react";
4851
5085
  import { Fragment as Fragment7, jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
4852
5086
  function Command({
4853
5087
  agentId,
@@ -4888,15 +5122,15 @@ function Command({
4888
5122
  const [showSettingsMenu, setShowSettingsMenu] = useState11(false);
4889
5123
  const [internalPlanMode, setInternalPlanMode] = useState11(planMode);
4890
5124
  const fileInputRef = React.useRef(null);
4891
- useEffect9(() => {
5125
+ useEffect10(() => {
4892
5126
  if (autoExecute && state === "idle" && command) {
4893
5127
  executeCommand();
4894
5128
  }
4895
5129
  }, [autoExecute]);
4896
- useEffect9(() => {
5130
+ useEffect10(() => {
4897
5131
  setInternalPlanMode(planMode);
4898
5132
  }, [planMode]);
4899
- useEffect9(() => {
5133
+ useEffect10(() => {
4900
5134
  const handleClickOutside = (event) => {
4901
5135
  const target = event.target;
4902
5136
  if (showSettingsMenu && !target.closest(".settings-menu-container")) {
@@ -5929,7 +6163,7 @@ function Prompt({
5929
6163
  }
5930
6164
 
5931
6165
  // src/components/Stream/Stream.tsx
5932
- import { useState as useState13, useEffect as useEffect10 } from "react";
6166
+ import { useState as useState13, useEffect as useEffect11 } from "react";
5933
6167
  import { jsx as jsx30, jsxs as jsxs24 } from "react/jsx-runtime";
5934
6168
  function Stream({
5935
6169
  agentId,
@@ -5949,7 +6183,7 @@ function Stream({
5949
6183
  const [text, setText] = useState13("");
5950
6184
  const [isStreaming, setIsStreaming] = useState13(false);
5951
6185
  const [isComplete, setIsComplete] = useState13(false);
5952
- useEffect10(() => {
6186
+ useEffect11(() => {
5953
6187
  if (autoStart && !isStreaming && !isComplete) {
5954
6188
  startStreaming();
5955
6189
  }
@@ -6220,7 +6454,7 @@ function Threads({
6220
6454
  }
6221
6455
 
6222
6456
  // src/components/AutoInterface/AutoInterface.tsx
6223
- import { useState as useState16, useRef as useRef10, useCallback as useCallback4, useEffect as useEffect11 } from "react";
6457
+ import { useState as useState16, useRef as useRef10, useCallback as useCallback5, useEffect as useEffect12 } from "react";
6224
6458
 
6225
6459
  // src/components/AutoInterface/LayoutRenderer.tsx
6226
6460
  import { useState as useState15 } from "react";
@@ -6498,11 +6732,11 @@ function AutoInterface({
6498
6732
  generateInterfaceContext(),
6499
6733
  context || ""
6500
6734
  ].filter(Boolean).join("\n\n");
6501
- const updateInterface = useCallback4((newSpec) => {
6735
+ const updateInterface = useCallback5((newSpec) => {
6502
6736
  setInterfaceSpec(newSpec);
6503
6737
  onInterfaceChange?.(newSpec);
6504
6738
  }, [onInterfaceChange]);
6505
- const handleAction = useCallback4((action) => {
6739
+ const handleAction = useCallback5((action) => {
6506
6740
  onAction?.(action);
6507
6741
  if (chatRef.current) {
6508
6742
  chatRef.current.sendMessage(
@@ -6510,7 +6744,7 @@ function AutoInterface({
6510
6744
  );
6511
6745
  }
6512
6746
  }, [onAction]);
6513
- const handleMessageComplete = useCallback4((result) => {
6747
+ const handleMessageComplete = useCallback5((result) => {
6514
6748
  if (!result?.data) return;
6515
6749
  const text = typeof result.data === "string" ? result.data : result.data.message || "";
6516
6750
  console.log("[AutoInterface] Chat message complete, text (" + text.length + " chars):", text.substring(0, 300));
@@ -6531,7 +6765,7 @@ function AutoInterface({
6531
6765
  }
6532
6766
  setIsGenerating(false);
6533
6767
  }, [interfaceSpec, updateInterface]);
6534
- useEffect11(() => {
6768
+ useEffect12(() => {
6535
6769
  if (!initialPrompt || initialInterface || useMock) return;
6536
6770
  if (!apiUrl) return;
6537
6771
  let cancelled = false;
@@ -6656,29 +6890,29 @@ function getThemeScript() {
6656
6890
  }
6657
6891
 
6658
6892
  // src/hooks/useInterfaceState.ts
6659
- import { useState as useState17, useCallback as useCallback5 } from "react";
6893
+ import { useState as useState17, useCallback as useCallback6 } from "react";
6660
6894
  function useInterfaceState(initialSpec) {
6661
6895
  const [spec, setSpec] = useState17(initialSpec || null);
6662
6896
  const [isStreaming, setIsStreaming] = useState17(false);
6663
- const setInterface = useCallback5((newSpec) => {
6897
+ const setInterface = useCallback6((newSpec) => {
6664
6898
  setSpec(newSpec);
6665
6899
  }, []);
6666
- const clearInterface = useCallback5(() => {
6900
+ const clearInterface = useCallback6(() => {
6667
6901
  setSpec(null);
6668
6902
  }, []);
6669
- const applyInterfaceUpdate = useCallback5((update) => {
6903
+ const applyInterfaceUpdate = useCallback6((update) => {
6670
6904
  setSpec((prev) => {
6671
6905
  if (!prev) return prev;
6672
6906
  return applyUpdate(prev, update);
6673
6907
  });
6674
6908
  }, []);
6675
- const applyInterfaceUpdates = useCallback5((updates) => {
6909
+ const applyInterfaceUpdates = useCallback6((updates) => {
6676
6910
  setSpec((prev) => {
6677
6911
  if (!prev) return prev;
6678
6912
  return applyUpdates(prev, updates);
6679
6913
  });
6680
6914
  }, []);
6681
- const getNode = useCallback5((id) => {
6915
+ const getNode = useCallback6((id) => {
6682
6916
  if (!spec) return null;
6683
6917
  return findNode(spec.root, id);
6684
6918
  }, [spec]);
@@ -6695,7 +6929,7 @@ function useInterfaceState(initialSpec) {
6695
6929
  }
6696
6930
 
6697
6931
  // src/hooks/useInterfaceAI.ts
6698
- import { useCallback as useCallback6, useRef as useRef11 } from "react";
6932
+ import { useCallback as useCallback7, useRef as useRef11 } from "react";
6699
6933
  function useInterfaceAI({
6700
6934
  agentId,
6701
6935
  apiUrl,
@@ -6715,7 +6949,7 @@ function useInterfaceAI({
6715
6949
  ...apiKey && { apiKey }
6716
6950
  });
6717
6951
  }
6718
- const sendMessage = useCallback6(async (message) => {
6952
+ const sendMessage = useCallback7(async (message) => {
6719
6953
  accumulatedTextRef.current = "";
6720
6954
  onStreamStart?.();
6721
6955
  const systemPrompt = [