@veltra/ai 2.1.10 → 2.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/dist/chat/use-chat.d.ts +15 -0
- package/dist/chat/use-chat.js +6 -0
- package/dist/chat/use-chat.js.map +1 -1
- package/dist/components/ai-chat/ai-chat.js +3 -1
- package/dist/components/ai-chat/ai-chat.js.map +1 -1
- package/dist/components/ai-chat/chat-input.js +160 -77
- package/dist/components/ai-chat/chat-input.js.map +1 -1
- package/dist/components/ai-chat/format-duration.js +13 -0
- package/dist/components/ai-chat/format-duration.js.map +1 -0
- package/dist/components/ai-chat/message-item.js +32 -23
- package/dist/components/ai-chat/message-item.js.map +1 -1
- package/dist/components/ai-chat/message-list.js +40 -5
- package/dist/components/ai-chat/message-list.js.map +1 -1
- package/dist/components/ai-chat/model-picker.js +31 -95
- package/dist/components/ai-chat/model-picker.js.map +1 -1
- package/dist/components/ai-chat/reasoning-picker.js +68 -0
- package/dist/components/ai-chat/reasoning-picker.js.map +1 -0
- package/dist/components/ai-chat/style2.css +123 -68
- package/dist/providers/types.d.ts +2 -0
- package/dist/types/ai-chat.d.ts +2 -1
- package/package.json +1 -1
- package/src/chat/use-chat.ts +6 -0
- package/src/components/ai-chat/__test__/ai-chat.test.ts +79 -28
- package/src/components/ai-chat/ai-chat.vue +2 -0
- package/src/components/ai-chat/chat-input.vue +91 -16
- package/src/components/ai-chat/format-duration.ts +8 -0
- package/src/components/ai-chat/message-item.vue +6 -0
- package/src/components/ai-chat/message-list.vue +48 -1
- package/src/components/ai-chat/model-picker.vue +11 -110
- package/src/components/ai-chat/reasoning-picker.vue +70 -0
- package/src/components/ai-chat/style.scss +137 -72
- package/src/providers/types.ts +2 -0
- package/src/types/ai-chat.ts +2 -1
|
@@ -1,69 +1,29 @@
|
|
|
1
1
|
import { AiChatDIKey } from "./di.js";
|
|
2
|
-
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, inject, mergeModels,
|
|
2
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, inject, mergeModels, normalizeClass, openBlock, renderList, toDisplayString, unref, useModel, useTemplateRef, withCtx } from "vue";
|
|
3
3
|
import { ArrowDown, Check } from "@veltra/icons/normal";
|
|
4
|
-
import { UDropdown, UIcon } from "@veltra/desktop";
|
|
4
|
+
import { UDropdown, UIcon, UScroll } from "@veltra/desktop";
|
|
5
5
|
import { bem } from "@veltra/utils";
|
|
6
6
|
//#region src/components/ai-chat/model-picker.vue
|
|
7
7
|
const _hoisted_1 = ["onClick"];
|
|
8
|
-
const _hoisted_2 = ["onClick"];
|
|
9
|
-
const _hoisted_3 = ["onClick"];
|
|
10
8
|
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
11
9
|
name: "UAiChatModelPicker",
|
|
12
10
|
__name: "model-picker",
|
|
13
11
|
props: /*@__PURE__*/ mergeModels({ models: {} }, {
|
|
14
12
|
"model": {},
|
|
15
|
-
"modelModifiers": {}
|
|
16
|
-
"reasoningLevel": {},
|
|
17
|
-
"reasoningLevelModifiers": {}
|
|
13
|
+
"modelModifiers": {}
|
|
18
14
|
}),
|
|
19
|
-
emits: ["update:model"
|
|
15
|
+
emits: ["update:model"],
|
|
20
16
|
setup(__props) {
|
|
21
17
|
const props = __props;
|
|
22
18
|
const model = useModel(__props, "model");
|
|
23
|
-
const reasoningLevel = useModel(__props, "reasoningLevel");
|
|
24
19
|
const cls = inject(AiChatDIKey)?.cls ?? bem("ai-chat");
|
|
25
20
|
const dropdownRef = useTemplateRef("dropdownRef");
|
|
26
|
-
/** 当前内联展开思考强度的模型 id(手风琴,同时只展开一个) */
|
|
27
|
-
const expandedReasoningId = shallowRef();
|
|
28
21
|
const currentModel = computed(() => {
|
|
29
22
|
return props.models.find((m) => m.id === model.value);
|
|
30
23
|
});
|
|
31
|
-
|
|
32
|
-
return currentModel.value?.reasoningLevels?.find((level) => level.value === reasoningLevel.value)?.label;
|
|
33
|
-
});
|
|
34
|
-
/** 取模型默认推理等级:显式配置优先,缺省落到首个可选等级 */
|
|
35
|
-
const resolveDefaultReasoningLevel = (item) => {
|
|
36
|
-
const levels = item.reasoningLevels ?? [];
|
|
37
|
-
if (!levels.length) return void 0;
|
|
38
|
-
return levels.find((level) => level.value === item.defaultReasoningLevel)?.value ?? levels[0]?.value;
|
|
39
|
-
};
|
|
40
|
-
/** 胶囊文案:已选中模型展示当前推理等级,其余展示该模型的默认等级 */
|
|
41
|
-
const getReasoningLabel = (item) => {
|
|
42
|
-
if (item.id === model.value && currentReasoningLabel.value) return currentReasoningLabel.value;
|
|
43
|
-
return item.reasoningLevels?.find((level) => level.value === resolveDefaultReasoningLevel(item))?.label;
|
|
44
|
-
};
|
|
45
|
-
const isReasoningActive = (item, value) => {
|
|
46
|
-
return item.id === model.value && reasoningLevel.value === value;
|
|
47
|
-
};
|
|
48
|
-
/** 关闭时收起内联思考强度,下次打开仍从模型列表开始 */
|
|
49
|
-
const handleVisibleChange = (visible) => {
|
|
50
|
-
if (!visible) expandedReasoningId.value = void 0;
|
|
51
|
-
};
|
|
52
|
-
/** 点击模型主体:直接选中模型,并同步到该模型默认推理等级 */
|
|
24
|
+
/** 选中模型即关闭;推理等级由 useChat 的模型联动校正 */
|
|
53
25
|
const handleSelectModel = (item) => {
|
|
54
26
|
model.value = item.id;
|
|
55
|
-
reasoningLevel.value = resolveDefaultReasoningLevel(item);
|
|
56
|
-
dropdownRef.value?.close();
|
|
57
|
-
};
|
|
58
|
-
/** 点击推理等级胶囊:内联展开/收起该模型的思考强度 */
|
|
59
|
-
const toggleReasoning = (item) => {
|
|
60
|
-
expandedReasoningId.value = expandedReasoningId.value === item.id ? void 0 : item.id;
|
|
61
|
-
nextTick(() => dropdownRef.value?.updateDropdown());
|
|
62
|
-
};
|
|
63
|
-
/** 选中思考强度:同时切到该等级所属模型,避免模型与推理等级错配 */
|
|
64
|
-
const handleSelectReasoning = (item, value) => {
|
|
65
|
-
model.value = item.id;
|
|
66
|
-
reasoningLevel.value = value;
|
|
67
27
|
dropdownRef.value?.close();
|
|
68
28
|
};
|
|
69
29
|
return (_ctx, _cache) => {
|
|
@@ -71,64 +31,40 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
71
31
|
ref_key: "dropdownRef",
|
|
72
32
|
ref: dropdownRef,
|
|
73
33
|
trigger: "click",
|
|
34
|
+
direction: "top",
|
|
35
|
+
alignment: "end",
|
|
74
36
|
width: "auto",
|
|
75
|
-
"min-width": "
|
|
76
|
-
"content-class": unref(cls).e("model-panel")
|
|
77
|
-
"onUpdate:visible": handleVisibleChange
|
|
37
|
+
"min-width": "240px",
|
|
38
|
+
"content-class": unref(cls).e("model-panel")
|
|
78
39
|
}, {
|
|
79
40
|
trigger: withCtx(() => [createElementVNode("button", {
|
|
80
41
|
type: "button",
|
|
81
42
|
class: normalizeClass(unref(cls).e("model-trigger"))
|
|
82
|
-
}, [
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
createVNode(unref(UIcon), { class: normalizeClass(unref(cls).e("model-trigger-arrow")) }, {
|
|
89
|
-
default: withCtx(() => [createVNode(unref(ArrowDown))]),
|
|
90
|
-
_: 1
|
|
91
|
-
}, 8, ["class"])
|
|
92
|
-
], 2)]),
|
|
93
|
-
content: withCtx(() => [createElementVNode("div", { class: normalizeClass(unref(cls).e("model-panel-list")) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.models, (item) => {
|
|
94
|
-
return openBlock(), createElementBlock("div", {
|
|
95
|
-
key: item.id,
|
|
96
|
-
class: normalizeClass([unref(cls).e("model-option"), unref(bem).is("active", item.id === model.value)])
|
|
97
|
-
}, [createElementVNode("div", {
|
|
98
|
-
class: normalizeClass(unref(cls).e("model-option-main")),
|
|
99
|
-
onClick: ($event) => handleSelectModel(item)
|
|
100
|
-
}, [createElementVNode("div", { class: normalizeClass(unref(cls).e("model-option-head")) }, [createElementVNode("span", { class: normalizeClass(unref(cls).e("model-option-name")) }, toDisplayString(item.label ?? item.id), 3), createElementVNode("span", { class: normalizeClass(unref(cls).e("model-option-actions")) }, [
|
|
101
|
-
item.reasoningLevels?.length ? (openBlock(), createElementBlock("button", {
|
|
102
|
-
key: 0,
|
|
103
|
-
type: "button",
|
|
104
|
-
class: normalizeClass([unref(cls).e("model-option-reasoning"), unref(bem).is("expanded", expandedReasoningId.value === item.id)]),
|
|
105
|
-
onClick: withModifiers(($event) => toggleReasoning(item), ["stop"])
|
|
106
|
-
}, [createElementVNode("span", null, toDisplayString(getReasoningLabel(item)), 1), createVNode(unref(UIcon), { class: normalizeClass(unref(cls).e("model-option-reasoning-arrow")) }, {
|
|
107
|
-
default: withCtx(() => [createVNode(unref(ArrowDown))]),
|
|
108
|
-
_: 1
|
|
109
|
-
}, 8, ["class"])], 10, _hoisted_2)) : createCommentVNode("v-if", true),
|
|
110
|
-
createCommentVNode(" 勾选槽位固定占位:未选中也保留位置,保证推理等级按钮跨行对齐 "),
|
|
111
|
-
createVNode(unref(UIcon), { class: normalizeClass([unref(cls).e("model-option-check"), unref(bem).is("hidden", item.id !== model.value)]) }, {
|
|
112
|
-
default: withCtx(() => [createVNode(unref(Check))]),
|
|
113
|
-
_: 1
|
|
114
|
-
}, 8, ["class"])
|
|
115
|
-
], 2)], 2), item.description ? (openBlock(), createElementBlock("div", {
|
|
116
|
-
key: 0,
|
|
117
|
-
class: normalizeClass(unref(cls).e("model-option-desc"))
|
|
118
|
-
}, toDisplayString(item.description), 3)) : createCommentVNode("v-if", true)], 10, _hoisted_1), item.reasoningLevels?.length ? (openBlock(), createElementBlock("div", {
|
|
119
|
-
key: 0,
|
|
120
|
-
class: normalizeClass([unref(cls).e("reasoning-levels"), unref(bem).is("expanded", expandedReasoningId.value === item.id)])
|
|
121
|
-
}, [createElementVNode("div", { class: normalizeClass(unref(cls).e("reasoning-levels-inner")) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(item.reasoningLevels, (level) => {
|
|
43
|
+
}, [createElementVNode("span", { class: normalizeClass(unref(cls).e("model-trigger-label")) }, toDisplayString(currentModel.value?.label ?? model.value ?? "模型"), 3), createVNode(unref(UIcon), { class: normalizeClass(unref(cls).e("model-trigger-arrow")) }, {
|
|
44
|
+
default: withCtx(() => [createVNode(unref(ArrowDown))]),
|
|
45
|
+
_: 1
|
|
46
|
+
}, 8, ["class"])], 2)]),
|
|
47
|
+
content: withCtx(() => [createVNode(unref(UScroll), { class: normalizeClass(unref(cls).e("model-panel-list")) }, {
|
|
48
|
+
default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.models, (item) => {
|
|
122
49
|
return openBlock(), createElementBlock("div", {
|
|
123
|
-
key:
|
|
124
|
-
class: normalizeClass([unref(cls).e("
|
|
125
|
-
|
|
126
|
-
|
|
50
|
+
key: item.id,
|
|
51
|
+
class: normalizeClass([unref(cls).e("model-option"), unref(bem).is("active", item.id === model.value)])
|
|
52
|
+
}, [createElementVNode("div", {
|
|
53
|
+
class: normalizeClass(unref(cls).e("model-option-main")),
|
|
54
|
+
onClick: ($event) => handleSelectModel(item)
|
|
55
|
+
}, [createElementVNode("div", { class: normalizeClass(unref(cls).e("model-option-head")) }, [createElementVNode("span", { class: normalizeClass(unref(cls).e("model-option-name")) }, toDisplayString(item.label ?? item.id), 3), item.id === model.value ? (openBlock(), createBlock(unref(UIcon), {
|
|
56
|
+
key: 0,
|
|
57
|
+
class: normalizeClass(unref(cls).e("model-option-check"))
|
|
58
|
+
}, {
|
|
127
59
|
default: withCtx(() => [createVNode(unref(Check))]),
|
|
128
60
|
_: 1
|
|
129
|
-
}, 8, ["class"])],
|
|
130
|
-
|
|
131
|
-
|
|
61
|
+
}, 8, ["class"])) : createCommentVNode("v-if", true)], 2), item.description ? (openBlock(), createElementBlock("div", {
|
|
62
|
+
key: 0,
|
|
63
|
+
class: normalizeClass(unref(cls).e("model-option-desc"))
|
|
64
|
+
}, toDisplayString(item.description), 3)) : createCommentVNode("v-if", true)], 10, _hoisted_1)], 2);
|
|
65
|
+
}), 128))]),
|
|
66
|
+
_: 1
|
|
67
|
+
}, 8, ["class"])]),
|
|
132
68
|
_: 1
|
|
133
69
|
}, 8, ["content-class"]);
|
|
134
70
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-picker.js","names":[],"sources":["../../../src/components/ai-chat/model-picker.vue"],"sourcesContent":["<template>\n <UDropdown\n ref=\"dropdownRef\"\n trigger=\"click\"\n width=\"auto\"\n min-width=\"220px\"\n :content-class=\"cls.e('model-panel')\"\n @update:visible=\"handleVisibleChange\"\n >\n <template #trigger>\n <button type=\"button\" :class=\"cls.e('model-trigger')\">\n <span :class=\"cls.e('model-trigger-label')\">\n {{ currentModel?.label ?? model ?? '模型' }}\n </span>\n <span v-if=\"currentReasoningLabel\" :class=\"cls.e('model-trigger-reasoning')\">\n {{ currentReasoningLabel }}\n </span>\n <UIcon :class=\"cls.e('model-trigger-arrow')\">\n <ArrowDown />\n </UIcon>\n </button>\n </template>\n\n <template #content>\n <div :class=\"cls.e('model-panel-list')\">\n <div\n v-for=\"item in models\"\n :key=\"item.id\"\n :class=\"[cls.e('model-option'), bem.is('active', item.id === model)]\"\n >\n <div :class=\"cls.e('model-option-main')\" @click=\"handleSelectModel(item)\">\n <div :class=\"cls.e('model-option-head')\">\n <span :class=\"cls.e('model-option-name')\">{{ item.label ?? item.id }}</span>\n <span :class=\"cls.e('model-option-actions')\">\n <button\n v-if=\"item.reasoningLevels?.length\"\n type=\"button\"\n :class=\"[\n cls.e('model-option-reasoning'),\n bem.is('expanded', expandedReasoningId === item.id)\n ]\"\n @click.stop=\"toggleReasoning(item)\"\n >\n <span>{{ getReasoningLabel(item) }}</span>\n <UIcon :class=\"cls.e('model-option-reasoning-arrow')\">\n <ArrowDown />\n </UIcon>\n </button>\n <!-- 勾选槽位固定占位:未选中也保留位置,保证推理等级按钮跨行对齐 -->\n <UIcon :class=\"[cls.e('model-option-check'), bem.is('hidden', item.id !== model)]\">\n <Check />\n </UIcon>\n </span>\n </div>\n <div v-if=\"item.description\" :class=\"cls.e('model-option-desc')\">\n {{ item.description }}\n </div>\n </div>\n\n <div\n v-if=\"item.reasoningLevels?.length\"\n :class=\"[\n cls.e('reasoning-levels'),\n bem.is('expanded', expandedReasoningId === item.id)\n ]\"\n >\n <div :class=\"cls.e('reasoning-levels-inner')\">\n <div\n v-for=\"level in item.reasoningLevels\"\n :key=\"level.value\"\n :class=\"[\n cls.e('reasoning-option'),\n bem.is('active', isReasoningActive(item, level.value))\n ]\"\n @click=\"handleSelectReasoning(item, level.value)\"\n >\n <span>{{ level.label }}</span>\n <UIcon\n :class=\"[\n cls.e('model-option-check'),\n bem.is('hidden', !isReasoningActive(item, level.value))\n ]\"\n >\n <Check />\n </UIcon>\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </UDropdown>\n</template>\n\n<script lang=\"ts\" setup>\nimport { UDropdown, UIcon } from '@veltra/desktop'\nimport { ArrowDown, Check } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, inject, nextTick, shallowRef, useTemplateRef } from 'vue'\n\nimport type { ChatModelOption } from '../../providers'\nimport { AiChatDIKey } from './di'\n\ndefineOptions({ name: 'UAiChatModelPicker' })\n\nconst props = defineProps<{\n /** 可选模型列表 */\n models: ChatModelOption[]\n}>()\n\nconst model = defineModel<string | undefined>('model')\nconst reasoningLevel = defineModel<string | undefined>('reasoningLevel')\n\nconst di = inject(AiChatDIKey)\nconst cls = di?.cls ?? bem('ai-chat')\n\nconst dropdownRef = useTemplateRef('dropdownRef')\n\n/** 当前内联展开思考强度的模型 id(手风琴,同时只展开一个) */\nconst expandedReasoningId = shallowRef<string>()\n\nconst currentModel = computed(() => {\n return props.models.find((m) => m.id === model.value)\n})\n\nconst currentReasoningLabel = computed(() => {\n return currentModel.value?.reasoningLevels?.find((level) => level.value === reasoningLevel.value)\n ?.label\n})\n\n/** 取模型默认推理等级:显式配置优先,缺省落到首个可选等级 */\nconst resolveDefaultReasoningLevel = (item: ChatModelOption) => {\n const levels = item.reasoningLevels ?? []\n if (!levels.length) return undefined\n return (\n levels.find((level) => level.value === item.defaultReasoningLevel)?.value ?? levels[0]?.value\n )\n}\n\n/** 胶囊文案:已选中模型展示当前推理等级,其余展示该模型的默认等级 */\nconst getReasoningLabel = (item: ChatModelOption) => {\n if (item.id === model.value && currentReasoningLabel.value) {\n return currentReasoningLabel.value\n }\n return item.reasoningLevels?.find((level) => level.value === resolveDefaultReasoningLevel(item))\n ?.label\n}\n\nconst isReasoningActive = (item: ChatModelOption, value: string) => {\n return item.id === model.value && reasoningLevel.value === value\n}\n\n/** 关闭时收起内联思考强度,下次打开仍从模型列表开始 */\nconst handleVisibleChange = (visible: boolean) => {\n if (!visible) expandedReasoningId.value = undefined\n}\n\n/** 点击模型主体:直接选中模型,并同步到该模型默认推理等级 */\nconst handleSelectModel = (item: ChatModelOption) => {\n model.value = item.id\n reasoningLevel.value = resolveDefaultReasoningLevel(item)\n dropdownRef.value?.close()\n}\n\n/** 点击推理等级胶囊:内联展开/收起该模型的思考强度 */\nconst toggleReasoning = (item: ChatModelOption) => {\n expandedReasoningId.value = expandedReasoningId.value === item.id ? undefined : item.id\n nextTick(() => dropdownRef.value?.updateDropdown())\n}\n\n/** 选中思考强度:同时切到该等级所属模型,避免模型与推理等级错配 */\nconst handleSelectReasoning = (item: ChatModelOption, value: string) => {\n model.value = item.id\n reasoningLevel.value = value\n dropdownRef.value?.close()\n}\n</script>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"model-picker.js","names":[],"sources":["../../../src/components/ai-chat/model-picker.vue"],"sourcesContent":["<template>\n <UDropdown\n ref=\"dropdownRef\"\n trigger=\"click\"\n direction=\"top\"\n alignment=\"end\"\n width=\"auto\"\n min-width=\"240px\"\n :content-class=\"cls.e('model-panel')\"\n >\n <template #trigger>\n <button type=\"button\" :class=\"cls.e('model-trigger')\">\n <span :class=\"cls.e('model-trigger-label')\">\n {{ currentModel?.label ?? model ?? '模型' }}\n </span>\n <UIcon :class=\"cls.e('model-trigger-arrow')\">\n <ArrowDown />\n </UIcon>\n </button>\n </template>\n\n <template #content>\n <UScroll :class=\"cls.e('model-panel-list')\">\n <div\n v-for=\"item in models\"\n :key=\"item.id\"\n :class=\"[cls.e('model-option'), bem.is('active', item.id === model)]\"\n >\n <div :class=\"cls.e('model-option-main')\" @click=\"handleSelectModel(item)\">\n <div :class=\"cls.e('model-option-head')\">\n <span :class=\"cls.e('model-option-name')\">{{ item.label ?? item.id }}</span>\n <UIcon v-if=\"item.id === model\" :class=\"cls.e('model-option-check')\">\n <Check />\n </UIcon>\n </div>\n <div v-if=\"item.description\" :class=\"cls.e('model-option-desc')\">\n {{ item.description }}\n </div>\n </div>\n </div>\n </UScroll>\n </template>\n </UDropdown>\n</template>\n\n<script lang=\"ts\" setup>\nimport { UDropdown, UIcon, UScroll } from '@veltra/desktop'\nimport { ArrowDown, Check } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, inject, useTemplateRef } from 'vue'\n\nimport type { ChatModelOption } from '../../providers'\nimport { AiChatDIKey } from './di'\n\ndefineOptions({ name: 'UAiChatModelPicker' })\n\nconst props = defineProps<{\n /** 可选模型列表 */\n models: ChatModelOption[]\n}>()\n\nconst model = defineModel<string | undefined>('model')\n\nconst di = inject(AiChatDIKey)\nconst cls = di?.cls ?? bem('ai-chat')\n\nconst dropdownRef = useTemplateRef('dropdownRef')\n\nconst currentModel = computed(() => {\n return props.models.find((m) => m.id === model.value)\n})\n\n/** 选中模型即关闭;推理等级由 useChat 的模型联动校正 */\nconst handleSelectModel = (item: ChatModelOption) => {\n model.value = item.id\n dropdownRef.value?.close()\n}\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;EAwDA,MAAM,QAAQ;EAKd,MAAM,QAAQ,SAA+B,SAAC,OAAO;EAGrD,MAAM,MADK,OAAO,WACN,CAAA,EAAI,OAAO,IAAI,SAAS;EAEpC,MAAM,cAAc,eAAe,aAAa;EAEhD,MAAM,eAAe,eAAe;GAClC,OAAO,MAAM,OAAO,MAAM,MAAM,EAAE,OAAO,MAAM,KAAK;EACtD,CAAC;;EAGD,MAAM,qBAAqB,SAA0B;GACnD,MAAM,QAAQ,KAAK;GACnB,YAAY,OAAO,MAAM;EAC3B;;GA3EE,OAAA,UAAA,GAAA,YAyCY,MAAA,SAAA,GAAA;IAxCN,SAAA;IAAJ,KAAI;IACJ,SAAQ;IACR,WAAU;IACV,WAAU;IACV,OAAM;IACN,aAAU;IACT,iBAAe,MAAA,GAAA,CAAG,CAAC,EAAC,aAAA;;IAEV,SAAO,cAQP,CAPT,mBAOS,UAAA;KAPD,MAAK;KAAU,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,eAAA,CAAA;IACjC,GAAA,CAAA,mBAEO,QAAA,EAFA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,qBAAA,CAAA,EACd,GAAA,gBAAA,aAAA,OAAc,SAAS,MAAA,SAAK,IAAA,GAAA,CAAA,GAEjC,YAEQ,MAAA,KAAA,GAAA,EAFA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,qBAAA,CAAA,EAAA,GAAA;KAClB,SAAA,cAAa,CAAb,YAAa,MAAA,SAAA,CAAA,CAAA,CAAA;;;IAKR,SAAO,cAmBN,CAlBV,YAkBU,MAAA,OAAA,GAAA,EAlBA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,kBAAA,CAAA,EAAA,GAAA;KAElB,SAAA,cAAsB,EADxB,UAAA,IAAA,GAAA,mBAgBM,UAAA,MAAA,WAfW,QAAA,SAAR,SAAI;MADb,OAAA,UAAA,GAAA,mBAgBM,OAAA;OAdH,KAAK,KAAK;OACV,OAAK,eAAA,CAAG,MAAA,GAAA,CAAG,CAAC,EAAC,cAAA,GAAkB,MAAA,GAAA,CAAG,CAAC,GAAE,UAAW,KAAK,OAAO,MAAA,KAAK,CAAA,CAAA;MAElE,GAAA,CAAA,mBAUM,OAAA;OAVA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,mBAAA,CAAA;OAAwB,UAAK,WAAE,kBAAkB,IAAI;MACrE,GAAA,CAAA,mBAKM,OAAA,EALA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,mBAAA,CAAA,EAAA,GAChB,CAAA,mBAA4E,QAAA,EAArE,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,mBAAA,CAAA,EAA0B,GAAA,gBAAA,KAAK,SAAS,KAAK,EAAE,GAAA,CAAA,GACrD,KAAK,OAAO,MAAA,SAAzB,UAAA,GAAA,YAEQ,MAAA,KAAA,GAAA;;OAFyB,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,oBAAA,CAAA;;OAC3C,SAAA,cAAS,CAAT,YAAS,MAAA,KAAA,CAAA,CAAA,CAAA;;MAGF,GAAA,GAAA,CAAA,OAAA,CAAA,KAAA,mBAAA,QAAA,IAAA,CAAA,GAAA,CAAA,GAAA,KAAK,eAAhB,UAAA,GAAA,mBAEM,OAAA;;OAFwB,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,mBAAA,CAAA;MACrC,GAAA,gBAAA,KAAK,WAAW,GAAA,CAAA,KAAA,mBAAA,QAAA,IAAA,CAAA,GAAA,IAAA,UAAA,CAAA,GAAA,CAAA"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { AiChatDIKey } from "./di.js";
|
|
2
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, inject, mergeModels, normalizeClass, openBlock, renderList, toDisplayString, unref, useModel, useTemplateRef, withCtx } from "vue";
|
|
3
|
+
import { ArrowDown, Check } from "@veltra/icons/normal";
|
|
4
|
+
import { UDropdown, UIcon } from "@veltra/desktop";
|
|
5
|
+
import { bem } from "@veltra/utils";
|
|
6
|
+
//#region src/components/ai-chat/reasoning-picker.vue
|
|
7
|
+
const _hoisted_1 = ["onClick"];
|
|
8
|
+
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
9
|
+
name: "UAiChatReasoningPicker",
|
|
10
|
+
__name: "reasoning-picker",
|
|
11
|
+
props: /*@__PURE__*/ mergeModels({ levels: {} }, {
|
|
12
|
+
"reasoningLevel": {},
|
|
13
|
+
"reasoningLevelModifiers": {}
|
|
14
|
+
}),
|
|
15
|
+
emits: ["update:reasoningLevel"],
|
|
16
|
+
setup(__props) {
|
|
17
|
+
const reasoningLevel = useModel(__props, "reasoningLevel");
|
|
18
|
+
const cls = inject(AiChatDIKey)?.cls ?? bem("ai-chat");
|
|
19
|
+
const dropdownRef = useTemplateRef("dropdownRef");
|
|
20
|
+
const currentLabel = computed(() => {
|
|
21
|
+
return __props.levels.find((level) => level.value === reasoningLevel.value)?.label ?? "推理";
|
|
22
|
+
});
|
|
23
|
+
const handleSelect = (value) => {
|
|
24
|
+
reasoningLevel.value = value;
|
|
25
|
+
dropdownRef.value?.close();
|
|
26
|
+
};
|
|
27
|
+
return (_ctx, _cache) => {
|
|
28
|
+
return __props.levels.length > 1 ? (openBlock(), createBlock(unref(UDropdown), {
|
|
29
|
+
key: 0,
|
|
30
|
+
ref_key: "dropdownRef",
|
|
31
|
+
ref: dropdownRef,
|
|
32
|
+
trigger: "click",
|
|
33
|
+
direction: "top",
|
|
34
|
+
alignment: "end",
|
|
35
|
+
width: "auto",
|
|
36
|
+
"min-width": "140px",
|
|
37
|
+
"content-class": unref(cls).e("reasoning-panel")
|
|
38
|
+
}, {
|
|
39
|
+
trigger: withCtx(() => [createElementVNode("button", {
|
|
40
|
+
type: "button",
|
|
41
|
+
class: normalizeClass(unref(cls).e("reasoning-trigger")),
|
|
42
|
+
title: "推理等级"
|
|
43
|
+
}, [createElementVNode("span", { class: normalizeClass(unref(cls).e("reasoning-trigger-label")) }, toDisplayString(currentLabel.value), 3), createVNode(unref(UIcon), { class: normalizeClass(unref(cls).e("reasoning-trigger-arrow")) }, {
|
|
44
|
+
default: withCtx(() => [createVNode(unref(ArrowDown))]),
|
|
45
|
+
_: 1
|
|
46
|
+
}, 8, ["class"])], 2)]),
|
|
47
|
+
content: withCtx(() => [createElementVNode("div", { class: normalizeClass(unref(cls).e("reasoning-list")) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.levels, (level) => {
|
|
48
|
+
return openBlock(), createElementBlock("div", {
|
|
49
|
+
key: level.value,
|
|
50
|
+
class: normalizeClass([unref(cls).e("reasoning-option"), unref(bem).is("active", level.value === reasoningLevel.value)]),
|
|
51
|
+
onClick: ($event) => handleSelect(level.value)
|
|
52
|
+
}, [createElementVNode("span", null, toDisplayString(level.label), 1), level.value === reasoningLevel.value ? (openBlock(), createBlock(unref(UIcon), {
|
|
53
|
+
key: 0,
|
|
54
|
+
class: normalizeClass(unref(cls).e("reasoning-check"))
|
|
55
|
+
}, {
|
|
56
|
+
default: withCtx(() => [createVNode(unref(Check))]),
|
|
57
|
+
_: 1
|
|
58
|
+
}, 8, ["class"])) : createCommentVNode("v-if", true)], 10, _hoisted_1);
|
|
59
|
+
}), 128))], 2)]),
|
|
60
|
+
_: 1
|
|
61
|
+
}, 8, ["content-class"])) : createCommentVNode("v-if", true);
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
//#endregion
|
|
66
|
+
export { _sfc_main as default };
|
|
67
|
+
|
|
68
|
+
//# sourceMappingURL=reasoning-picker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reasoning-picker.js","names":[],"sources":["../../../src/components/ai-chat/reasoning-picker.vue"],"sourcesContent":["<template>\n <UDropdown\n v-if=\"levels.length > 1\"\n ref=\"dropdownRef\"\n trigger=\"click\"\n direction=\"top\"\n alignment=\"end\"\n width=\"auto\"\n min-width=\"140px\"\n :content-class=\"cls.e('reasoning-panel')\"\n >\n <template #trigger>\n <button type=\"button\" :class=\"cls.e('reasoning-trigger')\" title=\"推理等级\">\n <span :class=\"cls.e('reasoning-trigger-label')\">{{ currentLabel }}</span>\n <UIcon :class=\"cls.e('reasoning-trigger-arrow')\">\n <ArrowDown />\n </UIcon>\n </button>\n </template>\n\n <template #content>\n <div :class=\"cls.e('reasoning-list')\">\n <div\n v-for=\"level in levels\"\n :key=\"level.value\"\n :class=\"[cls.e('reasoning-option'), bem.is('active', level.value === reasoningLevel)]\"\n @click=\"handleSelect(level.value)\"\n >\n <span>{{ level.label }}</span>\n <UIcon v-if=\"level.value === reasoningLevel\" :class=\"cls.e('reasoning-check')\">\n <Check />\n </UIcon>\n </div>\n </div>\n </template>\n </UDropdown>\n</template>\n\n<script lang=\"ts\" setup>\nimport { UDropdown, UIcon } from '@veltra/desktop'\nimport { ArrowDown, Check } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, inject, useTemplateRef } from 'vue'\n\nimport type { ChatReasoningLevel } from '../../providers'\nimport { AiChatDIKey } from './di'\n\ndefineOptions({ name: 'UAiChatReasoningPicker' })\n\nconst { levels } = defineProps<{\n /** 当前模型的推理等级列表;不足两档时没有切换意义,不渲染 */\n levels: ChatReasoningLevel[]\n}>()\n\nconst reasoningLevel = defineModel<string | undefined>('reasoningLevel')\n\nconst di = inject(AiChatDIKey)\nconst cls = di?.cls ?? bem('ai-chat')\n\nconst dropdownRef = useTemplateRef('dropdownRef')\n\nconst currentLabel = computed(() => {\n return levels.find((level) => level.value === reasoningLevel.value)?.label ?? '推理'\n})\n\nconst handleSelect = (value: string) => {\n reasoningLevel.value = value\n dropdownRef.value?.close()\n}\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;EAsDA,MAAM,iBAAiB,SAA+B,SAAC,gBAAgB;EAGvE,MAAM,MADK,OAAO,WACN,CAAA,EAAI,OAAO,IAAI,SAAS;EAEpC,MAAM,cAAc,eAAe,aAAa;EAEhD,MAAM,eAAe,eAAe;GAClC,OAAO,QAAA,OAAO,MAAM,UAAU,MAAM,UAAU,eAAe,KAAK,CAAC,EAAE,SAAS;EAChF,CAAC;EAED,MAAM,gBAAgB,UAAkB;GACtC,eAAe,QAAQ;GACvB,YAAY,OAAO,MAAM;EAC3B;;GAlEU,OAAA,QAAA,OAAO,SAAM,KADrB,UAAA,GAAA,YAkCY,MAAA,SAAA,GAAA;;IAhCN,SAAA;IAAJ,KAAI;IACJ,SAAQ;IACR,WAAU;IACV,WAAU;IACV,OAAM;IACN,aAAU;IACT,iBAAe,MAAA,GAAA,CAAG,CAAC,EAAC,iBAAA;;IAEV,SAAO,cAMP,CALT,mBAKS,UAAA;KALD,MAAK;KAAU,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,mBAAA,CAAA;KAAuB,OAAM;IAC9D,GAAA,CAAA,mBAAyE,QAAA,EAAlE,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,yBAAA,CAAA,EAAgC,GAAA,gBAAA,aAAA,KAAY,GAAA,CAAA,GAC/D,YAEQ,MAAA,KAAA,GAAA,EAFA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,yBAAA,CAAA,EAAA,GAAA;KAClB,SAAA,cAAa,CAAb,YAAa,MAAA,SAAA,CAAA,CAAA,CAAA;;;IAKR,SAAO,cAaV,CAZN,mBAYM,OAAA,EAZA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,gBAAA,CAAA,EAAA,GAChB,EAAA,UAAA,IAAA,GAAA,mBAUM,UAAA,MAAA,WATY,QAAA,SAAT,UAAK;KADd,OAAA,UAAA,GAAA,mBAUM,OAAA;MARH,KAAK,MAAM;MACX,OAAK,eAAA,CAAG,MAAA,GAAA,CAAG,CAAC,EAAC,kBAAA,GAAsB,MAAA,GAAA,CAAG,CAAC,GAAE,UAAW,MAAM,UAAU,eAAA,KAAc,CAAA,CAAA;MAClF,UAAK,WAAE,aAAa,MAAM,KAAK;KAEhC,GAAA,CAAA,mBAA8B,QAAA,MAAA,gBAArB,MAAM,KAAK,GAAA,CAAA,GACP,MAAM,UAAU,eAAA,SAA7B,UAAA,GAAA,YAEQ,MAAA,KAAA,GAAA;;MAFsC,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,iBAAA,CAAA;;MACxD,SAAA,cAAS,CAAT,YAAS,MAAA,KAAA,CAAA,CAAA,CAAA"}
|
|
@@ -169,13 +169,20 @@
|
|
|
169
169
|
position: relative;
|
|
170
170
|
flex: 1;
|
|
171
171
|
min-height: 0;
|
|
172
|
-
width: 100
|
|
173
|
-
|
|
172
|
+
width: calc(100% + var(--u-gap-default) * 2);
|
|
173
|
+
margin: 0 calc(var(--u-gap-default) * -1);
|
|
174
174
|
display: flex;
|
|
175
175
|
flex-direction: column;
|
|
176
176
|
padding: var(--u-gap-default) 0;
|
|
177
177
|
box-sizing: border-box;
|
|
178
178
|
}
|
|
179
|
+
.u-ai-chat__list-content {
|
|
180
|
+
width: 100%;
|
|
181
|
+
max-width: calc(800px + var(--u-gap-default) * 2);
|
|
182
|
+
margin: 0 auto;
|
|
183
|
+
padding: 0 var(--u-gap-default);
|
|
184
|
+
box-sizing: border-box;
|
|
185
|
+
}
|
|
179
186
|
.u-ai-chat__list {
|
|
180
187
|
flex: 1;
|
|
181
188
|
min-height: 0;
|
|
@@ -183,6 +190,8 @@
|
|
|
183
190
|
.u-ai-chat__welcome {
|
|
184
191
|
flex-shrink: 0;
|
|
185
192
|
width: 100%;
|
|
193
|
+
max-width: 800px;
|
|
194
|
+
margin: 0 auto;
|
|
186
195
|
height: 48px;
|
|
187
196
|
display: flex;
|
|
188
197
|
justify-content: flex-start;
|
|
@@ -271,7 +280,7 @@
|
|
|
271
280
|
align-items: center;
|
|
272
281
|
flex-wrap: nowrap;
|
|
273
282
|
gap: var(--u-gap-small);
|
|
274
|
-
padding: 0
|
|
283
|
+
padding: 0 0 var(--u-gap-small);
|
|
275
284
|
width: fit-content;
|
|
276
285
|
}
|
|
277
286
|
.u-ai-chat__working .u-ai-orb {
|
|
@@ -290,6 +299,11 @@
|
|
|
290
299
|
.u-ai-chat__working-text {
|
|
291
300
|
font-size: var(--u-font-size-main-small);
|
|
292
301
|
}
|
|
302
|
+
.u-ai-chat__working-time {
|
|
303
|
+
font-size: var(--u-font-size-main-small);
|
|
304
|
+
color: var(--u-text-color-assist);
|
|
305
|
+
font-variant-numeric: tabular-nums;
|
|
306
|
+
}
|
|
293
307
|
@media (prefers-reduced-motion: reduce) {
|
|
294
308
|
.u-ai-chat .u-ai-chat__welcome.is-enter .u-ai-orb,
|
|
295
309
|
.u-ai-chat .u-ai-chat__working .u-ai-orb {
|
|
@@ -411,6 +425,12 @@
|
|
|
411
425
|
flex-wrap: wrap;
|
|
412
426
|
gap: var(--u-gap-small);
|
|
413
427
|
}
|
|
428
|
+
.u-ai-chat__message-duration {
|
|
429
|
+
align-self: center;
|
|
430
|
+
color: var(--u-text-color-assist);
|
|
431
|
+
font-size: var(--u-font-size-main-small);
|
|
432
|
+
font-variant-numeric: tabular-nums;
|
|
433
|
+
}
|
|
414
434
|
.u-ai-chat__reasoning {
|
|
415
435
|
margin-bottom: 4px;
|
|
416
436
|
color: var(--u-text-color-second);
|
|
@@ -997,11 +1017,39 @@
|
|
|
997
1017
|
text-overflow: ellipsis;
|
|
998
1018
|
white-space: nowrap;
|
|
999
1019
|
}
|
|
1000
|
-
.u-ai-chat__model-trigger-
|
|
1020
|
+
.u-ai-chat__model-trigger-arrow {
|
|
1001
1021
|
flex-shrink: 0;
|
|
1022
|
+
font-size: 12px;
|
|
1002
1023
|
color: var(--u-text-color-assist);
|
|
1003
1024
|
}
|
|
1004
|
-
.u-ai-
|
|
1025
|
+
.u-ai-chat__reasoning-trigger {
|
|
1026
|
+
display: flex;
|
|
1027
|
+
justify-content: flex-start;
|
|
1028
|
+
align-items: center;
|
|
1029
|
+
flex-wrap: nowrap;
|
|
1030
|
+
gap: 4px;
|
|
1031
|
+
flex-shrink: 0;
|
|
1032
|
+
padding: 2px var(--u-gap-small);
|
|
1033
|
+
border: none;
|
|
1034
|
+
border-radius: var(--u-radius-default);
|
|
1035
|
+
background: transparent;
|
|
1036
|
+
color: var(--u-text-color-assist);
|
|
1037
|
+
font-family: inherit;
|
|
1038
|
+
font-size: inherit;
|
|
1039
|
+
cursor: pointer;
|
|
1040
|
+
transition: background-color 0.15s ease, color 0.15s ease;
|
|
1041
|
+
}
|
|
1042
|
+
.u-ai-chat__reasoning-trigger:hover {
|
|
1043
|
+
background-color: var(--u-bg-color-hover);
|
|
1044
|
+
color: var(--u-text-color-main);
|
|
1045
|
+
}
|
|
1046
|
+
.u-ai-chat__reasoning-trigger-label {
|
|
1047
|
+
overflow: hidden;
|
|
1048
|
+
text-overflow: ellipsis;
|
|
1049
|
+
white-space: nowrap;
|
|
1050
|
+
max-width: 72px;
|
|
1051
|
+
}
|
|
1052
|
+
.u-ai-chat__reasoning-trigger-arrow {
|
|
1005
1053
|
flex-shrink: 0;
|
|
1006
1054
|
font-size: 12px;
|
|
1007
1055
|
color: var(--u-text-color-assist);
|
|
@@ -1032,14 +1080,32 @@
|
|
|
1032
1080
|
.u-ai-chat__input-clear:hover {
|
|
1033
1081
|
color: var(--u-color-danger);
|
|
1034
1082
|
}
|
|
1035
|
-
.u-ai-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1083
|
+
.u-ai-chat__usage-trigger {
|
|
1084
|
+
display: inline-flex;
|
|
1085
|
+
justify-content: center;
|
|
1086
|
+
align-items: center;
|
|
1087
|
+
flex-wrap: nowrap;
|
|
1088
|
+
flex-shrink: 0;
|
|
1089
|
+
padding: 2px;
|
|
1090
|
+
border: none;
|
|
1091
|
+
border-radius: 50%;
|
|
1092
|
+
background: transparent;
|
|
1093
|
+
}
|
|
1094
|
+
.u-ai-chat__usage-ring {
|
|
1095
|
+
display: block;
|
|
1096
|
+
width: 14px;
|
|
1097
|
+
height: 14px;
|
|
1098
|
+
}
|
|
1099
|
+
.u-ai-chat__usage-ring-track {
|
|
1100
|
+
fill: none;
|
|
1101
|
+
stroke: var(--u-border-color);
|
|
1102
|
+
stroke-width: 5;
|
|
1103
|
+
}
|
|
1104
|
+
.u-ai-chat__usage-ring-value {
|
|
1105
|
+
fill: none;
|
|
1106
|
+
stroke: var(--u-color-primary);
|
|
1107
|
+
stroke-width: 5;
|
|
1108
|
+
transition: stroke-dasharray 0.3s ease;
|
|
1043
1109
|
}
|
|
1044
1110
|
.u-ai-chat__input-stop {
|
|
1045
1111
|
display: block;
|
|
@@ -1078,12 +1144,35 @@
|
|
|
1078
1144
|
font-size: 12px;
|
|
1079
1145
|
}
|
|
1080
1146
|
|
|
1147
|
+
.u-ai-chat__usage-panel {
|
|
1148
|
+
padding: 6px 4px;
|
|
1149
|
+
user-select: none;
|
|
1150
|
+
}
|
|
1151
|
+
.u-ai-chat__usage-panel .u-ai-chat__usage-row {
|
|
1152
|
+
display: flex;
|
|
1153
|
+
justify-content: space-between;
|
|
1154
|
+
align-items: center;
|
|
1155
|
+
flex-wrap: nowrap;
|
|
1156
|
+
gap: 24px;
|
|
1157
|
+
padding: 3px 8px;
|
|
1158
|
+
font-size: var(--u-font-size-main-small);
|
|
1159
|
+
line-height: 1.6;
|
|
1160
|
+
}
|
|
1161
|
+
.u-ai-chat__usage-panel .u-ai-chat__usage-label {
|
|
1162
|
+
color: var(--u-text-color-assist);
|
|
1163
|
+
}
|
|
1164
|
+
.u-ai-chat__usage-panel .u-ai-chat__usage-value {
|
|
1165
|
+
color: var(--u-text-color-main);
|
|
1166
|
+
font-variant-numeric: tabular-nums;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1081
1169
|
.u-ai-chat__model-panel {
|
|
1082
1170
|
padding: 4px;
|
|
1083
1171
|
user-select: none;
|
|
1084
1172
|
}
|
|
1085
1173
|
.u-ai-chat__model-panel .u-ai-chat__model-panel-list {
|
|
1086
1174
|
min-width: 0;
|
|
1175
|
+
max-height: 280px;
|
|
1087
1176
|
}
|
|
1088
1177
|
.u-ai-chat__model-panel .u-ai-chat__model-option-main {
|
|
1089
1178
|
padding: 6px 8px;
|
|
@@ -1096,93 +1185,59 @@
|
|
|
1096
1185
|
}
|
|
1097
1186
|
.u-ai-chat__model-panel .u-ai-chat__model-option-head {
|
|
1098
1187
|
display: flex;
|
|
1099
|
-
justify-content:
|
|
1188
|
+
justify-content: flex-start;
|
|
1100
1189
|
align-items: center;
|
|
1101
1190
|
flex-wrap: nowrap;
|
|
1102
|
-
gap:
|
|
1191
|
+
gap: 8px;
|
|
1192
|
+
min-width: 0;
|
|
1103
1193
|
color: var(--u-text-color-main);
|
|
1104
1194
|
}
|
|
1195
|
+
.u-ai-chat__model-panel .u-ai-chat__model-option.is-active .u-ai-chat__model-option-name {
|
|
1196
|
+
color: var(--u-color-primary);
|
|
1197
|
+
font-weight: 600;
|
|
1198
|
+
}
|
|
1105
1199
|
.u-ai-chat__model-panel .u-ai-chat__model-option-name {
|
|
1106
1200
|
overflow: hidden;
|
|
1107
1201
|
text-overflow: ellipsis;
|
|
1108
1202
|
white-space: nowrap;
|
|
1109
1203
|
}
|
|
1110
|
-
.u-ai-chat__model-panel .u-ai-chat__model-option-actions {
|
|
1111
|
-
display: flex;
|
|
1112
|
-
justify-content: flex-start;
|
|
1113
|
-
align-items: center;
|
|
1114
|
-
flex-wrap: nowrap;
|
|
1115
|
-
flex-shrink: 0;
|
|
1116
|
-
gap: 6px;
|
|
1117
|
-
}
|
|
1118
1204
|
.u-ai-chat__model-panel .u-ai-chat__model-option-check {
|
|
1119
1205
|
flex-shrink: 0;
|
|
1206
|
+
margin-left: auto;
|
|
1120
1207
|
font-size: 14px;
|
|
1121
1208
|
color: var(--u-color-primary);
|
|
1122
1209
|
}
|
|
1123
|
-
.u-ai-chat__model-panel .u-ai-chat__model-option-check.is-hidden {
|
|
1124
|
-
visibility: hidden;
|
|
1125
|
-
}
|
|
1126
|
-
.u-ai-chat__model-panel .u-ai-chat__model-option-reasoning {
|
|
1127
|
-
display: flex;
|
|
1128
|
-
justify-content: flex-start;
|
|
1129
|
-
align-items: center;
|
|
1130
|
-
flex-wrap: nowrap;
|
|
1131
|
-
gap: 2px;
|
|
1132
|
-
flex-shrink: 0;
|
|
1133
|
-
padding: 1px 6px;
|
|
1134
|
-
border: none;
|
|
1135
|
-
border-radius: var(--u-radius-small);
|
|
1136
|
-
background: var(--u-color-primary-light-9);
|
|
1137
|
-
color: var(--u-color-primary);
|
|
1138
|
-
font-size: var(--u-font-size-main-small);
|
|
1139
|
-
cursor: pointer;
|
|
1140
|
-
transition: background-color 0.15s ease, color 0.15s ease;
|
|
1141
|
-
}
|
|
1142
|
-
.u-ai-chat__model-panel .u-ai-chat__model-option-reasoning:hover {
|
|
1143
|
-
background: var(--u-color-primary-light-7);
|
|
1144
|
-
}
|
|
1145
|
-
.u-ai-chat__model-panel .u-ai-chat__model-option-reasoning-arrow {
|
|
1146
|
-
font-size: 12px;
|
|
1147
|
-
transition: transform 0.2s ease;
|
|
1148
|
-
}
|
|
1149
|
-
.u-ai-chat__model-panel .u-ai-chat__model-option-reasoning.is-expanded .u-ai-chat__model-option-reasoning-arrow {
|
|
1150
|
-
transform: rotate(180deg);
|
|
1151
|
-
}
|
|
1152
1210
|
.u-ai-chat__model-panel .u-ai-chat__model-option-desc {
|
|
1153
1211
|
margin-top: 2px;
|
|
1154
1212
|
font-size: var(--u-font-size-main-small);
|
|
1155
1213
|
color: var(--u-text-color-assist);
|
|
1156
1214
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
}
|
|
1162
|
-
.u-ai-chat__model-panel .u-ai-chat__reasoning-levels.is-expanded {
|
|
1163
|
-
grid-template-rows: 1fr;
|
|
1164
|
-
}
|
|
1165
|
-
.u-ai-chat__model-panel .u-ai-chat__reasoning-levels-inner {
|
|
1166
|
-
min-height: 0;
|
|
1167
|
-
overflow: hidden;
|
|
1215
|
+
|
|
1216
|
+
.u-ai-chat__reasoning-panel {
|
|
1217
|
+
padding: 4px;
|
|
1218
|
+
user-select: none;
|
|
1168
1219
|
}
|
|
1169
|
-
.u-ai-
|
|
1220
|
+
.u-ai-chat__reasoning-panel .u-ai-chat__reasoning-option {
|
|
1170
1221
|
display: flex;
|
|
1171
1222
|
justify-content: space-between;
|
|
1172
1223
|
align-items: center;
|
|
1173
1224
|
flex-wrap: nowrap;
|
|
1174
1225
|
gap: 12px;
|
|
1175
|
-
|
|
1176
|
-
padding: 5px 8px;
|
|
1226
|
+
padding: 6px 8px;
|
|
1177
1227
|
border-radius: calc(var(--u-radius-default) + 1px);
|
|
1178
1228
|
font-size: var(--u-font-size-main-small);
|
|
1179
1229
|
color: var(--u-text-color-main);
|
|
1180
1230
|
cursor: pointer;
|
|
1181
1231
|
transition: background-color 0.15s ease, color 0.15s ease;
|
|
1182
1232
|
}
|
|
1183
|
-
.u-ai-
|
|
1233
|
+
.u-ai-chat__reasoning-panel .u-ai-chat__reasoning-option:hover {
|
|
1184
1234
|
background-color: var(--u-bg-color-hover);
|
|
1185
1235
|
}
|
|
1186
|
-
.u-ai-
|
|
1236
|
+
.u-ai-chat__reasoning-panel .u-ai-chat__reasoning-option.is-active {
|
|
1237
|
+
color: var(--u-color-primary);
|
|
1238
|
+
}
|
|
1239
|
+
.u-ai-chat__reasoning-panel .u-ai-chat__reasoning-check {
|
|
1240
|
+
flex-shrink: 0;
|
|
1241
|
+
font-size: 14px;
|
|
1187
1242
|
color: var(--u-color-primary);
|
|
1188
1243
|
}
|
|
@@ -18,6 +18,8 @@ export interface ChatModel {
|
|
|
18
18
|
reasoningLevels?: ChatReasoningLevel[];
|
|
19
19
|
/** 默认推理等级(须落在 reasoningLevels 内) */
|
|
20
20
|
defaultReasoningLevel?: string;
|
|
21
|
+
/** 上下文窗口上限(token)。配置后输入栏用量环按「最近一次请求总 token / 上限」显示占比 */
|
|
22
|
+
contextWindow?: number;
|
|
21
23
|
}
|
|
22
24
|
/** 模型服务商配置(内嵌 models) */
|
|
23
25
|
export interface ChatProvider {
|
package/dist/types/ai-chat.d.ts
CHANGED
|
@@ -42,7 +42,8 @@ export interface AiChatProps {
|
|
|
42
42
|
rendererProps?: Record<string, unknown>;
|
|
43
43
|
/**
|
|
44
44
|
* 是否展示 token 用量明细(缓存命中 / 未命中;缺字段不显示)。
|
|
45
|
-
* 默认 false
|
|
45
|
+
* 默认 false:用量环明细面板只列 总 / 输入 / 输出;true 时追加有数据的缓存项。
|
|
46
|
+
* 接口未返回 usage 时不展示用量环。
|
|
46
47
|
*/
|
|
47
48
|
tokenUsageDetail?: boolean;
|
|
48
49
|
/**
|
package/package.json
CHANGED
package/src/chat/use-chat.ts
CHANGED
|
@@ -60,6 +60,8 @@ export function useChat(options: UseChatOptions) {
|
|
|
60
60
|
const tokenUsage = ref<ChatTokenUsage | null>(null)
|
|
61
61
|
/** 最近一轮用户对话(含工具多轮请求)的 token;该轮无 usage 时为 null */
|
|
62
62
|
const lastTurnUsage = ref<ChatTokenUsage | null>(null)
|
|
63
|
+
/** 最近一次单次请求的 token(未跨轮累计),其 totalTokens 近似当前上下文占用 */
|
|
64
|
+
const lastRequestUsage = ref<ChatTokenUsage | null>(null)
|
|
63
65
|
|
|
64
66
|
/** session 下由 jobs/snapshot 整体替换;函数 transport 保持空数组 */
|
|
65
67
|
const jobs = ref<ChatJob[]>([])
|
|
@@ -344,6 +346,7 @@ export function useChat(options: UseChatOptions) {
|
|
|
344
346
|
emit('tool-call', toolCall)
|
|
345
347
|
},
|
|
346
348
|
onUsage: (usage) => {
|
|
349
|
+
lastRequestUsage.value = usage
|
|
347
350
|
lastTurnUsage.value = lastTurnUsage.value
|
|
348
351
|
? addTokenUsage(lastTurnUsage.value, usage)
|
|
349
352
|
: usage
|
|
@@ -462,6 +465,7 @@ export function useChat(options: UseChatOptions) {
|
|
|
462
465
|
if (session()) {
|
|
463
466
|
sessionRuntime.resetLocal()
|
|
464
467
|
lastTurnUsage.value = null
|
|
468
|
+
lastRequestUsage.value = null
|
|
465
469
|
return
|
|
466
470
|
}
|
|
467
471
|
abort()
|
|
@@ -469,6 +473,7 @@ export function useChat(options: UseChatOptions) {
|
|
|
469
473
|
messages.value = []
|
|
470
474
|
tokenUsage.value = null
|
|
471
475
|
lastTurnUsage.value = null
|
|
476
|
+
lastRequestUsage.value = null
|
|
472
477
|
snapshot()
|
|
473
478
|
}
|
|
474
479
|
|
|
@@ -481,6 +486,7 @@ export function useChat(options: UseChatOptions) {
|
|
|
481
486
|
jobs,
|
|
482
487
|
tokenUsage,
|
|
483
488
|
lastTurnUsage,
|
|
489
|
+
lastRequestUsage,
|
|
484
490
|
projections,
|
|
485
491
|
title,
|
|
486
492
|
pendingApprovals,
|