@fv-ui/ai-chat 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +33 -3
- package/es/index.mjs +52 -17
- package/lib/index.js +52 -17
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -34,6 +34,8 @@ type __VLS_Props$g = {
|
|
|
34
34
|
bgColor?: string;
|
|
35
35
|
width?: string;
|
|
36
36
|
};
|
|
37
|
+
/** typing 对象形态的 interval 语义:自适应模式下的"基础节拍"(每拍最小步进 1 字的期望速率),
|
|
38
|
+
* 实际步进随积压放大(见 startTyping 内注释) */
|
|
37
39
|
typing?: boolean | {
|
|
38
40
|
step?: number;
|
|
39
41
|
interval?: number;
|
|
@@ -1484,7 +1486,7 @@ type __VLS_Props$3 = {
|
|
|
1484
1486
|
suggestions?: AiMessagesSuggestion[];
|
|
1485
1487
|
/** 空态建议区标题 */
|
|
1486
1488
|
suggestionsTitle?: string;
|
|
1487
|
-
/** 用户上滚免打扰阈值(px)
|
|
1489
|
+
/** 用户上滚免打扰阈值(px):距底不超过该值视为「钉在底部」,流式/完成自动跟随 */
|
|
1488
1490
|
scrollThreshold?: number;
|
|
1489
1491
|
/** 显示滚到底部悬浮按钮 */
|
|
1490
1492
|
showScrollButton?: boolean;
|
|
@@ -1510,6 +1512,11 @@ type __VLS_Props$3 = {
|
|
|
1510
1512
|
userName?: string;
|
|
1511
1513
|
/** 助手名称(透传 AiMessage) */
|
|
1512
1514
|
assistantName?: string;
|
|
1515
|
+
/** 打字机(透传 AiMessage;仅 assistant 流式期间生效,落定/历史消息整段渲染) */
|
|
1516
|
+
typing?: boolean | {
|
|
1517
|
+
step?: number;
|
|
1518
|
+
interval?: number;
|
|
1519
|
+
};
|
|
1513
1520
|
};
|
|
1514
1521
|
declare function scrollToBottom(smooth?: boolean): void;
|
|
1515
1522
|
/** 回到顶部:与 scrollToBottom 同款 try/catch 回落(happy-dom 旧版 scrollTo(options) 可能缺实现) */
|
|
@@ -1568,6 +1575,10 @@ declare const __VLS_base$2: vue.DefineComponent<__VLS_Props$3, {
|
|
|
1568
1575
|
"onBranch-change"?: ((dir: BranchDirection, message: MessageNode) => any) | undefined;
|
|
1569
1576
|
}>, {
|
|
1570
1577
|
name: string;
|
|
1578
|
+
typing: boolean | {
|
|
1579
|
+
step?: number;
|
|
1580
|
+
interval?: number;
|
|
1581
|
+
};
|
|
1571
1582
|
icon: string | Component;
|
|
1572
1583
|
nodes: Record<string, MessageNode>;
|
|
1573
1584
|
description: string;
|
|
@@ -1650,6 +1661,12 @@ type __VLS_Props$2 = {
|
|
|
1650
1661
|
placeholder?: string;
|
|
1651
1662
|
/** 工具名 → 步骤展示名(透传 useChat toolStepLabel,流式工具轨迹步骤的 title) */
|
|
1652
1663
|
toolStepLabel?: (toolName: string) => string;
|
|
1664
|
+
/** 打字机(透传 AiMessages;仅 assistant 流式期间逐字平滑打出,
|
|
1665
|
+
* 落定/历史消息整段渲染;true 用默认节奏,对象形态自定义 step/interval) */
|
|
1666
|
+
typing?: boolean | {
|
|
1667
|
+
step?: number;
|
|
1668
|
+
interval?: number;
|
|
1669
|
+
};
|
|
1653
1670
|
/** 输入区阴影效果(透传 Sender;关闭传 false) */
|
|
1654
1671
|
senderShadow?: boolean;
|
|
1655
1672
|
/** 空态建议列表 */
|
|
@@ -1752,6 +1769,10 @@ declare const __VLS_base$1: vue.DefineComponent<__VLS_Props$2, {
|
|
|
1752
1769
|
}) => any) | undefined;
|
|
1753
1770
|
}>, {
|
|
1754
1771
|
title: string;
|
|
1772
|
+
typing: boolean | {
|
|
1773
|
+
step?: number;
|
|
1774
|
+
interval?: number;
|
|
1775
|
+
};
|
|
1755
1776
|
placeholder: string;
|
|
1756
1777
|
accept: string;
|
|
1757
1778
|
baseURL: string;
|
|
@@ -1823,6 +1844,11 @@ type __VLS_Props$1 = {
|
|
|
1823
1844
|
assistantName?: string;
|
|
1824
1845
|
/** Thinking autoCollapse 透传 */
|
|
1825
1846
|
autoCollapse?: boolean;
|
|
1847
|
+
/** 打字机(透传 Typewriter;仅 assistant 流式期间生效,落定/历史消息整段渲染) */
|
|
1848
|
+
typing?: boolean | {
|
|
1849
|
+
step?: number;
|
|
1850
|
+
interval?: number;
|
|
1851
|
+
};
|
|
1826
1852
|
};
|
|
1827
1853
|
declare var __VLS_1: {
|
|
1828
1854
|
message: MessageNode;
|
|
@@ -1839,7 +1865,7 @@ declare var __VLS_33: {
|
|
|
1839
1865
|
message: MessageNode;
|
|
1840
1866
|
files: ChatFile[];
|
|
1841
1867
|
};
|
|
1842
|
-
declare var
|
|
1868
|
+
declare var __VLS_67: {
|
|
1843
1869
|
message: MessageNode;
|
|
1844
1870
|
};
|
|
1845
1871
|
type __VLS_Slots = {} & {
|
|
@@ -1855,7 +1881,7 @@ type __VLS_Slots = {} & {
|
|
|
1855
1881
|
} & {
|
|
1856
1882
|
'message-files'?: (props: typeof __VLS_33) => any;
|
|
1857
1883
|
} & {
|
|
1858
|
-
'message-actions'?: (props: typeof
|
|
1884
|
+
'message-actions'?: (props: typeof __VLS_67) => any;
|
|
1859
1885
|
};
|
|
1860
1886
|
declare const __VLS_base: vue.DefineComponent<__VLS_Props$1, {
|
|
1861
1887
|
message: vue.ComputedRef<MessageNode>;
|
|
@@ -1868,6 +1894,10 @@ declare const __VLS_base: vue.DefineComponent<__VLS_Props$1, {
|
|
|
1868
1894
|
onRegenerate?: ((message: MessageNode) => any) | undefined;
|
|
1869
1895
|
"onBranch-change"?: ((dir: BranchDirection, message: MessageNode) => any) | undefined;
|
|
1870
1896
|
}>, {
|
|
1897
|
+
typing: boolean | {
|
|
1898
|
+
step?: number;
|
|
1899
|
+
interval?: number;
|
|
1900
|
+
};
|
|
1871
1901
|
autoCollapse: boolean;
|
|
1872
1902
|
branch: AiMessageBranchInfo;
|
|
1873
1903
|
showActions: boolean;
|
package/es/index.mjs
CHANGED
|
@@ -1595,7 +1595,9 @@ var _sfc_main$h = /* @__PURE__ */ defineComponent({
|
|
|
1595
1595
|
const step = (typeof props.typing === "object" ? props.typing.step : void 0) ?? 2;
|
|
1596
1596
|
const interval = (typeof props.typing === "object" ? props.typing.interval : void 0) ?? 50;
|
|
1597
1597
|
timer = setInterval(() => {
|
|
1598
|
-
|
|
1598
|
+
const backlog = props.content.length - cursor;
|
|
1599
|
+
const speed = Math.min(step * 6, Math.max(1, Math.round(step * (1 + backlog / 8))));
|
|
1600
|
+
cursor = Math.min(props.content.length, cursor + speed);
|
|
1599
1601
|
renderedContent.value = props.content.slice(0, cursor);
|
|
1600
1602
|
progress.value = props.content.length ? cursor / props.content.length : 1;
|
|
1601
1603
|
emit("writing");
|
|
@@ -6049,7 +6051,7 @@ const _hoisted_12 = {
|
|
|
6049
6051
|
class: "fv-msg-error"
|
|
6050
6052
|
};
|
|
6051
6053
|
const _hoisted_13 = {
|
|
6052
|
-
key:
|
|
6054
|
+
key: 3,
|
|
6053
6055
|
class: "fv-msg-pending"
|
|
6054
6056
|
};
|
|
6055
6057
|
const _hoisted_14 = {
|
|
@@ -6081,7 +6083,8 @@ var _sfc_main$3 = /* @__PURE__ */ defineComponent({
|
|
|
6081
6083
|
assistantAvatar: { type: String, required: false, default: "" },
|
|
6082
6084
|
userName: { type: String, required: false, default: "" },
|
|
6083
6085
|
assistantName: { type: String, required: false, default: "" },
|
|
6084
|
-
autoCollapse: { type: Boolean, required: false, default: true }
|
|
6086
|
+
autoCollapse: { type: Boolean, required: false, default: true },
|
|
6087
|
+
typing: { type: [Boolean, Object], required: false, default: false }
|
|
6085
6088
|
},
|
|
6086
6089
|
emits: ["copy", "regenerate", "branch-change"],
|
|
6087
6090
|
setup(__props, { expose: __expose, emit: __emit }) {
|
|
@@ -6096,6 +6099,9 @@ var _sfc_main$3 = /* @__PURE__ */ defineComponent({
|
|
|
6096
6099
|
});
|
|
6097
6100
|
const hasReasoning = computed(() => !!props.message.reasoning);
|
|
6098
6101
|
const hasToolSteps = computed(() => (props.message.toolSteps?.length ?? 0) > 0);
|
|
6102
|
+
const useTypewriter = computed(
|
|
6103
|
+
() => !isUser.value && !!props.typing && (props.message.status === "pending" || props.message.status === "streaming")
|
|
6104
|
+
);
|
|
6099
6105
|
const toolChainItems = computed(() => {
|
|
6100
6106
|
const steps = props.message.toolSteps ?? [];
|
|
6101
6107
|
const running = props.message.status === "pending" || props.message.status === "streaming";
|
|
@@ -6231,7 +6237,7 @@ var _sfc_main$3 = /* @__PURE__ */ defineComponent({
|
|
|
6231
6237
|
class: "fv-msg-tools",
|
|
6232
6238
|
items: toolChainItems.value
|
|
6233
6239
|
}, null, 8, ["items"])) : createCommentVNode("v-if", true),
|
|
6234
|
-
createCommentVNode(" \u601D\u8003\u94FE\u6298\u53E0(assistant \u542B reasoning \u65F6) "),
|
|
6240
|
+
createCommentVNode(" \u601D\u8003\u94FE\u6298\u53E0(assistant \u542B reasoning \u65F6);\u6D41\u5F0F\u671F\u95F4\u7ECF\u6253\u5B57\u673A\u5E73\u6ED1\u6253\u51FA "),
|
|
6235
6241
|
!isUser.value && hasReasoning.value ? (openBlock(), createBlock(_sfc_main$a, {
|
|
6236
6242
|
key: 3,
|
|
6237
6243
|
content: __props.message.reasoning,
|
|
@@ -6239,9 +6245,15 @@ var _sfc_main$3 = /* @__PURE__ */ defineComponent({
|
|
|
6239
6245
|
"auto-collapse": __props.autoCollapse
|
|
6240
6246
|
}, {
|
|
6241
6247
|
content: withCtx(() => [
|
|
6242
|
-
|
|
6248
|
+
useTypewriter.value ? (openBlock(), createBlock(_sfc_main$h, {
|
|
6249
|
+
key: 0,
|
|
6250
|
+
content: __props.message.reasoning ?? "",
|
|
6251
|
+
"is-markdown": "",
|
|
6252
|
+
typing: __props.typing
|
|
6253
|
+
}, null, 8, ["content", "typing"])) : (openBlock(), createBlock(_sfc_main$i, {
|
|
6254
|
+
key: 1,
|
|
6243
6255
|
content: __props.message.reasoning ?? ""
|
|
6244
|
-
}, null, 8, ["content"])
|
|
6256
|
+
}, null, 8, ["content"]))
|
|
6245
6257
|
]),
|
|
6246
6258
|
_: 1
|
|
6247
6259
|
/* STABLE */
|
|
@@ -6254,8 +6266,13 @@ var _sfc_main$3 = /* @__PURE__ */ defineComponent({
|
|
|
6254
6266
|
toDisplayString(__props.message.error || "\u751F\u6210\u5931\u8D25"),
|
|
6255
6267
|
1
|
|
6256
6268
|
/* TEXT */
|
|
6257
|
-
)) : __props.message.content ? (openBlock(), createBlock(_sfc_main$
|
|
6269
|
+
)) : useTypewriter.value && __props.message.content ? (openBlock(), createBlock(_sfc_main$h, {
|
|
6258
6270
|
key: 1,
|
|
6271
|
+
content: __props.message.content,
|
|
6272
|
+
"is-markdown": "",
|
|
6273
|
+
typing: __props.typing
|
|
6274
|
+
}, null, 8, ["content", "typing"])) : __props.message.content ? (openBlock(), createBlock(_sfc_main$i, {
|
|
6275
|
+
key: 2,
|
|
6259
6276
|
content: __props.message.content
|
|
6260
6277
|
}, null, 8, ["content"])) : __props.message.status === "pending" || __props.message.status === "streaming" ? (openBlock(), createElementBlock("span", _hoisted_13, [
|
|
6261
6278
|
(openBlock(), createElementBlock(
|
|
@@ -6450,7 +6467,7 @@ var _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|
|
6450
6467
|
description: { type: String, required: false, default: "" },
|
|
6451
6468
|
suggestions: { type: Array, required: false, default: () => [] },
|
|
6452
6469
|
suggestionsTitle: { type: String, required: false, default: "" },
|
|
6453
|
-
scrollThreshold: { type: Number, required: false, default:
|
|
6470
|
+
scrollThreshold: { type: Number, required: false, default: 100 },
|
|
6454
6471
|
showScrollButton: { type: Boolean, required: false, default: true },
|
|
6455
6472
|
scrollButtonThreshold: { type: Number, required: false, default: 100 },
|
|
6456
6473
|
showActions: { type: Boolean, required: false, default: true },
|
|
@@ -6462,7 +6479,8 @@ var _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|
|
6462
6479
|
userAvatar: { type: String, required: false, default: "" },
|
|
6463
6480
|
assistantAvatar: { type: String, required: false, default: "" },
|
|
6464
6481
|
userName: { type: String, required: false, default: "" },
|
|
6465
|
-
assistantName: { type: String, required: false, default: "" }
|
|
6482
|
+
assistantName: { type: String, required: false, default: "" },
|
|
6483
|
+
typing: { type: [Boolean, Object], required: false, default: false }
|
|
6466
6484
|
},
|
|
6467
6485
|
emits: ["select", "copy", "regenerate", "branch-change"],
|
|
6468
6486
|
setup(__props, { expose: __expose, emit: __emit }) {
|
|
@@ -6475,21 +6493,35 @@ var _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|
|
6475
6493
|
if (!el) return 0;
|
|
6476
6494
|
return el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
6477
6495
|
}
|
|
6496
|
+
const isPinnedToBottom = ref(true);
|
|
6478
6497
|
function scrollToBottom(smooth = true) {
|
|
6479
6498
|
const el = scrollRef.value;
|
|
6480
6499
|
if (!el) return;
|
|
6500
|
+
isPinnedToBottom.value = true;
|
|
6481
6501
|
try {
|
|
6482
6502
|
el.scrollTo({ top: el.scrollHeight, behavior: smooth ? "smooth" : "auto" });
|
|
6483
6503
|
} catch {
|
|
6484
6504
|
el.scrollTop = el.scrollHeight;
|
|
6485
6505
|
}
|
|
6486
6506
|
}
|
|
6487
|
-
function
|
|
6488
|
-
|
|
6507
|
+
function scrollToBottomNow() {
|
|
6508
|
+
const el = scrollRef.value;
|
|
6509
|
+
if (!el) return;
|
|
6510
|
+
isPinnedToBottom.value = true;
|
|
6511
|
+
try {
|
|
6512
|
+
el.scrollTo({ top: el.scrollHeight, behavior: "auto" });
|
|
6513
|
+
} catch {
|
|
6514
|
+
el.scrollTop = el.scrollHeight;
|
|
6515
|
+
}
|
|
6516
|
+
}
|
|
6517
|
+
function followIfPinned() {
|
|
6518
|
+
if (isPinnedToBottom.value) nextTick(() => scrollToBottomNow());
|
|
6489
6519
|
}
|
|
6490
6520
|
const isAwayFromBottom = ref(false);
|
|
6491
6521
|
function onScroll() {
|
|
6492
|
-
|
|
6522
|
+
const d = distanceFromBottom();
|
|
6523
|
+
isPinnedToBottom.value = d <= props.scrollThreshold;
|
|
6524
|
+
isAwayFromBottom.value = d > props.scrollButtonThreshold;
|
|
6493
6525
|
}
|
|
6494
6526
|
watch(() => props.scrollButtonThreshold, onScroll);
|
|
6495
6527
|
onMounted(() => {
|
|
@@ -6497,7 +6529,7 @@ var _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|
|
6497
6529
|
onScroll();
|
|
6498
6530
|
});
|
|
6499
6531
|
watch(() => props.messages.length, (nv, ov) => {
|
|
6500
|
-
if (nv > (ov ?? 0))
|
|
6532
|
+
if (nv > (ov ?? 0)) followIfPinned();
|
|
6501
6533
|
});
|
|
6502
6534
|
watch(
|
|
6503
6535
|
() => props.messages.filter((m) => m.role === "user").length,
|
|
@@ -6510,11 +6542,11 @@ var _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|
|
6510
6542
|
(sum, m) => sum + m.content.length + (m.reasoning?.length ?? 0) + (m.toolSteps?.reduce((s, t) => s + (t.status?.length ?? 0) + (t.title?.length ?? 0), 0) ?? 0) + (m.toolSteps?.length ?? 0),
|
|
6511
6543
|
0
|
|
6512
6544
|
),
|
|
6513
|
-
() =>
|
|
6545
|
+
() => followIfPinned()
|
|
6514
6546
|
);
|
|
6515
6547
|
watch(
|
|
6516
6548
|
() => props.messages.reduce((sum, m) => sum + (m.status === "done" || m.status === "error" ? 1 : 0), 0),
|
|
6517
|
-
() =>
|
|
6549
|
+
() => followIfPinned()
|
|
6518
6550
|
);
|
|
6519
6551
|
function branchInfo(m) {
|
|
6520
6552
|
if (!m) return void 0;
|
|
@@ -6601,6 +6633,7 @@ var _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|
|
6601
6633
|
"assistant-avatar": __props.assistantAvatar,
|
|
6602
6634
|
"user-name": __props.userName,
|
|
6603
6635
|
"assistant-name": __props.assistantName,
|
|
6636
|
+
typing: __props.typing,
|
|
6604
6637
|
onCopy,
|
|
6605
6638
|
onRegenerate,
|
|
6606
6639
|
onBranchChange: onBranch
|
|
@@ -6650,7 +6683,7 @@ var _sfc_main$2 = /* @__PURE__ */ defineComponent({
|
|
|
6650
6683
|
]),
|
|
6651
6684
|
key: "5"
|
|
6652
6685
|
} : void 0
|
|
6653
|
-
]), 1032, ["message", "branch", "show-actions", "show-branch-switch", "show-user-avatar", "show-user-name", "show-assistant-avatar", "show-assistant-name", "user-avatar", "assistant-avatar", "user-name", "assistant-name"]);
|
|
6686
|
+
]), 1032, ["message", "branch", "show-actions", "show-branch-switch", "show-user-avatar", "show-user-name", "show-assistant-avatar", "show-assistant-name", "user-avatar", "assistant-avatar", "user-name", "assistant-name", "typing"]);
|
|
6654
6687
|
}),
|
|
6655
6688
|
128
|
|
6656
6689
|
/* KEYED_FRAGMENT */
|
|
@@ -6988,6 +7021,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
6988
7021
|
assistantName: { type: String, required: false, default: "" },
|
|
6989
7022
|
placeholder: { type: String, required: false, default: "" },
|
|
6990
7023
|
toolStepLabel: { type: Function, required: false, default: void 0 },
|
|
7024
|
+
typing: { type: [Boolean, Object], required: false, default: false },
|
|
6991
7025
|
senderShadow: { type: Boolean, required: false, default: true },
|
|
6992
7026
|
suggestions: { type: Array, required: false, default: () => [] },
|
|
6993
7027
|
features: { type: Object, required: false, default: () => ({}) },
|
|
@@ -7261,6 +7295,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
7261
7295
|
"assistant-avatar": __props.assistantAvatar,
|
|
7262
7296
|
"user-name": __props.userName,
|
|
7263
7297
|
"assistant-name": __props.assistantName,
|
|
7298
|
+
typing: __props.typing,
|
|
7264
7299
|
onSelect: onSelectSuggestion,
|
|
7265
7300
|
onCopy,
|
|
7266
7301
|
onRegenerate,
|
|
@@ -7332,7 +7367,7 @@ var _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
7332
7367
|
]),
|
|
7333
7368
|
key: "8"
|
|
7334
7369
|
} : void 0
|
|
7335
|
-
]), 1032, ["messages", "nodes", "name", "suggestions", "show-scroll-button", "scroll-button-threshold", "show-user-avatar", "show-user-name", "show-assistant-avatar", "show-assistant-name", "user-avatar", "assistant-avatar", "user-name", "assistant-name"]),
|
|
7370
|
+
]), 1032, ["messages", "nodes", "name", "suggestions", "show-scroll-button", "scroll-button-threshold", "show-user-avatar", "show-user-name", "show-assistant-avatar", "show-assistant-name", "user-avatar", "assistant-avatar", "user-name", "assistant-name", "typing"]),
|
|
7336
7371
|
__props.features?.artifacts && artifacts.value.length ? (openBlock(), createBlock(_sfc_main$1, {
|
|
7337
7372
|
key: 0,
|
|
7338
7373
|
artifacts: artifacts.value,
|
package/lib/index.js
CHANGED
|
@@ -1611,7 +1611,9 @@
|
|
|
1611
1611
|
const step = (typeof props.typing === "object" ? props.typing.step : void 0) ?? 2;
|
|
1612
1612
|
const interval = (typeof props.typing === "object" ? props.typing.interval : void 0) ?? 50;
|
|
1613
1613
|
timer = setInterval(() => {
|
|
1614
|
-
|
|
1614
|
+
const backlog = props.content.length - cursor;
|
|
1615
|
+
const speed = Math.min(step * 6, Math.max(1, Math.round(step * (1 + backlog / 8))));
|
|
1616
|
+
cursor = Math.min(props.content.length, cursor + speed);
|
|
1615
1617
|
renderedContent.value = props.content.slice(0, cursor);
|
|
1616
1618
|
progress.value = props.content.length ? cursor / props.content.length : 1;
|
|
1617
1619
|
emit("writing");
|
|
@@ -6065,7 +6067,7 @@ ${fileParts.join("\n")}` });
|
|
|
6065
6067
|
class: "fv-msg-error"
|
|
6066
6068
|
};
|
|
6067
6069
|
const _hoisted_13 = {
|
|
6068
|
-
key:
|
|
6070
|
+
key: 3,
|
|
6069
6071
|
class: "fv-msg-pending"
|
|
6070
6072
|
};
|
|
6071
6073
|
const _hoisted_14 = {
|
|
@@ -6097,7 +6099,8 @@ ${fileParts.join("\n")}` });
|
|
|
6097
6099
|
assistantAvatar: { type: String, required: false, default: "" },
|
|
6098
6100
|
userName: { type: String, required: false, default: "" },
|
|
6099
6101
|
assistantName: { type: String, required: false, default: "" },
|
|
6100
|
-
autoCollapse: { type: Boolean, required: false, default: true }
|
|
6102
|
+
autoCollapse: { type: Boolean, required: false, default: true },
|
|
6103
|
+
typing: { type: [Boolean, Object], required: false, default: false }
|
|
6101
6104
|
},
|
|
6102
6105
|
emits: ["copy", "regenerate", "branch-change"],
|
|
6103
6106
|
setup(__props, { expose: __expose, emit: __emit }) {
|
|
@@ -6112,6 +6115,9 @@ ${fileParts.join("\n")}` });
|
|
|
6112
6115
|
});
|
|
6113
6116
|
const hasReasoning = vue.computed(() => !!props.message.reasoning);
|
|
6114
6117
|
const hasToolSteps = vue.computed(() => (props.message.toolSteps?.length ?? 0) > 0);
|
|
6118
|
+
const useTypewriter = vue.computed(
|
|
6119
|
+
() => !isUser.value && !!props.typing && (props.message.status === "pending" || props.message.status === "streaming")
|
|
6120
|
+
);
|
|
6115
6121
|
const toolChainItems = vue.computed(() => {
|
|
6116
6122
|
const steps = props.message.toolSteps ?? [];
|
|
6117
6123
|
const running = props.message.status === "pending" || props.message.status === "streaming";
|
|
@@ -6247,7 +6253,7 @@ ${fileParts.join("\n")}` });
|
|
|
6247
6253
|
class: "fv-msg-tools",
|
|
6248
6254
|
items: toolChainItems.value
|
|
6249
6255
|
}, null, 8, ["items"])) : vue.createCommentVNode("v-if", true),
|
|
6250
|
-
vue.createCommentVNode(" \u601D\u8003\u94FE\u6298\u53E0(assistant \u542B reasoning \u65F6) "),
|
|
6256
|
+
vue.createCommentVNode(" \u601D\u8003\u94FE\u6298\u53E0(assistant \u542B reasoning \u65F6);\u6D41\u5F0F\u671F\u95F4\u7ECF\u6253\u5B57\u673A\u5E73\u6ED1\u6253\u51FA "),
|
|
6251
6257
|
!isUser.value && hasReasoning.value ? (vue.openBlock(), vue.createBlock(_sfc_main$a, {
|
|
6252
6258
|
key: 3,
|
|
6253
6259
|
content: __props.message.reasoning,
|
|
@@ -6255,9 +6261,15 @@ ${fileParts.join("\n")}` });
|
|
|
6255
6261
|
"auto-collapse": __props.autoCollapse
|
|
6256
6262
|
}, {
|
|
6257
6263
|
content: vue.withCtx(() => [
|
|
6258
|
-
vue.
|
|
6264
|
+
useTypewriter.value ? (vue.openBlock(), vue.createBlock(_sfc_main$h, {
|
|
6265
|
+
key: 0,
|
|
6266
|
+
content: __props.message.reasoning ?? "",
|
|
6267
|
+
"is-markdown": "",
|
|
6268
|
+
typing: __props.typing
|
|
6269
|
+
}, null, 8, ["content", "typing"])) : (vue.openBlock(), vue.createBlock(_sfc_main$i, {
|
|
6270
|
+
key: 1,
|
|
6259
6271
|
content: __props.message.reasoning ?? ""
|
|
6260
|
-
}, null, 8, ["content"])
|
|
6272
|
+
}, null, 8, ["content"]))
|
|
6261
6273
|
]),
|
|
6262
6274
|
_: 1
|
|
6263
6275
|
/* STABLE */
|
|
@@ -6270,8 +6282,13 @@ ${fileParts.join("\n")}` });
|
|
|
6270
6282
|
vue.toDisplayString(__props.message.error || "\u751F\u6210\u5931\u8D25"),
|
|
6271
6283
|
1
|
|
6272
6284
|
/* TEXT */
|
|
6273
|
-
)) : __props.message.content ? (vue.openBlock(), vue.createBlock(_sfc_main$
|
|
6285
|
+
)) : useTypewriter.value && __props.message.content ? (vue.openBlock(), vue.createBlock(_sfc_main$h, {
|
|
6274
6286
|
key: 1,
|
|
6287
|
+
content: __props.message.content,
|
|
6288
|
+
"is-markdown": "",
|
|
6289
|
+
typing: __props.typing
|
|
6290
|
+
}, null, 8, ["content", "typing"])) : __props.message.content ? (vue.openBlock(), vue.createBlock(_sfc_main$i, {
|
|
6291
|
+
key: 2,
|
|
6275
6292
|
content: __props.message.content
|
|
6276
6293
|
}, null, 8, ["content"])) : __props.message.status === "pending" || __props.message.status === "streaming" ? (vue.openBlock(), vue.createElementBlock("span", _hoisted_13, [
|
|
6277
6294
|
(vue.openBlock(), vue.createElementBlock(
|
|
@@ -6466,7 +6483,7 @@ ${fileParts.join("\n")}` });
|
|
|
6466
6483
|
description: { type: String, required: false, default: "" },
|
|
6467
6484
|
suggestions: { type: Array, required: false, default: () => [] },
|
|
6468
6485
|
suggestionsTitle: { type: String, required: false, default: "" },
|
|
6469
|
-
scrollThreshold: { type: Number, required: false, default:
|
|
6486
|
+
scrollThreshold: { type: Number, required: false, default: 100 },
|
|
6470
6487
|
showScrollButton: { type: Boolean, required: false, default: true },
|
|
6471
6488
|
scrollButtonThreshold: { type: Number, required: false, default: 100 },
|
|
6472
6489
|
showActions: { type: Boolean, required: false, default: true },
|
|
@@ -6478,7 +6495,8 @@ ${fileParts.join("\n")}` });
|
|
|
6478
6495
|
userAvatar: { type: String, required: false, default: "" },
|
|
6479
6496
|
assistantAvatar: { type: String, required: false, default: "" },
|
|
6480
6497
|
userName: { type: String, required: false, default: "" },
|
|
6481
|
-
assistantName: { type: String, required: false, default: "" }
|
|
6498
|
+
assistantName: { type: String, required: false, default: "" },
|
|
6499
|
+
typing: { type: [Boolean, Object], required: false, default: false }
|
|
6482
6500
|
},
|
|
6483
6501
|
emits: ["select", "copy", "regenerate", "branch-change"],
|
|
6484
6502
|
setup(__props, { expose: __expose, emit: __emit }) {
|
|
@@ -6491,21 +6509,35 @@ ${fileParts.join("\n")}` });
|
|
|
6491
6509
|
if (!el) return 0;
|
|
6492
6510
|
return el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
6493
6511
|
}
|
|
6512
|
+
const isPinnedToBottom = vue.ref(true);
|
|
6494
6513
|
function scrollToBottom(smooth = true) {
|
|
6495
6514
|
const el = scrollRef.value;
|
|
6496
6515
|
if (!el) return;
|
|
6516
|
+
isPinnedToBottom.value = true;
|
|
6497
6517
|
try {
|
|
6498
6518
|
el.scrollTo({ top: el.scrollHeight, behavior: smooth ? "smooth" : "auto" });
|
|
6499
6519
|
} catch {
|
|
6500
6520
|
el.scrollTop = el.scrollHeight;
|
|
6501
6521
|
}
|
|
6502
6522
|
}
|
|
6503
|
-
function
|
|
6504
|
-
|
|
6523
|
+
function scrollToBottomNow() {
|
|
6524
|
+
const el = scrollRef.value;
|
|
6525
|
+
if (!el) return;
|
|
6526
|
+
isPinnedToBottom.value = true;
|
|
6527
|
+
try {
|
|
6528
|
+
el.scrollTo({ top: el.scrollHeight, behavior: "auto" });
|
|
6529
|
+
} catch {
|
|
6530
|
+
el.scrollTop = el.scrollHeight;
|
|
6531
|
+
}
|
|
6532
|
+
}
|
|
6533
|
+
function followIfPinned() {
|
|
6534
|
+
if (isPinnedToBottom.value) vue.nextTick(() => scrollToBottomNow());
|
|
6505
6535
|
}
|
|
6506
6536
|
const isAwayFromBottom = vue.ref(false);
|
|
6507
6537
|
function onScroll() {
|
|
6508
|
-
|
|
6538
|
+
const d = distanceFromBottom();
|
|
6539
|
+
isPinnedToBottom.value = d <= props.scrollThreshold;
|
|
6540
|
+
isAwayFromBottom.value = d > props.scrollButtonThreshold;
|
|
6509
6541
|
}
|
|
6510
6542
|
vue.watch(() => props.scrollButtonThreshold, onScroll);
|
|
6511
6543
|
vue.onMounted(() => {
|
|
@@ -6513,7 +6545,7 @@ ${fileParts.join("\n")}` });
|
|
|
6513
6545
|
onScroll();
|
|
6514
6546
|
});
|
|
6515
6547
|
vue.watch(() => props.messages.length, (nv, ov) => {
|
|
6516
|
-
if (nv > (ov ?? 0))
|
|
6548
|
+
if (nv > (ov ?? 0)) followIfPinned();
|
|
6517
6549
|
});
|
|
6518
6550
|
vue.watch(
|
|
6519
6551
|
() => props.messages.filter((m) => m.role === "user").length,
|
|
@@ -6526,11 +6558,11 @@ ${fileParts.join("\n")}` });
|
|
|
6526
6558
|
(sum, m) => sum + m.content.length + (m.reasoning?.length ?? 0) + (m.toolSteps?.reduce((s, t) => s + (t.status?.length ?? 0) + (t.title?.length ?? 0), 0) ?? 0) + (m.toolSteps?.length ?? 0),
|
|
6527
6559
|
0
|
|
6528
6560
|
),
|
|
6529
|
-
() =>
|
|
6561
|
+
() => followIfPinned()
|
|
6530
6562
|
);
|
|
6531
6563
|
vue.watch(
|
|
6532
6564
|
() => props.messages.reduce((sum, m) => sum + (m.status === "done" || m.status === "error" ? 1 : 0), 0),
|
|
6533
|
-
() =>
|
|
6565
|
+
() => followIfPinned()
|
|
6534
6566
|
);
|
|
6535
6567
|
function branchInfo(m) {
|
|
6536
6568
|
if (!m) return void 0;
|
|
@@ -6617,6 +6649,7 @@ ${fileParts.join("\n")}` });
|
|
|
6617
6649
|
"assistant-avatar": __props.assistantAvatar,
|
|
6618
6650
|
"user-name": __props.userName,
|
|
6619
6651
|
"assistant-name": __props.assistantName,
|
|
6652
|
+
typing: __props.typing,
|
|
6620
6653
|
onCopy,
|
|
6621
6654
|
onRegenerate,
|
|
6622
6655
|
onBranchChange: onBranch
|
|
@@ -6666,7 +6699,7 @@ ${fileParts.join("\n")}` });
|
|
|
6666
6699
|
]),
|
|
6667
6700
|
key: "5"
|
|
6668
6701
|
} : void 0
|
|
6669
|
-
]), 1032, ["message", "branch", "show-actions", "show-branch-switch", "show-user-avatar", "show-user-name", "show-assistant-avatar", "show-assistant-name", "user-avatar", "assistant-avatar", "user-name", "assistant-name"]);
|
|
6702
|
+
]), 1032, ["message", "branch", "show-actions", "show-branch-switch", "show-user-avatar", "show-user-name", "show-assistant-avatar", "show-assistant-name", "user-avatar", "assistant-avatar", "user-name", "assistant-name", "typing"]);
|
|
6670
6703
|
}),
|
|
6671
6704
|
128
|
|
6672
6705
|
/* KEYED_FRAGMENT */
|
|
@@ -7004,6 +7037,7 @@ ${fileParts.join("\n")}` });
|
|
|
7004
7037
|
assistantName: { type: String, required: false, default: "" },
|
|
7005
7038
|
placeholder: { type: String, required: false, default: "" },
|
|
7006
7039
|
toolStepLabel: { type: Function, required: false, default: void 0 },
|
|
7040
|
+
typing: { type: [Boolean, Object], required: false, default: false },
|
|
7007
7041
|
senderShadow: { type: Boolean, required: false, default: true },
|
|
7008
7042
|
suggestions: { type: Array, required: false, default: () => [] },
|
|
7009
7043
|
features: { type: Object, required: false, default: () => ({}) },
|
|
@@ -7277,6 +7311,7 @@ ${fileParts.join("\n")}` });
|
|
|
7277
7311
|
"assistant-avatar": __props.assistantAvatar,
|
|
7278
7312
|
"user-name": __props.userName,
|
|
7279
7313
|
"assistant-name": __props.assistantName,
|
|
7314
|
+
typing: __props.typing,
|
|
7280
7315
|
onSelect: onSelectSuggestion,
|
|
7281
7316
|
onCopy,
|
|
7282
7317
|
onRegenerate,
|
|
@@ -7348,7 +7383,7 @@ ${fileParts.join("\n")}` });
|
|
|
7348
7383
|
]),
|
|
7349
7384
|
key: "8"
|
|
7350
7385
|
} : void 0
|
|
7351
|
-
]), 1032, ["messages", "nodes", "name", "suggestions", "show-scroll-button", "scroll-button-threshold", "show-user-avatar", "show-user-name", "show-assistant-avatar", "show-assistant-name", "user-avatar", "assistant-avatar", "user-name", "assistant-name"]),
|
|
7386
|
+
]), 1032, ["messages", "nodes", "name", "suggestions", "show-scroll-button", "scroll-button-threshold", "show-user-avatar", "show-user-name", "show-assistant-avatar", "show-assistant-name", "user-avatar", "assistant-avatar", "user-name", "assistant-name", "typing"]),
|
|
7352
7387
|
__props.features?.artifacts && artifacts.value.length ? (vue.openBlock(), vue.createBlock(_sfc_main$1, {
|
|
7353
7388
|
key: 0,
|
|
7354
7389
|
artifacts: artifacts.value,
|