@fv-ui/ai-chat 0.1.1 → 0.3.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/lib/index.js CHANGED
@@ -1542,8 +1542,8 @@
1542
1542
  }
1543
1543
  }
1544
1544
 
1545
- const _hoisted_1$h = ["innerHTML"];
1546
- var _sfc_main$h = /* @__PURE__ */ vue.defineComponent({
1545
+ const _hoisted_1$i = ["innerHTML"];
1546
+ var _sfc_main$i = /* @__PURE__ */ vue.defineComponent({
1547
1547
  ...{ name: "FvMarkdown" },
1548
1548
  __name: "index",
1549
1549
  props: {
@@ -1566,12 +1566,12 @@
1566
1566
  return vue.openBlock(), vue.createElementBlock("div", {
1567
1567
  class: "fv-xmarkdown markdown-body",
1568
1568
  innerHTML: html.value
1569
- }, null, 8, _hoisted_1$h);
1569
+ }, null, 8, _hoisted_1$i);
1570
1570
  };
1571
1571
  }
1572
1572
  });
1573
1573
 
1574
- const _hoisted_1$g = {
1574
+ const _hoisted_1$h = {
1575
1575
  key: 1,
1576
1576
  class: "fv-typewriter-text"
1577
1577
  };
@@ -1579,7 +1579,7 @@
1579
1579
  key: 0,
1580
1580
  class: "fv-typewriter-suffix"
1581
1581
  };
1582
- var _sfc_main$g = /* @__PURE__ */ vue.defineComponent({
1582
+ var _sfc_main$h = /* @__PURE__ */ vue.defineComponent({
1583
1583
  ...{ name: "FvTypewriter" },
1584
1584
  __name: "index",
1585
1585
  props: {
@@ -1669,11 +1669,11 @@
1669
1669
  },
1670
1670
  [
1671
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 "),
1672
- __props.isMarkdown ? (vue.openBlock(), vue.createBlock(_sfc_main$h, {
1672
+ __props.isMarkdown ? (vue.openBlock(), vue.createBlock(_sfc_main$i, {
1673
1673
  key: 0,
1674
1674
  content: renderedContent.value,
1675
1675
  "is-typing": isTyping.value
1676
- }, null, 8, ["content", "is-typing"])) : (vue.openBlock(), vue.createElementBlock("span", _hoisted_1$g, [
1676
+ }, null, 8, ["content", "is-typing"])) : (vue.openBlock(), vue.createElementBlock("span", _hoisted_1$h, [
1677
1677
  vue.createTextVNode(
1678
1678
  vue.toDisplayString(renderedContent.value),
1679
1679
  1
@@ -1708,12 +1708,12 @@
1708
1708
  }
1709
1709
  });
1710
1710
 
1711
- const _hoisted_1$f = {
1711
+ const _hoisted_1$g = {
1712
1712
  key: 0,
1713
1713
  class: "fv-bubble-avatar"
1714
1714
  };
1715
1715
  const _hoisted_2$e = { class: "fv-bubble-main" };
1716
- const _hoisted_3$d = {
1716
+ const _hoisted_3$e = {
1717
1717
  key: 0,
1718
1718
  class: "fv-bubble-header"
1719
1719
  };
@@ -1729,7 +1729,7 @@
1729
1729
  key: 1,
1730
1730
  class: "fv-bubble-footer"
1731
1731
  };
1732
- var _sfc_main$f = /* @__PURE__ */ vue.defineComponent({
1732
+ var _sfc_main$g = /* @__PURE__ */ vue.defineComponent({
1733
1733
  ...{ name: "FvBubble" },
1734
1734
  __name: "index",
1735
1735
  props: {
@@ -1796,7 +1796,7 @@
1796
1796
  },
1797
1797
  [
1798
1798
  vue.createCommentVNode(" \u5934\u50CF:placement \u540C\u7F18\u6392\u5217(end \u7ECF row-reverse \u7FFB\u8F6C\u5230\u53F3\u4FA7),#avatar \u63D2\u69FD\u4F18\u5148 "),
1799
- _ctx.$slots.avatar || __props.avatar || __props.avatarIcon ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$f, [
1799
+ _ctx.$slots.avatar || __props.avatar || __props.avatarIcon ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$g, [
1800
1800
  _ctx.$slots.avatar ? vue.renderSlot(_ctx.$slots, "avatar", {}, void 0, void 0, 0) : (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElAvatar), {
1801
1801
  key: 1,
1802
1802
  size: __props.avatarSize || void 0,
@@ -1810,7 +1810,7 @@
1810
1810
  }, null, 8, ["size", "shape", "icon", "src", "src-set", "alt", "fit"]))
1811
1811
  ])) : vue.createCommentVNode("v-if", true),
1812
1812
  vue.createElementVNode("div", _hoisted_2$e, [
1813
- _ctx.$slots.header ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$d, [
1813
+ _ctx.$slots.header ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$e, [
1814
1814
  vue.renderSlot(_ctx.$slots, "header")
1815
1815
  ])) : vue.createCommentVNode("v-if", true),
1816
1816
  vue.createElementVNode(
@@ -1854,7 +1854,7 @@
1854
1854
  { key: 2 },
1855
1855
  [
1856
1856
  vue.createCommentVNode(" typing \u900F\u4F20 Typewriter;\u975E typing:isMarkdown \u2192 XMarkdown,\u5426\u5219\u7EAF\u6587\u672C "),
1857
- vue.createVNode(_sfc_main$g, {
1857
+ vue.createVNode(_sfc_main$h, {
1858
1858
  ref_key: "twRef",
1859
1859
  ref: twRef,
1860
1860
  content: __props.content,
@@ -1868,7 +1868,7 @@
1868
1868
  ],
1869
1869
  2112
1870
1870
  /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */
1871
- )) : __props.isMarkdown ? (vue.openBlock(), vue.createBlock(_sfc_main$h, {
1871
+ )) : __props.isMarkdown ? (vue.openBlock(), vue.createBlock(_sfc_main$i, {
1872
1872
  key: 3,
1873
1873
  content: __props.content
1874
1874
  }, null, 8, ["content"])) : (vue.openBlock(), vue.createElementBlock(
@@ -1894,8 +1894,8 @@
1894
1894
  }
1895
1895
  });
1896
1896
 
1897
- const _hoisted_1$e = { class: "fv-bubble-list" };
1898
- var _sfc_main$e = /* @__PURE__ */ vue.defineComponent({
1897
+ const _hoisted_1$f = { class: "fv-bubble-list" };
1898
+ var _sfc_main$f = /* @__PURE__ */ vue.defineComponent({
1899
1899
  ...{ name: "FvBubbleList" },
1900
1900
  __name: "index",
1901
1901
  props: {
@@ -1995,7 +1995,7 @@
1995
1995
  }));
1996
1996
  __expose({ scrollToTop, scrollToBottom, scrollToBubble });
1997
1997
  return (_ctx, _cache) => {
1998
- return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$e, [
1998
+ return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$f, [
1999
1999
  vue.createElementVNode(
2000
2000
  "div",
2001
2001
  {
@@ -2010,7 +2010,7 @@
2010
2010
  vue.Fragment,
2011
2011
  null,
2012
2012
  vue.renderList(__props.list, (item, index) => {
2013
- return vue.openBlock(), vue.createBlock(_sfc_main$f, vue.mergeProps({
2013
+ return vue.openBlock(), vue.createBlock(_sfc_main$g, vue.mergeProps({
2014
2014
  key: item.key,
2015
2015
  ref_for: true,
2016
2016
  ref: (el) => setBubbleRef(el, index)
@@ -2118,12 +2118,145 @@
2118
2118
  }
2119
2119
  });
2120
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
+
2121
2254
  const _hoisted_1$d = {
2122
2255
  key: 0,
2123
2256
  class: "fv-sender-header"
2124
2257
  };
2125
2258
  const _hoisted_2$d = { class: "fv-sender-body" };
2126
- const _hoisted_3$c = {
2259
+ const _hoisted_3$d = {
2127
2260
  key: 0,
2128
2261
  class: "fv-sender-body-top"
2129
2262
  };
@@ -2132,24 +2265,38 @@
2132
2265
  key: 0,
2133
2266
  class: "fv-sender-prefix"
2134
2267
  };
2135
- const _hoisted_6$5 = ["value", "placeholder", "disabled", "readonly"];
2136
- const _hoisted_7$4 = {
2137
- key: 1,
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,
2138
2276
  class: "fv-sender-actions"
2139
2277
  };
2140
- const _hoisted_8$3 = {
2141
- key: 2,
2278
+ const _hoisted_10$2 = {
2279
+ key: 4,
2142
2280
  class: "fv-sender-actions"
2143
2281
  };
2144
- const _hoisted_9$2 = {
2282
+ const _hoisted_11$2 = {
2145
2283
  key: 0,
2146
2284
  class: "fv-sender-action-left"
2147
2285
  };
2148
- const _hoisted_10$2 = {
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 = {
2149
2292
  key: 1,
2150
2293
  class: "fv-sender-footer"
2151
2294
  };
2152
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;
2153
2300
  var _sfc_main$d = /* @__PURE__ */ vue.defineComponent({
2154
2301
  ...{ name: "FvSender" },
2155
2302
  __name: "index",
@@ -2163,6 +2310,8 @@
2163
2310
  disabled: { type: Boolean, required: false, default: false },
2164
2311
  readOnly: { type: Boolean, required: false, default: false },
2165
2312
  clearable: { type: Boolean, required: false, default: false },
2313
+ round: { type: Boolean, required: false, default: true },
2314
+ shadow: { type: Boolean, required: false, default: false },
2166
2315
  inputWidth: { type: String, required: false, default: "" },
2167
2316
  variant: { type: String, required: false, default: "default" },
2168
2317
  submitBtnDisabled: { type: Boolean, required: false, default: false },
@@ -2335,11 +2484,136 @@
2335
2484
  for (const f of Array.from(files)) emit("paste-file", f);
2336
2485
  }
2337
2486
  }
2338
- function onSpeechClick() {
2339
- console.warn("useRecord \u672A\u63A5\u5165,Task 15 \u63A5\u7EBF");
2487
+ const { start: startRecord, stop: stopRecord, isRecording, text: recordText, error: recordError } = useRecord();
2488
+ async function exitRecordingFocusInput() {
2489
+ await vue.nextTick();
2490
+ focus("end");
2340
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();
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
+ });
2341
2603
  const inputDisabled = vue.computed(() => props.disabled || props.loading);
2342
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
+ );
2343
2617
  const slots = vue.useSlots();
2344
2618
  const hasActionListSlot = vue.computed(() => !!slots["action-list"]);
2345
2619
  __expose({ submit, cancel, clear, focus, blur, openHeader, closeHeader });
@@ -2347,7 +2621,7 @@
2347
2621
  return vue.openBlock(), vue.createElementBlock(
2348
2622
  "div",
2349
2623
  {
2350
- 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 }]])
2351
2625
  },
2352
2626
  [
2353
2627
  vue.createCommentVNode(" \u5934\u90E8:#header \u63D2\u69FD,openHeader/closeHeader \u63A7\u5236 "),
@@ -2356,7 +2630,7 @@
2356
2630
  ])) : vue.createCommentVNode("v-if", true),
2357
2631
  vue.createElementVNode("div", _hoisted_2$d, [
2358
2632
  vue.createCommentVNode(" body \u9876\u90E8\u533A:\u5728\u8F93\u5165\u6846\u4E0A\u65B9\u3001body \u8FB9\u6846\u5185(\u5982\u9644\u4EF6\u5217\u8868) "),
2359
- _ctx.$slots["body-top"] ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$c, [
2633
+ _ctx.$slots["body-top"] ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$d, [
2360
2634
  vue.renderSlot(_ctx.$slots, "body-top")
2361
2635
  ])) : vue.createCommentVNode("v-if", true),
2362
2636
  vue.createCommentVNode(" \u4E3B\u4F53\u884C:\u524D\u7F00 + \u8F93\u5165 + \u64CD\u4F5C "),
@@ -2365,9 +2639,89 @@
2365
2639
  _ctx.$slots.prefix ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_5$7, [
2366
2640
  vue.renderSlot(_ctx.$slots, "prefix")
2367
2641
  ])) : vue.createCommentVNode("v-if", true),
2368
- vue.createElementVNode(
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(
2369
2644
  "div",
2370
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,
2371
2725
  class: "fv-sender-input",
2372
2726
  style: vue.normalizeStyle(__props.inputWidth && __props.variant === "default" ? { width: __props.inputWidth } : void 0)
2373
2727
  },
@@ -2387,7 +2741,7 @@
2387
2741
  onPaste,
2388
2742
  onFocus: _cache[0] || (_cache[0] = ($event) => isFocused.value = true),
2389
2743
  onBlur: onTextareaBlur
2390
- }, null, 40, _hoisted_6$5),
2744
+ }, null, 40, _hoisted_8$3),
2391
2745
  vue.createCommentVNode(" \u6307\u4EE4\u5F39\u6846:\u5185\u7F6E el-popover,\u5B9A\u4F4D\u6309 triggerPopover* \u7CFB\u5217 "),
2392
2746
  vue.createVNode(vue.unref(elementPlus.ElPopover), {
2393
2747
  visible: triggerVisible.value && __props.triggerStrings.length > 0,
@@ -2418,12 +2772,12 @@
2418
2772
  ],
2419
2773
  4
2420
2774
  /* STYLE */
2421
- ),
2775
+ )),
2422
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) "),
2423
- hasActionListSlot.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_7$4, [
2777
+ hasActionListSlot.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_9$2, [
2424
2778
  vue.renderSlot(_ctx.$slots, "action-list")
2425
- ])) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_8$3, [
2426
- _ctx.$slots["action-left"] ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_9$2, [
2779
+ ])) : (vue.openBlock(), vue.createElementBlock("div", _hoisted_10$2, [
2780
+ _ctx.$slots["action-left"] ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_11$2, [
2427
2781
  vue.renderSlot(_ctx.$slots, "action-left")
2428
2782
  ])) : vue.createCommentVNode("v-if", true),
2429
2783
  __props.clearable && value.value ? (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElButton), {
@@ -2446,7 +2800,7 @@
2446
2800
  _: 1
2447
2801
  /* STABLE */
2448
2802
  }, 8, ["disabled"])) : vue.createCommentVNode("v-if", true),
2449
- __props.allowSpeech ? (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElButton), {
2803
+ __props.allowSpeech && !vue.unref(isRecording) ? (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElButton), {
2450
2804
  key: 2,
2451
2805
  class: "fv-sender-speech",
2452
2806
  text: "",
@@ -2466,29 +2820,49 @@
2466
2820
  _: 1
2467
2821
  /* STABLE */
2468
2822
  }, 8, ["disabled"])) : vue.createCommentVNode("v-if", true),
2469
- vue.createCommentVNode(" loading \u2192 \u505C\u6B62\u6309\u94AE;\u5426\u5219\u53D1\u9001\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 "),
2470
2824
  __props.loading ? (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElButton), {
2471
2825
  key: 3,
2472
2826
  class: "fv-sender-stop",
2473
2827
  type: "primary",
2828
+ circle: __props.round,
2474
2829
  "aria-label": "\u505C\u6B62",
2475
2830
  onClick: cancel
2476
2831
  }, {
2477
2832
  default: vue.withCtx(() => [
2478
2833
  vue.createVNode(vue.unref(elementPlus.ElIcon), null, {
2479
2834
  default: vue.withCtx(() => [
2480
- vue.createVNode(vue.unref(ElementPlusIconsVue.VideoPause))
2835
+ vue.createVNode(_sfc_main$e)
2481
2836
  ]),
2482
2837
  _: 1
2483
2838
  /* STABLE */
2484
- })
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
+ ]))
2485
2858
  ]),
2486
2859
  _: 1
2487
2860
  /* STABLE */
2488
- })) : (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElButton), {
2861
+ }, 8, ["circle"])) : (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElButton), {
2489
2862
  key: 4,
2490
2863
  class: "fv-sender-submit",
2491
2864
  type: "primary",
2865
+ circle: __props.round,
2492
2866
  disabled: !submitable(),
2493
2867
  "aria-label": "\u53D1\u9001",
2494
2868
  onClick: doSubmit
@@ -2504,13 +2878,13 @@
2504
2878
  ]),
2505
2879
  _: 1
2506
2880
  /* STABLE */
2507
- }, 8, ["disabled"]))
2881
+ }, 8, ["circle", "disabled"]))
2508
2882
  ]))
2509
2883
  ]),
2510
2884
  vue.createCommentVNode(" /fv-sender-row ")
2511
2885
  ]),
2512
2886
  vue.createCommentVNode(" \u5E95\u90E8 "),
2513
- _ctx.$slots.footer ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_10$2, [
2887
+ _ctx.$slots.footer ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_14$2, [
2514
2888
  vue.renderSlot(_ctx.$slots, "footer")
2515
2889
  ])) : vue.createCommentVNode("v-if", true)
2516
2890
  ],
@@ -2526,7 +2900,7 @@
2526
2900
  key: 0,
2527
2901
  class: "fv-mention-title"
2528
2902
  };
2529
- const _hoisted_3$b = {
2903
+ const _hoisted_3$c = {
2530
2904
  key: 1,
2531
2905
  class: "fv-mention-loading"
2532
2906
  };
@@ -2894,7 +3268,7 @@
2894
3268
  1
2895
3269
  /* TEXT */
2896
3270
  )) : vue.createCommentVNode("v-if", true),
2897
- asyncLoading.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$b, "\u52A0\u8F7D\u4E2D\u2026")) : (vue.openBlock(true), vue.createElementBlock(
3271
+ asyncLoading.value ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$c, "\u52A0\u8F7D\u4E2D\u2026")) : (vue.openBlock(true), vue.createElementBlock(
2898
3272
  vue.Fragment,
2899
3273
  { key: 2 },
2900
3274
  vue.renderList(candidates.value, (item, index) => {
@@ -2953,7 +3327,7 @@
2953
3327
  class: "fv-editor-header"
2954
3328
  };
2955
3329
  const _hoisted_2$b = { class: "fv-editor-body" };
2956
- const _hoisted_3$a = {
3330
+ const _hoisted_3$b = {
2957
3331
  key: 0,
2958
3332
  class: "fv-editor-prefix"
2959
3333
  };
@@ -3731,7 +4105,7 @@
3731
4105
  ])) : vue.createCommentVNode("v-if", true),
3732
4106
  vue.createElementVNode("div", _hoisted_2$b, [
3733
4107
  vue.createCommentVNode(" \u524D\u7F00\u533A "),
3734
- _ctx.$slots.prefix ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$a, [
4108
+ _ctx.$slots.prefix ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$b, [
3735
4109
  vue.renderSlot(_ctx.$slots, "prefix")
3736
4110
  ])) : vue.createCommentVNode("v-if", true),
3737
4111
  vue.createElementVNode("div", _hoisted_4$a, [
@@ -4016,7 +4390,7 @@
4016
4390
  key: 0,
4017
4391
  class: "fv-thinking-dot"
4018
4392
  };
4019
- const _hoisted_3$9 = { class: "fv-thinking-label" };
4393
+ const _hoisted_3$a = { class: "fv-thinking-label" };
4020
4394
  const _hoisted_4$9 = ["hidden"];
4021
4395
  var _sfc_main$a = /* @__PURE__ */ vue.defineComponent({
4022
4396
  ...{ name: "FvThinking" },
@@ -4112,7 +4486,7 @@
4112
4486
  }))
4113
4487
  ])
4114
4488
  ]),
4115
- vue.createElementVNode("span", _hoisted_3$9, [
4489
+ vue.createElementVNode("span", _hoisted_3$a, [
4116
4490
  vue.renderSlot(_ctx.$slots, "label", {}, () => [
4117
4491
  vue.createTextVNode(
4118
4492
  vue.toDisplayString(statusText.value),
@@ -4172,7 +4546,7 @@
4172
4546
  key: 4,
4173
4547
  class: "fv-chain-dot"
4174
4548
  };
4175
- const _hoisted_3$8 = { class: "fv-chain-main" };
4549
+ const _hoisted_3$9 = { class: "fv-chain-main" };
4176
4550
  const _hoisted_4$8 = { class: "fv-chain-title" };
4177
4551
  const _hoisted_5$4 = {
4178
4552
  key: 0,
@@ -4247,7 +4621,7 @@
4247
4621
  })) : (vue.openBlock(), vue.createElementBlock("span", _hoisted_2$9))
4248
4622
  ]),
4249
4623
  vue.createCommentVNode(" \u5185\u5BB9:#content \u4F5C\u7528\u57DF\u63D2\u69FD\u8986\u76D6\u9ED8\u8BA4 title/description "),
4250
- vue.createElementVNode("div", _hoisted_3$8, [
4624
+ vue.createElementVNode("div", _hoisted_3$9, [
4251
4625
  vue.renderSlot(_ctx.$slots, "content", {
4252
4626
  item,
4253
4627
  index
@@ -4286,7 +4660,7 @@
4286
4660
 
4287
4661
  const _hoisted_1$8 = { class: "fv-welcome" };
4288
4662
  const _hoisted_2$8 = { class: "fv-welcome-icon" };
4289
- const _hoisted_3$7 = {
4663
+ const _hoisted_3$8 = {
4290
4664
  key: 0,
4291
4665
  class: "fv-welcome-name"
4292
4666
  };
@@ -4333,7 +4707,7 @@
4333
4707
  ])
4334
4708
  ]),
4335
4709
  vue.createCommentVNode(" #name \u63D2\u69FD\u8986\u76D6\u540D\u79F0 "),
4336
- _ctx.$slots.name || __props.name ? (vue.openBlock(), vue.createElementBlock("h2", _hoisted_3$7, [
4710
+ _ctx.$slots.name || __props.name ? (vue.openBlock(), vue.createElementBlock("h2", _hoisted_3$8, [
4337
4711
  vue.renderSlot(_ctx.$slots, "name", {}, () => [
4338
4712
  vue.createTextVNode(
4339
4713
  vue.toDisplayString(__props.name),
@@ -4362,7 +4736,7 @@
4362
4736
  key: 0,
4363
4737
  class: "fv-prompts-title"
4364
4738
  };
4365
- const _hoisted_3$6 = { class: "fv-prompts-list" };
4739
+ const _hoisted_3$7 = { class: "fv-prompts-list" };
4366
4740
  const _hoisted_4$6 = ["role", "tabindex", "onClick"];
4367
4741
  const _hoisted_5$3 = { class: "fv-prompt-item-main" };
4368
4742
  const _hoisted_6$2 = { class: "fv-prompt-item-text" };
@@ -4405,7 +4779,7 @@
4405
4779
  1
4406
4780
  /* TEXT */
4407
4781
  )) : vue.createCommentVNode("v-if", true),
4408
- vue.createElementVNode("div", _hoisted_3$6, [
4782
+ vue.createElementVNode("div", _hoisted_3$7, [
4409
4783
  (vue.openBlock(true), vue.createElementBlock(
4410
4784
  vue.Fragment,
4411
4785
  null,
@@ -4470,7 +4844,7 @@
4470
4844
 
4471
4845
  const _hoisted_1$6 = ["src", "alt"];
4472
4846
  const _hoisted_2$6 = { class: "fv-files-card-info" };
4473
- const _hoisted_3$5 = ["title"];
4847
+ const _hoisted_3$6 = ["title"];
4474
4848
  const _hoisted_4$5 = {
4475
4849
  key: 0,
4476
4850
  class: "fv-files-card-size"
@@ -4612,7 +4986,7 @@
4612
4986
  vue.createElementVNode("div", {
4613
4987
  class: "fv-files-card-name",
4614
4988
  title: __props.name
4615
- }, vue.toDisplayString(__props.name), 9, _hoisted_3$5),
4989
+ }, vue.toDisplayString(__props.name), 9, _hoisted_3$6),
4616
4990
  sizeText.value ? (vue.openBlock(), vue.createElementBlock(
4617
4991
  "div",
4618
4992
  _hoisted_4$5,
@@ -4656,7 +5030,7 @@
4656
5030
 
4657
5031
  const _hoisted_1$5 = { class: "fv-attachments" };
4658
5032
  const _hoisted_2$5 = ["accept"];
4659
- const _hoisted_3$4 = ["disabled"];
5033
+ const _hoisted_3$5 = ["disabled"];
4660
5034
  const _hoisted_4$4 = {
4661
5035
  key: 1,
4662
5036
  class: "fv-attachments-list"
@@ -4798,7 +5172,7 @@
4798
5172
  _: 1
4799
5173
  /* STABLE */
4800
5174
  })
4801
- ], 10, _hoisted_3$4)) : vue.createCommentVNode("v-if", true),
5175
+ ], 10, _hoisted_3$5)) : vue.createCommentVNode("v-if", true),
4802
5176
  vue.createCommentVNode(" \u6587\u4EF6\u5217\u8868:FilesCard;#list-thumb/#list-item \u8986\u76D6\u9879\u6E32\u67D3(triggerOnly \u5E03\u5C40\u4E0D\u6E32\u67D3) "),
4803
5177
  list.value.length && __props.layout !== "triggerOnly" ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_4$4, [
4804
5178
  (vue.openBlock(true), vue.createElementBlock(
@@ -4876,7 +5250,7 @@
4876
5250
  class: "fv-conversations-group-title"
4877
5251
  };
4878
5252
  const _hoisted_2$4 = ["onClick"];
4879
- const _hoisted_3$3 = { class: "fv-conversations-item-content" };
5253
+ const _hoisted_3$4 = { class: "fv-conversations-item-content" };
4880
5254
  const _hoisted_4$3 = ["title"];
4881
5255
  var _sfc_main$4 = /* @__PURE__ */ vue.defineComponent({
4882
5256
  ...{ name: "FvConversations" },
@@ -5008,7 +5382,7 @@
5008
5382
  onClick: ($event) => onItemClick(item)
5009
5383
  }, [
5010
5384
  vue.createCommentVNode(" \u884C\u5185\u5BB9:#item \u8986\u76D6\u6574\u884C(\u9ED8\u8BA4\u6E32\u67D3 label) "),
5011
- vue.createElementVNode("div", _hoisted_3$3, [
5385
+ vue.createElementVNode("div", _hoisted_3$4, [
5012
5386
  vue.renderSlot(_ctx.$slots, "item", {
5013
5387
  item,
5014
5388
  active: isActive(item)
@@ -5646,16 +6020,22 @@ ${fileParts.join("\n")}` });
5646
6020
  }
5647
6021
 
5648
6022
  const _hoisted_1$3 = ["data-message-id"];
5649
- const _hoisted_2$3 = { class: "fv-msg-avatar" };
5650
- const _hoisted_3$2 = { class: "fv-msg-main" };
5651
- const _hoisted_4$2 = { class: "fv-msg-header" };
6023
+ const _hoisted_2$3 = {
6024
+ key: 0,
6025
+ class: "fv-msg-avatar"
6026
+ };
6027
+ const _hoisted_3$3 = { class: "fv-msg-main" };
6028
+ const _hoisted_4$2 = {
6029
+ key: 0,
6030
+ class: "fv-msg-header"
6031
+ };
5652
6032
  const _hoisted_5 = { class: "fv-msg-role" };
5653
6033
  const _hoisted_6 = {
5654
6034
  key: 0,
5655
6035
  class: "fv-msg-model"
5656
6036
  };
5657
6037
  const _hoisted_7 = {
5658
- key: 0,
6038
+ key: 1,
5659
6039
  class: "fv-msg-files"
5660
6040
  };
5661
6041
  const _hoisted_8 = ["src", "alt"];
@@ -5664,7 +6044,7 @@ ${fileParts.join("\n")}` });
5664
6044
  class: "fv-msg-file-name"
5665
6045
  };
5666
6046
  const _hoisted_10 = {
5667
- key: 2,
6047
+ key: 3,
5668
6048
  class: "fv-msg-content"
5669
6049
  };
5670
6050
  const _hoisted_11 = {
@@ -5676,11 +6056,11 @@ ${fileParts.join("\n")}` });
5676
6056
  class: "fv-msg-pending"
5677
6057
  };
5678
6058
  const _hoisted_13 = {
5679
- key: 3,
6059
+ key: 4,
5680
6060
  class: "fv-msg-content fv-msg-content-user"
5681
6061
  };
5682
6062
  const _hoisted_14 = {
5683
- key: 4,
6063
+ key: 5,
5684
6064
  class: "fv-msg-actions"
5685
6065
  };
5686
6066
  const _hoisted_15 = {
@@ -5696,6 +6076,8 @@ ${fileParts.join("\n")}` });
5696
6076
  branch: { type: Object, required: false, default: void 0 },
5697
6077
  showActions: { type: Boolean, required: false, default: true },
5698
6078
  showBranchSwitch: { type: Boolean, required: false, default: true },
6079
+ showUserAvatar: { type: Boolean, required: false, default: true },
6080
+ showUserName: { type: Boolean, required: false, default: true },
5699
6081
  autoCollapse: { type: Boolean, required: false, default: true }
5700
6082
  },
5701
6083
  emits: ["copy", "regenerate", "branch-change"],
@@ -5726,8 +6108,8 @@ ${fileParts.join("\n")}` });
5726
6108
  class: vue.normalizeClass(["fv-msg", [`is-${__props.message.role}`, { "is-error": __props.message.status === "error", "is-user": isUser.value }]]),
5727
6109
  "data-message-id": __props.message.id
5728
6110
  }, [
5729
- vue.createCommentVNode(" \u5934\u50CF:user \u53F3\u4FA7(\u81EA\u8EAB);assistant \u5DE6\u4FA7 "),
5730
- vue.createElementVNode("div", _hoisted_2$3, [
6111
+ vue.createCommentVNode(" \u5934\u50CF:user \u53F3\u4FA7(\u81EA\u8EAB),assistant \u5DE6\u4FA7;showUserAvatar=false \u65F6\u7528\u6237\u6D88\u606F\u4E0D\u6E32\u67D3 "),
6112
+ !isUser.value || __props.showUserAvatar ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_2$3, [
5731
6113
  vue.renderSlot(_ctx.$slots, "message-avatar", { message: __props.message }, () => [
5732
6114
  isUser.value ? (vue.openBlock(), vue.createBlock(vue.unref(elementPlus.ElIcon), {
5733
6115
  key: 0,
@@ -5749,10 +6131,10 @@ ${fileParts.join("\n")}` });
5749
6131
  /* STABLE */
5750
6132
  }))
5751
6133
  ])
5752
- ]),
5753
- vue.createElementVNode("div", _hoisted_3$2, [
5754
- vue.createCommentVNode(" \u5934\u90E8:\u89D2\u8272\u540D + \u6A21\u578B\u6807\u8BB0 "),
5755
- vue.createElementVNode("div", _hoisted_4$2, [
6134
+ ])) : vue.createCommentVNode("v-if", true),
6135
+ vue.createElementVNode("div", _hoisted_3$3, [
6136
+ vue.createCommentVNode(" \u5934\u90E8:\u89D2\u8272\u540D + \u6A21\u578B\u6807\u8BB0;showUserName=false \u65F6\u7528\u6237\u6D88\u606F\u4E0D\u6E32\u67D3 "),
6137
+ !isUser.value || __props.showUserName ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_4$2, [
5756
6138
  vue.createElementVNode(
5757
6139
  "span",
5758
6140
  _hoisted_5,
@@ -5767,7 +6149,7 @@ ${fileParts.join("\n")}` });
5767
6149
  1
5768
6150
  /* TEXT */
5769
6151
  )) : vue.createCommentVNode("v-if", true)
5770
- ]),
6152
+ ])) : vue.createCommentVNode("v-if", true),
5771
6153
  vue.createCommentVNode(" \u9644\u4EF6:user \u6D88\u606F files \u4EE5 FilesCard \u5E73\u94FA "),
5772
6154
  __props.message.files?.length ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_7, [
5773
6155
  vue.renderSlot(_ctx.$slots, "message-files", {
@@ -5809,13 +6191,13 @@ ${fileParts.join("\n")}` });
5809
6191
  ])) : vue.createCommentVNode("v-if", true),
5810
6192
  vue.createCommentVNode(" \u601D\u8003\u94FE\u6298\u53E0(assistant \u542B reasoning \u65F6) "),
5811
6193
  !isUser.value && hasReasoning.value ? (vue.openBlock(), vue.createBlock(_sfc_main$a, {
5812
- key: 1,
6194
+ key: 2,
5813
6195
  content: __props.message.reasoning,
5814
6196
  status: thinkingStatus.value,
5815
6197
  "auto-collapse": __props.autoCollapse
5816
6198
  }, {
5817
6199
  content: vue.withCtx(() => [
5818
- vue.createVNode(_sfc_main$h, {
6200
+ vue.createVNode(_sfc_main$i, {
5819
6201
  content: __props.message.reasoning ?? ""
5820
6202
  }, null, 8, ["content"])
5821
6203
  ]),
@@ -5830,7 +6212,7 @@ ${fileParts.join("\n")}` });
5830
6212
  vue.toDisplayString(__props.message.error || "\u751F\u6210\u5931\u8D25"),
5831
6213
  1
5832
6214
  /* TEXT */
5833
- )) : __props.message.content ? (vue.openBlock(), vue.createBlock(_sfc_main$h, {
6215
+ )) : __props.message.content ? (vue.openBlock(), vue.createBlock(_sfc_main$i, {
5834
6216
  key: 1,
5835
6217
  content: __props.message.content
5836
6218
  }, null, 8, ["content"])) : __props.message.status === "pending" || __props.message.status === "streaming" ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_12, [
@@ -5985,6 +6367,7 @@ ${fileParts.join("\n")}` });
5985
6367
  key: 0,
5986
6368
  class: "fv-messages-empty"
5987
6369
  };
6370
+ const _hoisted_3$2 = { class: "fv-messages-inner" };
5988
6371
  var _sfc_main$2 = /* @__PURE__ */ vue.defineComponent({
5989
6372
  ...{ name: "FvAiMessages" },
5990
6373
  __name: "index",
@@ -5998,7 +6381,9 @@ ${fileParts.join("\n")}` });
5998
6381
  suggestionsTitle: { type: String, required: false, default: "" },
5999
6382
  scrollThreshold: { type: Number, required: false, default: 40 },
6000
6383
  showActions: { type: Boolean, required: false, default: true },
6001
- showBranchSwitch: { type: Boolean, required: false, default: true }
6384
+ showBranchSwitch: { type: Boolean, required: false, default: true },
6385
+ showUserAvatar: { type: Boolean, required: false, default: true },
6386
+ showUserName: { type: Boolean, required: false, default: true }
6002
6387
  },
6003
6388
  emits: ["select", "copy", "regenerate", "branch-change"],
6004
6389
  setup(__props, { expose: __expose, emit: __emit }) {
@@ -6089,7 +6474,7 @@ ${fileParts.join("\n")}` });
6089
6474
  vue.Fragment,
6090
6475
  { key: 1 },
6091
6476
  [
6092
- vue.createCommentVNode(" \u6D88\u606F\u5217\u8868 "),
6477
+ vue.createCommentVNode(" \u6D88\u606F\u5217\u8868:\u5185\u5C42 fv-messages-inner \u63A7\u5236\u6EDA\u52A8\u5185\u5BB9\u6700\u5927\u5BBD\u5EA6\u5E76\u5DE6\u53F3\u5C45\u4E2D(--fv-messages-max-width \u53EF\u8986\u76D6) "),
6093
6478
  vue.createElementVNode(
6094
6479
  "div",
6095
6480
  {
@@ -6098,42 +6483,46 @@ ${fileParts.join("\n")}` });
6098
6483
  class: "fv-messages-scroll"
6099
6484
  },
6100
6485
  [
6101
- (vue.openBlock(true), vue.createElementBlock(
6102
- vue.Fragment,
6103
- null,
6104
- vue.renderList(__props.messages, (m) => {
6105
- return vue.openBlock(), vue.createBlock(_sfc_main$3, {
6106
- key: m.id,
6107
- message: m,
6108
- branch: branchInfo(m),
6109
- "show-actions": __props.showActions,
6110
- "show-branch-switch": __props.showBranchSwitch,
6111
- onCopy,
6112
- onRegenerate,
6113
- onBranchChange: onBranch
6114
- }, vue.createSlots({
6115
- _: 2
6116
- /* DYNAMIC */
6117
- }, [
6118
- _ctx.$slots["message-avatar"] ? {
6119
- name: "message-avatar",
6120
- fn: vue.withCtx((scope) => [
6121
- vue.renderSlot(_ctx.$slots, "message-avatar", vue.mergeProps({ ref_for: true }, scope))
6122
- ]),
6123
- key: "0"
6124
- } : void 0,
6125
- _ctx.$slots["message-actions"] ? {
6126
- name: "message-actions",
6127
- fn: vue.withCtx((scope) => [
6128
- vue.renderSlot(_ctx.$slots, "message-actions", vue.mergeProps({ ref_for: true }, scope))
6129
- ]),
6130
- key: "1"
6131
- } : void 0
6132
- ]), 1032, ["message", "branch", "show-actions", "show-branch-switch"]);
6133
- }),
6134
- 128
6135
- /* KEYED_FRAGMENT */
6136
- ))
6486
+ vue.createElementVNode("div", _hoisted_3$2, [
6487
+ (vue.openBlock(true), vue.createElementBlock(
6488
+ vue.Fragment,
6489
+ null,
6490
+ vue.renderList(__props.messages, (m) => {
6491
+ return vue.openBlock(), vue.createBlock(_sfc_main$3, {
6492
+ key: m.id,
6493
+ message: m,
6494
+ branch: branchInfo(m),
6495
+ "show-actions": __props.showActions,
6496
+ "show-branch-switch": __props.showBranchSwitch,
6497
+ "show-user-avatar": __props.showUserAvatar,
6498
+ "show-user-name": __props.showUserName,
6499
+ onCopy,
6500
+ onRegenerate,
6501
+ onBranchChange: onBranch
6502
+ }, vue.createSlots({
6503
+ _: 2
6504
+ /* DYNAMIC */
6505
+ }, [
6506
+ _ctx.$slots["message-avatar"] ? {
6507
+ name: "message-avatar",
6508
+ fn: vue.withCtx((scope) => [
6509
+ vue.renderSlot(_ctx.$slots, "message-avatar", vue.mergeProps({ ref_for: true }, scope))
6510
+ ]),
6511
+ key: "0"
6512
+ } : void 0,
6513
+ _ctx.$slots["message-actions"] ? {
6514
+ name: "message-actions",
6515
+ fn: vue.withCtx((scope) => [
6516
+ vue.renderSlot(_ctx.$slots, "message-actions", vue.mergeProps({ ref_for: true }, scope))
6517
+ ]),
6518
+ key: "1"
6519
+ } : void 0
6520
+ ]), 1032, ["message", "branch", "show-actions", "show-branch-switch", "show-user-avatar", "show-user-name"]);
6521
+ }),
6522
+ 128
6523
+ /* KEYED_FRAGMENT */
6524
+ ))
6525
+ ])
6137
6526
  ],
6138
6527
  512
6139
6528
  /* NEED_PATCH */
@@ -6401,7 +6790,10 @@ ${fileParts.join("\n")}` });
6401
6790
  }
6402
6791
  });
6403
6792
 
6404
- const _hoisted_1 = { class: "fv-ai-chat-header" };
6793
+ const _hoisted_1 = {
6794
+ key: 0,
6795
+ class: "fv-ai-chat-header"
6796
+ };
6405
6797
  const _hoisted_2 = { class: "fv-ai-chat-title" };
6406
6798
  const _hoisted_3 = { class: "fv-ai-chat-body" };
6407
6799
  const _hoisted_4 = { class: "fv-ai-chat-footer" };
@@ -6416,7 +6808,12 @@ ${fileParts.join("\n")}` });
6416
6808
  adapter: { type: Object, required: false, default: void 0 },
6417
6809
  messages: { type: Array, required: false, default: void 0 },
6418
6810
  title: { type: String, required: false, default: "" },
6811
+ showHeader: { type: Boolean, required: false, default: false },
6812
+ centerWelcome: { type: Boolean, required: false, default: true },
6813
+ showUserAvatar: { type: Boolean, required: false, default: true },
6814
+ showUserName: { type: Boolean, required: false, default: true },
6419
6815
  placeholder: { type: String, required: false, default: "" },
6816
+ senderShadow: { type: Boolean, required: false, default: true },
6420
6817
  suggestions: { type: Array, required: false, default: () => [] },
6421
6818
  features: { type: Object, required: false, default: () => ({}) },
6422
6819
  allowAttachments: { type: Boolean, required: false, default: false },
@@ -6464,6 +6861,7 @@ ${fileParts.join("\n")}` });
6464
6861
  const { send, stop: stopChat, regenerate: regenerateChat, msgs, isLoading } = chat;
6465
6862
  const nodeTable = chat.messages;
6466
6863
  const chain = chat.chain;
6864
+ const isCenterWelcome = vue.computed(() => props.centerWelcome && chain.value.length === 0);
6467
6865
  let flushTimer = null;
6468
6866
  vue.watch(chain, (c) => {
6469
6867
  if (flushTimer) clearTimeout(flushTimer);
@@ -6629,11 +7027,11 @@ ${fileParts.join("\n")}` });
6629
7027
  return vue.openBlock(), vue.createElementBlock(
6630
7028
  "div",
6631
7029
  {
6632
- class: vue.normalizeClass(["fv-ai-chat", { "is-dark": isDark.value }])
7030
+ class: vue.normalizeClass(["fv-ai-chat", { "is-dark": isDark.value, "is-center-welcome": isCenterWelcome.value }])
6633
7031
  },
6634
7032
  [
6635
7033
  vue.createCommentVNode(" \u9876\u680F:\u6807\u9898 + \u6A21\u578B\u9009\u62E9\u5668(models \u6709\u503C\u65F6 multiple) "),
6636
- vue.createElementVNode("div", _hoisted_1, [
7034
+ __props.showHeader ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1, [
6637
7035
  vue.createElementVNode(
6638
7036
  "div",
6639
7037
  _hoisted_2,
@@ -6669,7 +7067,7 @@ ${fileParts.join("\n")}` });
6669
7067
  _: 1
6670
7068
  /* STABLE */
6671
7069
  }, 8, ["modelValue"])) : vue.createCommentVNode("v-if", true)
6672
- ]),
7070
+ ])) : vue.createCommentVNode("v-if", true),
6673
7071
  vue.createCommentVNode(" \u6D88\u606F\u533A:\u7A7A\u65F6 Welcome + Prompts(AiMessages \u5185\u7F6E);\u5F00\u542F artifacts \u65F6\u53F3\u4FA7\u62FC\u9884\u89C8\u9762\u677F "),
6674
7072
  vue.createElementVNode("div", _hoisted_3, [
6675
7073
  vue.createVNode(_sfc_main$2, {
@@ -6677,6 +7075,8 @@ ${fileParts.join("\n")}` });
6677
7075
  nodes: vue.unref(nodeTable),
6678
7076
  name: __props.title,
6679
7077
  suggestions: __props.suggestions,
7078
+ "show-user-avatar": __props.showUserAvatar,
7079
+ "show-user-name": __props.showUserName,
6680
7080
  onSelect: onSelectSuggestion,
6681
7081
  onCopy,
6682
7082
  onRegenerate,
@@ -6706,7 +7106,7 @@ ${fileParts.join("\n")}` });
6706
7106
  ]),
6707
7107
  key: "2"
6708
7108
  } : void 0
6709
- ]), 1032, ["messages", "nodes", "name", "suggestions"]),
7109
+ ]), 1032, ["messages", "nodes", "name", "suggestions", "show-user-avatar", "show-user-name"]),
6710
7110
  __props.features?.artifacts && artifacts.value.length ? (vue.openBlock(), vue.createBlock(_sfc_main$1, {
6711
7111
  key: 0,
6712
7112
  artifacts: artifacts.value,
@@ -6720,6 +7120,7 @@ ${fileParts.join("\n")}` });
6720
7120
  vue.createVNode(_sfc_main$d, {
6721
7121
  placeholder: __props.placeholder,
6722
7122
  loading: vue.unref(isLoading),
7123
+ shadow: __props.senderShadow,
6723
7124
  variant: "updown",
6724
7125
  maxlength: 8e3,
6725
7126
  "allow-empty-submit": __props.allowAttachments && !!pendingFiles.value.length,
@@ -6764,7 +7165,7 @@ ${fileParts.join("\n")}` });
6764
7165
  ]),
6765
7166
  key: "2"
6766
7167
  } : void 0
6767
- ]), 1032, ["placeholder", "loading", "allow-empty-submit"])
7168
+ ]), 1032, ["placeholder", "loading", "shadow", "allow-empty-submit"])
6768
7169
  ])
6769
7170
  ],
6770
7171
  2
@@ -6895,105 +7296,6 @@ ${fileParts.join("\n")}` });
6895
7296
  });
6896
7297
  }
6897
7298
 
6898
- function getSpeechRecognitionCtor() {
6899
- if (typeof window === "undefined") return void 0;
6900
- const w = window;
6901
- const ctor = w.SpeechRecognition ?? w.webkitSpeechRecognition;
6902
- return typeof ctor === "function" ? ctor : void 0;
6903
- }
6904
- function useRecord() {
6905
- const isRecording = vue.ref(false);
6906
- const text = vue.ref("");
6907
- const audioUrl = vue.ref(null);
6908
- const error = vue.ref(null);
6909
- let mediaStream = null;
6910
- let mediaRecorder = null;
6911
- let chunks = [];
6912
- let recognition = null;
6913
- let finalText = "";
6914
- function revokeAudioUrl() {
6915
- if (audioUrl.value) {
6916
- URL.revokeObjectURL(audioUrl.value);
6917
- audioUrl.value = null;
6918
- }
6919
- }
6920
- async function start() {
6921
- if (isRecording.value) return;
6922
- error.value = null;
6923
- text.value = "";
6924
- finalText = "";
6925
- chunks = [];
6926
- revokeAudioUrl();
6927
- if (typeof MediaRecorder === "undefined" || !navigator.mediaDevices?.getUserMedia) {
6928
- error.value = new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301\u97F3\u9891\u5F55\u5236");
6929
- return;
6930
- }
6931
- try {
6932
- mediaStream = await navigator.mediaDevices.getUserMedia({ audio: true });
6933
- } catch (e) {
6934
- error.value = e instanceof Error ? e : new Error(String(e));
6935
- return;
6936
- }
6937
- mediaRecorder = new MediaRecorder(mediaStream);
6938
- mediaRecorder.ondataavailable = (e) => {
6939
- if (e.data && e.data.size > 0) chunks.push(e.data);
6940
- };
6941
- mediaRecorder.onstop = () => {
6942
- if (chunks.length > 0) {
6943
- const blob = new Blob(chunks, { type: mediaRecorder?.mimeType || "audio/webm" });
6944
- audioUrl.value = URL.createObjectURL(blob);
6945
- }
6946
- mediaStream?.getTracks().forEach((t) => t.stop());
6947
- mediaStream = null;
6948
- };
6949
- const SRCtor = getSpeechRecognitionCtor();
6950
- if (SRCtor) {
6951
- try {
6952
- recognition = new SRCtor();
6953
- recognition.continuous = true;
6954
- recognition.interimResults = true;
6955
- recognition.onresult = (event) => {
6956
- let interim = "";
6957
- for (let i = event.resultIndex; i < event.results.length; i++) {
6958
- const r = event.results[i];
6959
- if (r.isFinal) finalText += r[0].transcript;
6960
- else interim += r[0].transcript;
6961
- }
6962
- text.value = finalText + interim;
6963
- };
6964
- recognition.onerror = (ev) => {
6965
- error.value = new Error(`\u8BED\u97F3\u8BC6\u522B\u9519\u8BEF: ${ev?.error ?? "unknown"}`);
6966
- };
6967
- recognition.start();
6968
- } catch {
6969
- recognition = null;
6970
- }
6971
- }
6972
- mediaRecorder.start();
6973
- isRecording.value = true;
6974
- }
6975
- function stop() {
6976
- if (!isRecording.value) return;
6977
- isRecording.value = false;
6978
- try {
6979
- recognition?.stop();
6980
- } catch {
6981
- }
6982
- recognition = null;
6983
- try {
6984
- if (mediaRecorder && mediaRecorder.state !== "inactive") mediaRecorder.stop();
6985
- } catch {
6986
- }
6987
- }
6988
- if (vue.getCurrentInstance()) {
6989
- vue.onBeforeUnmount(() => {
6990
- stop();
6991
- revokeAudioUrl();
6992
- });
6993
- }
6994
- return { start, stop, isRecording, text, audioUrl, error };
6995
- }
6996
-
6997
7299
  class DetOmsError extends Error {
6998
7300
  constructor(message, code) {
6999
7301
  super(message);
@@ -7196,18 +7498,18 @@ ${fileParts.join("\n")}` });
7196
7498
  exports.FvAiMessage = _sfc_main$3;
7197
7499
  exports.FvAiMessages = _sfc_main$2;
7198
7500
  exports.FvAttachments = _sfc_main$5;
7199
- exports.FvBubble = _sfc_main$f;
7200
- exports.FvBubbleList = _sfc_main$e;
7501
+ exports.FvBubble = _sfc_main$g;
7502
+ exports.FvBubbleList = _sfc_main$f;
7201
7503
  exports.FvConversations = _sfc_main$4;
7202
7504
  exports.FvEditorSender = _sfc_main$b;
7203
7505
  exports.FvFilesCard = _sfc_main$6;
7204
- exports.FvMarkdown = _sfc_main$h;
7506
+ exports.FvMarkdown = _sfc_main$i;
7205
7507
  exports.FvMentionSender = _sfc_main$c;
7206
7508
  exports.FvPrompts = _sfc_main$7;
7207
7509
  exports.FvSender = _sfc_main$d;
7208
7510
  exports.FvThinking = _sfc_main$a;
7209
7511
  exports.FvThoughtChain = _sfc_main$9;
7210
- exports.FvTypewriter = _sfc_main$g;
7512
+ exports.FvTypewriter = _sfc_main$h;
7211
7513
  exports.FvWelcome = _sfc_main$8;
7212
7514
  exports.OpenAIAdapter = OpenAIAdapter;
7213
7515
  exports.XRequest = XRequest;