@copilotkitnext/react 1.51.2 → 1.51.3-next.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/index.js CHANGED
@@ -104,7 +104,8 @@ var CopilotChatDefaultLabels = {
104
104
  chatDisclaimerText: "AI can make mistakes. Please verify important information.",
105
105
  chatToggleOpenLabel: "Open chat",
106
106
  chatToggleCloseLabel: "Close chat",
107
- modalHeaderTitle: "CopilotKit Chat"
107
+ modalHeaderTitle: "CopilotKit Chat",
108
+ welcomeMessageText: "How can I help you today?"
108
109
  };
109
110
  var CopilotChatConfiguration = (0, import_react.createContext)(null);
110
111
  var CopilotChatConfigurationProvider = ({ children, labels, agentId, threadId, isModalDefaultOpen }) => {
@@ -457,28 +458,126 @@ var AudioRecorderError = class extends Error {
457
458
  var CopilotChatAudioRecorder = (0, import_react2.forwardRef)((props, ref) => {
458
459
  const { className, ...divProps } = props;
459
460
  const canvasRef = (0, import_react2.useRef)(null);
460
- const getLoudness = (n) => {
461
- const elapsed = Date.now() / 1e3;
462
- const samples = [];
463
- for (let i = 0; i < n; i++) {
464
- const position = i / n * 10 + elapsed * 0.5;
465
- const wave1 = Math.sin(position * 2) * 0.3;
466
- const wave2 = Math.sin(position * 5 + elapsed) * 0.2;
467
- const wave3 = Math.sin(position * 0.5 + elapsed * 0.3) * 0.4;
468
- const noise = (Math.random() - 0.5) * 0.1;
469
- const envelope = Math.sin(elapsed * 0.7) * 0.5 + 0.5;
470
- let amplitude = (wave1 + wave2 + wave3 + noise) * envelope;
471
- amplitude = Math.max(0, Math.min(1, amplitude * 0.5 + 0.3));
472
- samples.push(amplitude);
473
- }
474
- return samples;
461
+ const [recorderState, setRecorderState] = (0, import_react2.useState)("idle");
462
+ const mediaRecorderRef = (0, import_react2.useRef)(null);
463
+ const audioChunksRef = (0, import_react2.useRef)([]);
464
+ const streamRef = (0, import_react2.useRef)(null);
465
+ const analyserRef = (0, import_react2.useRef)(null);
466
+ const audioContextRef = (0, import_react2.useRef)(null);
467
+ const animationIdRef = (0, import_react2.useRef)(null);
468
+ const amplitudeHistoryRef = (0, import_react2.useRef)([]);
469
+ const frameCountRef = (0, import_react2.useRef)(0);
470
+ const scrollOffsetRef = (0, import_react2.useRef)(0);
471
+ const smoothedAmplitudeRef = (0, import_react2.useRef)(0);
472
+ const fadeOpacityRef = (0, import_react2.useRef)(0);
473
+ const cleanup = (0, import_react2.useCallback)(() => {
474
+ if (animationIdRef.current) {
475
+ cancelAnimationFrame(animationIdRef.current);
476
+ animationIdRef.current = null;
477
+ }
478
+ if (mediaRecorderRef.current && mediaRecorderRef.current.state !== "inactive") {
479
+ try {
480
+ mediaRecorderRef.current.stop();
481
+ } catch {
482
+ }
483
+ }
484
+ if (streamRef.current) {
485
+ streamRef.current.getTracks().forEach((track) => track.stop());
486
+ streamRef.current = null;
487
+ }
488
+ if (audioContextRef.current && audioContextRef.current.state !== "closed") {
489
+ audioContextRef.current.close().catch(() => {
490
+ });
491
+ audioContextRef.current = null;
492
+ }
493
+ mediaRecorderRef.current = null;
494
+ analyserRef.current = null;
495
+ audioChunksRef.current = [];
496
+ amplitudeHistoryRef.current = [];
497
+ frameCountRef.current = 0;
498
+ scrollOffsetRef.current = 0;
499
+ smoothedAmplitudeRef.current = 0;
500
+ fadeOpacityRef.current = 0;
501
+ }, []);
502
+ const start = (0, import_react2.useCallback)(async () => {
503
+ if (recorderState !== "idle") {
504
+ throw new AudioRecorderError("Recorder is already active");
505
+ }
506
+ try {
507
+ const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
508
+ streamRef.current = stream;
509
+ const audioContext = new AudioContext();
510
+ audioContextRef.current = audioContext;
511
+ const source = audioContext.createMediaStreamSource(stream);
512
+ const analyser = audioContext.createAnalyser();
513
+ analyser.fftSize = 2048;
514
+ source.connect(analyser);
515
+ analyserRef.current = analyser;
516
+ const mimeType = MediaRecorder.isTypeSupported("audio/webm;codecs=opus") ? "audio/webm;codecs=opus" : MediaRecorder.isTypeSupported("audio/webm") ? "audio/webm" : MediaRecorder.isTypeSupported("audio/mp4") ? "audio/mp4" : "";
517
+ const options = mimeType ? { mimeType } : {};
518
+ const mediaRecorder = new MediaRecorder(stream, options);
519
+ mediaRecorderRef.current = mediaRecorder;
520
+ audioChunksRef.current = [];
521
+ mediaRecorder.ondataavailable = (event) => {
522
+ if (event.data.size > 0) {
523
+ audioChunksRef.current.push(event.data);
524
+ }
525
+ };
526
+ mediaRecorder.start(100);
527
+ setRecorderState("recording");
528
+ } catch (error) {
529
+ cleanup();
530
+ if (error instanceof Error && error.name === "NotAllowedError") {
531
+ throw new AudioRecorderError("Microphone permission denied");
532
+ }
533
+ if (error instanceof Error && error.name === "NotFoundError") {
534
+ throw new AudioRecorderError("No microphone found");
535
+ }
536
+ throw new AudioRecorderError(
537
+ error instanceof Error ? error.message : "Failed to start recording"
538
+ );
539
+ }
540
+ }, [recorderState, cleanup]);
541
+ const stop = (0, import_react2.useCallback)(() => {
542
+ return new Promise((resolve, reject) => {
543
+ const mediaRecorder = mediaRecorderRef.current;
544
+ if (!mediaRecorder || recorderState !== "recording") {
545
+ reject(new AudioRecorderError("No active recording"));
546
+ return;
547
+ }
548
+ setRecorderState("processing");
549
+ mediaRecorder.onstop = () => {
550
+ const mimeType = mediaRecorder.mimeType || "audio/webm";
551
+ const audioBlob = new Blob(audioChunksRef.current, { type: mimeType });
552
+ cleanup();
553
+ setRecorderState("idle");
554
+ resolve(audioBlob);
555
+ };
556
+ mediaRecorder.onerror = () => {
557
+ cleanup();
558
+ setRecorderState("idle");
559
+ reject(new AudioRecorderError("Recording failed"));
560
+ };
561
+ mediaRecorder.stop();
562
+ });
563
+ }, [recorderState, cleanup]);
564
+ const calculateAmplitude = (dataArray) => {
565
+ let sum = 0;
566
+ for (let i = 0; i < dataArray.length; i++) {
567
+ const sample = (dataArray[i] ?? 128) / 128 - 1;
568
+ sum += sample * sample;
569
+ }
570
+ return Math.sqrt(sum / dataArray.length);
475
571
  };
476
572
  (0, import_react2.useEffect)(() => {
477
573
  const canvas = canvasRef.current;
478
574
  if (!canvas) return;
479
575
  const ctx = canvas.getContext("2d");
480
576
  if (!ctx) return;
481
- let animationId;
577
+ const barWidth = 2;
578
+ const barGap = 1;
579
+ const barSpacing = barWidth + barGap;
580
+ const scrollSpeed = 1 / 3;
482
581
  const draw = () => {
483
582
  const rect = canvas.getBoundingClientRect();
484
583
  const dpr = window.devicePixelRatio || 1;
@@ -486,64 +585,87 @@ var CopilotChatAudioRecorder = (0, import_react2.forwardRef)((props, ref) => {
486
585
  canvas.width = rect.width * dpr;
487
586
  canvas.height = rect.height * dpr;
488
587
  ctx.scale(dpr, dpr);
489
- ctx.imageSmoothingEnabled = false;
490
588
  }
491
- const barWidth = 2;
492
- const minHeight = 2;
493
- const maxHeight = 20;
494
- const gap = 2;
495
- const numSamples = Math.ceil(rect.width / (barWidth + gap));
496
- const loudnessData = getLoudness(numSamples);
589
+ const maxBars = Math.floor(rect.width / barSpacing) + 2;
590
+ if (analyserRef.current && recorderState === "recording") {
591
+ if (amplitudeHistoryRef.current.length === 0) {
592
+ amplitudeHistoryRef.current = new Array(maxBars).fill(0);
593
+ }
594
+ if (fadeOpacityRef.current < 1) {
595
+ fadeOpacityRef.current = Math.min(1, fadeOpacityRef.current + 0.03);
596
+ }
597
+ scrollOffsetRef.current += scrollSpeed;
598
+ const bufferLength = analyserRef.current.fftSize;
599
+ const dataArray = new Uint8Array(bufferLength);
600
+ analyserRef.current.getByteTimeDomainData(dataArray);
601
+ const rawAmplitude = calculateAmplitude(dataArray);
602
+ const attackSpeed = 0.12;
603
+ const decaySpeed = 0.08;
604
+ const speed = rawAmplitude > smoothedAmplitudeRef.current ? attackSpeed : decaySpeed;
605
+ smoothedAmplitudeRef.current += (rawAmplitude - smoothedAmplitudeRef.current) * speed;
606
+ if (scrollOffsetRef.current >= barSpacing) {
607
+ scrollOffsetRef.current -= barSpacing;
608
+ amplitudeHistoryRef.current.push(smoothedAmplitudeRef.current);
609
+ if (amplitudeHistoryRef.current.length > maxBars) {
610
+ amplitudeHistoryRef.current = amplitudeHistoryRef.current.slice(-maxBars);
611
+ }
612
+ }
613
+ }
497
614
  ctx.clearRect(0, 0, rect.width, rect.height);
498
615
  const computedStyle = getComputedStyle(canvas);
499
- const currentForeground = computedStyle.color;
500
- ctx.fillStyle = currentForeground;
616
+ ctx.fillStyle = computedStyle.color;
617
+ ctx.globalAlpha = fadeOpacityRef.current;
501
618
  const centerY = rect.height / 2;
502
- for (let i = 0; i < loudnessData.length; i++) {
503
- const sample = loudnessData[i] ?? 0;
504
- const barHeight = Math.round(
505
- sample * (maxHeight - minHeight) + minHeight
506
- );
507
- const x = Math.round(i * (barWidth + gap));
508
- const y = Math.round(centerY - barHeight / 2);
509
- ctx.fillRect(x, y, barWidth, barHeight);
619
+ const maxAmplitude = rect.height / 2 - 2;
620
+ const history = amplitudeHistoryRef.current;
621
+ if (history.length > 0) {
622
+ const offset = scrollOffsetRef.current;
623
+ const edgeFadeWidth = 12;
624
+ for (let i = 0; i < history.length; i++) {
625
+ const amplitude = history[i] ?? 0;
626
+ const scaledAmplitude = Math.min(amplitude * 4, 1);
627
+ const barHeight = Math.max(2, scaledAmplitude * maxAmplitude * 2);
628
+ const x = rect.width - (history.length - i) * barSpacing - offset;
629
+ const y = centerY - barHeight / 2;
630
+ if (x + barWidth > 0 && x < rect.width) {
631
+ let edgeOpacity = 1;
632
+ if (x < edgeFadeWidth) {
633
+ edgeOpacity = Math.max(0, x / edgeFadeWidth);
634
+ } else if (x > rect.width - edgeFadeWidth) {
635
+ edgeOpacity = Math.max(0, (rect.width - x) / edgeFadeWidth);
636
+ }
637
+ ctx.globalAlpha = fadeOpacityRef.current * edgeOpacity;
638
+ ctx.fillRect(x, y, barWidth, barHeight);
639
+ }
640
+ }
510
641
  }
511
- animationId = requestAnimationFrame(draw);
642
+ animationIdRef.current = requestAnimationFrame(draw);
512
643
  };
513
644
  draw();
514
645
  return () => {
515
- if (animationId) {
516
- cancelAnimationFrame(animationId);
646
+ if (animationIdRef.current) {
647
+ cancelAnimationFrame(animationIdRef.current);
517
648
  }
518
649
  };
519
- }, []);
650
+ }, [recorderState]);
651
+ (0, import_react2.useEffect)(() => {
652
+ return cleanup;
653
+ }, [cleanup]);
520
654
  (0, import_react2.useImperativeHandle)(
521
655
  ref,
522
656
  () => ({
523
657
  get state() {
524
- return "idle";
525
- },
526
- start: async () => {
658
+ return recorderState;
527
659
  },
528
- stop: () => new Promise((resolve) => {
529
- const emptyBlob = new Blob([], { type: "audio/webm" });
530
- resolve(emptyBlob);
531
- }),
532
- dispose: () => {
533
- }
660
+ start,
661
+ stop,
662
+ dispose: cleanup
534
663
  }),
535
- []
664
+ [recorderState, start, stop, cleanup]
536
665
  );
537
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: (0, import_tailwind_merge2.twMerge)("h-[44px] w-full px-5", className), ...divProps, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
538
- "canvas",
539
- {
540
- ref: canvasRef,
541
- className: "w-full h-full",
542
- style: { imageRendering: "pixelated" }
543
- }
544
- ) });
666
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: (0, import_tailwind_merge2.twMerge)("w-full py-3 px-5", className), ...divProps, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("canvas", { ref: canvasRef, className: "block w-full h-[26px]" }) });
545
667
  });
546
- CopilotChatAudioRecorder.displayName = "WebAudioRecorder";
668
+ CopilotChatAudioRecorder.displayName = "CopilotChatAudioRecorder";
547
669
 
548
670
  // src/lib/slots.tsx
549
671
  var import_react3 = __toESM(require("react"));
@@ -611,6 +733,7 @@ function CopilotChatInput({
611
733
  onStartTranscribe,
612
734
  onCancelTranscribe,
613
735
  onFinishTranscribe,
736
+ onFinishTranscribeWithAudio,
614
737
  onAddFile,
615
738
  onChange,
616
739
  value,
@@ -915,8 +1038,22 @@ function CopilotChatInput({
915
1038
  const BoundCancelTranscribeButton = renderSlot(cancelTranscribeButton, CopilotChatInput.CancelTranscribeButton, {
916
1039
  onClick: onCancelTranscribe
917
1040
  });
1041
+ const handleFinishTranscribe = (0, import_react4.useCallback)(async () => {
1042
+ const recorder = audioRecorderRef.current;
1043
+ if (recorder && recorder.state === "recording") {
1044
+ try {
1045
+ const audioBlob = await recorder.stop();
1046
+ if (onFinishTranscribeWithAudio) {
1047
+ await onFinishTranscribeWithAudio(audioBlob);
1048
+ }
1049
+ } catch (error) {
1050
+ console.error("Failed to stop recording:", error);
1051
+ }
1052
+ }
1053
+ onFinishTranscribe?.();
1054
+ }, [onFinishTranscribe, onFinishTranscribeWithAudio]);
918
1055
  const BoundFinishTranscribeButton = renderSlot(finishTranscribeButton, CopilotChatInput.FinishTranscribeButton, {
919
- onClick: onFinishTranscribe
1056
+ onClick: handleFinishTranscribe
920
1057
  });
921
1058
  const BoundAddMenuButton = renderSlot(addMenuButton, CopilotChatInput.AddMenuButton, {
922
1059
  disabled: mode === "transcribe",
@@ -1216,10 +1353,10 @@ function CopilotChatInput({
1216
1353
  "div",
1217
1354
  {
1218
1355
  className: (0, import_tailwind_merge3.twMerge)(
1219
- "relative flex min-w-0 flex-col",
1356
+ "relative flex min-w-0 flex-col min-h-[50px] justify-center",
1220
1357
  isExpanded ? "col-span-3 row-start-1" : "col-start-2 row-start-1"
1221
1358
  ),
1222
- children: mode === "transcribe" ? BoundAudioRecorder : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1359
+ children: mode === "transcribe" ? BoundAudioRecorder : mode === "processing" ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex w-full items-center justify-center py-3 px-5", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react2.Loader2, { className: "size-[26px] animate-spin text-muted-foreground" }) }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1223
1360
  BoundTextArea,
1224
1361
  slashMenu
1225
1362
  ] })
@@ -2021,6 +2158,7 @@ var CopilotKitProvider = ({
2021
2158
  children,
2022
2159
  runtimeUrl,
2023
2160
  headers = {},
2161
+ credentials,
2024
2162
  publicApiKey,
2025
2163
  publicLicenseKey,
2026
2164
  properties = {},
@@ -2166,6 +2304,7 @@ var CopilotKitProvider = ({
2166
2304
  runtimeUrl: chatApiEndpoint,
2167
2305
  runtimeTransport: useSingleEndpoint ? "single" : "rest",
2168
2306
  headers: mergedHeaders,
2307
+ credentials,
2169
2308
  properties,
2170
2309
  agents__unsafe_dev_only: agents,
2171
2310
  tools: allTools,
@@ -2214,9 +2353,10 @@ var CopilotKitProvider = ({
2214
2353
  copilotkit.setRuntimeUrl(chatApiEndpoint);
2215
2354
  copilotkit.setRuntimeTransport(useSingleEndpoint ? "single" : "rest");
2216
2355
  copilotkit.setHeaders(mergedHeaders);
2356
+ copilotkit.setCredentials(credentials);
2217
2357
  copilotkit.setProperties(properties);
2218
2358
  copilotkit.setAgents__unsafe_dev_only(agents);
2219
- }, [chatApiEndpoint, mergedHeaders, properties, agents, useSingleEndpoint]);
2359
+ }, [chatApiEndpoint, mergedHeaders, credentials, properties, agents, useSingleEndpoint]);
2220
2360
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2221
2361
  CopilotKitContext.Provider,
2222
2362
  {
@@ -3767,6 +3907,7 @@ function CopilotChatView({
3767
3907
  inputContainer,
3768
3908
  disclaimer,
3769
3909
  suggestionView,
3910
+ welcomeScreen,
3770
3911
  messages = [],
3771
3912
  autoScroll = true,
3772
3913
  inputProps,
@@ -3846,6 +3987,21 @@ function CopilotChatView({
3846
3987
  BoundDisclaimer
3847
3988
  ] })
3848
3989
  });
3990
+ const isEmpty = messages.length === 0;
3991
+ const welcomeScreenDisabled = welcomeScreen === false;
3992
+ const shouldShowWelcomeScreen = isEmpty && !welcomeScreenDisabled;
3993
+ if (shouldShowWelcomeScreen) {
3994
+ const welcomeScreenSlot = welcomeScreen === true ? void 0 : welcomeScreen;
3995
+ const BoundWelcomeScreen = renderSlot(
3996
+ welcomeScreenSlot,
3997
+ CopilotChatView.WelcomeScreen,
3998
+ {
3999
+ input: BoundInput,
4000
+ suggestionView: BoundSuggestionView ?? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_jsx_runtime19.Fragment, {})
4001
+ }
4002
+ );
4003
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: (0, import_tailwind_merge7.twMerge)("relative h-full", className), ...props, children: BoundWelcomeScreen });
4004
+ }
3849
4005
  if (children) {
3850
4006
  return children({
3851
4007
  messageView: BoundMessageView,
@@ -4025,25 +4181,188 @@ function CopilotChatView({
4025
4181
  }
4026
4182
  );
4027
4183
  };
4184
+ CopilotChatView2.WelcomeMessage = ({
4185
+ className,
4186
+ ...props
4187
+ }) => {
4188
+ const config = useCopilotChatConfiguration();
4189
+ const labels = config?.labels ?? CopilotChatDefaultLabels;
4190
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4191
+ "h1",
4192
+ {
4193
+ className: cn(
4194
+ "text-xl sm:text-2xl font-medium text-foreground text-center",
4195
+ className
4196
+ ),
4197
+ ...props,
4198
+ children: labels.welcomeMessageText
4199
+ }
4200
+ );
4201
+ };
4202
+ CopilotChatView2.WelcomeScreen = ({
4203
+ welcomeMessage,
4204
+ input,
4205
+ suggestionView,
4206
+ className,
4207
+ children,
4208
+ ...props
4209
+ }) => {
4210
+ const BoundWelcomeMessage = renderSlot(
4211
+ welcomeMessage,
4212
+ CopilotChatView2.WelcomeMessage,
4213
+ {}
4214
+ );
4215
+ if (children) {
4216
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_jsx_runtime19.Fragment, { children: children({
4217
+ welcomeMessage: BoundWelcomeMessage,
4218
+ input,
4219
+ suggestionView,
4220
+ className,
4221
+ ...props
4222
+ }) });
4223
+ }
4224
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
4225
+ "div",
4226
+ {
4227
+ className: cn(
4228
+ "h-full flex flex-col items-center justify-center px-4",
4229
+ className
4230
+ ),
4231
+ ...props,
4232
+ children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "w-full max-w-3xl flex flex-col items-center", children: [
4233
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "mb-6", children: BoundWelcomeMessage }),
4234
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "w-full", children: input }),
4235
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { className: "mt-4 flex justify-center", children: suggestionView })
4236
+ ] })
4237
+ }
4238
+ );
4239
+ };
4028
4240
  })(CopilotChatView || (CopilotChatView = {}));
4029
4241
  var CopilotChatView_default = CopilotChatView;
4030
4242
 
4031
4243
  // src/components/chat/CopilotChat.tsx
4032
- var import_shared8 = require("@copilotkitnext/shared");
4244
+ var import_shared9 = require("@copilotkitnext/shared");
4033
4245
  var import_react25 = require("react");
4034
4246
  var import_ts_deepmerge = require("ts-deepmerge");
4035
4247
  var import_client = require("@ag-ui/client");
4248
+
4249
+ // src/lib/transcription-client.ts
4250
+ var import_shared8 = require("@copilotkitnext/shared");
4251
+ async function blobToBase64(blob) {
4252
+ return new Promise((resolve, reject) => {
4253
+ const reader = new FileReader();
4254
+ reader.onloadend = () => {
4255
+ const result = reader.result;
4256
+ const base64 = result.split(",")[1];
4257
+ resolve(base64 ?? "");
4258
+ };
4259
+ reader.onerror = () => reject(new Error("Failed to read audio data"));
4260
+ reader.readAsDataURL(blob);
4261
+ });
4262
+ }
4263
+ function isTranscriptionErrorResponse(data) {
4264
+ return typeof data === "object" && data !== null && "error" in data && "message" in data && typeof data.error === "string" && typeof data.message === "string";
4265
+ }
4266
+ function parseTranscriptionError(response) {
4267
+ return {
4268
+ code: response.error,
4269
+ message: response.message,
4270
+ retryable: response.retryable ?? false
4271
+ };
4272
+ }
4273
+ var TranscriptionError = class extends Error {
4274
+ info;
4275
+ constructor(info) {
4276
+ super(info.message);
4277
+ this.name = "TranscriptionError";
4278
+ this.info = info;
4279
+ }
4280
+ };
4281
+ async function transcribeAudio(core, audioBlob, filename = "recording.webm") {
4282
+ const runtimeUrl = core.runtimeUrl;
4283
+ if (!runtimeUrl) {
4284
+ throw new TranscriptionError({
4285
+ code: import_shared8.TranscriptionErrorCode.INVALID_REQUEST,
4286
+ message: "Runtime URL is not configured",
4287
+ retryable: false
4288
+ });
4289
+ }
4290
+ const headers = { ...core.headers };
4291
+ let response;
4292
+ try {
4293
+ if (core.runtimeTransport === "single") {
4294
+ const base64Audio = await blobToBase64(audioBlob);
4295
+ headers["Content-Type"] = "application/json";
4296
+ response = await fetch(runtimeUrl, {
4297
+ method: "POST",
4298
+ headers,
4299
+ body: JSON.stringify({
4300
+ method: "transcribe",
4301
+ body: {
4302
+ audio: base64Audio,
4303
+ mimeType: audioBlob.type || "audio/webm",
4304
+ filename
4305
+ }
4306
+ })
4307
+ });
4308
+ } else {
4309
+ delete headers["Content-Type"];
4310
+ const formData = new FormData();
4311
+ formData.append("audio", audioBlob, filename);
4312
+ response = await fetch(`${runtimeUrl}/transcribe`, {
4313
+ method: "POST",
4314
+ headers,
4315
+ body: formData
4316
+ });
4317
+ }
4318
+ } catch (error) {
4319
+ throw new TranscriptionError({
4320
+ code: import_shared8.TranscriptionErrorCode.NETWORK_ERROR,
4321
+ message: error instanceof Error ? error.message : "Network request failed",
4322
+ retryable: true
4323
+ });
4324
+ }
4325
+ if (!response.ok) {
4326
+ let errorData;
4327
+ try {
4328
+ errorData = await response.json();
4329
+ } catch {
4330
+ throw new TranscriptionError({
4331
+ code: import_shared8.TranscriptionErrorCode.PROVIDER_ERROR,
4332
+ message: `HTTP ${response.status}: ${response.statusText}`,
4333
+ retryable: response.status >= 500
4334
+ });
4335
+ }
4336
+ if (isTranscriptionErrorResponse(errorData)) {
4337
+ throw new TranscriptionError(parseTranscriptionError(errorData));
4338
+ }
4339
+ throw new TranscriptionError({
4340
+ code: import_shared8.TranscriptionErrorCode.PROVIDER_ERROR,
4341
+ message: typeof errorData === "object" && errorData !== null && "message" in errorData ? String(errorData.message) : "Transcription failed",
4342
+ retryable: response.status >= 500
4343
+ });
4344
+ }
4345
+ return await response.json();
4346
+ }
4347
+
4348
+ // src/components/chat/CopilotChat.tsx
4036
4349
  var import_jsx_runtime20 = require("react/jsx-runtime");
4037
4350
  function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen, ...props }) {
4038
4351
  const existingConfig = useCopilotChatConfiguration();
4039
- const resolvedAgentId = agentId ?? existingConfig?.agentId ?? import_shared8.DEFAULT_AGENT_ID;
4352
+ const resolvedAgentId = agentId ?? existingConfig?.agentId ?? import_shared9.DEFAULT_AGENT_ID;
4040
4353
  const resolvedThreadId = (0, import_react25.useMemo)(
4041
- () => threadId ?? existingConfig?.threadId ?? (0, import_shared8.randomUUID)(),
4354
+ () => threadId ?? existingConfig?.threadId ?? (0, import_shared9.randomUUID)(),
4042
4355
  [threadId, existingConfig?.threadId]
4043
4356
  );
4044
4357
  const { agent } = useAgent({ agentId: resolvedAgentId });
4045
4358
  const { copilotkit } = useCopilotKit();
4046
4359
  const { suggestions: autoSuggestions } = useSuggestions({ agentId: resolvedAgentId });
4360
+ const [transcribeMode, setTranscribeMode] = (0, import_react25.useState)("input");
4361
+ const [inputValue, setInputValue] = (0, import_react25.useState)("");
4362
+ const [transcriptionError, setTranscriptionError] = (0, import_react25.useState)(null);
4363
+ const [isTranscribing, setIsTranscribing] = (0, import_react25.useState)(false);
4364
+ const isTranscriptionEnabled = copilotkit.audioFileTranscriptionEnabled;
4365
+ const isMediaRecorderSupported = typeof window !== "undefined" && typeof MediaRecorder !== "undefined";
4047
4366
  const {
4048
4367
  inputProps: providedInputProps,
4049
4368
  messageView: providedMessageView,
@@ -4069,10 +4388,11 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4069
4388
  const onSubmitInput = (0, import_react25.useCallback)(
4070
4389
  async (value) => {
4071
4390
  agent.addMessage({
4072
- id: (0, import_shared8.randomUUID)(),
4391
+ id: (0, import_shared9.randomUUID)(),
4073
4392
  role: "user",
4074
4393
  content: value
4075
4394
  });
4395
+ setInputValue("");
4076
4396
  try {
4077
4397
  await copilotkit.runAgent({ agent });
4078
4398
  } catch (error) {
@@ -4084,7 +4404,7 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4084
4404
  const handleSelectSuggestion = (0, import_react25.useCallback)(
4085
4405
  async (suggestion) => {
4086
4406
  agent.addMessage({
4087
- id: (0, import_shared8.randomUUID)(),
4407
+ id: (0, import_shared9.randomUUID)(),
4088
4408
  role: "user",
4089
4409
  content: suggestion.message
4090
4410
  });
@@ -4108,6 +4428,75 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4108
4428
  }
4109
4429
  }
4110
4430
  }, [agent, copilotkit]);
4431
+ const handleStartTranscribe = (0, import_react25.useCallback)(() => {
4432
+ setTranscriptionError(null);
4433
+ setTranscribeMode("transcribe");
4434
+ }, []);
4435
+ const handleCancelTranscribe = (0, import_react25.useCallback)(() => {
4436
+ setTranscriptionError(null);
4437
+ setTranscribeMode("input");
4438
+ }, []);
4439
+ const handleFinishTranscribe = (0, import_react25.useCallback)(() => {
4440
+ setTranscribeMode("input");
4441
+ }, []);
4442
+ const handleFinishTranscribeWithAudio = (0, import_react25.useCallback)(async (audioBlob) => {
4443
+ setIsTranscribing(true);
4444
+ try {
4445
+ setTranscriptionError(null);
4446
+ const result = await transcribeAudio(copilotkit, audioBlob);
4447
+ setInputValue((prev) => {
4448
+ const trimmedPrev = prev.trim();
4449
+ if (trimmedPrev) {
4450
+ return `${trimmedPrev} ${result.text}`;
4451
+ }
4452
+ return result.text;
4453
+ });
4454
+ } catch (error) {
4455
+ console.error("CopilotChat: Transcription failed", error);
4456
+ if (error instanceof TranscriptionError) {
4457
+ const { code, retryable, message } = error.info;
4458
+ switch (code) {
4459
+ case import_shared9.TranscriptionErrorCode.RATE_LIMITED:
4460
+ setTranscriptionError("Too many requests. Please wait a moment.");
4461
+ break;
4462
+ case import_shared9.TranscriptionErrorCode.AUTH_FAILED:
4463
+ setTranscriptionError("Authentication error. Please check your configuration.");
4464
+ break;
4465
+ case import_shared9.TranscriptionErrorCode.AUDIO_TOO_LONG:
4466
+ setTranscriptionError("Recording is too long. Please try a shorter recording.");
4467
+ break;
4468
+ case import_shared9.TranscriptionErrorCode.AUDIO_TOO_SHORT:
4469
+ setTranscriptionError("Recording is too short. Please try again.");
4470
+ break;
4471
+ case import_shared9.TranscriptionErrorCode.INVALID_AUDIO_FORMAT:
4472
+ setTranscriptionError("Audio format not supported.");
4473
+ break;
4474
+ case import_shared9.TranscriptionErrorCode.SERVICE_NOT_CONFIGURED:
4475
+ setTranscriptionError("Transcription service is not available.");
4476
+ break;
4477
+ case import_shared9.TranscriptionErrorCode.NETWORK_ERROR:
4478
+ setTranscriptionError("Network error. Please check your connection.");
4479
+ break;
4480
+ default:
4481
+ setTranscriptionError(
4482
+ retryable ? "Transcription failed. Please try again." : message
4483
+ );
4484
+ }
4485
+ } else {
4486
+ setTranscriptionError("Transcription failed. Please try again.");
4487
+ }
4488
+ } finally {
4489
+ setIsTranscribing(false);
4490
+ }
4491
+ }, [copilotkit]);
4492
+ (0, import_react25.useEffect)(() => {
4493
+ if (transcriptionError) {
4494
+ const timer = setTimeout(() => {
4495
+ setTranscriptionError(null);
4496
+ }, 5e3);
4497
+ return () => clearTimeout(timer);
4498
+ }
4499
+ }, [transcriptionError]);
4111
4500
  const mergedProps = (0, import_ts_deepmerge.merge)(
4112
4501
  {
4113
4502
  isRunning: agent.isRunning,
@@ -4124,27 +4513,56 @@ function CopilotChat({ agentId, threadId, labels, chatView, isModalDefaultOpen,
4124
4513
  const hasMessages = agent.messages.length > 0;
4125
4514
  const shouldAllowStop = agent.isRunning && hasMessages;
4126
4515
  const effectiveStopHandler = shouldAllowStop ? providedStopHandler ?? stopCurrentRun : providedStopHandler;
4516
+ const showTranscription = isTranscriptionEnabled && isMediaRecorderSupported;
4517
+ const effectiveMode = isTranscribing ? "processing" : transcribeMode;
4127
4518
  const finalInputProps = {
4128
4519
  ...providedInputProps,
4129
4520
  onSubmitMessage: onSubmitInput,
4130
4521
  onStop: effectiveStopHandler,
4131
- isRunning: agent.isRunning
4522
+ isRunning: agent.isRunning,
4523
+ mode: effectiveMode,
4524
+ value: inputValue,
4525
+ onChange: setInputValue,
4526
+ // Only provide transcription handlers if feature is available
4527
+ onStartTranscribe: showTranscription ? handleStartTranscribe : void 0,
4528
+ onCancelTranscribe: showTranscription ? handleCancelTranscribe : void 0,
4529
+ onFinishTranscribe: showTranscription ? handleFinishTranscribe : void 0,
4530
+ onFinishTranscribeWithAudio: showTranscription ? handleFinishTranscribeWithAudio : void 0
4132
4531
  };
4133
- finalInputProps.mode = agent.isRunning ? "processing" : finalInputProps.mode ?? "input";
4134
4532
  const messages = (0, import_react25.useMemo)(() => [...agent.messages], [JSON.stringify(agent.messages)]);
4135
4533
  const finalProps = (0, import_ts_deepmerge.merge)(mergedProps, {
4136
4534
  messages,
4137
4535
  inputProps: finalInputProps
4138
4536
  });
4139
4537
  const RenderedChatView = renderSlot(chatView, CopilotChatView, finalProps);
4140
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4538
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
4141
4539
  CopilotChatConfigurationProvider,
4142
4540
  {
4143
4541
  agentId: resolvedAgentId,
4144
4542
  threadId: resolvedThreadId,
4145
4543
  labels,
4146
4544
  isModalDefaultOpen,
4147
- children: RenderedChatView
4545
+ children: [
4546
+ transcriptionError && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
4547
+ "div",
4548
+ {
4549
+ style: {
4550
+ position: "absolute",
4551
+ bottom: "100px",
4552
+ left: "50%",
4553
+ transform: "translateX(-50%)",
4554
+ backgroundColor: "#ef4444",
4555
+ color: "white",
4556
+ padding: "8px 16px",
4557
+ borderRadius: "8px",
4558
+ fontSize: "14px",
4559
+ zIndex: 50
4560
+ },
4561
+ children: transcriptionError
4562
+ }
4563
+ ),
4564
+ RenderedChatView
4565
+ ]
4148
4566
  }
4149
4567
  );
4150
4568
  }
@@ -4451,6 +4869,45 @@ function CopilotSidebarView({ header, width, ...props }) {
4451
4869
  ] });
4452
4870
  }
4453
4871
  CopilotSidebarView.displayName = "CopilotSidebarView";
4872
+ ((CopilotSidebarView2) => {
4873
+ CopilotSidebarView2.WelcomeScreen = ({
4874
+ welcomeMessage,
4875
+ input,
4876
+ suggestionView,
4877
+ className,
4878
+ children,
4879
+ ...props
4880
+ }) => {
4881
+ const BoundWelcomeMessage = renderSlot(
4882
+ welcomeMessage,
4883
+ CopilotChatView_default.WelcomeMessage,
4884
+ {}
4885
+ );
4886
+ if (children) {
4887
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_jsx_runtime23.Fragment, { children: children({
4888
+ welcomeMessage: BoundWelcomeMessage,
4889
+ input,
4890
+ suggestionView,
4891
+ className,
4892
+ ...props
4893
+ }) });
4894
+ }
4895
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
4896
+ "div",
4897
+ {
4898
+ className: cn("h-full flex flex-col", className),
4899
+ ...props,
4900
+ children: [
4901
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "flex-1 flex flex-col items-center justify-center px-4", children: BoundWelcomeMessage }),
4902
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "px-8 pb-4", children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "max-w-3xl mx-auto", children: [
4903
+ /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("div", { className: "mb-4 flex justify-center", children: suggestionView }),
4904
+ input
4905
+ ] }) })
4906
+ ]
4907
+ }
4908
+ );
4909
+ };
4910
+ })(CopilotSidebarView || (CopilotSidebarView = {}));
4454
4911
 
4455
4912
  // src/components/chat/CopilotPopupView.tsx
4456
4913
  var import_react29 = require("react");
@@ -4610,6 +5067,46 @@ function CopilotPopupView({
4610
5067
  ] });
4611
5068
  }
4612
5069
  CopilotPopupView.displayName = "CopilotPopupView";
5070
+ ((CopilotPopupView2) => {
5071
+ CopilotPopupView2.WelcomeScreen = ({
5072
+ welcomeMessage,
5073
+ input,
5074
+ suggestionView,
5075
+ className,
5076
+ children,
5077
+ ...props
5078
+ }) => {
5079
+ const BoundWelcomeMessage = renderSlot(
5080
+ welcomeMessage,
5081
+ CopilotChatView_default.WelcomeMessage,
5082
+ {}
5083
+ );
5084
+ if (children) {
5085
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_jsx_runtime24.Fragment, { children: children({
5086
+ welcomeMessage: BoundWelcomeMessage,
5087
+ input,
5088
+ suggestionView,
5089
+ className,
5090
+ ...props
5091
+ }) });
5092
+ }
5093
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
5094
+ "div",
5095
+ {
5096
+ className: cn("h-full flex flex-col", className),
5097
+ ...props,
5098
+ children: [
5099
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "flex-1 flex flex-col items-center justify-center px-4", children: BoundWelcomeMessage }),
5100
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "px-6 pb-4", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "max-w-3xl mx-auto", children: [
5101
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { className: "mb-4 flex justify-center", children: suggestionView }),
5102
+ input
5103
+ ] }) })
5104
+ ]
5105
+ }
5106
+ );
5107
+ };
5108
+ })(CopilotPopupView || (CopilotPopupView = {}));
5109
+ var CopilotPopupView_default = CopilotPopupView;
4613
5110
 
4614
5111
  // src/components/chat/CopilotSidebar.tsx
4615
5112
  var import_react30 = require("react");
@@ -4632,6 +5129,7 @@ function CopilotSidebar({ header, defaultOpen, width, ...chatProps }) {
4632
5129
  return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
4633
5130
  CopilotChat,
4634
5131
  {
5132
+ welcomeScreen: CopilotSidebarView.WelcomeScreen,
4635
5133
  ...chatProps,
4636
5134
  chatView: SidebarViewOverride,
4637
5135
  isModalDefaultOpen: defaultOpen
@@ -4661,7 +5159,7 @@ function CopilotPopup({
4661
5159
  ...restProps
4662
5160
  } = viewProps;
4663
5161
  return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
4664
- CopilotPopupView,
5162
+ CopilotPopupView_default,
4665
5163
  {
4666
5164
  ...restProps,
4667
5165
  header: header ?? viewHeader,
@@ -4676,6 +5174,7 @@ function CopilotPopup({
4676
5174
  return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
4677
5175
  CopilotChat,
4678
5176
  {
5177
+ welcomeScreen: CopilotPopupView_default.WelcomeScreen,
4679
5178
  ...chatProps,
4680
5179
  chatView: PopupViewOverride,
4681
5180
  isModalDefaultOpen: defaultOpen