@smartspace/chat-ui 1.14.4-dev.5c029a3 → 1.14.4-dev.7129fda
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.css +1 -1
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +178 -139
- package/dist/index.js +680 -45
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import MuiButton from '@mui/material/Button';
|
|
2
|
-
import
|
|
3
|
-
import { Loader2, ChevronDown, Check, Cpu, SlidersHorizontal, X, Globe, Zap,
|
|
2
|
+
import IconButton2 from '@mui/material/IconButton';
|
|
3
|
+
import { Loader2, ChevronDown, Check, Cpu, SlidersHorizontal, X, Globe, Zap, Square, Mic, Paperclip, Send, Minimize2, AlertTriangle, FileImage, FileVideo, FileAudio, FileArchive, FileCode, FileSpreadsheet, Presentation, FileText, ChevronRight, ChevronUp, ExternalLink, Download, ShieldAlert, Copy } from 'lucide-react';
|
|
4
4
|
import * as React9 from 'react';
|
|
5
|
-
import React9__default, { createContext, forwardRef, useImperativeHandle, useRef, useState, useEffect, useMemo, useCallback, useContext, useSyncExternalStore } from 'react';
|
|
5
|
+
import React9__default, { createContext, forwardRef, useImperativeHandle, useRef, useState, useEffect, useMemo, useCallback, useContext, useSyncExternalStore, Fragment as Fragment$1 } from 'react';
|
|
6
6
|
import { createPortal } from 'react-dom';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
|
-
import { useQuery, queryOptions, useQueryClient, useMutation
|
|
8
|
+
import { useQuery, queryOptions, useQueryClient, useMutation } from '@tanstack/react-query';
|
|
9
9
|
import { toast } from 'sonner';
|
|
10
10
|
import { ChatZod } from '@smartspace/api-client';
|
|
11
11
|
import { z } from 'zod';
|
|
@@ -17,7 +17,7 @@ import { codeBlockSchema, commonmark } from '@milkdown/preset-commonmark';
|
|
|
17
17
|
import { Slice, NodeType } from '@milkdown/prose/model';
|
|
18
18
|
import { MilkdownProvider, useEditor, Milkdown } from '@milkdown/react';
|
|
19
19
|
import { PluginKey, Plugin } from '@milkdown/prose/state';
|
|
20
|
-
import {
|
|
20
|
+
import { DecorationSet, Decoration } from '@milkdown/prose/view';
|
|
21
21
|
import 'crypto';
|
|
22
22
|
import '@milkdown/prose';
|
|
23
23
|
import '@milkdown/prose/inputrules';
|
|
@@ -600,9 +600,14 @@ var useThread = ({
|
|
|
600
600
|
};
|
|
601
601
|
var useThreadIsRunning = (workspaceId, threadId) => {
|
|
602
602
|
const queryClient = useQueryClient();
|
|
603
|
+
const service = useChatService();
|
|
603
604
|
const { data: detailThread } = useQuery({
|
|
604
|
-
|
|
605
|
-
|
|
605
|
+
...threadDetailOptions({
|
|
606
|
+
service,
|
|
607
|
+
workspaceId: workspaceId ?? "",
|
|
608
|
+
threadId: threadId ?? ""
|
|
609
|
+
}),
|
|
610
|
+
enabled: false
|
|
606
611
|
});
|
|
607
612
|
const listThread = workspaceId && threadId ? getThreadPlaceholderFromListCache(queryClient, workspaceId, threadId) : void 0;
|
|
608
613
|
const { data: optimistic } = useQuery({
|
|
@@ -837,6 +842,37 @@ function useMessages(threadId) {
|
|
|
837
842
|
});
|
|
838
843
|
}
|
|
839
844
|
|
|
845
|
+
// src/domains/speech/queryKeys.ts
|
|
846
|
+
var speechKeys = {
|
|
847
|
+
all: ["speech"],
|
|
848
|
+
config: () => [...speechKeys.all, "config"]
|
|
849
|
+
};
|
|
850
|
+
|
|
851
|
+
// src/domains/speech/queries.ts
|
|
852
|
+
var unavailable = { enabled: false, defaultLocale: "en-US" };
|
|
853
|
+
var isNotFound = (error) => error?.type === "NotFound" || error?.response?.status === 404;
|
|
854
|
+
function useSpeechConfig() {
|
|
855
|
+
const service = useChatService();
|
|
856
|
+
const supported = !!service.getSpeechConfig && !!service.getSpeechToken;
|
|
857
|
+
return useQuery({
|
|
858
|
+
...queryOptions({
|
|
859
|
+
queryKey: speechKeys.config(),
|
|
860
|
+
queryFn: async () => {
|
|
861
|
+
try {
|
|
862
|
+
return await service.getSpeechConfig?.() ?? unavailable;
|
|
863
|
+
} catch (error) {
|
|
864
|
+
if (isNotFound(error)) return unavailable;
|
|
865
|
+
throw error;
|
|
866
|
+
}
|
|
867
|
+
},
|
|
868
|
+
staleTime: Infinity,
|
|
869
|
+
retry: (failureCount) => failureCount < 2,
|
|
870
|
+
retryDelay: 1e3
|
|
871
|
+
}),
|
|
872
|
+
enabled: supported
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
|
|
840
876
|
// ../../node_modules/.pnpm/@milkdown+exception@7.20.0/node_modules/@milkdown/exception/lib/index.js
|
|
841
877
|
var ErrorCode = /* @__PURE__ */ (function(ErrorCode2) {
|
|
842
878
|
ErrorCode2["docTypeError"] = "docTypeError";
|
|
@@ -970,6 +1006,75 @@ var autolink = $prose(() => {
|
|
|
970
1006
|
}
|
|
971
1007
|
});
|
|
972
1008
|
});
|
|
1009
|
+
var dictationGhostKey = new PluginKey("dictationGhost");
|
|
1010
|
+
function buildDecorations2(state, text6) {
|
|
1011
|
+
const doc = state.doc;
|
|
1012
|
+
const from = state.selection.head;
|
|
1013
|
+
const before = from > 0 ? doc.textBetween(from - 1, from, " ") : "";
|
|
1014
|
+
const display = before !== "" && !/\s/.test(before) ? ` ${text6}` : text6;
|
|
1015
|
+
const widget = Decoration.widget(
|
|
1016
|
+
from,
|
|
1017
|
+
() => {
|
|
1018
|
+
const span = document.createElement("span");
|
|
1019
|
+
span.className = "md-editor__dictation-ghost";
|
|
1020
|
+
span.setAttribute("aria-hidden", "true");
|
|
1021
|
+
span.appendChild(document.createTextNode(display));
|
|
1022
|
+
const dots = document.createElement("span");
|
|
1023
|
+
dots.className = "md-editor__dictation-dots";
|
|
1024
|
+
for (let i = 0; i < 3; i += 1)
|
|
1025
|
+
dots.appendChild(document.createElement("span"));
|
|
1026
|
+
span.appendChild(dots);
|
|
1027
|
+
return span;
|
|
1028
|
+
},
|
|
1029
|
+
{
|
|
1030
|
+
// Render after the caret so typing still lands where the user expects.
|
|
1031
|
+
side: 1,
|
|
1032
|
+
// Keyed by content so ProseMirror reuses the node between renders instead
|
|
1033
|
+
// of tearing it down on every interim result.
|
|
1034
|
+
key: `dictation-ghost:${display}`
|
|
1035
|
+
}
|
|
1036
|
+
);
|
|
1037
|
+
const decorations = [widget];
|
|
1038
|
+
const $head = state.selection.$head;
|
|
1039
|
+
if ($head.depth > 0) {
|
|
1040
|
+
decorations.push(
|
|
1041
|
+
Decoration.node($head.before(), $head.after(), {
|
|
1042
|
+
class: "md-editor__has-dictation-ghost"
|
|
1043
|
+
})
|
|
1044
|
+
);
|
|
1045
|
+
}
|
|
1046
|
+
return DecorationSet.create(doc, decorations);
|
|
1047
|
+
}
|
|
1048
|
+
var dictationGhost = $prose(() => {
|
|
1049
|
+
return new Plugin({
|
|
1050
|
+
key: dictationGhostKey,
|
|
1051
|
+
state: {
|
|
1052
|
+
init: () => "",
|
|
1053
|
+
apply(tr, previous2) {
|
|
1054
|
+
const meta = tr.getMeta(dictationGhostKey);
|
|
1055
|
+
if (meta) return meta.text;
|
|
1056
|
+
if (tr.docChanged || tr.selectionSet) return "";
|
|
1057
|
+
return previous2;
|
|
1058
|
+
}
|
|
1059
|
+
},
|
|
1060
|
+
props: {
|
|
1061
|
+
decorations(state) {
|
|
1062
|
+
const text6 = dictationGhostKey.getState(state) ?? "";
|
|
1063
|
+
if (!text6) return DecorationSet.empty;
|
|
1064
|
+
return buildDecorations2(state, text6);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1068
|
+
});
|
|
1069
|
+
function setDictationGhost(view, text6) {
|
|
1070
|
+
const current = dictationGhostKey.getState(view.state) ?? "";
|
|
1071
|
+
if (current === text6) return;
|
|
1072
|
+
const tr = view.state.tr.setMeta(dictationGhostKey, {
|
|
1073
|
+
text: text6
|
|
1074
|
+
});
|
|
1075
|
+
tr.setMeta("addToHistory", false);
|
|
1076
|
+
view.dispatch(tr);
|
|
1077
|
+
}
|
|
973
1078
|
|
|
974
1079
|
// src/shared/markdown/extensions/fileTag.ts
|
|
975
1080
|
var fileTag = $node("fileTag", () => ({
|
|
@@ -1879,7 +1984,7 @@ function EditorInner({
|
|
|
1879
1984
|
} catch {
|
|
1880
1985
|
}
|
|
1881
1986
|
});
|
|
1882
|
-
}).use(commonmark).use(history).use(clipboard).use(listener).use(autolink).use(fileTag).use(ssImageNode).use(ssImageView).use(htmlPreviewView).use(enableMentions ? mention : fileTag);
|
|
1987
|
+
}).use(commonmark).use(history).use(clipboard).use(listener).use(autolink).use(dictationGhost).use(fileTag).use(ssImageNode).use(ssImageView).use(htmlPreviewView).use(enableMentions ? mention : fileTag);
|
|
1883
1988
|
},
|
|
1884
1989
|
[isEditable]
|
|
1885
1990
|
);
|
|
@@ -2089,6 +2194,25 @@ function EditorInner({
|
|
|
2089
2194
|
return value ?? "";
|
|
2090
2195
|
}
|
|
2091
2196
|
},
|
|
2197
|
+
insertText: (text6) => {
|
|
2198
|
+
const view = viewRef.current;
|
|
2199
|
+
if (!view || !isEditable || !text6) return;
|
|
2200
|
+
try {
|
|
2201
|
+
const { from } = view.state.selection;
|
|
2202
|
+
const before = from > 0 ? view.state.doc.textBetween(from - 1, from, " ") : "";
|
|
2203
|
+
const needsSpace = before !== "" && !/\s/.test(before);
|
|
2204
|
+
insertTextAtSelection(view, (needsSpace ? " " : "") + text6);
|
|
2205
|
+
} catch {
|
|
2206
|
+
}
|
|
2207
|
+
},
|
|
2208
|
+
setDictationGhost: (text6) => {
|
|
2209
|
+
const view = viewRef.current;
|
|
2210
|
+
if (!view || !isEditable) return;
|
|
2211
|
+
try {
|
|
2212
|
+
setDictationGhost(view, text6);
|
|
2213
|
+
} catch {
|
|
2214
|
+
}
|
|
2215
|
+
},
|
|
2092
2216
|
clear: () => {
|
|
2093
2217
|
const view = viewRef.current;
|
|
2094
2218
|
if (!view) return;
|
|
@@ -2432,6 +2556,326 @@ var MarkdownEditor = forwardRef((props, ref) => {
|
|
|
2432
2556
|
return /* @__PURE__ */ jsx(MilkdownProvider, { children: /* @__PURE__ */ jsx(EditorInner, { ...props, editorHandleRef: ref }) });
|
|
2433
2557
|
});
|
|
2434
2558
|
MarkdownEditor.displayName = "MarkdownEditor";
|
|
2559
|
+
var TOKEN_EXPIRY_MARGIN_MS = 15e3;
|
|
2560
|
+
var noLevelMonitor = () => void 0;
|
|
2561
|
+
var SILENCE_HINT_MS = 6e3;
|
|
2562
|
+
var SILENCE_LEVEL = 3;
|
|
2563
|
+
function monitorInputLevel(stream, onSilent, onSound) {
|
|
2564
|
+
let context;
|
|
2565
|
+
let source;
|
|
2566
|
+
let analyser;
|
|
2567
|
+
try {
|
|
2568
|
+
context = new AudioContext();
|
|
2569
|
+
source = context.createMediaStreamSource(stream);
|
|
2570
|
+
analyser = context.createAnalyser();
|
|
2571
|
+
analyser.fftSize = 512;
|
|
2572
|
+
source.connect(analyser);
|
|
2573
|
+
} catch {
|
|
2574
|
+
return () => void 0;
|
|
2575
|
+
}
|
|
2576
|
+
if (context.state === "suspended")
|
|
2577
|
+
void context.resume().catch(() => void 0);
|
|
2578
|
+
const samples = new Uint8Array(analyser.fftSize);
|
|
2579
|
+
let lastSound = Date.now();
|
|
2580
|
+
let silent = false;
|
|
2581
|
+
let stopped = false;
|
|
2582
|
+
const poll = window.setInterval(() => {
|
|
2583
|
+
if (context.state !== "running") return;
|
|
2584
|
+
analyser.getByteTimeDomainData(samples);
|
|
2585
|
+
let peak = 0;
|
|
2586
|
+
for (const sample of samples) peak = Math.max(peak, Math.abs(sample - 128));
|
|
2587
|
+
if (peak > SILENCE_LEVEL) {
|
|
2588
|
+
lastSound = Date.now();
|
|
2589
|
+
if (silent) {
|
|
2590
|
+
silent = false;
|
|
2591
|
+
onSound();
|
|
2592
|
+
}
|
|
2593
|
+
} else if (!silent && Date.now() - lastSound > SILENCE_HINT_MS) {
|
|
2594
|
+
silent = true;
|
|
2595
|
+
onSilent();
|
|
2596
|
+
}
|
|
2597
|
+
}, 250);
|
|
2598
|
+
return () => {
|
|
2599
|
+
if (stopped) return;
|
|
2600
|
+
stopped = true;
|
|
2601
|
+
window.clearInterval(poll);
|
|
2602
|
+
try {
|
|
2603
|
+
source.disconnect();
|
|
2604
|
+
void context.close().catch(() => void 0);
|
|
2605
|
+
} catch {
|
|
2606
|
+
}
|
|
2607
|
+
};
|
|
2608
|
+
}
|
|
2609
|
+
var STALE_TOKEN_FLOOR_MS = 6e4;
|
|
2610
|
+
function kickOff(fn) {
|
|
2611
|
+
let promise;
|
|
2612
|
+
try {
|
|
2613
|
+
promise = Promise.resolve(fn());
|
|
2614
|
+
} catch (e) {
|
|
2615
|
+
promise = Promise.reject(e);
|
|
2616
|
+
}
|
|
2617
|
+
promise.catch(() => void 0);
|
|
2618
|
+
return promise;
|
|
2619
|
+
}
|
|
2620
|
+
var isSupported = () => typeof window !== "undefined" && window.isSecureContext && !!navigator.mediaDevices?.getUserMedia;
|
|
2621
|
+
function classifyTokenFailure(error) {
|
|
2622
|
+
const code3 = error?.code;
|
|
2623
|
+
if (code3 === "SP500") return "unavailable";
|
|
2624
|
+
if (error?.type === "NotFound") {
|
|
2625
|
+
return "unavailable";
|
|
2626
|
+
}
|
|
2627
|
+
return "unavailable-temporarily";
|
|
2628
|
+
}
|
|
2629
|
+
function useDictation({
|
|
2630
|
+
region,
|
|
2631
|
+
locale,
|
|
2632
|
+
getToken,
|
|
2633
|
+
onPhrase,
|
|
2634
|
+
onInterim,
|
|
2635
|
+
maxDurationMs = 18e4,
|
|
2636
|
+
idleTimeoutMs = 2e4
|
|
2637
|
+
}) {
|
|
2638
|
+
const [state, setState] = useState("idle");
|
|
2639
|
+
const [error, setError] = useState(null);
|
|
2640
|
+
const [deviceLabel, setDeviceLabel] = useState("");
|
|
2641
|
+
const [silent, setSilent] = useState(false);
|
|
2642
|
+
const sessionRef = useRef(null);
|
|
2643
|
+
const generationRef = useRef(0);
|
|
2644
|
+
const onPhraseRef = useRef(onPhrase);
|
|
2645
|
+
const onInterimRef = useRef(onInterim);
|
|
2646
|
+
useEffect(() => {
|
|
2647
|
+
onPhraseRef.current = onPhrase;
|
|
2648
|
+
onInterimRef.current = onInterim;
|
|
2649
|
+
});
|
|
2650
|
+
const clearInterim = useCallback(() => onInterimRef.current?.(""), []);
|
|
2651
|
+
const supported = isSupported();
|
|
2652
|
+
const available = supported && !!region && !!getToken;
|
|
2653
|
+
useEffect(() => {
|
|
2654
|
+
if (!available) return;
|
|
2655
|
+
const nav = navigator;
|
|
2656
|
+
if (nav.connection?.saveData) return;
|
|
2657
|
+
const warm = () => void kickOff(() => import('microsoft-cognitiveservices-speech-sdk'));
|
|
2658
|
+
if (typeof window.requestIdleCallback === "function") {
|
|
2659
|
+
const id2 = window.requestIdleCallback(warm, { timeout: 2e3 });
|
|
2660
|
+
return () => window.cancelIdleCallback(id2);
|
|
2661
|
+
}
|
|
2662
|
+
const id = window.setTimeout(warm, 1500);
|
|
2663
|
+
return () => window.clearTimeout(id);
|
|
2664
|
+
}, [available]);
|
|
2665
|
+
const stop = useCallback(() => {
|
|
2666
|
+
generationRef.current += 1;
|
|
2667
|
+
const session = sessionRef.current;
|
|
2668
|
+
sessionRef.current = null;
|
|
2669
|
+
clearInterim();
|
|
2670
|
+
setState("idle");
|
|
2671
|
+
setSilent(false);
|
|
2672
|
+
setDeviceLabel("");
|
|
2673
|
+
if (!session) return;
|
|
2674
|
+
window.clearTimeout(session.timers.idle);
|
|
2675
|
+
window.clearTimeout(session.timers.max);
|
|
2676
|
+
session.stopLevelMonitor();
|
|
2677
|
+
session.stream.getTracks().forEach((t) => t.stop());
|
|
2678
|
+
session.recognizer.stopContinuousRecognitionAsync(
|
|
2679
|
+
() => session.recognizer.close(),
|
|
2680
|
+
() => session.recognizer.close()
|
|
2681
|
+
);
|
|
2682
|
+
}, [clearInterim]);
|
|
2683
|
+
const start = useCallback(async () => {
|
|
2684
|
+
if (!available || sessionRef.current) return;
|
|
2685
|
+
const generation = ++generationRef.current;
|
|
2686
|
+
const superseded = () => generationRef.current !== generation;
|
|
2687
|
+
setError(null);
|
|
2688
|
+
setSilent(false);
|
|
2689
|
+
setState("starting");
|
|
2690
|
+
const streamPromise = kickOff(
|
|
2691
|
+
() => navigator.mediaDevices.getUserMedia({
|
|
2692
|
+
audio: {
|
|
2693
|
+
echoCancellation: true,
|
|
2694
|
+
noiseSuppression: true,
|
|
2695
|
+
autoGainControl: true
|
|
2696
|
+
}
|
|
2697
|
+
})
|
|
2698
|
+
);
|
|
2699
|
+
const tokenPromise = kickOff(getToken);
|
|
2700
|
+
const sdkPromise = kickOff(
|
|
2701
|
+
() => import('microsoft-cognitiveservices-speech-sdk')
|
|
2702
|
+
);
|
|
2703
|
+
let stream;
|
|
2704
|
+
try {
|
|
2705
|
+
stream = await streamPromise;
|
|
2706
|
+
} catch (e) {
|
|
2707
|
+
if (superseded()) return;
|
|
2708
|
+
const name = e?.name;
|
|
2709
|
+
setError(
|
|
2710
|
+
name === "NotAllowedError" || name === "SecurityError" ? "permission-denied" : name === "NotFoundError" || name === "OverconstrainedError" ? "no-microphone" : "unknown"
|
|
2711
|
+
);
|
|
2712
|
+
setState("idle");
|
|
2713
|
+
return;
|
|
2714
|
+
}
|
|
2715
|
+
if (superseded()) {
|
|
2716
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
2717
|
+
return;
|
|
2718
|
+
}
|
|
2719
|
+
let token;
|
|
2720
|
+
try {
|
|
2721
|
+
token = await tokenPromise;
|
|
2722
|
+
if (Date.parse(token.expiresOn) - Date.now() < STALE_TOKEN_FLOOR_MS) {
|
|
2723
|
+
token = await getToken();
|
|
2724
|
+
}
|
|
2725
|
+
} catch (e) {
|
|
2726
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
2727
|
+
if (superseded()) return;
|
|
2728
|
+
setError(classifyTokenFailure(e));
|
|
2729
|
+
setState("idle");
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2732
|
+
if (superseded()) {
|
|
2733
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
2734
|
+
return;
|
|
2735
|
+
}
|
|
2736
|
+
let recognizer = null;
|
|
2737
|
+
let published = null;
|
|
2738
|
+
try {
|
|
2739
|
+
const sdk = await sdkPromise;
|
|
2740
|
+
if (superseded()) {
|
|
2741
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
2742
|
+
return;
|
|
2743
|
+
}
|
|
2744
|
+
const speechConfig = sdk.SpeechConfig.fromAuthorizationToken(
|
|
2745
|
+
token.token,
|
|
2746
|
+
region
|
|
2747
|
+
);
|
|
2748
|
+
speechConfig.speechRecognitionLanguage = locale;
|
|
2749
|
+
speechConfig.setProfanity(sdk.ProfanityOption.Raw);
|
|
2750
|
+
speechConfig.setProperty(
|
|
2751
|
+
sdk.PropertyId.Speech_SegmentationSilenceTimeoutMs,
|
|
2752
|
+
"700"
|
|
2753
|
+
);
|
|
2754
|
+
recognizer = new sdk.SpeechRecognizer(
|
|
2755
|
+
speechConfig,
|
|
2756
|
+
sdk.AudioConfig.fromStreamInput(stream)
|
|
2757
|
+
);
|
|
2758
|
+
const untilExpiry = Date.parse(token.expiresOn) - Date.now() - TOKEN_EXPIRY_MARGIN_MS;
|
|
2759
|
+
const sessionCapMs = Number.isFinite(untilExpiry) && untilExpiry > 0 ? Math.min(maxDurationMs, untilExpiry) : maxDurationMs;
|
|
2760
|
+
const session = {
|
|
2761
|
+
recognizer,
|
|
2762
|
+
stream,
|
|
2763
|
+
timers: {},
|
|
2764
|
+
stopLevelMonitor: noLevelMonitor
|
|
2765
|
+
};
|
|
2766
|
+
const armIdle = () => {
|
|
2767
|
+
window.clearTimeout(session.timers.idle);
|
|
2768
|
+
session.timers.idle = window.setTimeout(stop, idleTimeoutMs);
|
|
2769
|
+
};
|
|
2770
|
+
const isLive = () => sessionRef.current === session;
|
|
2771
|
+
sessionRef.current = session;
|
|
2772
|
+
published = session;
|
|
2773
|
+
const goLive = () => {
|
|
2774
|
+
if (!isLive()) return;
|
|
2775
|
+
session.timers.max ??= window.setTimeout(stop, sessionCapMs);
|
|
2776
|
+
if (session.stopLevelMonitor === noLevelMonitor) {
|
|
2777
|
+
try {
|
|
2778
|
+
setDeviceLabel(stream.getAudioTracks()[0]?.label ?? "");
|
|
2779
|
+
session.stopLevelMonitor = monitorInputLevel(
|
|
2780
|
+
stream,
|
|
2781
|
+
() => {
|
|
2782
|
+
if (isLive()) setSilent(true);
|
|
2783
|
+
},
|
|
2784
|
+
() => {
|
|
2785
|
+
if (isLive()) setSilent(false);
|
|
2786
|
+
}
|
|
2787
|
+
);
|
|
2788
|
+
} catch {
|
|
2789
|
+
session.stopLevelMonitor = noLevelMonitor;
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
armIdle();
|
|
2793
|
+
setState("listening");
|
|
2794
|
+
};
|
|
2795
|
+
recognizer.sessionStarted = () => goLive();
|
|
2796
|
+
recognizer.recognizing = (_sender, event) => {
|
|
2797
|
+
if (!isLive()) return;
|
|
2798
|
+
onInterimRef.current?.(event.result.text ?? "");
|
|
2799
|
+
armIdle();
|
|
2800
|
+
};
|
|
2801
|
+
recognizer.recognized = (_sender, event) => {
|
|
2802
|
+
if (!isLive()) return;
|
|
2803
|
+
const text6 = event.result.text?.trim();
|
|
2804
|
+
clearInterim();
|
|
2805
|
+
if (event.result.reason === sdk.ResultReason.RecognizedSpeech && text6) {
|
|
2806
|
+
onPhraseRef.current(text6);
|
|
2807
|
+
}
|
|
2808
|
+
armIdle();
|
|
2809
|
+
};
|
|
2810
|
+
recognizer.canceled = (_sender, event) => {
|
|
2811
|
+
if (!isLive()) return;
|
|
2812
|
+
if (event.reason === sdk.CancellationReason.Error) {
|
|
2813
|
+
console.warn("[dictation] cancelled, errorCode:", event.errorCode);
|
|
2814
|
+
setError(
|
|
2815
|
+
event.errorCode === sdk.CancellationErrorCode.ConnectionFailure ? "network" : event.errorCode === sdk.CancellationErrorCode.AuthenticationFailure ? "unavailable-temporarily" : "unknown"
|
|
2816
|
+
);
|
|
2817
|
+
}
|
|
2818
|
+
stop();
|
|
2819
|
+
};
|
|
2820
|
+
recognizer.sessionStopped = () => {
|
|
2821
|
+
if (isLive()) stop();
|
|
2822
|
+
};
|
|
2823
|
+
try {
|
|
2824
|
+
const connection = sdk.Connection.fromRecognizer(recognizer);
|
|
2825
|
+
connection.connected = () => goLive();
|
|
2826
|
+
connection.openConnection();
|
|
2827
|
+
} catch {
|
|
2828
|
+
}
|
|
2829
|
+
await new Promise(
|
|
2830
|
+
(resolve, reject) => session.recognizer.startContinuousRecognitionAsync(resolve, reject)
|
|
2831
|
+
);
|
|
2832
|
+
goLive();
|
|
2833
|
+
} catch (e) {
|
|
2834
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
2835
|
+
try {
|
|
2836
|
+
recognizer?.close();
|
|
2837
|
+
} catch {
|
|
2838
|
+
}
|
|
2839
|
+
if (published) {
|
|
2840
|
+
window.clearTimeout(published.timers.idle);
|
|
2841
|
+
window.clearTimeout(published.timers.max);
|
|
2842
|
+
published.stopLevelMonitor();
|
|
2843
|
+
if (sessionRef.current === published) sessionRef.current = null;
|
|
2844
|
+
}
|
|
2845
|
+
if (superseded()) return;
|
|
2846
|
+
console.warn("[dictation] failed to start:", e);
|
|
2847
|
+
setError((prev) => prev ?? "unavailable-temporarily");
|
|
2848
|
+
setState("idle");
|
|
2849
|
+
}
|
|
2850
|
+
}, [
|
|
2851
|
+
available,
|
|
2852
|
+
clearInterim,
|
|
2853
|
+
getToken,
|
|
2854
|
+
idleTimeoutMs,
|
|
2855
|
+
locale,
|
|
2856
|
+
maxDurationMs,
|
|
2857
|
+
region,
|
|
2858
|
+
stop
|
|
2859
|
+
]);
|
|
2860
|
+
useEffect(() => stop, [stop]);
|
|
2861
|
+
const toggle = useCallback(() => {
|
|
2862
|
+
if (state === "idle") void start();
|
|
2863
|
+
else stop();
|
|
2864
|
+
}, [start, state, stop]);
|
|
2865
|
+
return {
|
|
2866
|
+
supported,
|
|
2867
|
+
available,
|
|
2868
|
+
state,
|
|
2869
|
+
error,
|
|
2870
|
+
/** Name of the microphone in use, once a session is live. May be ''. */
|
|
2871
|
+
deviceLabel,
|
|
2872
|
+
/** Listening, but the microphone has produced no sound for a while. */
|
|
2873
|
+
silent,
|
|
2874
|
+
start,
|
|
2875
|
+
stop,
|
|
2876
|
+
toggle
|
|
2877
|
+
};
|
|
2878
|
+
}
|
|
2435
2879
|
function useFlowRunVariables(flowRunId) {
|
|
2436
2880
|
const service = useChatService();
|
|
2437
2881
|
return useQuery({
|
|
@@ -3737,6 +4181,58 @@ var ChatVariablesForm = forwardRef(({ workspace, threadId, setVariables }, ref)
|
|
|
3737
4181
|
) }) })
|
|
3738
4182
|
] });
|
|
3739
4183
|
});
|
|
4184
|
+
var dictationErrorMessages = {
|
|
4185
|
+
"permission-denied": "Microphone access is blocked \u2014 allow it in your browser settings and try again",
|
|
4186
|
+
"no-microphone": "No microphone was found",
|
|
4187
|
+
network: "Could not reach the speech service \u2014 try again",
|
|
4188
|
+
"unavailable-temporarily": "Dictation is starting up \u2014 try again in a moment",
|
|
4189
|
+
unavailable: "Dictation is not available on this workspace",
|
|
4190
|
+
unknown: "Dictation stopped unexpectedly \u2014 try again"
|
|
4191
|
+
};
|
|
4192
|
+
function DictationButton({
|
|
4193
|
+
state,
|
|
4194
|
+
error,
|
|
4195
|
+
disabled,
|
|
4196
|
+
onToggle,
|
|
4197
|
+
deviceLabel,
|
|
4198
|
+
size = "sm"
|
|
4199
|
+
}) {
|
|
4200
|
+
const listening = state === "listening";
|
|
4201
|
+
const starting = state === "starting";
|
|
4202
|
+
const label = listening ? "Stop dictation" : "Dictate a message";
|
|
4203
|
+
const hint = listening && deviceLabel ? `${label} \u2014 ${deviceLabel}` : label;
|
|
4204
|
+
const dims = size === "sm" ? "h-8 w-8" : "h-9 w-9";
|
|
4205
|
+
const icon = size === "sm" ? "h-4 w-4" : "h-5 w-5";
|
|
4206
|
+
return /* @__PURE__ */ jsxs(
|
|
4207
|
+
IconButton2,
|
|
4208
|
+
{
|
|
4209
|
+
type: "button",
|
|
4210
|
+
onClick: onToggle,
|
|
4211
|
+
disabled,
|
|
4212
|
+
"aria-pressed": listening,
|
|
4213
|
+
"aria-label": label,
|
|
4214
|
+
title: error ? dictationErrorMessages[error] : hint,
|
|
4215
|
+
className: `relative ${dims} rounded-full transition-colors ${listening ? "bg-destructive text-destructive-foreground hover:bg-destructive/90" : "text-muted-foreground hover:bg-secondary"}`,
|
|
4216
|
+
children: [
|
|
4217
|
+
starting && /* @__PURE__ */ jsx(
|
|
4218
|
+
"span",
|
|
4219
|
+
{
|
|
4220
|
+
"aria-hidden": "true",
|
|
4221
|
+
className: "absolute inset-0 animate-spin rounded-full border-2 border-transparent border-t-current opacity-70"
|
|
4222
|
+
}
|
|
4223
|
+
),
|
|
4224
|
+
listening && /* @__PURE__ */ jsx(
|
|
4225
|
+
"span",
|
|
4226
|
+
{
|
|
4227
|
+
"aria-hidden": "true",
|
|
4228
|
+
className: "absolute inset-0 animate-ping rounded-full bg-destructive/30"
|
|
4229
|
+
}
|
|
4230
|
+
),
|
|
4231
|
+
listening ? /* @__PURE__ */ jsx(Square, { className: `relative ${icon} fill-current` }) : /* @__PURE__ */ jsx(Mic, { className: `relative ${icon}` })
|
|
4232
|
+
]
|
|
4233
|
+
}
|
|
4234
|
+
);
|
|
4235
|
+
}
|
|
3740
4236
|
|
|
3741
4237
|
// src/domains/workspaces/queryKeys.ts
|
|
3742
4238
|
var workspaceKeys = {
|
|
@@ -3969,6 +4465,40 @@ function MessageComposer(_props = {}) {
|
|
|
3969
4465
|
cancelReset();
|
|
3970
4466
|
}
|
|
3971
4467
|
}, [isSending, cancelAccepted, cancelErrored, cancelReset]);
|
|
4468
|
+
const attachEditor = useCallback(
|
|
4469
|
+
(handle2) => {
|
|
4470
|
+
if (handle2) editorRef.current = handle2;
|
|
4471
|
+
},
|
|
4472
|
+
// Both flags matter: the portal mounts on `isMobile && isFullscreen`, and
|
|
4473
|
+
// nothing resets isFullscreen when the breakpoint flips, so crossing it
|
|
4474
|
+
// while fullscreen would unmount the portal editor without re-attaching the
|
|
4475
|
+
// inline one — leaving a destroyed handle and silently swallowing phrases.
|
|
4476
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
4477
|
+
[isFullscreen, isMobile]
|
|
4478
|
+
);
|
|
4479
|
+
const { data: speechConfig, isFetching: speechConfigLoading } = useSpeechConfig();
|
|
4480
|
+
const getSpeechToken = useMemo(
|
|
4481
|
+
() => chatService.getSpeechToken?.bind(chatService),
|
|
4482
|
+
[chatService]
|
|
4483
|
+
);
|
|
4484
|
+
const dictation = useDictation({
|
|
4485
|
+
region: speechConfig?.enabled ? speechConfig.region : null,
|
|
4486
|
+
locale: speechConfig?.defaultLocale ?? "en-US",
|
|
4487
|
+
getToken: getSpeechToken,
|
|
4488
|
+
// Final phrases become real text; provisional ones render as greyed ghost
|
|
4489
|
+
// text at the caret and never enter the document.
|
|
4490
|
+
onPhrase: (text6) => editorRef.current?.insertText(`${text6} `),
|
|
4491
|
+
onInterim: (text6) => editorRef.current?.setDictationGhost(text6)
|
|
4492
|
+
});
|
|
4493
|
+
const { stop: stopDictation } = dictation;
|
|
4494
|
+
const dictationUnavailableReason = speechConfigLoading ? "Checking whether dictation is available" : dictation.supported ? "Dictation is not enabled for this workspace" : "Dictation is not available in this browser";
|
|
4495
|
+
const handleDictationToggle = () => {
|
|
4496
|
+
if (dictation.state === "idle") editorRef.current?.focus();
|
|
4497
|
+
dictation.toggle();
|
|
4498
|
+
};
|
|
4499
|
+
useEffect(() => {
|
|
4500
|
+
if (disabled) stopDictation();
|
|
4501
|
+
}, [disabled, stopDictation]);
|
|
3972
4502
|
useEffect(() => {
|
|
3973
4503
|
if (typeof window === "undefined") return;
|
|
3974
4504
|
window.__ssDownloadFile = (id) => getFileBlobUrl(id);
|
|
@@ -4088,6 +4618,7 @@ function MessageComposer(_props = {}) {
|
|
|
4088
4618
|
const latestText = editorRef.current?.getMarkdown() ?? newMessage;
|
|
4089
4619
|
const sent = handleSendMessage(latestText, uploadedAttachments);
|
|
4090
4620
|
if (!sent) return;
|
|
4621
|
+
stopDictation();
|
|
4091
4622
|
handleClearAttachments();
|
|
4092
4623
|
setEditorKey((k) => k + 1);
|
|
4093
4624
|
};
|
|
@@ -4194,7 +4725,7 @@ function MessageComposer(_props = {}) {
|
|
|
4194
4725
|
/* @__PURE__ */ jsx("div", { className: "max-h-[400px] w-full overflow-y-auto", children: /* @__PURE__ */ jsx(
|
|
4195
4726
|
MarkdownEditor,
|
|
4196
4727
|
{
|
|
4197
|
-
ref:
|
|
4728
|
+
ref: attachEditor,
|
|
4198
4729
|
value: newMessage,
|
|
4199
4730
|
onChange: (md) => setNewMessage(md),
|
|
4200
4731
|
onKeyDown: handleComposerKeyDown,
|
|
@@ -4211,10 +4742,18 @@ function MessageComposer(_props = {}) {
|
|
|
4211
4742
|
},
|
|
4212
4743
|
`composer-md-${editorKey}`
|
|
4213
4744
|
) }),
|
|
4745
|
+
/* @__PURE__ */ jsx("div", { role: "status", className: "sr-only", children: dictation.state === "listening" ? dictation.silent ? `Listening, but not hearing anything from ${dictation.deviceLabel || "your microphone"}` : "Listening" : dictation.state === "starting" ? "Starting dictation" : "" }),
|
|
4746
|
+
dictation.state === "listening" && dictation.silent && /* @__PURE__ */ jsxs("div", { className: "px-5 pb-1 text-xs text-muted-foreground", children: [
|
|
4747
|
+
"Not hearing anything from",
|
|
4748
|
+
" ",
|
|
4749
|
+
dictation.deviceLabel || "your microphone",
|
|
4750
|
+
"."
|
|
4751
|
+
] }),
|
|
4752
|
+
dictation.error && /* @__PURE__ */ jsx("div", { role: "alert", className: "px-5 pb-1 text-xs text-destructive", children: dictationErrorMessages[dictation.error] }),
|
|
4214
4753
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-3 pb-3", children: [
|
|
4215
4754
|
/* @__PURE__ */ jsxs("div", { className: "ss-composer__actions flex min-w-0 flex-1 items-center gap-1", children: [
|
|
4216
4755
|
supportsFiles && /* @__PURE__ */ jsx(
|
|
4217
|
-
|
|
4756
|
+
IconButton2,
|
|
4218
4757
|
{
|
|
4219
4758
|
type: "button",
|
|
4220
4759
|
onClick: handlePickFilesClick,
|
|
@@ -4235,19 +4774,39 @@ function MessageComposer(_props = {}) {
|
|
|
4235
4774
|
)
|
|
4236
4775
|
] }),
|
|
4237
4776
|
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [
|
|
4238
|
-
/* @__PURE__ */ jsx(
|
|
4239
|
-
|
|
4777
|
+
dictation.available ? /* @__PURE__ */ jsx(
|
|
4778
|
+
DictationButton,
|
|
4240
4779
|
{
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
children: /* @__PURE__ */ jsx(Mic, { className: "h-4 w-4" })
|
|
4780
|
+
state: dictation.state,
|
|
4781
|
+
error: dictation.error,
|
|
4782
|
+
disabled,
|
|
4783
|
+
onToggle: handleDictationToggle,
|
|
4784
|
+
deviceLabel: dictation.deviceLabel
|
|
4247
4785
|
}
|
|
4786
|
+
) : (
|
|
4787
|
+
// A disabled button receives no pointer events, so its own
|
|
4788
|
+
// `title` never renders a tooltip. The wrapper is what the user
|
|
4789
|
+
// actually hovers.
|
|
4790
|
+
/* @__PURE__ */ jsx(
|
|
4791
|
+
"span",
|
|
4792
|
+
{
|
|
4793
|
+
title: dictationUnavailableReason,
|
|
4794
|
+
className: "inline-flex cursor-not-allowed",
|
|
4795
|
+
children: /* @__PURE__ */ jsx(
|
|
4796
|
+
IconButton2,
|
|
4797
|
+
{
|
|
4798
|
+
type: "button",
|
|
4799
|
+
disabled: true,
|
|
4800
|
+
"aria-label": `Dictate a message. ${dictationUnavailableReason}`,
|
|
4801
|
+
className: "h-8 w-8 rounded-full text-muted-foreground",
|
|
4802
|
+
children: /* @__PURE__ */ jsx(Mic, { className: "h-4 w-4" })
|
|
4803
|
+
}
|
|
4804
|
+
)
|
|
4805
|
+
}
|
|
4806
|
+
)
|
|
4248
4807
|
),
|
|
4249
4808
|
canStop ? /* @__PURE__ */ jsx(
|
|
4250
|
-
|
|
4809
|
+
IconButton2,
|
|
4251
4810
|
{
|
|
4252
4811
|
onClick: handleStopRun,
|
|
4253
4812
|
className: "chat-send h-8 w-8 rounded-full",
|
|
@@ -4261,7 +4820,7 @@ function MessageComposer(_props = {}) {
|
|
|
4261
4820
|
)
|
|
4262
4821
|
}
|
|
4263
4822
|
) : /* @__PURE__ */ jsx(
|
|
4264
|
-
|
|
4823
|
+
IconButton2,
|
|
4265
4824
|
{
|
|
4266
4825
|
onClick: handleSendMessageAndClear,
|
|
4267
4826
|
className: `chat-send h-8 w-8 rounded-full ${sendDisabled ? "cursor-not-allowed" : ""}`,
|
|
@@ -4290,7 +4849,7 @@ function MessageComposer(_props = {}) {
|
|
|
4290
4849
|
},
|
|
4291
4850
|
children: /* @__PURE__ */ jsxs("div", { className: "relative h-full w-full bg-background border shadow-lg", children: [
|
|
4292
4851
|
/* @__PURE__ */ jsx(
|
|
4293
|
-
|
|
4852
|
+
IconButton2,
|
|
4294
4853
|
{
|
|
4295
4854
|
type: "button",
|
|
4296
4855
|
size: "small",
|
|
@@ -4308,7 +4867,7 @@ function MessageComposer(_props = {}) {
|
|
|
4308
4867
|
/* @__PURE__ */ jsx("div", { className: "flex-1 p-4", children: /* @__PURE__ */ jsx(
|
|
4309
4868
|
MarkdownEditor,
|
|
4310
4869
|
{
|
|
4311
|
-
ref:
|
|
4870
|
+
ref: attachEditor,
|
|
4312
4871
|
value: newMessage,
|
|
4313
4872
|
onChange: (md) => setNewMessage(md),
|
|
4314
4873
|
onKeyDown: handleComposerKeyDown,
|
|
@@ -4323,10 +4882,18 @@ function MessageComposer(_props = {}) {
|
|
|
4323
4882
|
},
|
|
4324
4883
|
`composer-md-${editorKey}`
|
|
4325
4884
|
) }),
|
|
4885
|
+
dictation.error && /* @__PURE__ */ jsx(
|
|
4886
|
+
"div",
|
|
4887
|
+
{
|
|
4888
|
+
"aria-hidden": "true",
|
|
4889
|
+
className: "px-4 pb-1 text-xs text-destructive",
|
|
4890
|
+
children: dictationErrorMessages[dictation.error]
|
|
4891
|
+
}
|
|
4892
|
+
),
|
|
4326
4893
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-t bg-background", children: [
|
|
4327
4894
|
/* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
4328
4895
|
supportsFiles && /* @__PURE__ */ jsx(
|
|
4329
|
-
|
|
4896
|
+
IconButton2,
|
|
4330
4897
|
{
|
|
4331
4898
|
type: "button",
|
|
4332
4899
|
onClick: handlePickFilesClick,
|
|
@@ -4341,8 +4908,19 @@ function MessageComposer(_props = {}) {
|
|
|
4341
4908
|
)
|
|
4342
4909
|
}
|
|
4343
4910
|
),
|
|
4911
|
+
dictation.available && /* @__PURE__ */ jsx(
|
|
4912
|
+
DictationButton,
|
|
4913
|
+
{
|
|
4914
|
+
size: "md",
|
|
4915
|
+
state: dictation.state,
|
|
4916
|
+
error: dictation.error,
|
|
4917
|
+
disabled,
|
|
4918
|
+
onToggle: handleDictationToggle,
|
|
4919
|
+
deviceLabel: dictation.deviceLabel
|
|
4920
|
+
}
|
|
4921
|
+
),
|
|
4344
4922
|
canStop ? /* @__PURE__ */ jsx(
|
|
4345
|
-
|
|
4923
|
+
IconButton2,
|
|
4346
4924
|
{
|
|
4347
4925
|
onClick: handleStopRun,
|
|
4348
4926
|
className: "chat-send h-9 w-9 rounded-full",
|
|
@@ -4356,7 +4934,7 @@ function MessageComposer(_props = {}) {
|
|
|
4356
4934
|
)
|
|
4357
4935
|
}
|
|
4358
4936
|
) : /* @__PURE__ */ jsx(
|
|
4359
|
-
|
|
4937
|
+
IconButton2,
|
|
4360
4938
|
{
|
|
4361
4939
|
onClick: handleSendMessageAndClear,
|
|
4362
4940
|
className: `chat-send h-9 w-9 rounded-full ${sendDisabled ? "cursor-not-allowed" : ""}`,
|
|
@@ -19070,6 +19648,34 @@ var getMessageErrorText = (error) => {
|
|
|
19070
19648
|
}
|
|
19071
19649
|
return STATUS_CODE_TEXT[error.code] ?? GENERIC_ERROR_TEXT;
|
|
19072
19650
|
};
|
|
19651
|
+
function extractProviderErrorDetail(raw2) {
|
|
19652
|
+
const start = raw2.indexOf("{");
|
|
19653
|
+
if (start === -1) return null;
|
|
19654
|
+
let parsed;
|
|
19655
|
+
try {
|
|
19656
|
+
parsed = JSON.parse(raw2.slice(start));
|
|
19657
|
+
} catch {
|
|
19658
|
+
return null;
|
|
19659
|
+
}
|
|
19660
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
19661
|
+
const record = parsed;
|
|
19662
|
+
const inner = typeof record.error === "object" && record.error !== null ? record.error : record;
|
|
19663
|
+
if (typeof inner.message !== "string") return null;
|
|
19664
|
+
return {
|
|
19665
|
+
message: inner.message,
|
|
19666
|
+
param: typeof inner.param === "string" ? inner.param : void 0
|
|
19667
|
+
};
|
|
19668
|
+
}
|
|
19669
|
+
var getMessageErrorDetail = (error) => {
|
|
19670
|
+
if (typeof error === "number" || !error.message) {
|
|
19671
|
+
return null;
|
|
19672
|
+
}
|
|
19673
|
+
const detail = extractProviderErrorDetail(error.message);
|
|
19674
|
+
if (!detail) {
|
|
19675
|
+
return error.message;
|
|
19676
|
+
}
|
|
19677
|
+
return detail.param ? `${detail.message} (param: ${detail.param})` : detail.message;
|
|
19678
|
+
};
|
|
19073
19679
|
var FileInfoSchema = z.object({
|
|
19074
19680
|
id: z.string(),
|
|
19075
19681
|
name: z.string()
|
|
@@ -19961,6 +20567,25 @@ var MessageBubble = (props) => {
|
|
|
19961
20567
|
/* @__PURE__ */ jsx("div", { className: "mt-1.5", children: metaRow })
|
|
19962
20568
|
] });
|
|
19963
20569
|
};
|
|
20570
|
+
function MessageErrorDetails({ detail }) {
|
|
20571
|
+
const [expanded, setExpanded] = useState(false);
|
|
20572
|
+
return /* @__PURE__ */ jsxs("div", { className: "mt-1", children: [
|
|
20573
|
+
/* @__PURE__ */ jsxs(
|
|
20574
|
+
"button",
|
|
20575
|
+
{
|
|
20576
|
+
type: "button",
|
|
20577
|
+
onClick: () => setExpanded((prev) => !prev),
|
|
20578
|
+
"aria-expanded": expanded,
|
|
20579
|
+
className: "m-0 inline-flex w-auto cursor-pointer appearance-none items-center gap-1 border-0 bg-transparent p-0 text-[11px] font-medium leading-none text-muted-foreground hover:text-foreground",
|
|
20580
|
+
children: [
|
|
20581
|
+
expanded ? /* @__PURE__ */ jsx(ChevronDown, { className: "h-3 w-3 shrink-0" }) : /* @__PURE__ */ jsx(ChevronRight, { className: "h-3 w-3 shrink-0" }),
|
|
20582
|
+
expanded ? "Hide details" : "Show details"
|
|
20583
|
+
]
|
|
20584
|
+
}
|
|
20585
|
+
),
|
|
20586
|
+
expanded && /* @__PURE__ */ jsx("div", { className: "mt-1.5 whitespace-pre-wrap break-words rounded-md bg-secondary/60 p-2 font-mono text-[11px] leading-relaxed text-muted-foreground", children: detail })
|
|
20587
|
+
] });
|
|
20588
|
+
}
|
|
19964
20589
|
var ThinkingSection = ({ status }) => /* @__PURE__ */ jsxs(
|
|
19965
20590
|
"div",
|
|
19966
20591
|
{
|
|
@@ -20045,14 +20670,14 @@ var MessageItem = ({
|
|
|
20045
20670
|
const { data: workspace } = useWorkspace(workspaceId);
|
|
20046
20671
|
const chatbotName = getChatbotName(workspace?.name);
|
|
20047
20672
|
const { addInputToMessageMutation } = useAddInputToMessage();
|
|
20048
|
-
const onSubmitUserForm = (messageId) => (name, value) => {
|
|
20673
|
+
const onSubmitUserForm = (messageId, channels = {}) => (name, value) => {
|
|
20049
20674
|
if (!threadId || !messageId) return;
|
|
20050
20675
|
addInputToMessageMutation.mutate({
|
|
20051
20676
|
threadId,
|
|
20052
20677
|
messageId,
|
|
20053
20678
|
name,
|
|
20054
20679
|
value,
|
|
20055
|
-
channels
|
|
20680
|
+
channels
|
|
20056
20681
|
});
|
|
20057
20682
|
};
|
|
20058
20683
|
const safeTime = (d) => {
|
|
@@ -20183,7 +20808,7 @@ var MessageItem = ({
|
|
|
20183
20808
|
userOutput: v.value && typeof v.value === "object" ? v.value : null,
|
|
20184
20809
|
chatbotName,
|
|
20185
20810
|
userInput: userInput?.value,
|
|
20186
|
-
onSubmitUserForm: onSubmitUserForm(message.id ?? "")
|
|
20811
|
+
onSubmitUserForm: onSubmitUserForm(message.id ?? "", v.channels)
|
|
20187
20812
|
},
|
|
20188
20813
|
`user-${message.id ?? "msg"}-${keyCounter++}`
|
|
20189
20814
|
)
|
|
@@ -20248,22 +20873,26 @@ var MessageItem = ({
|
|
|
20248
20873
|
);
|
|
20249
20874
|
}
|
|
20250
20875
|
(message.errors ?? []).forEach((error, errorIndex) => {
|
|
20876
|
+
const key = `error-${message.id ?? "msg"}-${errorIndex}-${error.errorCode ?? error.code}`;
|
|
20877
|
+
const detail = getMessageErrorDetail(error);
|
|
20251
20878
|
bubbles.push(
|
|
20252
|
-
/* @__PURE__ */
|
|
20253
|
-
|
|
20254
|
-
|
|
20255
|
-
|
|
20256
|
-
|
|
20257
|
-
|
|
20258
|
-
|
|
20259
|
-
|
|
20260
|
-
|
|
20261
|
-
|
|
20262
|
-
|
|
20263
|
-
|
|
20264
|
-
|
|
20265
|
-
|
|
20266
|
-
|
|
20879
|
+
/* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
20880
|
+
/* @__PURE__ */ jsx(
|
|
20881
|
+
MessageBubble,
|
|
20882
|
+
{
|
|
20883
|
+
createdBy: chatbotName,
|
|
20884
|
+
createdAt: message.createdAt,
|
|
20885
|
+
type: "Output" /* OUTPUT */,
|
|
20886
|
+
content: [{ text: getMessageErrorText(error) }],
|
|
20887
|
+
files: [],
|
|
20888
|
+
sources: [],
|
|
20889
|
+
chatbotName,
|
|
20890
|
+
userOutput: null,
|
|
20891
|
+
userInput: null
|
|
20892
|
+
}
|
|
20893
|
+
),
|
|
20894
|
+
detail && /* @__PURE__ */ jsx(MessageErrorDetails, { detail })
|
|
20895
|
+
] }, key)
|
|
20267
20896
|
);
|
|
20268
20897
|
});
|
|
20269
20898
|
return bubbles;
|
|
@@ -20518,6 +21147,11 @@ function MessageListSkeleton() {
|
|
|
20518
21147
|
i
|
|
20519
21148
|
)) });
|
|
20520
21149
|
}
|
|
21150
|
+
|
|
21151
|
+
// src/shared/utils/enums.ts
|
|
21152
|
+
var isMember = (e, value) => Object.values(e).includes(value);
|
|
21153
|
+
|
|
21154
|
+
// src/domains/messages/mapper.ts
|
|
20521
21155
|
var {
|
|
20522
21156
|
messageThreadsThreadMessagesIdMessagesResponse: messagesResponseSchema
|
|
20523
21157
|
} = ChatZod;
|
|
@@ -20544,6 +21178,7 @@ function mapMessageValueDtoToModel(dto) {
|
|
|
20544
21178
|
createdByUserId: dto.createdByUserId ?? void 0
|
|
20545
21179
|
};
|
|
20546
21180
|
}
|
|
21181
|
+
var mapMessageValuesDtoToModels = (dtos) => dtos.filter((dto) => isMember(MessageValueType, dto.type)).map(mapMessageValueDtoToModel);
|
|
20547
21182
|
function mapMessageErrorDtoToModel(dto) {
|
|
20548
21183
|
const { error_code, ...rest } = dto;
|
|
20549
21184
|
return {
|
|
@@ -20561,7 +21196,7 @@ function mapMessageDtoToModel(dto) {
|
|
|
20561
21196
|
createdByUserId: dto.createdByUserId ?? void 0,
|
|
20562
21197
|
messageThreadId: dto.messageThreadId ?? void 0,
|
|
20563
21198
|
errors: dto.errors?.map(mapMessageErrorDtoToModel) ?? void 0,
|
|
20564
|
-
values: dto.values
|
|
21199
|
+
values: dto.values && mapMessageValuesDtoToModels(dto.values)
|
|
20565
21200
|
};
|
|
20566
21201
|
}
|
|
20567
21202
|
function mapMessagesDtoToModels(dto) {
|
|
@@ -20691,6 +21326,6 @@ function getUserPhotoUrl(userId) {
|
|
|
20691
21326
|
return `${base2}/users/${userId}/photo`;
|
|
20692
21327
|
}
|
|
20693
21328
|
|
|
20694
|
-
export { ChatProvider, ChatVariablesForm, DRAFT_THREAD_PREFIX, DateFromApi, MarkdownEditor, MessageComposer, MessageList, MessageListSkeleton, MessageMarkdown, MessageValueType, NEW_THREAD_ID, THREAD_LIST_PAGE_SIZE, applyDeltaToMessage, applyThreadToCache, createDraftThreadId, createThreadId, downloadFileBlobOptions, filesKeys, flowRunsKeys, getMessageErrorText, getModelIcon, getRetryStatusText, getThreadPlaceholderFromListCache, getUserPhotoUrl, invalidateWorkspaceThreadLists, isDraftThreadId, mapFileInfoDtoToModel, mapMentionUserDtoToModel, mapMessageDtoToModel, mapMessageErrorDtoToModel, mapMessageValueDtoToModel, mapMessagesDtoToModels, mapSignalRThreadSummaryToModel, mapThreadDtoToModel, mapThreadsResponseDtoToModel, mapWorkspaceDtoToModel, mapWorkspacesDtoToModels, markDraftThreadId, messagesKeys, messagesListOptions, messagesMutationsKeys, modelsKeys, parseDateTime, parseDateTimeHuman, parseRetryStatus, randomUUID, setThreadOptimisticRunning, setThreadRunningInLists, taggableUsersOptions, threadDetailOptions, threadsKeys, unmarkDraftThreadId, useAddInputToMessage, useChatContext, useChatIdentity, useChatService, useDownloadFileBlobQuery, useFileMutations, useFlowRunVariables, useIsDraftThreadId, useMessages, useModels, useSendMessage, useTaggableWorkspaceUsers, useThread, useThreadIsRunning, useUpdateFlowRunVariable, useWorkspace, utcDate, workspaceDetailOptions, workspaceKeys };
|
|
21329
|
+
export { ChatProvider, ChatVariablesForm, DRAFT_THREAD_PREFIX, DateFromApi, DictationButton, MarkdownEditor, MessageComposer, MessageList, MessageListSkeleton, MessageMarkdown, MessageValueType, NEW_THREAD_ID, THREAD_LIST_PAGE_SIZE, applyDeltaToMessage, applyThreadToCache, createDraftThreadId, createThreadId, downloadFileBlobOptions, filesKeys, flowRunsKeys, getMessageErrorDetail, getMessageErrorText, getModelIcon, getRetryStatusText, getThreadPlaceholderFromListCache, getUserPhotoUrl, invalidateWorkspaceThreadLists, isDraftThreadId, mapFileInfoDtoToModel, mapMentionUserDtoToModel, mapMessageDtoToModel, mapMessageErrorDtoToModel, mapMessageValueDtoToModel, mapMessageValuesDtoToModels, mapMessagesDtoToModels, mapSignalRThreadSummaryToModel, mapThreadDtoToModel, mapThreadsResponseDtoToModel, mapWorkspaceDtoToModel, mapWorkspacesDtoToModels, markDraftThreadId, messagesKeys, messagesListOptions, messagesMutationsKeys, modelsKeys, parseDateTime, parseDateTimeHuman, parseRetryStatus, randomUUID, setThreadOptimisticRunning, setThreadRunningInLists, speechKeys, taggableUsersOptions, threadDetailOptions, threadsKeys, unmarkDraftThreadId, useAddInputToMessage, useChatContext, useChatIdentity, useChatService, useDictation, useDownloadFileBlobQuery, useFileMutations, useFlowRunVariables, useIsDraftThreadId, useMessages, useModels, useSendMessage, useSpeechConfig, useTaggableWorkspaceUsers, useThread, useThreadIsRunning, useUpdateFlowRunVariable, useWorkspace, utcDate, workspaceDetailOptions, workspaceKeys };
|
|
20695
21330
|
//# sourceMappingURL=index.js.map
|
|
20696
21331
|
//# sourceMappingURL=index.js.map
|