@veltra/ai 2.0.0 → 2.1.1
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/index.d.ts +2 -2
- package/dist/chat/transports/openai.js +1 -1
- package/dist/chat/transports/openai.js.map +1 -1
- package/dist/chat/types.d.ts +37 -2
- package/dist/chat/use-chat.d.ts +23 -1
- package/dist/chat/use-chat.js +91 -10
- package/dist/chat/use-chat.js.map +1 -1
- package/dist/components/ai-chat/ai-chat.js +207 -46
- package/dist/components/ai-chat/ai-chat.js.map +1 -1
- package/dist/components/ai-chat/chat-input.js +37 -12
- package/dist/components/ai-chat/chat-input.js.map +1 -1
- package/dist/components/ai-chat/di.js.map +1 -1
- package/dist/components/ai-chat/message-item.js +101 -23
- package/dist/components/ai-chat/message-item.js.map +1 -1
- package/dist/components/ai-chat/message-list.js +217 -32
- package/dist/components/ai-chat/message-list.js.map +1 -1
- package/dist/components/ai-chat/model-picker.js +69 -46
- package/dist/components/ai-chat/model-picker.js.map +1 -1
- package/dist/components/ai-chat/queue-list.js +70 -0
- package/dist/components/ai-chat/queue-list.js.map +1 -0
- package/dist/components/ai-chat/side-panel.js +96 -0
- package/dist/components/ai-chat/side-panel.js.map +1 -0
- package/dist/components/ai-chat/style.d.ts +4 -0
- package/dist/components/ai-chat/style.js +4 -0
- package/dist/components/ai-chat/style2.css +457 -74
- package/dist/components/ai-chat/tool-call.js +60 -26
- package/dist/components/ai-chat/tool-call.js.map +1 -1
- package/dist/components/ai-orb/ai-orb.js +94 -0
- package/dist/components/ai-orb/ai-orb.js.map +1 -0
- package/dist/components/ai-orb/orb-renderer.js +411 -0
- package/dist/components/ai-orb/orb-renderer.js.map +1 -0
- package/dist/components/ai-orb/style.d.ts +1 -0
- package/dist/components/ai-orb/style.js +1 -0
- package/dist/components/ai-orb/style2.css +23 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +2 -1
- package/dist/style.js +1 -0
- package/dist/types/ai-chat.d.ts +21 -7
- package/dist/types/ai-orb.d.ts +27 -0
- package/dist/types/index.d.ts +2 -1
- package/package.json +5 -5
- package/src/chat/__test__/use-chat.test.ts +266 -13
- package/src/chat/transports/__test__/openai.test.ts +78 -0
- package/src/chat/transports/openai.ts +2 -2
- package/src/chat/types.ts +37 -1
- package/src/chat/use-chat.ts +120 -11
- package/src/components/ai-chat/__test__/ai-chat.test.ts +628 -12
- package/src/components/ai-chat/ai-chat.vue +227 -31
- package/src/components/ai-chat/chat-input.vue +41 -11
- package/src/components/ai-chat/di.ts +2 -0
- package/src/components/ai-chat/message-item.vue +98 -11
- package/src/components/ai-chat/message-list.vue +261 -21
- package/src/components/ai-chat/model-picker.vue +110 -59
- package/src/components/ai-chat/queue-list.vue +63 -0
- package/src/components/ai-chat/side-panel.vue +76 -0
- package/src/components/ai-chat/style.scss +527 -74
- package/src/components/ai-chat/style.ts +4 -0
- package/src/components/ai-chat/tool-call.vue +86 -42
- package/src/components/ai-orb/__test__/ai-orb.test.ts +251 -0
- package/src/components/ai-orb/ai-orb.vue +107 -0
- package/src/components/ai-orb/index.ts +1 -0
- package/src/components/ai-orb/orb-renderer.ts +505 -0
- package/src/components/ai-orb/style.scss +10 -0
- package/src/components/ai-orb/style.ts +1 -0
- package/src/components/index.ts +1 -0
- package/src/style.ts +1 -0
- package/src/tools/ask-question/__test__/ask-question.test.ts +1 -1
- package/src/types/ai-chat.ts +21 -6
- package/src/types/ai-orb.ts +27 -0
- package/src/types/index.ts +1 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { AiChatDIKey } from "./di.js";
|
|
2
|
-
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode,
|
|
3
|
-
import { ArrowDown,
|
|
2
|
+
import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, inject, mergeModels, nextTick, normalizeClass, openBlock, renderList, shallowRef, toDisplayString, unref, useModel, useTemplateRef, withCtx, withModifiers } from "vue";
|
|
3
|
+
import { ArrowDown, Check } from "@veltra/icons/normal";
|
|
4
4
|
import { UDropdown, UIcon } 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
8
|
const _hoisted_2 = ["onClick"];
|
|
9
|
+
const _hoisted_3 = ["onClick"];
|
|
9
10
|
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
10
11
|
name: "UAiChatModelPicker",
|
|
11
12
|
__name: "model-picker",
|
|
@@ -22,27 +23,46 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
22
23
|
const reasoningLevel = useModel(__props, "reasoningLevel");
|
|
23
24
|
const cls = inject(AiChatDIKey)?.cls ?? bem("ai-chat");
|
|
24
25
|
const dropdownRef = useTemplateRef("dropdownRef");
|
|
25
|
-
/**
|
|
26
|
-
const
|
|
26
|
+
/** 当前内联展开思考强度的模型 id(手风琴,同时只展开一个) */
|
|
27
|
+
const expandedReasoningId = shallowRef();
|
|
27
28
|
const currentModel = computed(() => {
|
|
28
29
|
return props.models.find((m) => m.id === model.value);
|
|
29
30
|
});
|
|
30
|
-
const reasoningLevels = computed(() => currentModel.value?.reasoningLevels ?? []);
|
|
31
31
|
const currentReasoningLabel = computed(() => {
|
|
32
|
-
return
|
|
32
|
+
return currentModel.value?.reasoningLevels?.find((level) => level.value === reasoningLevel.value)?.label;
|
|
33
33
|
});
|
|
34
|
-
/**
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
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;
|
|
38
51
|
};
|
|
39
|
-
/**
|
|
40
|
-
const handleSelectModel = (
|
|
41
|
-
model.value = id;
|
|
42
|
-
|
|
52
|
+
/** 点击模型主体:直接选中模型,并同步到该模型默认推理等级 */
|
|
53
|
+
const handleSelectModel = (item) => {
|
|
54
|
+
model.value = item.id;
|
|
55
|
+
reasoningLevel.value = resolveDefaultReasoningLevel(item);
|
|
43
56
|
dropdownRef.value?.close();
|
|
44
57
|
};
|
|
45
|
-
|
|
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;
|
|
46
66
|
reasoningLevel.value = value;
|
|
47
67
|
dropdownRef.value?.close();
|
|
48
68
|
};
|
|
@@ -51,8 +71,10 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
51
71
|
ref_key: "dropdownRef",
|
|
52
72
|
ref: dropdownRef,
|
|
53
73
|
trigger: "click",
|
|
74
|
+
width: "auto",
|
|
54
75
|
"min-width": "220px",
|
|
55
|
-
"content-class": unref(cls).e("model-panel")
|
|
76
|
+
"content-class": unref(cls).e("model-panel"),
|
|
77
|
+
"onUpdate:visible": handleVisibleChange
|
|
56
78
|
}, {
|
|
57
79
|
trigger: withCtx(() => [createElementVNode("button", {
|
|
58
80
|
type: "button",
|
|
@@ -68,44 +90,45 @@ const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
|
68
90
|
_: 1
|
|
69
91
|
}, 8, ["class"])
|
|
70
92
|
], 2)]),
|
|
71
|
-
content: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.models, (item) => {
|
|
93
|
+
content: withCtx(() => [createElementVNode("div", { class: normalizeClass(unref(cls).e("model-panel-list")) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(__props.models, (item) => {
|
|
72
94
|
return openBlock(), createElementBlock("div", {
|
|
73
95
|
key: item.id,
|
|
74
|
-
class: normalizeClass([unref(cls).e("model-option"), unref(bem).is("active", item.id === model.value)])
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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", {
|
|
83
116
|
key: 0,
|
|
84
117
|
class: normalizeClass(unref(cls).e("model-option-desc"))
|
|
85
|
-
}, toDisplayString(item.description), 3)) : createCommentVNode("v-if", true)], 10, _hoisted_1)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
createElementVNode("div", {
|
|
89
|
-
class: normalizeClass(unref(cls).e("reasoning-row")),
|
|
90
|
-
onClick: toggleReasoning
|
|
91
|
-
}, [_cache[0] || (_cache[0] = createElementVNode("span", null, "思考强度", -1)), createElementVNode("span", { class: normalizeClass(unref(cls).e("reasoning-row-value")) }, [createTextVNode(toDisplayString(currentReasoningLabel.value) + " ", 1), createVNode(unref(UIcon), null, {
|
|
92
|
-
default: withCtx(() => [reasoningExpanded.value ? (openBlock(), createBlock(unref(ArrowDown), { key: 0 })) : (openBlock(), createBlock(unref(ArrowRight), { key: 1 }))]),
|
|
93
|
-
_: 1
|
|
94
|
-
})], 2)], 2),
|
|
95
|
-
reasoningExpanded.value ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(reasoningLevels.value, (level) => {
|
|
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) => {
|
|
96
122
|
return openBlock(), createElementBlock("div", {
|
|
97
123
|
key: level.value,
|
|
98
|
-
class: normalizeClass([unref(cls).e("reasoning-option"), unref(bem).is("active", level.value
|
|
99
|
-
onClick: ($event) => handleSelectReasoning(level.value)
|
|
100
|
-
}, [createElementVNode("span", null, toDisplayString(level.label), 1),
|
|
101
|
-
key: 0,
|
|
102
|
-
class: normalizeClass(unref(cls).e("model-option-check"))
|
|
103
|
-
}, {
|
|
124
|
+
class: normalizeClass([unref(cls).e("reasoning-option"), unref(bem).is("active", isReasoningActive(item, level.value))]),
|
|
125
|
+
onClick: ($event) => handleSelectReasoning(item, level.value)
|
|
126
|
+
}, [createElementVNode("span", null, toDisplayString(level.label), 1), createVNode(unref(UIcon), { class: normalizeClass([unref(cls).e("model-option-check"), unref(bem).is("hidden", !isReasoningActive(item, level.value))]) }, {
|
|
104
127
|
default: withCtx(() => [createVNode(unref(Check))]),
|
|
105
128
|
_: 1
|
|
106
|
-
}, 8, ["class"])
|
|
107
|
-
}), 128)) : createCommentVNode("v-if", true)
|
|
108
|
-
|
|
129
|
+
}, 8, ["class"])], 10, _hoisted_3);
|
|
130
|
+
}), 128))], 2)], 2)) : createCommentVNode("v-if", true)], 2);
|
|
131
|
+
}), 128))], 2)]),
|
|
109
132
|
_: 1
|
|
110
133
|
}, 8, ["content-class"]);
|
|
111
134
|
};
|
|
@@ -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 min-width=\"220px\"\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 <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\n v-for=\"item in models\"\n :key=\"item.id\"\n :class=\"[cls.e('model-option'), bem.is('active', item.id === model)]\"\n @click=\"handleSelectModel(item.id)\"\n >\n <div :class=\"cls.e('model-option-head')\">\n <span>{{ 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\n <template v-if=\"reasoningLevels.length\">\n <div :class=\"cls.e('model-divider')\" />\n\n <div :class=\"cls.e('reasoning-row')\" @click=\"toggleReasoning\">\n <span>思考强度</span>\n <span :class=\"cls.e('reasoning-row-value')\">\n {{ currentReasoningLabel }}\n <UIcon>\n <ArrowDown v-if=\"reasoningExpanded\" />\n <ArrowRight v-else />\n </UIcon>\n </span>\n </div>\n\n <template v-if=\"reasoningExpanded\">\n <div\n v-for=\"level in reasoningLevels\"\n :key=\"level.value\"\n :class=\"[cls.e('reasoning-option'), bem.is('active', level.value === reasoningLevel)]\"\n @click=\"handleSelectReasoning(level.value)\"\n >\n <span>{{ level.label }}</span>\n <UIcon v-if=\"level.value === reasoningLevel\" :class=\"cls.e('model-option-check')\">\n <Check />\n </UIcon>\n </div>\n </template>\n </template>\n </template>\n </UDropdown>\n</template>\n\n<script lang=\"ts\" setup>\nimport { UDropdown, UIcon } from '@veltra/desktop'\nimport { ArrowDown, ArrowRight, Check } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, inject, nextTick, ref, 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>('model')\nconst reasoningLevel = defineModel<string>('reasoningLevel')\n\nconst di = inject(AiChatDIKey)\nconst cls = di?.cls ?? bem('ai-chat')\n\nconst dropdownRef = useTemplateRef('dropdownRef')\n\n/** 思考强度子列表是否展开 */\nconst reasoningExpanded = ref(false)\n\nconst currentModel = computed(() => {\n return props.models.find((m) => m.id === model.value)\n})\n\nconst reasoningLevels = computed(() => currentModel.value?.reasoningLevels ?? [])\n\nconst currentReasoningLabel = computed(() => {\n return reasoningLevels.value.find((l) => l.value === reasoningLevel.value)?.label\n})\n\n/** 展开/收起思考强度子列表,并重置面板位置防止溢出 */\nconst toggleReasoning = () => {\n reasoningExpanded.value = !reasoningExpanded.value\n nextTick(() => dropdownRef.value?.updateDropdown())\n}\n\n/** 选中模型(推理等级校正在 useChat 中完成) */\nconst handleSelectModel = (id: string) => {\n model.value = id\n reasoningExpanded.value = false\n dropdownRef.value?.close()\n}\n\nconst handleSelectReasoning = (value: string) => {\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 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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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { AiChatDIKey } from "./di.js";
|
|
2
|
+
import { Fragment, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, inject, normalizeClass, openBlock, renderList, toDisplayString, unref, withCtx } from "vue";
|
|
3
|
+
import { CaretRight, Close, EditPen } from "@veltra/icons/normal";
|
|
4
|
+
import { UIcon } from "@veltra/desktop";
|
|
5
|
+
import { bem } from "@veltra/utils";
|
|
6
|
+
//#region src/components/ai-chat/queue-list.vue
|
|
7
|
+
const _hoisted_1 = ["title"];
|
|
8
|
+
const _hoisted_2 = ["onClick"];
|
|
9
|
+
const _hoisted_3 = ["onClick"];
|
|
10
|
+
const _hoisted_4 = ["onClick"];
|
|
11
|
+
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
12
|
+
name: "UAiChatQueueList",
|
|
13
|
+
__name: "queue-list",
|
|
14
|
+
props: { queue: {} },
|
|
15
|
+
emits: [
|
|
16
|
+
"start-now",
|
|
17
|
+
"edit",
|
|
18
|
+
"remove"
|
|
19
|
+
],
|
|
20
|
+
setup(__props, { emit: __emit }) {
|
|
21
|
+
const emit = __emit;
|
|
22
|
+
const cls = inject(AiChatDIKey)?.cls ?? bem("ai-chat");
|
|
23
|
+
return (_ctx, _cache) => {
|
|
24
|
+
return __props.queue.length ? (openBlock(), createElementBlock("div", {
|
|
25
|
+
key: 0,
|
|
26
|
+
class: normalizeClass(unref(cls).e("queue"))
|
|
27
|
+
}, [createElementVNode("div", { class: normalizeClass(unref(cls).e("queue-head")) }, "待发送队列 · " + toDisplayString(__props.queue.length), 3), (openBlock(true), createElementBlock(Fragment, null, renderList(__props.queue, (item) => {
|
|
28
|
+
return openBlock(), createElementBlock("div", {
|
|
29
|
+
key: item.id,
|
|
30
|
+
class: normalizeClass(unref(cls).e("queue-item"))
|
|
31
|
+
}, [createElementVNode("span", {
|
|
32
|
+
class: normalizeClass(unref(cls).e("queue-text")),
|
|
33
|
+
title: item.content
|
|
34
|
+
}, toDisplayString(item.content), 11, _hoisted_1), createElementVNode("span", { class: normalizeClass(unref(cls).e("queue-actions")) }, [
|
|
35
|
+
createElementVNode("button", {
|
|
36
|
+
type: "button",
|
|
37
|
+
class: normalizeClass([unref(cls).e("queue-action"), unref(cls).em("queue-action", "primary")]),
|
|
38
|
+
title: "中断当前会话,立即执行该条",
|
|
39
|
+
onClick: ($event) => emit("start-now", item.id)
|
|
40
|
+
}, [createVNode(unref(UIcon), null, {
|
|
41
|
+
default: withCtx(() => [createVNode(unref(CaretRight))]),
|
|
42
|
+
_: 1
|
|
43
|
+
}), _cache[0] || (_cache[0] = createTextVNode("立即开始 ", -1))], 10, _hoisted_2),
|
|
44
|
+
createElementVNode("button", {
|
|
45
|
+
type: "button",
|
|
46
|
+
class: normalizeClass(unref(cls).e("queue-action")),
|
|
47
|
+
title: "取回输入框编辑",
|
|
48
|
+
onClick: ($event) => emit("edit", item.id)
|
|
49
|
+
}, [createVNode(unref(UIcon), null, {
|
|
50
|
+
default: withCtx(() => [createVNode(unref(EditPen))]),
|
|
51
|
+
_: 1
|
|
52
|
+
})], 10, _hoisted_3),
|
|
53
|
+
createElementVNode("button", {
|
|
54
|
+
type: "button",
|
|
55
|
+
class: normalizeClass(unref(cls).e("queue-action")),
|
|
56
|
+
title: "移出队列",
|
|
57
|
+
onClick: ($event) => emit("remove", item.id)
|
|
58
|
+
}, [createVNode(unref(UIcon), null, {
|
|
59
|
+
default: withCtx(() => [createVNode(unref(Close))]),
|
|
60
|
+
_: 1
|
|
61
|
+
})], 10, _hoisted_4)
|
|
62
|
+
], 2)], 2);
|
|
63
|
+
}), 128))], 2)) : createCommentVNode("v-if", true);
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
//#endregion
|
|
68
|
+
export { _sfc_main as default };
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=queue-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queue-list.js","names":[],"sources":["../../../src/components/ai-chat/queue-list.vue"],"sourcesContent":["<template>\n <div v-if=\"queue.length\" :class=\"cls.e('queue')\">\n <div :class=\"cls.e('queue-head')\">待发送队列 · {{ queue.length }}</div>\n <div v-for=\"item in queue\" :key=\"item.id\" :class=\"cls.e('queue-item')\">\n <span :class=\"cls.e('queue-text')\" :title=\"item.content\">{{ item.content }}</span>\n <span :class=\"cls.e('queue-actions')\">\n <button\n type=\"button\"\n :class=\"[cls.e('queue-action'), cls.em('queue-action', 'primary')]\"\n title=\"中断当前会话,立即执行该条\"\n @click=\"emit('start-now', item.id)\"\n >\n <UIcon><CaretRight /></UIcon>立即开始\n </button>\n <button\n type=\"button\"\n :class=\"cls.e('queue-action')\"\n title=\"取回输入框编辑\"\n @click=\"emit('edit', item.id)\"\n >\n <UIcon><EditPen /></UIcon>\n </button>\n <button\n type=\"button\"\n :class=\"cls.e('queue-action')\"\n title=\"移出队列\"\n @click=\"emit('remove', item.id)\"\n >\n <UIcon><Close /></UIcon>\n </button>\n </span>\n </div>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { UIcon } from '@veltra/desktop'\nimport { CaretRight, Close, EditPen } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { inject } from 'vue'\n\nimport type { ChatQueuedMessage } from '../../chat/types'\nimport { AiChatDIKey } from './di'\n\ndefineOptions({ name: 'UAiChatQueueList' })\n\ndefineProps<{\n /** 待发送队列(按执行顺序排列) */\n queue: ChatQueuedMessage[]\n}>()\n\nconst emit = defineEmits<{\n /** 立即开始:中断当前会话并插队执行 */\n (e: 'start-now', id: string): void\n /** 编辑:取回输入框重新编辑 */\n (e: 'edit', id: string): void\n /** 移出队列 */\n (e: 'remove', id: string): void\n}>()\n\nconst di = inject(AiChatDIKey)\nconst cls = di?.cls ?? bem('ai-chat')\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;EAmDA,MAAM,OAAO;EAUb,MAAM,MADK,OAAO,WACN,CAAA,EAAI,OAAO,IAAI,SAAS;;GA5DvB,OAAA,QAAA,MAAM,UAAA,UAAA,GAAjB,mBA+BM,OAAA;;IA/BoB,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,OAAA,CAAA;GACpC,GAAA,CAAA,mBAAkE,OAAA,EAA5D,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,YAAA,CAAA,EAAA,GAAgB,aAAQ,gBAAG,QAAA,MAAM,MAAM,GAAA,CAAA,IAAA,UAAA,IAAA,GACzD,mBA4BM,UAAA,MAAA,WA5Bc,QAAA,QAAR,SAAI;IAAhB,OAAA,UAAA,GAAA,mBA4BM,OAAA;KA5BsB,KAAK,KAAK;KAAK,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,YAAA,CAAA;IACrD,GAAA,CAAA,mBAAkF,QAAA;KAA3E,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,YAAA,CAAA;KAAiB,OAAO,KAAK;IAAY,GAAA,gBAAA,KAAK,OAAO,GAAA,IAAA,UAAA,GACxE,mBAyBO,QAAA,EAzBA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,eAAA,CAAA,EAAA,GAAA;KACjB,mBAOS,UAAA;MANP,MAAK;MACJ,OAAK,eAAA,CAAG,MAAA,GAAA,CAAG,CAAC,EAAC,cAAA,GAAkB,MAAA,GAAA,CAAG,CAAC,GAAE,gBAAA,SAAA,CAAA,CAAA;MACtC,OAAM;MACL,UAAK,WAAE,KAAI,aAAc,KAAK,EAAE;KAEjC,GAAA,CAAA,YAA6B,MAAA,KAAA,GAAA,MAAA;MAAR,SAAA,cAAA,CAAd,YAAc,MAAA,UAAA,CAAA,CAAA,CAAA;;KAAQ,CAAA,GAAA,OAAA,OAAA,OAAA,KAAA,gBAAA,SAC/B,EAAA,EAAA,GAAA,IAAA,UAAA;KACA,mBAOS,UAAA;MANP,MAAK;MACJ,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,cAAA,CAAA;MACb,OAAM;MACL,UAAK,WAAE,KAAI,QAAS,KAAK,EAAE;KAE5B,GAAA,CAAA,YAA0B,MAAA,KAAA,GAAA,MAAA;MAAR,SAAA,cAAA,CAAX,YAAW,MAAA,OAAA,CAAA,CAAA,CAAA;;;KAEpB,mBAOS,UAAA;MANP,MAAK;MACJ,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,cAAA,CAAA;MACb,OAAM;MACL,UAAK,WAAE,KAAI,UAAW,KAAK,EAAE;KAE9B,GAAA,CAAA,YAAwB,MAAA,KAAA,GAAA,MAAA;MAAR,SAAA,cAAA,CAAT,YAAS,MAAA,KAAA,CAAA,CAAA,CAAA"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { AiChatDIKey } from "./di.js";
|
|
2
|
+
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, inject, normalizeClass, openBlock, resolveDynamicComponent, toDisplayString, unref, withCtx } from "vue";
|
|
3
|
+
import { CircleCheckFilled, CircleClose, Close, Loading, WarningFilled } from "@veltra/icons/normal";
|
|
4
|
+
import { UIcon, UScroll } from "@veltra/desktop";
|
|
5
|
+
import { bem } from "@veltra/utils";
|
|
6
|
+
//#region src/components/ai-chat/side-panel.vue
|
|
7
|
+
const _sfc_main = /*@__PURE__*/ defineComponent({
|
|
8
|
+
name: "UAiChatSidePanel",
|
|
9
|
+
__name: "side-panel",
|
|
10
|
+
props: { toolCall: {} },
|
|
11
|
+
emits: ["close"],
|
|
12
|
+
setup(__props, { emit: __emit }) {
|
|
13
|
+
const props = __props;
|
|
14
|
+
const emit = __emit;
|
|
15
|
+
const di = inject(AiChatDIKey);
|
|
16
|
+
const cls = di?.cls ?? bem("ai-chat");
|
|
17
|
+
/** 当前调用对应的工具定义(解析 icon/label/render) */
|
|
18
|
+
const tool = computed(() => di?.tools.value[props.toolCall.name]);
|
|
19
|
+
const STATUS_META = {
|
|
20
|
+
pending: {
|
|
21
|
+
icon: Loading,
|
|
22
|
+
class: "is-active"
|
|
23
|
+
},
|
|
24
|
+
running: {
|
|
25
|
+
icon: Loading,
|
|
26
|
+
class: "is-active"
|
|
27
|
+
},
|
|
28
|
+
"awaiting-confirm": {
|
|
29
|
+
icon: WarningFilled,
|
|
30
|
+
class: "is-warning"
|
|
31
|
+
},
|
|
32
|
+
success: {
|
|
33
|
+
icon: CircleCheckFilled,
|
|
34
|
+
class: "is-success"
|
|
35
|
+
},
|
|
36
|
+
error: {
|
|
37
|
+
icon: CircleClose,
|
|
38
|
+
class: "is-danger"
|
|
39
|
+
},
|
|
40
|
+
rejected: {
|
|
41
|
+
icon: Close,
|
|
42
|
+
class: "is-danger"
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const statusIcon = computed(() => STATUS_META[props.toolCall.status]?.icon ?? Loading);
|
|
46
|
+
const statusClass = computed(() => STATUS_META[props.toolCall.status]?.class ?? "");
|
|
47
|
+
/** 头部图标:工具自定义图标优先,缺省用状态图标(与工具卡片一致) */
|
|
48
|
+
const headerIcon = computed(() => tool.value?.icon ?? statusIcon.value);
|
|
49
|
+
/** 面板标题:优先工具定义的 panelTitle(业务对象 + 动作),缺省回落到 label ?? name */
|
|
50
|
+
const panelTitle = computed(() => {
|
|
51
|
+
const t = tool.value;
|
|
52
|
+
const pt = t?.panelTitle;
|
|
53
|
+
if (typeof pt === "function") return pt(props.toolCall);
|
|
54
|
+
return pt ?? t?.label ?? props.toolCall.name;
|
|
55
|
+
});
|
|
56
|
+
/** 未提供 render 时的兜底展示(面板工具通常都应提供 render) */
|
|
57
|
+
const fallbackResult = computed(() => {
|
|
58
|
+
const raw = props.toolCall.error ?? props.toolCall.result ?? "";
|
|
59
|
+
try {
|
|
60
|
+
return JSON.stringify(JSON.parse(raw), null, 2);
|
|
61
|
+
} catch {
|
|
62
|
+
return raw;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return (_ctx, _cache) => {
|
|
66
|
+
return openBlock(), createElementBlock("aside", { class: normalizeClass(unref(cls).e("panel")) }, [createElementVNode("div", { class: normalizeClass(unref(cls).e("panel-card")) }, [createElementVNode("div", { class: normalizeClass(unref(cls).e("panel-header")) }, [
|
|
67
|
+
createVNode(unref(UIcon), { class: normalizeClass([unref(cls).e("panel-icon"), statusClass.value]) }, {
|
|
68
|
+
default: withCtx(() => [(openBlock(), createBlock(resolveDynamicComponent(headerIcon.value)))]),
|
|
69
|
+
_: 1
|
|
70
|
+
}, 8, ["class"]),
|
|
71
|
+
createElementVNode("span", { class: normalizeClass(unref(cls).e("panel-title")) }, toDisplayString(panelTitle.value), 3),
|
|
72
|
+
createVNode(unref(UIcon), {
|
|
73
|
+
class: normalizeClass(unref(cls).e("panel-close")),
|
|
74
|
+
title: "关闭面板",
|
|
75
|
+
onClick: _cache[0] || (_cache[0] = ($event) => emit("close"))
|
|
76
|
+
}, {
|
|
77
|
+
default: withCtx(() => [createVNode(unref(Close))]),
|
|
78
|
+
_: 1
|
|
79
|
+
}, 8, ["class"])
|
|
80
|
+
], 2), createVNode(unref(UScroll), { class: normalizeClass(unref(cls).e("panel-body")) }, {
|
|
81
|
+
default: withCtx(() => [createCommentVNode(" render 组件与工具卡片同一契约(ChatToolRenderProps),可随 toolCall 状态实时更新 "), tool.value?.render ? (openBlock(), createBlock(resolveDynamicComponent(tool.value.render), {
|
|
82
|
+
key: 0,
|
|
83
|
+
"tool-call": __props.toolCall
|
|
84
|
+
}, null, 8, ["tool-call"])) : (openBlock(), createElementBlock("pre", {
|
|
85
|
+
key: 1,
|
|
86
|
+
class: normalizeClass(unref(cls).e("panel-fallback"))
|
|
87
|
+
}, toDisplayString(fallbackResult.value), 3))]),
|
|
88
|
+
_: 1
|
|
89
|
+
}, 8, ["class"])], 2)], 2);
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
//#endregion
|
|
94
|
+
export { _sfc_main as default };
|
|
95
|
+
|
|
96
|
+
//# sourceMappingURL=side-panel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"side-panel.js","names":[],"sources":["../../../src/components/ai-chat/side-panel.vue"],"sourcesContent":["<template>\n <aside :class=\"cls.e('panel')\">\n <div :class=\"cls.e('panel-card')\">\n <div :class=\"cls.e('panel-header')\">\n <UIcon :class=\"[cls.e('panel-icon'), statusClass]\">\n <component :is=\"headerIcon\" />\n </UIcon>\n <span :class=\"cls.e('panel-title')\">{{ panelTitle }}</span>\n <UIcon :class=\"cls.e('panel-close')\" title=\"关闭面板\" @click=\"emit('close')\">\n <Close />\n </UIcon>\n </div>\n\n <UScroll :class=\"cls.e('panel-body')\">\n <!-- render 组件与工具卡片同一契约(ChatToolRenderProps),可随 toolCall 状态实时更新 -->\n <component :is=\"tool.render\" v-if=\"tool?.render\" :tool-call=\"toolCall\" />\n <pre v-else :class=\"cls.e('panel-fallback')\">{{ fallbackResult }}</pre>\n </UScroll>\n </div>\n </aside>\n</template>\n\n<script lang=\"ts\" setup>\nimport { UIcon, UScroll } from '@veltra/desktop'\nimport { CircleCheckFilled, CircleClose, Close, Loading, WarningFilled } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, inject, type Component } from 'vue'\n\nimport type { ChatToolCall } from '../../chat/types'\nimport { AiChatDIKey } from './di'\n\ndefineOptions({ name: 'UAiChatSidePanel' })\n\nconst props = defineProps<{ toolCall: ChatToolCall }>()\n\nconst emit = defineEmits<{ (e: 'close'): void }>()\n\nconst di = inject(AiChatDIKey)\nconst cls = di?.cls ?? bem('ai-chat')\n\n/** 当前调用对应的工具定义(解析 icon/label/render) */\nconst tool = computed(() => di?.tools.value[props.toolCall.name])\n\nconst STATUS_META: Record<string, { icon: Component; class: string }> = {\n pending: { icon: Loading, class: 'is-active' },\n running: { icon: Loading, class: 'is-active' },\n 'awaiting-confirm': { icon: WarningFilled, class: 'is-warning' },\n success: { icon: CircleCheckFilled, class: 'is-success' },\n error: { icon: CircleClose, class: 'is-danger' },\n rejected: { icon: Close, class: 'is-danger' }\n}\n\nconst statusIcon = computed(() => STATUS_META[props.toolCall.status]?.icon ?? Loading)\nconst statusClass = computed(() => STATUS_META[props.toolCall.status]?.class ?? '')\n\n/** 头部图标:工具自定义图标优先,缺省用状态图标(与工具卡片一致) */\nconst headerIcon = computed(() => tool.value?.icon ?? statusIcon.value)\n\n/** 面板标题:优先工具定义的 panelTitle(业务对象 + 动作),缺省回落到 label ?? name */\nconst panelTitle = computed(() => {\n const t = tool.value\n const pt = t?.panelTitle\n if (typeof pt === 'function') return pt(props.toolCall)\n return pt ?? t?.label ?? props.toolCall.name\n})\n\n/** 未提供 render 时的兜底展示(面板工具通常都应提供 render) */\nconst fallbackResult = computed(() => {\n const raw = props.toolCall.error ?? props.toolCall.result ?? ''\n try {\n return JSON.stringify(JSON.parse(raw), null, 2)\n } catch {\n return raw\n }\n})\n</script>\n"],"mappings":";;;;;;;;;;;;EAiCA,MAAM,QAAQ;EAEd,MAAM,OAAO;EAEb,MAAM,KAAK,OAAO,WAAW;EAC7B,MAAM,MAAM,IAAI,OAAO,IAAI,SAAS;;EAGpC,MAAM,OAAO,eAAe,IAAI,MAAM,MAAM,MAAM,SAAS,KAAK;EAEhE,MAAM,cAAkE;GACtE,SAAS;IAAE,MAAM;IAAS,OAAO;GAAY;GAC7C,SAAS;IAAE,MAAM;IAAS,OAAO;GAAY;GAC7C,oBAAoB;IAAE,MAAM;IAAe,OAAO;GAAa;GAC/D,SAAS;IAAE,MAAM;IAAmB,OAAO;GAAa;GACxD,OAAO;IAAE,MAAM;IAAa,OAAO;GAAY;GAC/C,UAAU;IAAE,MAAM;IAAO,OAAO;GAAY;EAC9C;EAEA,MAAM,aAAa,eAAe,YAAY,MAAM,SAAS,OAAO,EAAE,QAAQ,OAAO;EACrF,MAAM,cAAc,eAAe,YAAY,MAAM,SAAS,OAAO,EAAE,SAAS,EAAE;;EAGlF,MAAM,aAAa,eAAe,KAAK,OAAO,QAAQ,WAAW,KAAK;;EAGtE,MAAM,aAAa,eAAe;GAChC,MAAM,IAAI,KAAK;GACf,MAAM,KAAK,GAAG;GACd,IAAI,OAAO,OAAO,YAAY,OAAO,GAAG,MAAM,QAAQ;GACtD,OAAO,MAAM,GAAG,SAAS,MAAM,SAAS;EAC1C,CAAC;;EAGD,MAAM,iBAAiB,eAAe;GACpC,MAAM,MAAM,MAAM,SAAS,SAAS,MAAM,SAAS,UAAU;GAC7D,IAAI;IACF,OAAO,KAAK,UAAU,KAAK,MAAM,GAAG,GAAG,MAAM,CAAC;GAChD,QAAQ;IACN,OAAO;GACT;EACF,CAAC;;GAzEC,OAAA,UAAA,GAAA,mBAkBQ,SAAA,EAlBA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,OAAA,CAAA,EAAA,GAClB,CAAA,mBAgBM,OAAA,EAhBA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,YAAA,CAAA,EAAA,GAChB,CAAA,mBAQM,OAAA,EARA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,cAAA,CAAA,EAAA,GAAA;IAChB,YAEQ,MAAA,KAAA,GAAA,EAFA,OAAK,eAAA,CAAG,MAAA,GAAA,CAAG,CAAC,EAAC,YAAA,GAAgB,YAAA,KAAW,CAAA,EAAA,GAAA;KAChB,SAAA,cAAA,EAAA,UAAA,GAA9B,YAA8B,wBAAd,WAAA,KAAU,CAAA,EAAA,CAAA;;;IAE5B,mBAA2D,QAAA,EAApD,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,aAAA,CAAA,EAAA,GAAoB,gBAAA,WAAA,KAAU,GAAA,CAAA;IACjD,YAEQ,MAAA,KAAA,GAAA;KAFA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,aAAA,CAAA;KAAiB,OAAM;KAAQ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,OAAA;;KACnD,SAAA,cAAA,CAAT,YAAS,MAAA,KAAA,CAAA,CAAA,CAAA;;;GAIb,GAAA,CAAA,GAAA,YAIU,MAAA,OAAA,GAAA,EAJA,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,YAAA,CAAA,EAAA,GAAA;IAC+C,SAAA,cAAA,CAAnE,mBAAA,8DAAA,GACmC,KAAA,OAAM,UAAA,UAAA,GAAzC,YAAyE,wBAAzD,KAAA,MAAK,MAAM,GAAA;;KAAuB,aAAW,QAAA;IAC7D,GAAA,MAAA,GAAA,CAAA,WAAA,CAAA,MAAA,UAAA,GAAA,mBAAuE,OAAA;;KAA1D,OAAK,eAAE,MAAA,GAAA,CAAG,CAAC,EAAC,gBAAA,CAAA;IAAuB,GAAA,gBAAA,eAAA,KAAc,GAAA,CAAA,EAAA,CAAA"}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import "@veltra/desktop/components/button/style";
|
|
2
|
+
import "@veltra/desktop/components/collapse/style";
|
|
2
3
|
import "@veltra/desktop/components/file-picker/style";
|
|
3
4
|
import "@veltra/desktop/components/icon/style";
|
|
4
5
|
import "@veltra/desktop/components/input/style";
|
|
6
|
+
import "@veltra/desktop/components/layout/style";
|
|
7
|
+
import "@veltra/desktop/components/message/style";
|
|
5
8
|
import "@veltra/desktop/components/scroll/style";
|
|
6
9
|
import "@veltra/desktop/components/select/style";
|
|
10
|
+
import "@veltra/styles/animations/shine.scss";
|
|
7
11
|
import "markstream-vue/index.css";
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import "./style2.css";
|
|
2
2
|
import "@veltra/desktop/components/button/style";
|
|
3
|
+
import "@veltra/desktop/components/collapse/style";
|
|
3
4
|
import "@veltra/desktop/components/file-picker/style";
|
|
4
5
|
import "@veltra/desktop/components/icon/style";
|
|
5
6
|
import "@veltra/desktop/components/input/style";
|
|
7
|
+
import "@veltra/desktop/components/layout/style";
|
|
8
|
+
import "@veltra/desktop/components/message/style";
|
|
6
9
|
import "@veltra/desktop/components/scroll/style";
|
|
7
10
|
import "@veltra/desktop/components/select/style";
|
|
11
|
+
import "@veltra/styles/animations/shine.scss";
|
|
8
12
|
import "markstream-vue/index.css";
|