@fv-ui/ai-chat 0.1.0 → 0.2.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/README.md +746 -741
- package/dist/index.d.ts +27 -12
- package/dist/style.css +1 -1
- package/es/index.mjs +441 -151
- package/lib/index.js +441 -151
- package/package.json +80 -80
package/lib/index.js
CHANGED
|
@@ -23,6 +23,13 @@
|
|
|
23
23
|
|
|
24
24
|
var ElementPlusIconsVue__namespace = /*#__PURE__*/_interopNamespaceDefault(ElementPlusIconsVue);
|
|
25
25
|
|
|
26
|
+
function unwrapPlugin(plugin) {
|
|
27
|
+
const candidate = plugin && typeof plugin === "object" && "default" in plugin ? plugin.default : plugin;
|
|
28
|
+
if (typeof candidate !== "function") {
|
|
29
|
+
throw new TypeError("[XMarkdown] \u65E0\u6548\u7684 markdown-it \u63D2\u4EF6:\u9884\u671F\u51FD\u6570,\u6536\u5230 " + typeof candidate);
|
|
30
|
+
}
|
|
31
|
+
return candidate;
|
|
32
|
+
}
|
|
26
33
|
function createMarkdownInstance(options) {
|
|
27
34
|
const md = new MarkdownIt({
|
|
28
35
|
html: true,
|
|
@@ -30,9 +37,9 @@
|
|
|
30
37
|
linkify: true,
|
|
31
38
|
highlight: options?.highlight
|
|
32
39
|
});
|
|
33
|
-
md.use(mdKatex);
|
|
40
|
+
md.use(unwrapPlugin(mdKatex));
|
|
34
41
|
if (options?.mdPlugins) {
|
|
35
|
-
for (const p of options.mdPlugins) md.use(p);
|
|
42
|
+
for (const p of options.mdPlugins) md.use(unwrapPlugin(p));
|
|
36
43
|
}
|
|
37
44
|
return md;
|
|
38
45
|
}
|
|
@@ -1535,8 +1542,8 @@
|
|
|
1535
1542
|
}
|
|
1536
1543
|
}
|
|
1537
1544
|
|
|
1538
|
-
const _hoisted_1$
|
|
1539
|
-
var _sfc_main$
|
|
1545
|
+
const _hoisted_1$i = ["innerHTML"];
|
|
1546
|
+
var _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
|
|
1540
1547
|
...{ name: "FvMarkdown" },
|
|
1541
1548
|
__name: "index",
|
|
1542
1549
|
props: {
|
|
@@ -1559,12 +1566,12 @@
|
|
|
1559
1566
|
return vue.openBlock(), vue.createElementBlock("div", {
|
|
1560
1567
|
class: "fv-xmarkdown markdown-body",
|
|
1561
1568
|
innerHTML: html.value
|
|
1562
|
-
}, null, 8, _hoisted_1$
|
|
1569
|
+
}, null, 8, _hoisted_1$i);
|
|
1563
1570
|
};
|
|
1564
1571
|
}
|
|
1565
1572
|
});
|
|
1566
1573
|
|
|
1567
|
-
const _hoisted_1$
|
|
1574
|
+
const _hoisted_1$h = {
|
|
1568
1575
|
key: 1,
|
|
1569
1576
|
class: "fv-typewriter-text"
|
|
1570
1577
|
};
|
|
@@ -1572,7 +1579,7 @@
|
|
|
1572
1579
|
key: 0,
|
|
1573
1580
|
class: "fv-typewriter-suffix"
|
|
1574
1581
|
};
|
|
1575
|
-
var _sfc_main$
|
|
1582
|
+
var _sfc_main$h = /* @__PURE__ */ vue.defineComponent({
|
|
1576
1583
|
...{ name: "FvTypewriter" },
|
|
1577
1584
|
__name: "index",
|
|
1578
1585
|
props: {
|
|
@@ -1662,11 +1669,11 @@
|
|
|
1662
1669
|
},
|
|
1663
1670
|
[
|
|
1664
1671
|
vue.createCommentVNode(" markdown \u6A21\u5F0F:suffix \u4E0D\u8FFD\u52A0(element-plus-x \u884C\u4E3A),isTyping \u900F\u4F20\u7ED9 XMarkdown \u4F5C\u6837\u5F0F\u94A9\u5B50 "),
|
|
1665
|
-
__props.isMarkdown ? (vue.openBlock(), vue.createBlock(_sfc_main$
|
|
1672
|
+
__props.isMarkdown ? (vue.openBlock(), vue.createBlock(_sfc_main$i, {
|
|
1666
1673
|
key: 0,
|
|
1667
1674
|
content: renderedContent.value,
|
|
1668
1675
|
"is-typing": isTyping.value
|
|
1669
|
-
}, null, 8, ["content", "is-typing"])) : (vue.openBlock(), vue.createElementBlock("span", _hoisted_1$
|
|
1676
|
+
}, null, 8, ["content", "is-typing"])) : (vue.openBlock(), vue.createElementBlock("span", _hoisted_1$h, [
|
|
1670
1677
|
vue.createTextVNode(
|
|
1671
1678
|
vue.toDisplayString(renderedContent.value),
|
|
1672
1679
|
1
|
|
@@ -1701,7 +1708,7 @@
|
|
|
1701
1708
|
}
|
|
1702
1709
|
});
|
|
1703
1710
|
|
|
1704
|
-
const _hoisted_1$
|
|
1711
|
+
const _hoisted_1$g = {
|
|
1705
1712
|
key: 0,
|
|
1706
1713
|
class: "fv-bubble-avatar"
|
|
1707
1714
|
};
|
|
@@ -1722,7 +1729,7 @@
|
|
|
1722
1729
|
key: 1,
|
|
1723
1730
|
class: "fv-bubble-footer"
|
|
1724
1731
|
};
|
|
1725
|
-
var _sfc_main$
|
|
1732
|
+
var _sfc_main$g = /* @__PURE__ */ vue.defineComponent({
|
|
1726
1733
|
...{ name: "FvBubble" },
|
|
1727
1734
|
__name: "index",
|
|
1728
1735
|
props: {
|
|
@@ -1789,7 +1796,7 @@
|
|
|
1789
1796
|
},
|
|
1790
1797
|
[
|
|
1791
1798
|
vue.createCommentVNode(" \u5934\u50CF:placement \u540C\u7F18\u6392\u5217(end \u7ECF row-reverse \u7FFB\u8F6C\u5230\u53F3\u4FA7),#avatar \u63D2\u69FD\u4F18\u5148 "),
|
|
1792
|
-
_ctx.$slots.avatar || __props.avatar || __props.avatarIcon ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$
|
|
1799
|
+
_ctx.$slots.avatar || __props.avatar || __props.avatarIcon ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$g, [
|
|
1793
1800
|
_ctx.$slots.avatar ? vue.renderSlot(_ctx.$slots, "avatar", {}, void 0, void 0, 0) : (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElAvatar), {
|
|
1794
1801
|
key: 1,
|
|
1795
1802
|
size: __props.avatarSize || void 0,
|
|
@@ -1847,7 +1854,7 @@
|
|
|
1847
1854
|
{ key: 2 },
|
|
1848
1855
|
[
|
|
1849
1856
|
vue.createCommentVNode(" typing \u900F\u4F20 Typewriter;\u975E typing:isMarkdown \u2192 XMarkdown,\u5426\u5219\u7EAF\u6587\u672C "),
|
|
1850
|
-
vue.createVNode(_sfc_main$
|
|
1857
|
+
vue.createVNode(_sfc_main$h, {
|
|
1851
1858
|
ref_key: "twRef",
|
|
1852
1859
|
ref: twRef,
|
|
1853
1860
|
content: __props.content,
|
|
@@ -1861,7 +1868,7 @@
|
|
|
1861
1868
|
],
|
|
1862
1869
|
2112
|
|
1863
1870
|
/* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */
|
|
1864
|
-
)) : __props.isMarkdown ? (vue.openBlock(), vue.createBlock(_sfc_main$
|
|
1871
|
+
)) : __props.isMarkdown ? (vue.openBlock(), vue.createBlock(_sfc_main$i, {
|
|
1865
1872
|
key: 3,
|
|
1866
1873
|
content: __props.content
|
|
1867
1874
|
}, null, 8, ["content"])) : (vue.openBlock(), vue.createElementBlock(
|
|
@@ -1887,8 +1894,8 @@
|
|
|
1887
1894
|
}
|
|
1888
1895
|
});
|
|
1889
1896
|
|
|
1890
|
-
const _hoisted_1$
|
|
1891
|
-
var _sfc_main$
|
|
1897
|
+
const _hoisted_1$f = { class: "fv-bubble-list" };
|
|
1898
|
+
var _sfc_main$f = /* @__PURE__ */ vue.defineComponent({
|
|
1892
1899
|
...{ name: "FvBubbleList" },
|
|
1893
1900
|
__name: "index",
|
|
1894
1901
|
props: {
|
|
@@ -1988,7 +1995,7 @@
|
|
|
1988
1995
|
}));
|
|
1989
1996
|
__expose({ scrollToTop, scrollToBottom, scrollToBubble });
|
|
1990
1997
|
return (_ctx, _cache) => {
|
|
1991
|
-
return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$
|
|
1998
|
+
return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$f, [
|
|
1992
1999
|
vue.createElementVNode(
|
|
1993
2000
|
"div",
|
|
1994
2001
|
{
|
|
@@ -2003,7 +2010,7 @@
|
|
|
2003
2010
|
vue.Fragment,
|
|
2004
2011
|
null,
|
|
2005
2012
|
vue.renderList(__props.list, (item, index) => {
|
|
2006
|
-
return vue.openBlock(), vue.createBlock(_sfc_main$
|
|
2013
|
+
return vue.openBlock(), vue.createBlock(_sfc_main$g, vue.mergeProps({
|
|
2007
2014
|
key: item.key,
|
|
2008
2015
|
ref_for: true,
|
|
2009
2016
|
ref: (el) => setBubbleRef(el, index)
|
|
@@ -2111,6 +2118,139 @@
|
|
|
2111
2118
|
}
|
|
2112
2119
|
});
|
|
2113
2120
|
|
|
2121
|
+
const _hoisted_1$e = {
|
|
2122
|
+
class: "fv-stop-icon",
|
|
2123
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2124
|
+
viewBox: "0 0 24 24",
|
|
2125
|
+
width: "1em",
|
|
2126
|
+
height: "1em",
|
|
2127
|
+
"aria-hidden": "true",
|
|
2128
|
+
focusable: "false"
|
|
2129
|
+
};
|
|
2130
|
+
var _sfc_main$e = /* @__PURE__ */ vue.defineComponent({
|
|
2131
|
+
...{ name: "FvStopIcon" },
|
|
2132
|
+
__name: "StopIcon",
|
|
2133
|
+
setup(__props) {
|
|
2134
|
+
return (_ctx, _cache) => {
|
|
2135
|
+
return vue.openBlock(), vue.createElementBlock("svg", _hoisted_1$e, [..._cache[0] || (_cache[0] = [
|
|
2136
|
+
vue.createElementVNode(
|
|
2137
|
+
"rect",
|
|
2138
|
+
{
|
|
2139
|
+
x: "5.5",
|
|
2140
|
+
y: "5.5",
|
|
2141
|
+
width: "13",
|
|
2142
|
+
height: "13",
|
|
2143
|
+
rx: "2.5",
|
|
2144
|
+
fill: "currentColor"
|
|
2145
|
+
},
|
|
2146
|
+
null,
|
|
2147
|
+
-1
|
|
2148
|
+
/* CACHED */
|
|
2149
|
+
)
|
|
2150
|
+
])]);
|
|
2151
|
+
};
|
|
2152
|
+
}
|
|
2153
|
+
});
|
|
2154
|
+
|
|
2155
|
+
function getSpeechRecognitionCtor() {
|
|
2156
|
+
if (typeof window === "undefined") return void 0;
|
|
2157
|
+
const w = window;
|
|
2158
|
+
const ctor = w.SpeechRecognition ?? w.webkitSpeechRecognition;
|
|
2159
|
+
return typeof ctor === "function" ? ctor : void 0;
|
|
2160
|
+
}
|
|
2161
|
+
function useRecord() {
|
|
2162
|
+
const isRecording = vue.ref(false);
|
|
2163
|
+
const text = vue.ref("");
|
|
2164
|
+
const audioUrl = vue.ref(null);
|
|
2165
|
+
const error = vue.ref(null);
|
|
2166
|
+
let mediaStream = null;
|
|
2167
|
+
let mediaRecorder = null;
|
|
2168
|
+
let chunks = [];
|
|
2169
|
+
let recognition = null;
|
|
2170
|
+
let finalText = "";
|
|
2171
|
+
function revokeAudioUrl() {
|
|
2172
|
+
if (audioUrl.value) {
|
|
2173
|
+
URL.revokeObjectURL(audioUrl.value);
|
|
2174
|
+
audioUrl.value = null;
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
async function start() {
|
|
2178
|
+
if (isRecording.value) return;
|
|
2179
|
+
error.value = null;
|
|
2180
|
+
text.value = "";
|
|
2181
|
+
finalText = "";
|
|
2182
|
+
chunks = [];
|
|
2183
|
+
revokeAudioUrl();
|
|
2184
|
+
if (typeof MediaRecorder === "undefined" || !navigator.mediaDevices?.getUserMedia) {
|
|
2185
|
+
error.value = new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u97F3\u9891\u5F55\u5236");
|
|
2186
|
+
return;
|
|
2187
|
+
}
|
|
2188
|
+
try {
|
|
2189
|
+
mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
2190
|
+
} catch (e) {
|
|
2191
|
+
error.value = e instanceof Error ? e : new Error(String(e));
|
|
2192
|
+
return;
|
|
2193
|
+
}
|
|
2194
|
+
mediaRecorder = new MediaRecorder(mediaStream);
|
|
2195
|
+
mediaRecorder.ondataavailable = (e) => {
|
|
2196
|
+
if (e.data && e.data.size > 0) chunks.push(e.data);
|
|
2197
|
+
};
|
|
2198
|
+
mediaRecorder.onstop = () => {
|
|
2199
|
+
if (chunks.length > 0) {
|
|
2200
|
+
const blob = new Blob(chunks, { type: mediaRecorder?.mimeType || "audio/webm" });
|
|
2201
|
+
audioUrl.value = URL.createObjectURL(blob);
|
|
2202
|
+
}
|
|
2203
|
+
mediaStream?.getTracks().forEach((t) => t.stop());
|
|
2204
|
+
mediaStream = null;
|
|
2205
|
+
};
|
|
2206
|
+
const SRCtor = getSpeechRecognitionCtor();
|
|
2207
|
+
if (SRCtor) {
|
|
2208
|
+
try {
|
|
2209
|
+
recognition = new SRCtor();
|
|
2210
|
+
recognition.continuous = true;
|
|
2211
|
+
recognition.interimResults = true;
|
|
2212
|
+
recognition.onresult = (event) => {
|
|
2213
|
+
let interim = "";
|
|
2214
|
+
for (let i = event.resultIndex; i < event.results.length; i++) {
|
|
2215
|
+
const r = event.results[i];
|
|
2216
|
+
if (r.isFinal) finalText += r[0].transcript;
|
|
2217
|
+
else interim += r[0].transcript;
|
|
2218
|
+
}
|
|
2219
|
+
text.value = finalText + interim;
|
|
2220
|
+
};
|
|
2221
|
+
recognition.onerror = (ev) => {
|
|
2222
|
+
error.value = new Error(`\u8BED\u97F3\u8BC6\u522B\u9519\u8BEF: ${ev?.error ?? "unknown"}`);
|
|
2223
|
+
};
|
|
2224
|
+
recognition.start();
|
|
2225
|
+
} catch {
|
|
2226
|
+
recognition = null;
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
mediaRecorder.start();
|
|
2230
|
+
isRecording.value = true;
|
|
2231
|
+
}
|
|
2232
|
+
function stop() {
|
|
2233
|
+
if (!isRecording.value) return;
|
|
2234
|
+
isRecording.value = false;
|
|
2235
|
+
try {
|
|
2236
|
+
recognition?.stop();
|
|
2237
|
+
} catch {
|
|
2238
|
+
}
|
|
2239
|
+
recognition = null;
|
|
2240
|
+
try {
|
|
2241
|
+
if (mediaRecorder && mediaRecorder.state !== "inactive") mediaRecorder.stop();
|
|
2242
|
+
} catch {
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2245
|
+
if (vue.getCurrentInstance()) {
|
|
2246
|
+
vue.onBeforeUnmount(() => {
|
|
2247
|
+
stop();
|
|
2248
|
+
revokeAudioUrl();
|
|
2249
|
+
});
|
|
2250
|
+
}
|
|
2251
|
+
return { start, stop, isRecording, text, audioUrl, error };
|
|
2252
|
+
}
|
|
2253
|
+
|
|
2114
2254
|
const _hoisted_1$d = {
|
|
2115
2255
|
key: 0,
|
|
2116
2256
|
class: "fv-sender-header"
|
|
@@ -2125,24 +2265,38 @@
|
|
|
2125
2265
|
key: 0,
|
|
2126
2266
|
class: "fv-sender-prefix"
|
|
2127
2267
|
};
|
|
2128
|
-
const _hoisted_6$5 =
|
|
2129
|
-
|
|
2130
|
-
|
|
2268
|
+
const _hoisted_6$5 = {
|
|
2269
|
+
class: "fv-sender-recording-wave",
|
|
2270
|
+
"aria-hidden": "true"
|
|
2271
|
+
};
|
|
2272
|
+
const _hoisted_7$4 = { class: "fv-sender-recording-duration" };
|
|
2273
|
+
const _hoisted_8$3 = ["value", "placeholder", "disabled", "readonly"];
|
|
2274
|
+
const _hoisted_9$2 = {
|
|
2275
|
+
key: 3,
|
|
2131
2276
|
class: "fv-sender-actions"
|
|
2132
2277
|
};
|
|
2133
|
-
const
|
|
2134
|
-
key:
|
|
2278
|
+
const _hoisted_10$2 = {
|
|
2279
|
+
key: 4,
|
|
2135
2280
|
class: "fv-sender-actions"
|
|
2136
2281
|
};
|
|
2137
|
-
const
|
|
2282
|
+
const _hoisted_11$2 = {
|
|
2138
2283
|
key: 0,
|
|
2139
2284
|
class: "fv-sender-action-left"
|
|
2140
2285
|
};
|
|
2141
|
-
const
|
|
2286
|
+
const _hoisted_12$2 = {
|
|
2287
|
+
class: "fv-sender-stop-ring",
|
|
2288
|
+
"aria-hidden": "true"
|
|
2289
|
+
};
|
|
2290
|
+
const _hoisted_13$2 = ["rx"];
|
|
2291
|
+
const _hoisted_14$2 = {
|
|
2142
2292
|
key: 1,
|
|
2143
2293
|
class: "fv-sender-footer"
|
|
2144
2294
|
};
|
|
2145
2295
|
const ROW_HEIGHT = 22;
|
|
2296
|
+
const WAVE_BARS = 48;
|
|
2297
|
+
const RING_HEAD = 10;
|
|
2298
|
+
const RING_TAIL = 40;
|
|
2299
|
+
const RING_LAYERS = 6;
|
|
2146
2300
|
var _sfc_main$d = /* @__PURE__ */ vue.defineComponent({
|
|
2147
2301
|
...{ name: "FvSender" },
|
|
2148
2302
|
__name: "index",
|
|
@@ -2156,6 +2310,8 @@
|
|
|
2156
2310
|
disabled: { type: Boolean, required: false, default: false },
|
|
2157
2311
|
readOnly: { type: Boolean, required: false, default: false },
|
|
2158
2312
|
clearable: { type: Boolean, required: false, default: false },
|
|
2313
|
+
round: { type: Boolean, required: false, default: true },
|
|
2314
|
+
shadow: { type: Boolean, required: false, default: false },
|
|
2159
2315
|
inputWidth: { type: String, required: false, default: "" },
|
|
2160
2316
|
variant: { type: String, required: false, default: "default" },
|
|
2161
2317
|
submitBtnDisabled: { type: Boolean, required: false, default: false },
|
|
@@ -2328,11 +2484,136 @@
|
|
|
2328
2484
|
for (const f of Array.from(files)) emit("paste-file", f);
|
|
2329
2485
|
}
|
|
2330
2486
|
}
|
|
2331
|
-
|
|
2332
|
-
|
|
2487
|
+
const { start: startRecord, stop: stopRecord, isRecording, text: recordText, error: recordError } = useRecord();
|
|
2488
|
+
async function exitRecordingFocusInput() {
|
|
2489
|
+
await vue.nextTick();
|
|
2490
|
+
focus("end");
|
|
2491
|
+
}
|
|
2492
|
+
function insertTextAtCursor(insert) {
|
|
2493
|
+
if (!insert) return;
|
|
2494
|
+
const el = textareaRef.value;
|
|
2495
|
+
const pos = el?.selectionStart ?? value.value.length;
|
|
2496
|
+
const next = value.value.slice(0, pos) + insert + value.value.slice(el?.selectionEnd ?? pos);
|
|
2497
|
+
setValue(next);
|
|
2498
|
+
vue.nextTick(() => {
|
|
2499
|
+
const t = textareaRef.value;
|
|
2500
|
+
if (t) t.setSelectionRange(pos + insert.length, pos + insert.length);
|
|
2501
|
+
});
|
|
2502
|
+
}
|
|
2503
|
+
async function onSpeechClick() {
|
|
2504
|
+
if (isRecording.value) return;
|
|
2505
|
+
await startRecord();
|
|
2506
|
+
if (recordError.value) {
|
|
2507
|
+
elementPlus.ElMessage.error(recordError.value.message || "\u65E0\u6CD5\u8BBF\u95EE\u9EA6\u514B\u98CE");
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
emit("recording-change", true);
|
|
2511
|
+
}
|
|
2512
|
+
async function onRecordConfirm() {
|
|
2513
|
+
stopRecord();
|
|
2514
|
+
emit("recording-change", false);
|
|
2515
|
+
insertTextAtCursor(recordText.value);
|
|
2516
|
+
await exitRecordingFocusInput();
|
|
2517
|
+
}
|
|
2518
|
+
async function onRecordCancel() {
|
|
2519
|
+
stopRecord();
|
|
2520
|
+
emit("recording-change", false);
|
|
2521
|
+
await exitRecordingFocusInput();
|
|
2333
2522
|
}
|
|
2523
|
+
function onRecordingKeydown(e) {
|
|
2524
|
+
if (e.key === "Escape") {
|
|
2525
|
+
e.preventDefault();
|
|
2526
|
+
onRecordCancel();
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
const waveLevels = vue.ref(Array(WAVE_BARS).fill(0.04));
|
|
2530
|
+
let audioCtx = null;
|
|
2531
|
+
let analyserSource = null;
|
|
2532
|
+
let waveRafId = 0;
|
|
2533
|
+
function normalizeRms(rms) {
|
|
2534
|
+
const scaled = Math.pow(rms * 10, 1.5);
|
|
2535
|
+
return Math.min(1, Math.max(0.04, scaled));
|
|
2536
|
+
}
|
|
2537
|
+
async function startWaveVisualizer() {
|
|
2538
|
+
if (typeof AudioContext === "undefined" || !navigator.mediaDevices?.getUserMedia) return;
|
|
2539
|
+
try {
|
|
2540
|
+
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
2541
|
+
audioCtx = new AudioContext();
|
|
2542
|
+
analyserSource = audioCtx.createMediaStreamSource(stream);
|
|
2543
|
+
const analyser = audioCtx.createAnalyser();
|
|
2544
|
+
analyser.minDecibels = -45;
|
|
2545
|
+
analyserSource.connect(analyser);
|
|
2546
|
+
const timeData = new Uint8Array(analyser.fftSize);
|
|
2547
|
+
const processFrame = () => {
|
|
2548
|
+
analyser.getByteTimeDomainData(timeData);
|
|
2549
|
+
let sumSquares = 0;
|
|
2550
|
+
for (let i = 0; i < timeData.length; i++) {
|
|
2551
|
+
const v = (timeData[i] - 128) / 128;
|
|
2552
|
+
sumSquares += v * v;
|
|
2553
|
+
}
|
|
2554
|
+
const level = normalizeRms(Math.sqrt(sumSquares / timeData.length));
|
|
2555
|
+
waveLevels.value = [...waveLevels.value.slice(1), level];
|
|
2556
|
+
waveRafId = requestAnimationFrame(processFrame);
|
|
2557
|
+
};
|
|
2558
|
+
waveRafId = requestAnimationFrame(processFrame);
|
|
2559
|
+
stream.getTracks().forEach((t) => t.addEventListener("ended", () => stopWaveVisualizer()));
|
|
2560
|
+
analyserSourceStream = stream;
|
|
2561
|
+
} catch {
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
let analyserSourceStream = null;
|
|
2565
|
+
function stopWaveVisualizer() {
|
|
2566
|
+
cancelAnimationFrame(waveRafId);
|
|
2567
|
+
waveRafId = 0;
|
|
2568
|
+
analyserSource?.disconnect();
|
|
2569
|
+
analyserSource = null;
|
|
2570
|
+
analyserSourceStream?.getTracks().forEach((t) => t.stop());
|
|
2571
|
+
analyserSourceStream = null;
|
|
2572
|
+
audioCtx?.close().catch(() => {
|
|
2573
|
+
});
|
|
2574
|
+
audioCtx = null;
|
|
2575
|
+
waveLevels.value = Array(WAVE_BARS).fill(0.04);
|
|
2576
|
+
}
|
|
2577
|
+
vue.watch(isRecording, (rec) => {
|
|
2578
|
+
if (rec) startWaveVisualizer();
|
|
2579
|
+
else stopWaveVisualizer();
|
|
2580
|
+
});
|
|
2581
|
+
const recordSeconds = vue.ref(0);
|
|
2582
|
+
let recordTimer = null;
|
|
2583
|
+
vue.watch(isRecording, (rec) => {
|
|
2584
|
+
if (rec) {
|
|
2585
|
+
recordSeconds.value = 0;
|
|
2586
|
+
recordTimer = setInterval(() => {
|
|
2587
|
+
recordSeconds.value++;
|
|
2588
|
+
}, 1e3);
|
|
2589
|
+
} else if (recordTimer) {
|
|
2590
|
+
clearInterval(recordTimer);
|
|
2591
|
+
recordTimer = null;
|
|
2592
|
+
}
|
|
2593
|
+
});
|
|
2594
|
+
const recordDuration = vue.computed(() => {
|
|
2595
|
+
const m = Math.floor(recordSeconds.value / 60);
|
|
2596
|
+
const s = recordSeconds.value % 60;
|
|
2597
|
+
return `${m}:${String(s).padStart(2, "0")}`;
|
|
2598
|
+
});
|
|
2599
|
+
vue.onBeforeUnmount(() => {
|
|
2600
|
+
stopWaveVisualizer();
|
|
2601
|
+
if (recordTimer) clearInterval(recordTimer);
|
|
2602
|
+
});
|
|
2334
2603
|
const inputDisabled = vue.computed(() => props.disabled || props.loading);
|
|
2335
2604
|
const inputReadonly = vue.computed(() => props.readOnly);
|
|
2605
|
+
const ringLayers = vue.computed(
|
|
2606
|
+
() => Array.from({ length: RING_LAYERS }, (_, i) => {
|
|
2607
|
+
const len = RING_HEAD + RING_TAIL * i / (RING_LAYERS - 1);
|
|
2608
|
+
const alpha = 0.9 * Math.pow(0.72, i);
|
|
2609
|
+
return {
|
|
2610
|
+
style: {
|
|
2611
|
+
"--ring-len": len.toFixed(2),
|
|
2612
|
+
"--ring-alpha": alpha.toFixed(3)
|
|
2613
|
+
}
|
|
2614
|
+
};
|
|
2615
|
+
})
|
|
2616
|
+
);
|
|
2336
2617
|
const slots = vue.useSlots();
|
|
2337
2618
|
const hasActionListSlot = vue.computed(() => !!slots["action-list"]);
|
|
2338
2619
|
__expose({ submit, cancel, clear, focus, blur, openHeader, closeHeader });
|
|
@@ -2340,7 +2621,7 @@
|
|
|
2340
2621
|
return vue.openBlock(), vue.createElementBlock(
|
|
2341
2622
|
"div",
|
|
2342
2623
|
{
|
|
2343
|
-
class: vue.normalizeClass(["fv-sender", [`variant-${__props.variant}`, { "is-disabled": __props.disabled, "is-loading": __props.loading, "is-focused": isFocused.value }]])
|
|
2624
|
+
class: vue.normalizeClass(["fv-sender", [`variant-${__props.variant}`, { "is-disabled": __props.disabled, "is-loading": __props.loading, "is-focused": isFocused.value, "has-shadow": __props.shadow }]])
|
|
2344
2625
|
},
|
|
2345
2626
|
[
|
|
2346
2627
|
vue.createCommentVNode(" \u5934\u90E8:#header \u63D2\u69FD,openHeader/closeHeader \u63A7\u5236 "),
|
|
@@ -2358,9 +2639,89 @@
|
|
|
2358
2639
|
_ctx.$slots.prefix ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_5$7, [
|
|
2359
2640
|
vue.renderSlot(_ctx.$slots, "prefix")
|
|
2360
2641
|
])) : vue.createCommentVNode("v-if", true),
|
|
2361
|
-
vue.
|
|
2642
|
+
vue.createCommentVNode(" \u8BED\u97F3\u5F55\u97F3\u4E2D:\u8F93\u5165\u533A\u539F\u4F4D\u66FF\u6362\u4E3A\u5F55\u97F3\u6761(\u53D6\u6D88/\u6CE2\u5F62+\u65F6\u957F/\u786E\u8BA4);Esc \u53D6\u6D88 "),
|
|
2643
|
+
vue.unref(isRecording) ? (vue.openBlock(), vue.createElementBlock(
|
|
2362
2644
|
"div",
|
|
2363
2645
|
{
|
|
2646
|
+
key: 1,
|
|
2647
|
+
class: "fv-sender-recording",
|
|
2648
|
+
tabindex: "-1",
|
|
2649
|
+
onKeydown: onRecordingKeydown
|
|
2650
|
+
},
|
|
2651
|
+
[
|
|
2652
|
+
vue.createVNode(vue.unref(elementPlus.ElButton), {
|
|
2653
|
+
class: "fv-sender-recording-cancel",
|
|
2654
|
+
text: "",
|
|
2655
|
+
circle: "",
|
|
2656
|
+
"aria-label": "\u53D6\u6D88\u5F55\u97F3",
|
|
2657
|
+
onClick: onRecordCancel
|
|
2658
|
+
}, {
|
|
2659
|
+
default: vue.withCtx(() => [
|
|
2660
|
+
vue.createVNode(vue.unref(elementPlus.ElIcon), null, {
|
|
2661
|
+
default: vue.withCtx(() => [
|
|
2662
|
+
vue.createVNode(vue.unref(ElementPlusIconsVue.Close))
|
|
2663
|
+
]),
|
|
2664
|
+
_: 1
|
|
2665
|
+
/* STABLE */
|
|
2666
|
+
})
|
|
2667
|
+
]),
|
|
2668
|
+
_: 1
|
|
2669
|
+
/* STABLE */
|
|
2670
|
+
}),
|
|
2671
|
+
vue.createElementVNode("div", _hoisted_6$5, [
|
|
2672
|
+
(vue.openBlock(true), vue.createElementBlock(
|
|
2673
|
+
vue.Fragment,
|
|
2674
|
+
null,
|
|
2675
|
+
vue.renderList(waveLevels.value, (lv, i) => {
|
|
2676
|
+
return vue.openBlock(), vue.createElementBlock(
|
|
2677
|
+
"span",
|
|
2678
|
+
{
|
|
2679
|
+
key: i,
|
|
2680
|
+
class: "fv-sender-recording-bar",
|
|
2681
|
+
style: vue.normalizeStyle({ height: `${Math.round(lv * 100)}%` })
|
|
2682
|
+
},
|
|
2683
|
+
null,
|
|
2684
|
+
4
|
|
2685
|
+
/* STYLE */
|
|
2686
|
+
);
|
|
2687
|
+
}),
|
|
2688
|
+
128
|
|
2689
|
+
/* KEYED_FRAGMENT */
|
|
2690
|
+
))
|
|
2691
|
+
]),
|
|
2692
|
+
vue.createElementVNode(
|
|
2693
|
+
"span",
|
|
2694
|
+
_hoisted_7$4,
|
|
2695
|
+
vue.toDisplayString(recordDuration.value),
|
|
2696
|
+
1
|
|
2697
|
+
/* TEXT */
|
|
2698
|
+
),
|
|
2699
|
+
vue.createVNode(vue.unref(elementPlus.ElButton), {
|
|
2700
|
+
class: "fv-sender-recording-confirm",
|
|
2701
|
+
type: "primary",
|
|
2702
|
+
circle: "",
|
|
2703
|
+
"aria-label": "\u786E\u8BA4\u5F55\u97F3",
|
|
2704
|
+
onClick: onRecordConfirm
|
|
2705
|
+
}, {
|
|
2706
|
+
default: vue.withCtx(() => [
|
|
2707
|
+
vue.createVNode(vue.unref(elementPlus.ElIcon), null, {
|
|
2708
|
+
default: vue.withCtx(() => [
|
|
2709
|
+
vue.createVNode(vue.unref(ElementPlusIconsVue.Check))
|
|
2710
|
+
]),
|
|
2711
|
+
_: 1
|
|
2712
|
+
/* STABLE */
|
|
2713
|
+
})
|
|
2714
|
+
]),
|
|
2715
|
+
_: 1
|
|
2716
|
+
/* STABLE */
|
|
2717
|
+
})
|
|
2718
|
+
],
|
|
2719
|
+
32
|
|
2720
|
+
/* NEED_HYDRATION */
|
|
2721
|
+
)) : (vue.openBlock(), vue.createElementBlock(
|
|
2722
|
+
"div",
|
|
2723
|
+
{
|
|
2724
|
+
key: 2,
|
|
2364
2725
|
class: "fv-sender-input",
|
|
2365
2726
|
style: vue.normalizeStyle(__props.inputWidth && __props.variant === "default" ? { width: __props.inputWidth } : void 0)
|
|
2366
2727
|
},
|
|
@@ -2380,7 +2741,7 @@
|
|
|
2380
2741
|
onPaste,
|
|
2381
2742
|
onFocus: _cache[0] || (_cache[0] = ($event) => isFocused.value = true),
|
|
2382
2743
|
onBlur: onTextareaBlur
|
|
2383
|
-
}, null, 40,
|
|
2744
|
+
}, null, 40, _hoisted_8$3),
|
|
2384
2745
|
vue.createCommentVNode(" \u6307\u4EE4\u5F39\u6846:\u5185\u7F6E el-popover,\u5B9A\u4F4D\u6309 triggerPopover* \u7CFB\u5217 "),
|
|
2385
2746
|
vue.createVNode(vue.unref(elementPlus.ElPopover), {
|
|
2386
2747
|
visible: triggerVisible.value && __props.triggerStrings.length > 0,
|
|
@@ -2411,12 +2772,12 @@
|
|
|
2411
2772
|
],
|
|
2412
2773
|
4
|
|
2413
2774
|
/* STYLE */
|
|
2414
|
-
),
|
|
2775
|
+
)),
|
|
2415
2776
|
vue.createCommentVNode(" \u64CD\u4F5C\u533A:#action-list \u5B58\u5728\u65F6\u5168\u81EA\u5B9A\u4E49,\u9690\u85CF\u5185\u7F6E\u6309\u94AE;#action-left \u7F6E\u4E8E\u64CD\u4F5C\u680F\u5DE6\u4FA7(\u4E0E\u63D0\u4EA4\u6309\u94AE\u540C\u884C) "),
|
|
2416
|
-
hasActionListSlot.value ? (vue.openBlock(), vue.createElementBlock("div",
|
|
2777
|
+
hasActionListSlot.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_9$2, [
|
|
2417
2778
|
vue.renderSlot(_ctx.$slots, "action-list")
|
|
2418
|
-
])) : (vue.openBlock(), vue.createElementBlock("div",
|
|
2419
|
-
_ctx.$slots["action-left"] ? (vue.openBlock(), vue.createElementBlock("div",
|
|
2779
|
+
])) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_10$2, [
|
|
2780
|
+
_ctx.$slots["action-left"] ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_11$2, [
|
|
2420
2781
|
vue.renderSlot(_ctx.$slots, "action-left")
|
|
2421
2782
|
])) : vue.createCommentVNode("v-if", true),
|
|
2422
2783
|
__props.clearable && value.value ? (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElButton), {
|
|
@@ -2439,7 +2800,7 @@
|
|
|
2439
2800
|
_: 1
|
|
2440
2801
|
/* STABLE */
|
|
2441
2802
|
}, 8, ["disabled"])) : vue.createCommentVNode("v-if", true),
|
|
2442
|
-
__props.allowSpeech ? (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElButton), {
|
|
2803
|
+
__props.allowSpeech && !vue.unref(isRecording) ? (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElButton), {
|
|
2443
2804
|
key: 2,
|
|
2444
2805
|
class: "fv-sender-speech",
|
|
2445
2806
|
text: "",
|
|
@@ -2459,29 +2820,49 @@
|
|
|
2459
2820
|
_: 1
|
|
2460
2821
|
/* STABLE */
|
|
2461
2822
|
}, 8, ["disabled"])) : vue.createCommentVNode("v-if", true),
|
|
2462
|
-
vue.createCommentVNode(" loading \u2192 \u505C\u6B62\u6309\u94AE
|
|
2823
|
+
vue.createCommentVNode(" loading \u2192 \u505C\u6B62\u6309\u94AE:SVG \u5149\u5E26\u6CBF\u8FB9\u6846\u5300\u901F\u6D41\u8F6C,\u65B9/\u5706\u6309\u94AE\u5747\u9002\u7528 "),
|
|
2463
2824
|
__props.loading ? (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElButton), {
|
|
2464
2825
|
key: 3,
|
|
2465
2826
|
class: "fv-sender-stop",
|
|
2466
2827
|
type: "primary",
|
|
2828
|
+
circle: __props.round,
|
|
2467
2829
|
"aria-label": "\u505C\u6B62",
|
|
2468
2830
|
onClick: cancel
|
|
2469
2831
|
}, {
|
|
2470
2832
|
default: vue.withCtx(() => [
|
|
2471
2833
|
vue.createVNode(vue.unref(elementPlus.ElIcon), null, {
|
|
2472
2834
|
default: vue.withCtx(() => [
|
|
2473
|
-
vue.createVNode(
|
|
2835
|
+
vue.createVNode(_sfc_main$e)
|
|
2474
2836
|
]),
|
|
2475
2837
|
_: 1
|
|
2476
2838
|
/* STABLE */
|
|
2477
|
-
})
|
|
2839
|
+
}),
|
|
2840
|
+
vue.createCommentVNode('\r\n \u6CBF\u8FB9\u6846\u6D41\u8F6C\u7684\u6E10\u53D8\u5149\u5E26(\u8682\u8681\u7EBF\u539F\u7406,pathLength=100 \u5F52\u4E00\u5468\u957F,dashoffset \u5300\u901F\u5E73\u79FB)\u3002\r\n \u6E10\u53D8\u96BE\u70B9:linearGradient \u6CBF\u76F4\u7EBF\u8DEF\u5F84\u5B9A\u4E49,\u65E0\u6CD5\u6CBF\u95ED\u5408\u6298\u7EBF\u5468\u957F\u6E10\u53D8 \u2014\u2014\r\n \u6545\u7528 6 \u5C42\u957F\u77ED/\u900F\u660E\u5EA6\u53CC\u9012\u51CF\u7684\u540C\u76F8\u4F4D dash \u53E0\u52A0,\u79BB\u6563\u9636\u68AF\u903C\u8FD1\u8FDE\u7EED\u6E10\u53D8:\r\n \u6BCF\u5C42\u957F\u5EA6\u4F9D\u6B21\u9012\u589E(\u5E26\u5934\u6700\u77ED\u6700\u5B9E,\u5E26\u5C3E\u6700\u957F\u6700\u865A),\u900F\u660E\u5EA6\u4F9D\u6B21\u9012\u51CF,\r\n \u53E0\u52A0\u540E\u7684\u900F\u660E\u5EA6\u5256\u9762\u4E3A\u591A\u7EA7\u53F0\u9636 \u2192 \u8FD0\u52A8\u4E2D\u8BFB\u4F5C"\u5934\u4EAE\u5C3E\u9690"\u7684\u8FDE\u7EED\u62D6\u5F71\u3002\r\n \u5C42\u6570\u7531 RING_LAYERS \u5E38\u91CF\u9A71\u52A8,\u900F\u660E\u5EA6\u63D2\u503C\u5728 CSS \u91CC\u6309 --ring-alpha \u53D8\u91CF\u7B97\u3002\r\n '),
|
|
2841
|
+
(vue.openBlock(), vue.createElementBlock("svg", _hoisted_12$2, [
|
|
2842
|
+
(vue.openBlock(true), vue.createElementBlock(
|
|
2843
|
+
vue.Fragment,
|
|
2844
|
+
null,
|
|
2845
|
+
vue.renderList(ringLayers.value, (layer, i) => {
|
|
2846
|
+
return vue.openBlock(), vue.createElementBlock("rect", {
|
|
2847
|
+
key: i,
|
|
2848
|
+
class: "fv-sender-stop-ring-rect",
|
|
2849
|
+
style: vue.normalizeStyle(layer.style),
|
|
2850
|
+
rx: __props.round ? "50%" : 2.5,
|
|
2851
|
+
pathLength: "100"
|
|
2852
|
+
}, null, 12, _hoisted_13$2);
|
|
2853
|
+
}),
|
|
2854
|
+
128
|
|
2855
|
+
/* KEYED_FRAGMENT */
|
|
2856
|
+
))
|
|
2857
|
+
]))
|
|
2478
2858
|
]),
|
|
2479
2859
|
_: 1
|
|
2480
2860
|
/* STABLE */
|
|
2481
|
-
})) : (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElButton), {
|
|
2861
|
+
}, 8, ["circle"])) : (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElButton), {
|
|
2482
2862
|
key: 4,
|
|
2483
2863
|
class: "fv-sender-submit",
|
|
2484
2864
|
type: "primary",
|
|
2865
|
+
circle: __props.round,
|
|
2485
2866
|
disabled: !submitable(),
|
|
2486
2867
|
"aria-label": "\u53D1\u9001",
|
|
2487
2868
|
onClick: doSubmit
|
|
@@ -2497,13 +2878,13 @@
|
|
|
2497
2878
|
]),
|
|
2498
2879
|
_: 1
|
|
2499
2880
|
/* STABLE */
|
|
2500
|
-
}, 8, ["disabled"]))
|
|
2881
|
+
}, 8, ["circle", "disabled"]))
|
|
2501
2882
|
]))
|
|
2502
2883
|
]),
|
|
2503
2884
|
vue.createCommentVNode(" /fv-sender-row ")
|
|
2504
2885
|
]),
|
|
2505
2886
|
vue.createCommentVNode(" \u5E95\u90E8 "),
|
|
2506
|
-
_ctx.$slots.footer ? (vue.openBlock(), vue.createElementBlock("div",
|
|
2887
|
+
_ctx.$slots.footer ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_14$2, [
|
|
2507
2888
|
vue.renderSlot(_ctx.$slots, "footer")
|
|
2508
2889
|
])) : vue.createCommentVNode("v-if", true)
|
|
2509
2890
|
],
|
|
@@ -5808,7 +6189,7 @@ ${fileParts.join("\n")}` });
|
|
|
5808
6189
|
"auto-collapse": __props.autoCollapse
|
|
5809
6190
|
}, {
|
|
5810
6191
|
content: vue.withCtx(() => [
|
|
5811
|
-
vue.createVNode(_sfc_main$
|
|
6192
|
+
vue.createVNode(_sfc_main$i, {
|
|
5812
6193
|
content: __props.message.reasoning ?? ""
|
|
5813
6194
|
}, null, 8, ["content"])
|
|
5814
6195
|
]),
|
|
@@ -5823,7 +6204,7 @@ ${fileParts.join("\n")}` });
|
|
|
5823
6204
|
vue.toDisplayString(__props.message.error || "\u751F\u6210\u5931\u8D25"),
|
|
5824
6205
|
1
|
|
5825
6206
|
/* TEXT */
|
|
5826
|
-
)) : __props.message.content ? (vue.openBlock(), vue.createBlock(_sfc_main$
|
|
6207
|
+
)) : __props.message.content ? (vue.openBlock(), vue.createBlock(_sfc_main$i, {
|
|
5827
6208
|
key: 1,
|
|
5828
6209
|
content: __props.message.content
|
|
5829
6210
|
}, null, 8, ["content"])) : __props.message.status === "pending" || __props.message.status === "streaming" ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_12, [
|
|
@@ -6394,7 +6775,10 @@ ${fileParts.join("\n")}` });
|
|
|
6394
6775
|
}
|
|
6395
6776
|
});
|
|
6396
6777
|
|
|
6397
|
-
const _hoisted_1 = {
|
|
6778
|
+
const _hoisted_1 = {
|
|
6779
|
+
key: 0,
|
|
6780
|
+
class: "fv-ai-chat-header"
|
|
6781
|
+
};
|
|
6398
6782
|
const _hoisted_2 = { class: "fv-ai-chat-title" };
|
|
6399
6783
|
const _hoisted_3 = { class: "fv-ai-chat-body" };
|
|
6400
6784
|
const _hoisted_4 = { class: "fv-ai-chat-footer" };
|
|
@@ -6409,7 +6793,10 @@ ${fileParts.join("\n")}` });
|
|
|
6409
6793
|
adapter: { type: Object, required: false, default: void 0 },
|
|
6410
6794
|
messages: { type: Array, required: false, default: void 0 },
|
|
6411
6795
|
title: { type: String, required: false, default: "" },
|
|
6796
|
+
showHeader: { type: Boolean, required: false, default: false },
|
|
6797
|
+
centerWelcome: { type: Boolean, required: false, default: true },
|
|
6412
6798
|
placeholder: { type: String, required: false, default: "" },
|
|
6799
|
+
senderShadow: { type: Boolean, required: false, default: true },
|
|
6413
6800
|
suggestions: { type: Array, required: false, default: () => [] },
|
|
6414
6801
|
features: { type: Object, required: false, default: () => ({}) },
|
|
6415
6802
|
allowAttachments: { type: Boolean, required: false, default: false },
|
|
@@ -6457,6 +6844,7 @@ ${fileParts.join("\n")}` });
|
|
|
6457
6844
|
const { send, stop: stopChat, regenerate: regenerateChat, msgs, isLoading } = chat;
|
|
6458
6845
|
const nodeTable = chat.messages;
|
|
6459
6846
|
const chain = chat.chain;
|
|
6847
|
+
const isCenterWelcome = vue.computed(() => props.centerWelcome && chain.value.length === 0);
|
|
6460
6848
|
let flushTimer = null;
|
|
6461
6849
|
vue.watch(chain, (c) => {
|
|
6462
6850
|
if (flushTimer) clearTimeout(flushTimer);
|
|
@@ -6622,11 +7010,11 @@ ${fileParts.join("\n")}` });
|
|
|
6622
7010
|
return vue.openBlock(), vue.createElementBlock(
|
|
6623
7011
|
"div",
|
|
6624
7012
|
{
|
|
6625
|
-
class: vue.normalizeClass(["fv-ai-chat", { "is-dark": isDark.value }])
|
|
7013
|
+
class: vue.normalizeClass(["fv-ai-chat", { "is-dark": isDark.value, "is-center-welcome": isCenterWelcome.value }])
|
|
6626
7014
|
},
|
|
6627
7015
|
[
|
|
6628
7016
|
vue.createCommentVNode(" \u9876\u680F:\u6807\u9898 + \u6A21\u578B\u9009\u62E9\u5668(models \u6709\u503C\u65F6 multiple) "),
|
|
6629
|
-
vue.
|
|
7017
|
+
__props.showHeader ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [
|
|
6630
7018
|
vue.createElementVNode(
|
|
6631
7019
|
"div",
|
|
6632
7020
|
_hoisted_2,
|
|
@@ -6662,7 +7050,7 @@ ${fileParts.join("\n")}` });
|
|
|
6662
7050
|
_: 1
|
|
6663
7051
|
/* STABLE */
|
|
6664
7052
|
}, 8, ["modelValue"])) : vue.createCommentVNode("v-if", true)
|
|
6665
|
-
]),
|
|
7053
|
+
])) : vue.createCommentVNode("v-if", true),
|
|
6666
7054
|
vue.createCommentVNode(" \u6D88\u606F\u533A:\u7A7A\u65F6 Welcome + Prompts(AiMessages \u5185\u7F6E);\u5F00\u542F artifacts \u65F6\u53F3\u4FA7\u62FC\u9884\u89C8\u9762\u677F "),
|
|
6667
7055
|
vue.createElementVNode("div", _hoisted_3, [
|
|
6668
7056
|
vue.createVNode(_sfc_main$2, {
|
|
@@ -6713,6 +7101,7 @@ ${fileParts.join("\n")}` });
|
|
|
6713
7101
|
vue.createVNode(_sfc_main$d, {
|
|
6714
7102
|
placeholder: __props.placeholder,
|
|
6715
7103
|
loading: vue.unref(isLoading),
|
|
7104
|
+
shadow: __props.senderShadow,
|
|
6716
7105
|
variant: "updown",
|
|
6717
7106
|
maxlength: 8e3,
|
|
6718
7107
|
"allow-empty-submit": __props.allowAttachments && !!pendingFiles.value.length,
|
|
@@ -6757,7 +7146,7 @@ ${fileParts.join("\n")}` });
|
|
|
6757
7146
|
]),
|
|
6758
7147
|
key: "2"
|
|
6759
7148
|
} : void 0
|
|
6760
|
-
]), 1032, ["placeholder", "loading", "allow-empty-submit"])
|
|
7149
|
+
]), 1032, ["placeholder", "loading", "shadow", "allow-empty-submit"])
|
|
6761
7150
|
])
|
|
6762
7151
|
],
|
|
6763
7152
|
2
|
|
@@ -6888,105 +7277,6 @@ ${fileParts.join("\n")}` });
|
|
|
6888
7277
|
});
|
|
6889
7278
|
}
|
|
6890
7279
|
|
|
6891
|
-
function getSpeechRecognitionCtor() {
|
|
6892
|
-
if (typeof window === "undefined") return void 0;
|
|
6893
|
-
const w = window;
|
|
6894
|
-
const ctor = w.SpeechRecognition ?? w.webkitSpeechRecognition;
|
|
6895
|
-
return typeof ctor === "function" ? ctor : void 0;
|
|
6896
|
-
}
|
|
6897
|
-
function useRecord() {
|
|
6898
|
-
const isRecording = vue.ref(false);
|
|
6899
|
-
const text = vue.ref("");
|
|
6900
|
-
const audioUrl = vue.ref(null);
|
|
6901
|
-
const error = vue.ref(null);
|
|
6902
|
-
let mediaStream = null;
|
|
6903
|
-
let mediaRecorder = null;
|
|
6904
|
-
let chunks = [];
|
|
6905
|
-
let recognition = null;
|
|
6906
|
-
let finalText = "";
|
|
6907
|
-
function revokeAudioUrl() {
|
|
6908
|
-
if (audioUrl.value) {
|
|
6909
|
-
URL.revokeObjectURL(audioUrl.value);
|
|
6910
|
-
audioUrl.value = null;
|
|
6911
|
-
}
|
|
6912
|
-
}
|
|
6913
|
-
async function start() {
|
|
6914
|
-
if (isRecording.value) return;
|
|
6915
|
-
error.value = null;
|
|
6916
|
-
text.value = "";
|
|
6917
|
-
finalText = "";
|
|
6918
|
-
chunks = [];
|
|
6919
|
-
revokeAudioUrl();
|
|
6920
|
-
if (typeof MediaRecorder === "undefined" || !navigator.mediaDevices?.getUserMedia) {
|
|
6921
|
-
error.value = new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u97F3\u9891\u5F55\u5236");
|
|
6922
|
-
return;
|
|
6923
|
-
}
|
|
6924
|
-
try {
|
|
6925
|
-
mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
|
6926
|
-
} catch (e) {
|
|
6927
|
-
error.value = e instanceof Error ? e : new Error(String(e));
|
|
6928
|
-
return;
|
|
6929
|
-
}
|
|
6930
|
-
mediaRecorder = new MediaRecorder(mediaStream);
|
|
6931
|
-
mediaRecorder.ondataavailable = (e) => {
|
|
6932
|
-
if (e.data && e.data.size > 0) chunks.push(e.data);
|
|
6933
|
-
};
|
|
6934
|
-
mediaRecorder.onstop = () => {
|
|
6935
|
-
if (chunks.length > 0) {
|
|
6936
|
-
const blob = new Blob(chunks, { type: mediaRecorder?.mimeType || "audio/webm" });
|
|
6937
|
-
audioUrl.value = URL.createObjectURL(blob);
|
|
6938
|
-
}
|
|
6939
|
-
mediaStream?.getTracks().forEach((t) => t.stop());
|
|
6940
|
-
mediaStream = null;
|
|
6941
|
-
};
|
|
6942
|
-
const SRCtor = getSpeechRecognitionCtor();
|
|
6943
|
-
if (SRCtor) {
|
|
6944
|
-
try {
|
|
6945
|
-
recognition = new SRCtor();
|
|
6946
|
-
recognition.continuous = true;
|
|
6947
|
-
recognition.interimResults = true;
|
|
6948
|
-
recognition.onresult = (event) => {
|
|
6949
|
-
let interim = "";
|
|
6950
|
-
for (let i = event.resultIndex; i < event.results.length; i++) {
|
|
6951
|
-
const r = event.results[i];
|
|
6952
|
-
if (r.isFinal) finalText += r[0].transcript;
|
|
6953
|
-
else interim += r[0].transcript;
|
|
6954
|
-
}
|
|
6955
|
-
text.value = finalText + interim;
|
|
6956
|
-
};
|
|
6957
|
-
recognition.onerror = (ev) => {
|
|
6958
|
-
error.value = new Error(`\u8BED\u97F3\u8BC6\u522B\u9519\u8BEF: ${ev?.error ?? "unknown"}`);
|
|
6959
|
-
};
|
|
6960
|
-
recognition.start();
|
|
6961
|
-
} catch {
|
|
6962
|
-
recognition = null;
|
|
6963
|
-
}
|
|
6964
|
-
}
|
|
6965
|
-
mediaRecorder.start();
|
|
6966
|
-
isRecording.value = true;
|
|
6967
|
-
}
|
|
6968
|
-
function stop() {
|
|
6969
|
-
if (!isRecording.value) return;
|
|
6970
|
-
isRecording.value = false;
|
|
6971
|
-
try {
|
|
6972
|
-
recognition?.stop();
|
|
6973
|
-
} catch {
|
|
6974
|
-
}
|
|
6975
|
-
recognition = null;
|
|
6976
|
-
try {
|
|
6977
|
-
if (mediaRecorder && mediaRecorder.state !== "inactive") mediaRecorder.stop();
|
|
6978
|
-
} catch {
|
|
6979
|
-
}
|
|
6980
|
-
}
|
|
6981
|
-
if (vue.getCurrentInstance()) {
|
|
6982
|
-
vue.onBeforeUnmount(() => {
|
|
6983
|
-
stop();
|
|
6984
|
-
revokeAudioUrl();
|
|
6985
|
-
});
|
|
6986
|
-
}
|
|
6987
|
-
return { start, stop, isRecording, text, audioUrl, error };
|
|
6988
|
-
}
|
|
6989
|
-
|
|
6990
7280
|
class DetOmsError extends Error {
|
|
6991
7281
|
constructor(message, code) {
|
|
6992
7282
|
super(message);
|
|
@@ -7189,18 +7479,18 @@ ${fileParts.join("\n")}` });
|
|
|
7189
7479
|
exports.FvAiMessage = _sfc_main$3;
|
|
7190
7480
|
exports.FvAiMessages = _sfc_main$2;
|
|
7191
7481
|
exports.FvAttachments = _sfc_main$5;
|
|
7192
|
-
exports.FvBubble = _sfc_main$
|
|
7193
|
-
exports.FvBubbleList = _sfc_main$
|
|
7482
|
+
exports.FvBubble = _sfc_main$g;
|
|
7483
|
+
exports.FvBubbleList = _sfc_main$f;
|
|
7194
7484
|
exports.FvConversations = _sfc_main$4;
|
|
7195
7485
|
exports.FvEditorSender = _sfc_main$b;
|
|
7196
7486
|
exports.FvFilesCard = _sfc_main$6;
|
|
7197
|
-
exports.FvMarkdown = _sfc_main$
|
|
7487
|
+
exports.FvMarkdown = _sfc_main$i;
|
|
7198
7488
|
exports.FvMentionSender = _sfc_main$c;
|
|
7199
7489
|
exports.FvPrompts = _sfc_main$7;
|
|
7200
7490
|
exports.FvSender = _sfc_main$d;
|
|
7201
7491
|
exports.FvThinking = _sfc_main$a;
|
|
7202
7492
|
exports.FvThoughtChain = _sfc_main$9;
|
|
7203
|
-
exports.FvTypewriter = _sfc_main$
|
|
7493
|
+
exports.FvTypewriter = _sfc_main$h;
|
|
7204
7494
|
exports.FvWelcome = _sfc_main$8;
|
|
7205
7495
|
exports.OpenAIAdapter = OpenAIAdapter;
|
|
7206
7496
|
exports.XRequest = XRequest;
|