@veltra/ai 2.2.1 → 2.2.3
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/fold.js +14 -2
- package/dist/chat/fold.js.map +1 -1
- package/dist/chat/session.d.ts +13 -6
- package/dist/chat/session.js.map +1 -1
- package/dist/chat/transports/openai.d.ts +2 -1
- package/dist/chat/transports/openai.js +20 -213
- package/dist/chat/transports/openai.js.map +1 -1
- package/dist/chat/use-chat-session.js +3 -13
- package/dist/chat/use-chat-session.js.map +1 -1
- package/dist/components/ai-chat/ai-chat.js.map +1 -1
- package/dist/components/ai-chat/approval-banner.js.map +1 -1
- package/dist/components/ai-chat/ask-question.js.map +1 -1
- package/dist/components/ai-chat/chat-input.js.map +1 -1
- package/dist/components/ai-chat/job-bar.js.map +1 -1
- package/dist/components/ai-chat/message-item.js.map +1 -1
- package/dist/components/ai-chat/message-list.js.map +1 -1
- package/dist/components/ai-chat/model-picker.js.map +1 -1
- package/dist/components/ai-chat/queue-list.js.map +1 -1
- package/dist/components/ai-chat/reasoning-picker.js.map +1 -1
- package/dist/components/ai-chat/side-panel.js.map +1 -1
- package/dist/components/ai-chat/tool-call.js.map +1 -1
- package/dist/components/ai-chat/tool-json.js.map +1 -1
- package/dist/components/ai-chat/turn-process.js.map +1 -1
- package/dist/components/ai-orb/ai-orb.js.map +1 -1
- package/package.json +3 -3
- package/src/chat/__test__/bedrock-harness-sim.test.ts +467 -0
- package/src/chat/__test__/fixtures/bedrock/harness.ts +735 -0
- package/src/chat/__test__/fixtures/bedrock/http.ts +117 -0
- package/src/chat/__test__/fixtures/bedrock/types.ts +15 -0
- package/src/chat/__test__/fold.test.ts +26 -1
- package/src/chat/__test__/session.test.ts +1 -1
- package/src/chat/__test__/use-chat.test.ts +6 -3
- package/src/chat/fold.ts +16 -1
- package/src/chat/session.ts +13 -6
- package/src/chat/transports/__test__/openai.test.ts +1 -1
- package/src/chat/transports/openai.ts +25 -288
- package/src/chat/use-chat-session.ts +4 -5
- package/src/components/ai-chat/__test__/ai-chat.test.ts +1 -1
- package/src/components/ai-chat/__test__/tool-icons.test.ts +1 -1
- package/src/components/ai-orb/__test__/ai-orb.test.ts +1 -1
- package/src/tools/ask-question/__test__/ask-question.test.ts +1 -1
|
@@ -1 +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,
|
|
1
|
+
{"version":3,"file":"reasoning-picker.js","names":["_useModel","_openBlock","_createBlock","_unref","_withCtx","_createElementVNode","_normalizeClass","_toDisplayString","_createVNode","_createElementBlock","_Fragment","_renderList","_createCommentVNode"],"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,iBAAiBA,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,KADrBC,UAAA,GAAAC,YAkCYC,MAAA,SAAA,GAAA;;IAhCN,SAAA;IAAJ,KAAI;IACJ,SAAQ;IACR,WAAU;IACV,WAAU;IACV,OAAM;IACN,aAAU;IACT,iBAAeA,MAAA,GAAA,CAAG,CAAC,EAAC,iBAAA;;IAEV,SAAOC,cAMP,CALTC,mBAKS,UAAA;KALD,MAAK;KAAU,OAAKC,eAAEH,MAAA,GAAA,CAAG,CAAC,EAAC,mBAAA,CAAA;KAAuB,OAAM;IAC9D,GAAA,CAAAE,mBAAyE,QAAA,EAAlE,OAAKC,eAAEH,MAAA,GAAA,CAAG,CAAC,EAAC,yBAAA,CAAA,EAAgC,GAAAI,gBAAA,aAAA,KAAY,GAAA,CAAA,GAC/DC,YAEQL,MAAA,KAAA,GAAA,EAFA,OAAKG,eAAEH,MAAA,GAAA,CAAG,CAAC,EAAC,yBAAA,CAAA,EAAA,GAAA;KAClB,SAAAC,cAAa,CAAbI,YAAaL,MAAA,SAAA,CAAA,CAAA,CAAA;;;IAKR,SAAOC,cAaV,CAZNC,mBAYM,OAAA,EAZA,OAAKC,eAAEH,MAAA,GAAA,CAAG,CAAC,EAAC,gBAAA,CAAA,EAAA,GAChB,EAAAF,UAAA,IAAA,GAAAQ,mBAUMC,UAAA,MAAAC,WATY,QAAA,SAAT,UAAK;KADd,OAAAV,UAAA,GAAAQ,mBAUM,OAAA;MARH,KAAK,MAAM;MACX,OAAKH,eAAA,CAAGH,MAAA,GAAA,CAAG,CAAC,EAAC,kBAAA,GAAsBA,MAAA,GAAA,CAAG,CAAC,GAAE,UAAW,MAAM,UAAU,eAAA,KAAc,CAAA,CAAA;MAClF,UAAK,WAAE,aAAa,MAAM,KAAK;KAEhC,GAAA,CAAAE,mBAA8B,QAAA,MAAAE,gBAArB,MAAM,KAAK,GAAA,CAAA,GACP,MAAM,UAAU,eAAA,SAA7BN,UAAA,GAAAC,YAEQC,MAAA,KAAA,GAAA;;MAFsC,OAAKG,eAAEH,MAAA,GAAA,CAAG,CAAC,EAAC,iBAAA,CAAA;;MACxD,SAAAC,cAAS,CAATI,YAASL,MAAA,KAAA,CAAA,CAAA,CAAA"}
|
|
@@ -1 +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 { Close } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, inject } from 'vue'\n\nimport type { ChatToolCall } from '../../chat/types'\nimport { AiChatDIKey } from './di'\nimport { resolveToolIcon } from './tool-icons'\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_CLASS: Record<string, string> = {\n pending: 'is-active',\n running: 'is-active',\n 'awaiting-confirm': 'is-warning',\n success: 'is-success',\n error: 'is-danger',\n rejected: 'is-danger'\n}\n\nconst statusClass = computed(() => STATUS_CLASS[props.toolCall.status] ?? '')\n\n/** 头部图标:meta.icon > 宿主覆盖 > 内置名称规则 > 兜底;状态点走 status class */\nconst headerIcon = computed(\n () => tool.value?.icon ?? resolveToolIcon(props.toolCall.name, di?.toolIcons?.value)\n)\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":";;;;;;;;;;;;;EAkCA,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,eAAuC;GAC3C,SAAS;GACT,SAAS;GACT,oBAAoB;GACpB,SAAS;GACT,OAAO;GACP,UAAU;EACZ;EAEA,MAAM,cAAc,eAAe,aAAa,MAAM,SAAS,WAAW,EAAE;;EAG5E,MAAM,aAAa,eACX,KAAK,OAAO,QAAQ,gBAAgB,MAAM,SAAS,MAAM,IAAI,WAAW,KAAK,CACrF;;EAGA,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;;GA3EC,
|
|
1
|
+
{"version":3,"file":"side-panel.js","names":["_openBlock","_createElementBlock","_normalizeClass","_unref","_createElementVNode","_createVNode","_withCtx","_createBlock","_resolveDynamicComponent","_toDisplayString","_createCommentVNode"],"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 { Close } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { computed, inject } from 'vue'\n\nimport type { ChatToolCall } from '../../chat/types'\nimport { AiChatDIKey } from './di'\nimport { resolveToolIcon } from './tool-icons'\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_CLASS: Record<string, string> = {\n pending: 'is-active',\n running: 'is-active',\n 'awaiting-confirm': 'is-warning',\n success: 'is-success',\n error: 'is-danger',\n rejected: 'is-danger'\n}\n\nconst statusClass = computed(() => STATUS_CLASS[props.toolCall.status] ?? '')\n\n/** 头部图标:meta.icon > 宿主覆盖 > 内置名称规则 > 兜底;状态点走 status class */\nconst headerIcon = computed(\n () => tool.value?.icon ?? resolveToolIcon(props.toolCall.name, di?.toolIcons?.value)\n)\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":";;;;;;;;;;;;;EAkCA,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,eAAuC;GAC3C,SAAS;GACT,SAAS;GACT,oBAAoB;GACpB,SAAS;GACT,OAAO;GACP,UAAU;EACZ;EAEA,MAAM,cAAc,eAAe,aAAa,MAAM,SAAS,WAAW,EAAE;;EAG5E,MAAM,aAAa,eACX,KAAK,OAAO,QAAQ,gBAAgB,MAAM,SAAS,MAAM,IAAI,WAAW,KAAK,CACrF;;EAGA,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;;GA3EC,OAAAA,UAAA,GAAAC,mBAkBQ,SAAA,EAlBA,OAAKC,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,OAAA,CAAA,EAAA,GAClB,CAAAC,mBAgBM,OAAA,EAhBA,OAAKF,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,YAAA,CAAA,EAAA,GAChB,CAAAC,mBAQM,OAAA,EARA,OAAKF,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,cAAA,CAAA,EAAA,GAAA;IAChBE,YAEQF,MAAA,KAAA,GAAA,EAFA,OAAKD,eAAA,CAAGC,MAAA,GAAA,CAAG,CAAC,EAAC,YAAA,GAAgB,YAAA,KAAW,CAAA,EAAA,GAAA;KAC9C,SAAAG,cAA8B,EAA9BN,UAAA,GAAAO,YAA8BC,wBAAd,WAAA,KAAU,CAAA,EAAA,CAAA;;;IAE5BJ,mBAA2D,QAAA,EAApD,OAAKF,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,aAAA,CAAA,EAAoB,GAAAM,gBAAA,WAAA,KAAU,GAAA,CAAA;IACjDJ,YAEQF,MAAA,KAAA,GAAA;KAFA,OAAKD,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,aAAA,CAAA;KAAiB,OAAM;KAAQ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,OAAA;;KAC5D,SAAAG,cAAS,CAATD,YAASF,MAAA,KAAA,CAAA,CAAA,CAAA;;;GAIb,GAAA,CAAA,GAAAE,YAIUF,MAAA,OAAA,GAAA,EAJA,OAAKD,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,YAAA,CAAA,EAAA,GAAA;IACpB,SAAAG,cAAmE,CAAnEI,mBAAA,8DAAA,GACmC,KAAA,OAAM,UAAzCV,UAAA,GAAAO,YAAyEC,wBAAzD,KAAA,MAAK,MAAM,GAAA;;KAAuB,aAAW,QAAA;IAC7D,GAAA,MAAA,GAAA,CAAA,WAAA,CAAA,MAAAR,UAAA,GAAAC,mBAAuE,OAAA;;KAA1D,OAAKC,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,gBAAA,CAAA;IAAuB,GAAAM,gBAAA,eAAA,KAAc,GAAA,CAAA,EAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-call.js","names":[],"sources":["../../../src/components/ai-chat/tool-call.vue"],"sourcesContent":["<template>\n <UCollapseItem\n v-model=\"isExpanded\"\n :class=\"[cls.e('tool-call'), bem.is(toolCall.status)]\"\n :destroy-on-collapse=\"destroyOnCollapse\"\n @change=\"markUserToggled\"\n >\n <template #header>\n <UIcon :class=\"[cls.e('tool-call-status'), statusClass]\">\n <component :is=\"headerIcon\" />\n </UIcon>\n <span :class=\"[cls.e('tool-call-name'), isActive && 'u-shine']\">\n {{ tool?.label ?? toolCall.name }}\n </span>\n <span v-if=\"summary\" :class=\"cls.e('tool-call-summary')\">{{ summary }}</span>\n\n <span\n v-if=\"toolCall.status === 'awaiting-confirm'\"\n :class=\"cls.e('tool-call-confirm')\"\n @click.stop\n @keydown.enter.stop\n @keydown.space.stop\n >\n <UButton size=\"small\" type=\"primary\" @click=\"emit('respond', true)\">允许</UButton>\n <UButton size=\"small\" text @click=\"emit('respond', false)\">拒绝</UButton>\n </span>\n </template>\n\n <!-- 面板工具:render 展示在右侧侧边面板,卡片 body 仅提供查看入口 -->\n <div v-if=\"isPanelTool\" :class=\"cls.e('tool-call-panel-entry')\">\n <span :class=\"cls.e('tool-call-panel-hint')\">内容展示在右侧面板</span>\n <UButton size=\"small\" text type=\"primary\" @click=\"di?.openPanel(toolCall.id)\">\n 查看面板\n </UButton>\n </div>\n <!-- 自定义渲染:meta.render / tool-<name> 插槽优先于通用工具视图 -->\n <component :is=\"tool.render\" v-else-if=\"tool?.render\" :tool-call=\"toolCall\" />\n <component :is=\"slotBody\" v-else-if=\"slotBody && hasResult\" />\n <template v-else>\n <ToolJson v-if=\"prettyArguments\" title=\"参数\" :text=\"prettyArguments\" />\n <ToolJson v-if=\"hasResult\" :title=\"resultTitle\" :text=\"displayResult\" />\n <ToolJson v-if=\"prettyView\" title=\"视图\" :text=\"prettyView\" />\n </template>\n </UCollapseItem>\n</template>\n\n<script lang=\"ts\" setup>\nimport { UButton, UCollapseItem, UIcon } from '@veltra/desktop'\nimport { bem } from '@veltra/utils'\nimport { computed, inject, ref, watch } from 'vue'\n\nimport type { ChatToolCall } from '../../chat/types'\nimport { AiChatDIKey } from './di'\nimport { resolveToolIcon } from './tool-icons'\nimport ToolJson from './tool-json.vue'\n\ndefineOptions({ name: 'UAiChatToolCall' })\n\nconst props = defineProps<{ toolCall: ChatToolCall }>()\n\nconst emit = defineEmits<{ (e: 'respond', approved: boolean): void }>()\n\nconst di = inject(AiChatDIKey)\nconst cls = di?.cls ?? bem('ai-chat')\n\n/** 把未知值格式成可读 JSON,循环引用等异常时退回 String,不得 throw */\nfunction formatUnknown(value: unknown): string {\n if (value == null) return ''\n if (typeof value === 'string') {\n try {\n return JSON.stringify(JSON.parse(value), null, 2)\n } catch {\n return value\n }\n }\n try {\n return JSON.stringify(value, null, 2) ?? String(value)\n } catch {\n return String(value)\n }\n}\n\n/** 当前调用对应的工具定义或渲染元信息(session 下忽略 execute / needsConfirm / terminal) */\nconst tool = computed(() => di?.tools.value[props.toolCall.name])\n\nconst isExpanded = ref(false)\n/** 用户手动切换过后,不再随 status 自动展开/折叠 */\nlet userToggled: boolean = false\n\n/** UCollapseItem 的 change 仅在用户点击头部时触发(程序化 v-model 变更不触发) */\nconst markUserToggled = () => {\n userToggled = true\n}\n\n/** 面板工具:render 展示在右侧侧边面板,卡片 body 仅保留「查看面板」入口 */\nconst isPanelTool = computed(() => tool.value?.renderTo === 'panel')\n\n/** 「UI 即答复」的终结工具(session 元信息无 terminal,恒为 false) */\nconst isAnswerTool = computed(() => {\n const t = tool.value\n return !!(t && 'terminal' in t && t.terminal)\n})\n\n/** 内联自定义 body(render / tool-<name> 插槽):进行中可能是提问表单等交互 UI,需展开可见 */\nconst hasInlineBody = computed(\n () => !isPanelTool.value && !!(tool.value?.render || di?.slots[`tool-${props.toolCall.name}`])\n)\n\n/** 完成后是否自动折叠:缺省一律折叠;「UI 即答复」的终结工具缺省保持展开 */\nconst autoCollapse = computed(() => tool.value?.autoCollapse ?? !isAnswerTool.value)\n\n/** 需要用户交互/展示的进行中状态:待确认(看参数再决策)、有内联自定义 body(提问表单等) */\nconst needsInteraction = computed(() => {\n if (props.toolCall.status === 'awaiting-confirm') return true\n const active = props.toolCall.status === 'pending' || props.toolCall.status === 'running'\n return active && hasInlineBody.value\n})\n\nconst isSettledStatus = (status: ChatToolCall['status']) =>\n status === 'success' || status === 'error' || status === 'rejected'\n\n// 默认折叠(含进行中);仅交互/答复类自动展开,用户手动切换过后不再自动变化\nwatch(\n () => props.toolCall.status,\n (status) => {\n if (userToggled) return\n if (isSettledStatus(status)) {\n isExpanded.value = !autoCollapse.value\n } else {\n isExpanded.value = needsInteraction.value\n }\n },\n { immediate: true }\n)\n\nconst STATUS_CLASS: Record<string, string> = {\n pending: 'is-active',\n running: 'is-active',\n 'awaiting-confirm': 'is-warning',\n success: 'is-success',\n error: 'is-danger',\n rejected: 'is-danger'\n}\n\nconst statusClass = computed(() => STATUS_CLASS[props.toolCall.status] ?? '')\n\n/** 进行中(pending/running):工具名带扫光效果 */\nconst isActive = computed(() => {\n return props.toolCall.status === 'pending' || props.toolCall.status === 'running'\n})\n\n/** 终态(成功/失败/拒绝) */\nconst isSettled = computed(() => isSettledStatus(props.toolCall.status))\n\n/**\n * 折叠后卸载内容 DOM:仅终态且非面板工具时启用——\n * 进行中/待确认要保留内容状态(确认按钮、表单),面板工具的 body 是「查看面板」入口必须可达\n */\nconst destroyOnCollapse = computed(() => isSettled.value && !isPanelTool.value)\n\n/** 头部图标:meta.icon > 宿主覆盖 > 内置名称规则 > 兜底;状态点走 status class */\nconst headerIcon = computed(\n () => tool.value?.icon ?? resolveToolIcon(props.toolCall.name, di?.toolIcons?.value)\n)\n\nconst parsedArguments = computed<Record<string, unknown> | null>(() => {\n if (!props.toolCall.arguments) return null\n try {\n return JSON.parse(props.toolCall.arguments)\n } catch {\n return null\n }\n})\n\n/** 折叠态参数摘要:取第一个字符串参数值 */\nconst summary = computed(() => {\n const args = parsedArguments.value\n if (!args) return ''\n for (const value of Object.values(args)) {\n if (typeof value === 'string' && value) {\n return value.length > 40 ? `${value.slice(0, 40)}…` : value\n }\n }\n return ''\n})\n\nconst prettyArguments = computed(() => {\n const args = parsedArguments.value\n if (args) return JSON.stringify(args, null, 2)\n return props.toolCall.arguments || ''\n})\n\nconst hasResult = computed(() => {\n return props.toolCall.result != null || props.toolCall.error != null\n})\n\nconst resultTitle = computed(() => (props.toolCall.status === 'error' ? '错误' : '结果'))\n\nconst displayResult = computed(() => formatUnknown(props.toolCall.error ?? props.toolCall.result))\n\n/** 服务端下发的 view,格式化为 JSON;不得丢弃、不得 throw */\nconst prettyView = computed(() => {\n if (props.toolCall.view == null) return ''\n return formatUnknown(props.toolCall.view)\n})\n\n/** 使用方通过 tool-<name> 插槽自定义该工具的展示(有结果时替换整个 body) */\nconst slotBody = computed(() => {\n const slot = di?.slots[`tool-${props.toolCall.name}`]\n return slot ? () => slot({ toolCall: props.toolCall }) : undefined\n})\n</script>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"tool-call.js","names":["_openBlock","_createBlock","_unref","_normalizeClass","_withCtx","_createVNode","_resolveDynamicComponent","_createElementVNode","_toDisplayString","_createElementBlock","_createCommentVNode","_withModifiers","_withKeys","_createTextVNode","_Fragment","ToolJson"],"sources":["../../../src/components/ai-chat/tool-call.vue"],"sourcesContent":["<template>\n <UCollapseItem\n v-model=\"isExpanded\"\n :class=\"[cls.e('tool-call'), bem.is(toolCall.status)]\"\n :destroy-on-collapse=\"destroyOnCollapse\"\n @change=\"markUserToggled\"\n >\n <template #header>\n <UIcon :class=\"[cls.e('tool-call-status'), statusClass]\">\n <component :is=\"headerIcon\" />\n </UIcon>\n <span :class=\"[cls.e('tool-call-name'), isActive && 'u-shine']\">\n {{ tool?.label ?? toolCall.name }}\n </span>\n <span v-if=\"summary\" :class=\"cls.e('tool-call-summary')\">{{ summary }}</span>\n\n <span\n v-if=\"toolCall.status === 'awaiting-confirm'\"\n :class=\"cls.e('tool-call-confirm')\"\n @click.stop\n @keydown.enter.stop\n @keydown.space.stop\n >\n <UButton size=\"small\" type=\"primary\" @click=\"emit('respond', true)\">允许</UButton>\n <UButton size=\"small\" text @click=\"emit('respond', false)\">拒绝</UButton>\n </span>\n </template>\n\n <!-- 面板工具:render 展示在右侧侧边面板,卡片 body 仅提供查看入口 -->\n <div v-if=\"isPanelTool\" :class=\"cls.e('tool-call-panel-entry')\">\n <span :class=\"cls.e('tool-call-panel-hint')\">内容展示在右侧面板</span>\n <UButton size=\"small\" text type=\"primary\" @click=\"di?.openPanel(toolCall.id)\">\n 查看面板\n </UButton>\n </div>\n <!-- 自定义渲染:meta.render / tool-<name> 插槽优先于通用工具视图 -->\n <component :is=\"tool.render\" v-else-if=\"tool?.render\" :tool-call=\"toolCall\" />\n <component :is=\"slotBody\" v-else-if=\"slotBody && hasResult\" />\n <template v-else>\n <ToolJson v-if=\"prettyArguments\" title=\"参数\" :text=\"prettyArguments\" />\n <ToolJson v-if=\"hasResult\" :title=\"resultTitle\" :text=\"displayResult\" />\n <ToolJson v-if=\"prettyView\" title=\"视图\" :text=\"prettyView\" />\n </template>\n </UCollapseItem>\n</template>\n\n<script lang=\"ts\" setup>\nimport { UButton, UCollapseItem, UIcon } from '@veltra/desktop'\nimport { bem } from '@veltra/utils'\nimport { computed, inject, ref, watch } from 'vue'\n\nimport type { ChatToolCall } from '../../chat/types'\nimport { AiChatDIKey } from './di'\nimport { resolveToolIcon } from './tool-icons'\nimport ToolJson from './tool-json.vue'\n\ndefineOptions({ name: 'UAiChatToolCall' })\n\nconst props = defineProps<{ toolCall: ChatToolCall }>()\n\nconst emit = defineEmits<{ (e: 'respond', approved: boolean): void }>()\n\nconst di = inject(AiChatDIKey)\nconst cls = di?.cls ?? bem('ai-chat')\n\n/** 把未知值格式成可读 JSON,循环引用等异常时退回 String,不得 throw */\nfunction formatUnknown(value: unknown): string {\n if (value == null) return ''\n if (typeof value === 'string') {\n try {\n return JSON.stringify(JSON.parse(value), null, 2)\n } catch {\n return value\n }\n }\n try {\n return JSON.stringify(value, null, 2) ?? String(value)\n } catch {\n return String(value)\n }\n}\n\n/** 当前调用对应的工具定义或渲染元信息(session 下忽略 execute / needsConfirm / terminal) */\nconst tool = computed(() => di?.tools.value[props.toolCall.name])\n\nconst isExpanded = ref(false)\n/** 用户手动切换过后,不再随 status 自动展开/折叠 */\nlet userToggled: boolean = false\n\n/** UCollapseItem 的 change 仅在用户点击头部时触发(程序化 v-model 变更不触发) */\nconst markUserToggled = () => {\n userToggled = true\n}\n\n/** 面板工具:render 展示在右侧侧边面板,卡片 body 仅保留「查看面板」入口 */\nconst isPanelTool = computed(() => tool.value?.renderTo === 'panel')\n\n/** 「UI 即答复」的终结工具(session 元信息无 terminal,恒为 false) */\nconst isAnswerTool = computed(() => {\n const t = tool.value\n return !!(t && 'terminal' in t && t.terminal)\n})\n\n/** 内联自定义 body(render / tool-<name> 插槽):进行中可能是提问表单等交互 UI,需展开可见 */\nconst hasInlineBody = computed(\n () => !isPanelTool.value && !!(tool.value?.render || di?.slots[`tool-${props.toolCall.name}`])\n)\n\n/** 完成后是否自动折叠:缺省一律折叠;「UI 即答复」的终结工具缺省保持展开 */\nconst autoCollapse = computed(() => tool.value?.autoCollapse ?? !isAnswerTool.value)\n\n/** 需要用户交互/展示的进行中状态:待确认(看参数再决策)、有内联自定义 body(提问表单等) */\nconst needsInteraction = computed(() => {\n if (props.toolCall.status === 'awaiting-confirm') return true\n const active = props.toolCall.status === 'pending' || props.toolCall.status === 'running'\n return active && hasInlineBody.value\n})\n\nconst isSettledStatus = (status: ChatToolCall['status']) =>\n status === 'success' || status === 'error' || status === 'rejected'\n\n// 默认折叠(含进行中);仅交互/答复类自动展开,用户手动切换过后不再自动变化\nwatch(\n () => props.toolCall.status,\n (status) => {\n if (userToggled) return\n if (isSettledStatus(status)) {\n isExpanded.value = !autoCollapse.value\n } else {\n isExpanded.value = needsInteraction.value\n }\n },\n { immediate: true }\n)\n\nconst STATUS_CLASS: Record<string, string> = {\n pending: 'is-active',\n running: 'is-active',\n 'awaiting-confirm': 'is-warning',\n success: 'is-success',\n error: 'is-danger',\n rejected: 'is-danger'\n}\n\nconst statusClass = computed(() => STATUS_CLASS[props.toolCall.status] ?? '')\n\n/** 进行中(pending/running):工具名带扫光效果 */\nconst isActive = computed(() => {\n return props.toolCall.status === 'pending' || props.toolCall.status === 'running'\n})\n\n/** 终态(成功/失败/拒绝) */\nconst isSettled = computed(() => isSettledStatus(props.toolCall.status))\n\n/**\n * 折叠后卸载内容 DOM:仅终态且非面板工具时启用——\n * 进行中/待确认要保留内容状态(确认按钮、表单),面板工具的 body 是「查看面板」入口必须可达\n */\nconst destroyOnCollapse = computed(() => isSettled.value && !isPanelTool.value)\n\n/** 头部图标:meta.icon > 宿主覆盖 > 内置名称规则 > 兜底;状态点走 status class */\nconst headerIcon = computed(\n () => tool.value?.icon ?? resolveToolIcon(props.toolCall.name, di?.toolIcons?.value)\n)\n\nconst parsedArguments = computed<Record<string, unknown> | null>(() => {\n if (!props.toolCall.arguments) return null\n try {\n return JSON.parse(props.toolCall.arguments)\n } catch {\n return null\n }\n})\n\n/** 折叠态参数摘要:取第一个字符串参数值 */\nconst summary = computed(() => {\n const args = parsedArguments.value\n if (!args) return ''\n for (const value of Object.values(args)) {\n if (typeof value === 'string' && value) {\n return value.length > 40 ? `${value.slice(0, 40)}…` : value\n }\n }\n return ''\n})\n\nconst prettyArguments = computed(() => {\n const args = parsedArguments.value\n if (args) return JSON.stringify(args, null, 2)\n return props.toolCall.arguments || ''\n})\n\nconst hasResult = computed(() => {\n return props.toolCall.result != null || props.toolCall.error != null\n})\n\nconst resultTitle = computed(() => (props.toolCall.status === 'error' ? '错误' : '结果'))\n\nconst displayResult = computed(() => formatUnknown(props.toolCall.error ?? props.toolCall.result))\n\n/** 服务端下发的 view,格式化为 JSON;不得丢弃、不得 throw */\nconst prettyView = computed(() => {\n if (props.toolCall.view == null) return ''\n return formatUnknown(props.toolCall.view)\n})\n\n/** 使用方通过 tool-<name> 插槽自定义该工具的展示(有结果时替换整个 body) */\nconst slotBody = computed(() => {\n const slot = di?.slots[`tool-${props.toolCall.name}`]\n return slot ? () => slot({ toolCall: props.toolCall }) : undefined\n})\n</script>\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-json.js","names":[],"sources":["../../../src/components/ai-chat/tool-json.vue"],"sourcesContent":["<template>\n <div :class=\"cls.e('tool-call-json')\">\n <div :class=\"cls.e('tool-call-json-head')\">\n <span :class=\"cls.e('tool-call-section')\">{{ title }}</span>\n <UButton\n v-if=\"overLimit\"\n :class=\"cls.e('tool-call-json-toggle')\"\n size=\"small\"\n text\n type=\"primary\"\n @click=\"expanded = !expanded\"\n >\n {{ expanded ? '收起' : '展开' }}\n </UButton>\n </div>\n <pre :class=\"[cls.e('tool-call-code'), bem.is('truncated', overLimit && !expanded)]\">{{\n shown\n }}</pre>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { UButton } from '@veltra/desktop'\nimport { bem } from '@veltra/utils'\nimport { computed, inject, ref } from 'vue'\n\nimport { AiChatDIKey } from './di'\n\ndefineOptions({ name: 'UAiChatToolJson' })\n\n/** 超过该字符数截断,展开后可见全文 */\nconst PREVIEW_LIMIT = 480\n\nconst { title, text } = defineProps<{\n /** 区块标题(参数 / 结果 / 视图) */\n title: string\n /** 已格式化的 JSON 或原文 */\n text: string\n}>()\n\nconst di = inject(AiChatDIKey)\nconst cls = di?.cls ?? bem('ai-chat')\n\nconst expanded = ref(false)\n\nconst overLimit = computed(() => text.length > PREVIEW_LIMIT)\n\nconst shown = computed(() => {\n if (expanded.value || !overLimit.value) return text\n return `${text.slice(0, PREVIEW_LIMIT)}…`\n})\n</script>\n"],"mappings":";;;;;AA+BA,MAAM,gBAAgB;;;;;;;;;EAUtB,MAAM,MADK,OAAO,WACN,CAAA,EAAI,OAAO,IAAI,SAAS;EAEpC,MAAM,WAAW,IAAI,KAAK;EAE1B,MAAM,YAAY,eAAe,QAAA,KAAK,SAAS,aAAa;EAE5D,MAAM,QAAQ,eAAe;GAC3B,IAAI,SAAS,SAAS,CAAC,UAAU,OAAO,OAAO,QAAA;GAC/C,OAAO,GAAG,QAAA,KAAK,MAAM,GAAG,aAAa,EAAE;EACzC,CAAC;;GAjDC,
|
|
1
|
+
{"version":3,"file":"tool-json.js","names":["_openBlock","_createElementBlock","_normalizeClass","_unref","_createElementVNode","_toDisplayString","_createBlock","_withCtx","_createTextVNode","_createCommentVNode"],"sources":["../../../src/components/ai-chat/tool-json.vue"],"sourcesContent":["<template>\n <div :class=\"cls.e('tool-call-json')\">\n <div :class=\"cls.e('tool-call-json-head')\">\n <span :class=\"cls.e('tool-call-section')\">{{ title }}</span>\n <UButton\n v-if=\"overLimit\"\n :class=\"cls.e('tool-call-json-toggle')\"\n size=\"small\"\n text\n type=\"primary\"\n @click=\"expanded = !expanded\"\n >\n {{ expanded ? '收起' : '展开' }}\n </UButton>\n </div>\n <pre :class=\"[cls.e('tool-call-code'), bem.is('truncated', overLimit && !expanded)]\">{{\n shown\n }}</pre>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { UButton } from '@veltra/desktop'\nimport { bem } from '@veltra/utils'\nimport { computed, inject, ref } from 'vue'\n\nimport { AiChatDIKey } from './di'\n\ndefineOptions({ name: 'UAiChatToolJson' })\n\n/** 超过该字符数截断,展开后可见全文 */\nconst PREVIEW_LIMIT = 480\n\nconst { title, text } = defineProps<{\n /** 区块标题(参数 / 结果 / 视图) */\n title: string\n /** 已格式化的 JSON 或原文 */\n text: string\n}>()\n\nconst di = inject(AiChatDIKey)\nconst cls = di?.cls ?? bem('ai-chat')\n\nconst expanded = ref(false)\n\nconst overLimit = computed(() => text.length > PREVIEW_LIMIT)\n\nconst shown = computed(() => {\n if (expanded.value || !overLimit.value) return text\n return `${text.slice(0, PREVIEW_LIMIT)}…`\n})\n</script>\n"],"mappings":";;;;;AA+BA,MAAM,gBAAgB;;;;;;;;;EAUtB,MAAM,MADK,OAAO,WACN,CAAA,EAAI,OAAO,IAAI,SAAS;EAEpC,MAAM,WAAW,IAAI,KAAK;EAE1B,MAAM,YAAY,eAAe,QAAA,KAAK,SAAS,aAAa;EAE5D,MAAM,QAAQ,eAAe;GAC3B,IAAI,SAAS,SAAS,CAAC,UAAU,OAAO,OAAO,QAAA;GAC/C,OAAO,GAAG,QAAA,KAAK,MAAM,GAAG,aAAa,EAAE;EACzC,CAAC;;GAjDC,OAAAA,UAAA,GAAAC,mBAiBM,OAAA,EAjBA,OAAKC,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,gBAAA,CAAA,EAAA,GAChB,CAAAC,mBAYM,OAAA,EAZA,OAAKF,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,qBAAA,CAAA,EAAA,GAChB,CAAAC,mBAA4D,QAAA,EAArD,OAAKF,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,mBAAA,CAAA,EAA0B,GAAAE,gBAAA,QAAA,KAAK,GAAA,CAAA,GAE1C,UAAA,SADRL,UAAA,GAAAM,YASUH,MAAA,OAAA,GAAA;;IAPP,OAAKD,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,uBAAA,CAAA;IACb,MAAK;IACL,MAAA;IACA,MAAK;IACJ,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,SAAA,QAAQ,CAAI,SAAA;;IAEpB,SAAAI,cAA4B,CAAzBC,gBAAAH,gBAAA,SAAA,QAAQ,OAAA,IAAA,GAAA,CAAA,CAAA,CAAA;;GAGf,GAAA,GAAA,CAAA,OAAA,CAAA,KAAA,mBAAA,QAAA,IAAA,CAAA,GAAA,CAAA,GAAAD,mBAEQ,OAAA,EAFF,OAAKF,eAAA,CAAGC,MAAA,GAAA,CAAG,CAAC,EAAC,gBAAA,GAAoBA,MAAA,GAAA,CAAG,CAAC,GAAE,aAAc,UAAA,SAAS,CAAK,SAAA,KAAQ,CAAA,CAAA,EAC/E,GAAAE,gBAAA,MAAA,KAAK,GAAA,CAAA,CAAA,GAAA,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"turn-process.js","names":[],"sources":["../../../src/components/ai-chat/turn-process.vue"],"sourcesContent":["<template>\n <div :class=\"[cls.e('process'), bem.is('expanded', expanded)]\">\n <div :class=\"cls.e('process-header')\" @click=\"expanded = !expanded\">\n <UIcon :class=\"cls.e('process-icon')\"><CircleCheck /></UIcon>\n <span :class=\"cls.e('process-title')\">已完成</span>\n <UIcon :class=\"cls.e('process-chevron')\"><ArrowRight /></UIcon>\n </div>\n <!-- 折叠时完全卸载过程 DOM:历史轮次的思考/工具卡片可能很多,常驻挂载浪费渲染成本 -->\n <div v-if=\"expanded\" :class=\"cls.e('process-content')\">\n <MessageItem\n v-for=\"msg in messages\"\n :key=\"msg.id\"\n :message=\"msg\"\n :is-last=\"false\"\n :renderer-props=\"rendererProps\"\n @respond=\"(id, approved) => emit('respond', id, approved)\"\n />\n </div>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { UIcon } from '@veltra/desktop'\nimport { ArrowRight, CircleCheck } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { inject, ref } from 'vue'\n\nimport type { ChatMessage } from '../../chat/types'\nimport { AiChatDIKey } from './di'\nimport MessageItem from './message-item.vue'\n\ndefineOptions({ name: 'UAiChatTurnProcess' })\n\ndefineProps<{\n /** 本轮最终答案之前的过程消息(思考 + 中间文本 + 工具卡片) */\n messages: ChatMessage[]\n /** 透传给 MarkdownRender 的属性 */\n rendererProps?: Record<string, unknown>\n}>()\n\nconst emit = defineEmits<{ (e: 'respond', toolCallId: string, approved: boolean): void }>()\n\nconst di = inject(AiChatDIKey)\nconst cls = di?.cls ?? bem('ai-chat')\n\nconst expanded = ref(false)\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;EAwCA,MAAM,OAAO;EAGb,MAAM,MADK,OAAO,WACN,CAAA,EAAI,OAAO,IAAI,SAAS;EAEpC,MAAM,WAAW,IAAI,KAAK;;GA5CxB,
|
|
1
|
+
{"version":3,"file":"turn-process.js","names":["_openBlock","_createElementBlock","_normalizeClass","_unref","_createElementVNode","_createVNode","_withCtx","_createCommentVNode","_Fragment","_renderList","_createBlock","MessageItem"],"sources":["../../../src/components/ai-chat/turn-process.vue"],"sourcesContent":["<template>\n <div :class=\"[cls.e('process'), bem.is('expanded', expanded)]\">\n <div :class=\"cls.e('process-header')\" @click=\"expanded = !expanded\">\n <UIcon :class=\"cls.e('process-icon')\"><CircleCheck /></UIcon>\n <span :class=\"cls.e('process-title')\">已完成</span>\n <UIcon :class=\"cls.e('process-chevron')\"><ArrowRight /></UIcon>\n </div>\n <!-- 折叠时完全卸载过程 DOM:历史轮次的思考/工具卡片可能很多,常驻挂载浪费渲染成本 -->\n <div v-if=\"expanded\" :class=\"cls.e('process-content')\">\n <MessageItem\n v-for=\"msg in messages\"\n :key=\"msg.id\"\n :message=\"msg\"\n :is-last=\"false\"\n :renderer-props=\"rendererProps\"\n @respond=\"(id, approved) => emit('respond', id, approved)\"\n />\n </div>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { UIcon } from '@veltra/desktop'\nimport { ArrowRight, CircleCheck } from '@veltra/icons/normal'\nimport { bem } from '@veltra/utils'\nimport { inject, ref } from 'vue'\n\nimport type { ChatMessage } from '../../chat/types'\nimport { AiChatDIKey } from './di'\nimport MessageItem from './message-item.vue'\n\ndefineOptions({ name: 'UAiChatTurnProcess' })\n\ndefineProps<{\n /** 本轮最终答案之前的过程消息(思考 + 中间文本 + 工具卡片) */\n messages: ChatMessage[]\n /** 透传给 MarkdownRender 的属性 */\n rendererProps?: Record<string, unknown>\n}>()\n\nconst emit = defineEmits<{ (e: 'respond', toolCallId: string, approved: boolean): void }>()\n\nconst di = inject(AiChatDIKey)\nconst cls = di?.cls ?? bem('ai-chat')\n\nconst expanded = ref(false)\n</script>\n"],"mappings":";;;;;;;;;;;;;;;;EAwCA,MAAM,OAAO;EAGb,MAAM,MADK,OAAO,WACN,CAAA,EAAI,OAAO,IAAI,SAAS;EAEpC,MAAM,WAAW,IAAI,KAAK;;GA5CxB,OAAAA,UAAA,GAAAC,mBAiBM,OAAA,EAjBA,OAAKC,eAAA,CAAGC,MAAA,GAAA,CAAG,CAAC,EAAC,SAAA,GAAaA,MAAA,GAAA,CAAG,CAAC,GAAE,YAAa,SAAA,KAAQ,CAAA,CAAA,EAAA,GAAA;IACzDC,mBAIM,OAAA;KAJA,OAAKF,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,gBAAA,CAAA;KAAqB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,SAAA,QAAQ,CAAI,SAAA;;KACxDE,YAA6DF,MAAA,KAAA,GAAA,EAArD,OAAKD,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,cAAA,CAAA,EAAA,GAAA;MAAkB,SAAAG,cAAe,CAAfD,YAAeF,MAAA,WAAA,CAAA,CAAA,CAAA;;;KACrDC,mBAAgD,QAAA,EAAzC,OAAKF,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,eAAA,CAAA,EAAmB,GAAA,OAAG,CAAA;KACzCE,YAA+DF,MAAA,KAAA,GAAA,EAAvD,OAAKD,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,iBAAA,CAAA,EAAA,GAAA;MAAqB,SAAAG,cAAc,CAAdD,YAAcF,MAAA,UAAA,CAAA,CAAA,CAAA;;;;IAEzDI,mBAAA,6CAAA;IACW,SAAA,SAAXP,UAAA,GAAAC,mBASM,OAAA;;KATgB,OAAKC,eAAEC,MAAA,GAAA,CAAG,CAAC,EAAC,iBAAA,CAAA;IAChC,GAAA,EAAAH,UAAA,IAAA,GAAAC,mBAOEO,UAAA,MAAAC,WANc,QAAA,WAAP,QAAG;KADZ,OAAAT,UAAA,GAAAU,YAOEC,aAAA;MALC,KAAK,IAAI;MACT,SAAS;MACT,WAAS;MACT,kBAAgB,QAAA;MAChB,WAAO,OAAA,OAAA,OAAA,MAAG,IAAI,aAAa,KAAI,WAAY,IAAI,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-orb.js","names":[],"sources":["../../../src/components/ai-orb/ai-orb.vue"],"sourcesContent":["<template>\n <canvas\n ref=\"canvasRef\"\n :class=\"cls.b\"\n :style=\"{ width: `${size}px`, height: `${size}px` }\"\n aria-hidden=\"true\"\n @pointermove=\"handlePointerMove\"\n @pointerleave=\"handlePointerLeave\"\n @pointerdown=\"handlePointerDown\"\n @click=\"emit('click')\"\n />\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\nimport { onBeforeUnmount, onMounted, useTemplateRef, watch } from 'vue'\n\nimport type { AiOrbEmits, AiOrbExposed, AiOrbProps, AiOrbReaction } from '../../types/ai-orb'\nimport { createOrbRenderer, type AiOrbRenderer } from './orb-renderer'\n\ndefineOptions({ name: 'UAiOrb' })\n\nconst props = withDefaults(defineProps<AiOrbProps>(), { size: 48, status: 'idle' })\nconst emit = defineEmits<AiOrbEmits>()\n\nconst cls = bem('ai-orb')\n\nconst canvasRef = useTemplateRef<HTMLCanvasElement>('canvasRef')\n\nlet renderer: AiOrbRenderer | null = null\nlet observer: IntersectionObserver | null = null\nlet motionQuery: MediaQueryList | null = null\n\n/** 指针坐标 → 单位球空间(球心为原点,1 = 球半径),交给渲染器做视线跟随 */\nconst handlePointerMove = (e: PointerEvent) => {\n const el = canvasRef.value\n if (!el || !renderer) return\n const rect = el.getBoundingClientRect()\n const ballRadius = rect.width * 0.36\n renderer.setPointer({\n x: (e.clientX - rect.left - rect.width / 2) / ballRadius,\n y: (e.clientY - rect.top - rect.height / 2) / ballRadius\n })\n}\n\nconst handlePointerLeave = () => renderer?.setPointer(null)\n\nconst handlePointerDown = () => renderer?.poke()\n\nconst applyVisibility = (visible: boolean) => {\n if (!renderer) return\n if (motionQuery?.matches) {\n // 减少动态偏好:只画一帧静态画面,不启动画循环\n renderer.stop()\n renderer.renderOnce()\n return\n }\n if (visible) renderer.start()\n else renderer.stop()\n}\n\nconst handleMotionChange = () => {\n // 偏好像素级开关变化时重评估(不可见时 start 也会被 IntersectionObserver 纠正)\n applyVisibility(true)\n}\n\nonMounted(() => {\n const canvas = canvasRef.value\n if (!canvas) return\n\n renderer = createOrbRenderer(canvas, { size: props.size, status: props.status })\n\n motionQuery = window.matchMedia?.('(prefers-reduced-motion: reduce)') ?? null\n motionQuery?.addEventListener?.('change', handleMotionChange)\n\n if (typeof IntersectionObserver !== 'undefined') {\n // 滚出视口的球暂停动画循环,长会话下同屏多个球也不累积开销\n observer = new IntersectionObserver((entries) => {\n applyVisibility(entries[0]?.isIntersecting ?? true)\n })\n observer.observe(canvas)\n } else {\n applyVisibility(true)\n }\n})\n\nwatch(\n () => props.status,\n (status) => renderer?.setStatus(status)\n)\n\nwatch(\n () => props.size,\n (size) => renderer?.resize(size)\n)\n\nconst react = (reaction: AiOrbReaction) => renderer?.trigger(reaction)\n\ndefineExpose<AiOrbExposed>({ react })\n\nonBeforeUnmount(() => {\n observer?.disconnect()\n motionQuery?.removeEventListener?.('change', handleMotionChange)\n renderer?.stop()\n renderer = null\n})\n</script>\n"],"mappings":";;;;;;;;;;;;;EAsBA,MAAM,QAAQ;EACd,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,QAAQ;EAExB,MAAM,YAAY,eAAkC,WAAW;EAE/D,IAAI,WAAiC;EACrC,IAAI,WAAwC;EAC5C,IAAI,cAAqC;;EAGzC,MAAM,qBAAqB,MAAoB;GAC7C,MAAM,KAAK,UAAU;GACrB,IAAI,CAAC,MAAM,CAAC,UAAU;GACtB,MAAM,OAAO,GAAG,sBAAsB;GACtC,MAAM,aAAa,KAAK,QAAQ;GAChC,SAAS,WAAW;IAClB,IAAI,EAAE,UAAU,KAAK,OAAO,KAAK,QAAQ,KAAK;IAC9C,IAAI,EAAE,UAAU,KAAK,MAAM,KAAK,SAAS,KAAK;GAChD,CAAC;EACH;EAEA,MAAM,2BAA2B,UAAU,WAAW,IAAI;EAE1D,MAAM,0BAA0B,UAAU,KAAK;EAE/C,MAAM,mBAAmB,YAAqB;GAC5C,IAAI,CAAC,UAAU;GACf,IAAI,aAAa,SAAS;IAExB,SAAS,KAAK;IACd,SAAS,WAAW;IACpB;GACF;GACA,IAAI,SAAS,SAAS,MAAM;QACvB,SAAS,KAAK;EACrB;EAEA,MAAM,2BAA2B;GAE/B,gBAAgB,IAAI;EACtB;EAEA,gBAAgB;GACd,MAAM,SAAS,UAAU;GACzB,IAAI,CAAC,QAAQ;GAEb,WAAW,kBAAkB,QAAQ;IAAE,MAAM,MAAM;IAAM,QAAQ,MAAM;GAAO,CAAC;GAE/E,cAAc,OAAO,aAAa,kCAAkC,KAAK;GACzE,aAAa,mBAAmB,UAAU,kBAAkB;GAE5D,IAAI,OAAO,yBAAyB,aAAa;IAE/C,WAAW,IAAI,sBAAsB,YAAY;KAC/C,gBAAgB,QAAQ,EAAE,EAAE,kBAAkB,IAAI;IACpD,CAAC;IACD,SAAS,QAAQ,MAAM;GACzB,OACE,gBAAgB,IAAI;EAExB,CAAC;EAED,YACQ,MAAM,SACX,WAAW,UAAU,UAAU,MAAM,CACxC;EAEA,YACQ,MAAM,OACX,SAAS,UAAU,OAAO,IAAI,CACjC;EAEA,MAAM,SAAS,aAA4B,UAAU,QAAQ,QAAQ;EAErE,SAA2B,EAAE,MAAM,CAAC;EAEpC,sBAAsB;GACpB,UAAU,WAAW;GACrB,aAAa,sBAAsB,UAAU,kBAAkB;GAC/D,UAAU,KAAK;GACf,WAAW;EACb,CAAC;;GAxGC,
|
|
1
|
+
{"version":3,"file":"ai-orb.js","names":["_openBlock","_createElementBlock","_normalizeClass","_unref","_normalizeStyle"],"sources":["../../../src/components/ai-orb/ai-orb.vue"],"sourcesContent":["<template>\n <canvas\n ref=\"canvasRef\"\n :class=\"cls.b\"\n :style=\"{ width: `${size}px`, height: `${size}px` }\"\n aria-hidden=\"true\"\n @pointermove=\"handlePointerMove\"\n @pointerleave=\"handlePointerLeave\"\n @pointerdown=\"handlePointerDown\"\n @click=\"emit('click')\"\n />\n</template>\n\n<script lang=\"ts\" setup>\nimport { bem } from '@veltra/utils'\nimport { onBeforeUnmount, onMounted, useTemplateRef, watch } from 'vue'\n\nimport type { AiOrbEmits, AiOrbExposed, AiOrbProps, AiOrbReaction } from '../../types/ai-orb'\nimport { createOrbRenderer, type AiOrbRenderer } from './orb-renderer'\n\ndefineOptions({ name: 'UAiOrb' })\n\nconst props = withDefaults(defineProps<AiOrbProps>(), { size: 48, status: 'idle' })\nconst emit = defineEmits<AiOrbEmits>()\n\nconst cls = bem('ai-orb')\n\nconst canvasRef = useTemplateRef<HTMLCanvasElement>('canvasRef')\n\nlet renderer: AiOrbRenderer | null = null\nlet observer: IntersectionObserver | null = null\nlet motionQuery: MediaQueryList | null = null\n\n/** 指针坐标 → 单位球空间(球心为原点,1 = 球半径),交给渲染器做视线跟随 */\nconst handlePointerMove = (e: PointerEvent) => {\n const el = canvasRef.value\n if (!el || !renderer) return\n const rect = el.getBoundingClientRect()\n const ballRadius = rect.width * 0.36\n renderer.setPointer({\n x: (e.clientX - rect.left - rect.width / 2) / ballRadius,\n y: (e.clientY - rect.top - rect.height / 2) / ballRadius\n })\n}\n\nconst handlePointerLeave = () => renderer?.setPointer(null)\n\nconst handlePointerDown = () => renderer?.poke()\n\nconst applyVisibility = (visible: boolean) => {\n if (!renderer) return\n if (motionQuery?.matches) {\n // 减少动态偏好:只画一帧静态画面,不启动画循环\n renderer.stop()\n renderer.renderOnce()\n return\n }\n if (visible) renderer.start()\n else renderer.stop()\n}\n\nconst handleMotionChange = () => {\n // 偏好像素级开关变化时重评估(不可见时 start 也会被 IntersectionObserver 纠正)\n applyVisibility(true)\n}\n\nonMounted(() => {\n const canvas = canvasRef.value\n if (!canvas) return\n\n renderer = createOrbRenderer(canvas, { size: props.size, status: props.status })\n\n motionQuery = window.matchMedia?.('(prefers-reduced-motion: reduce)') ?? null\n motionQuery?.addEventListener?.('change', handleMotionChange)\n\n if (typeof IntersectionObserver !== 'undefined') {\n // 滚出视口的球暂停动画循环,长会话下同屏多个球也不累积开销\n observer = new IntersectionObserver((entries) => {\n applyVisibility(entries[0]?.isIntersecting ?? true)\n })\n observer.observe(canvas)\n } else {\n applyVisibility(true)\n }\n})\n\nwatch(\n () => props.status,\n (status) => renderer?.setStatus(status)\n)\n\nwatch(\n () => props.size,\n (size) => renderer?.resize(size)\n)\n\nconst react = (reaction: AiOrbReaction) => renderer?.trigger(reaction)\n\ndefineExpose<AiOrbExposed>({ react })\n\nonBeforeUnmount(() => {\n observer?.disconnect()\n motionQuery?.removeEventListener?.('change', handleMotionChange)\n renderer?.stop()\n renderer = null\n})\n</script>\n"],"mappings":";;;;;;;;;;;;;EAsBA,MAAM,QAAQ;EACd,MAAM,OAAO;EAEb,MAAM,MAAM,IAAI,QAAQ;EAExB,MAAM,YAAY,eAAkC,WAAW;EAE/D,IAAI,WAAiC;EACrC,IAAI,WAAwC;EAC5C,IAAI,cAAqC;;EAGzC,MAAM,qBAAqB,MAAoB;GAC7C,MAAM,KAAK,UAAU;GACrB,IAAI,CAAC,MAAM,CAAC,UAAU;GACtB,MAAM,OAAO,GAAG,sBAAsB;GACtC,MAAM,aAAa,KAAK,QAAQ;GAChC,SAAS,WAAW;IAClB,IAAI,EAAE,UAAU,KAAK,OAAO,KAAK,QAAQ,KAAK;IAC9C,IAAI,EAAE,UAAU,KAAK,MAAM,KAAK,SAAS,KAAK;GAChD,CAAC;EACH;EAEA,MAAM,2BAA2B,UAAU,WAAW,IAAI;EAE1D,MAAM,0BAA0B,UAAU,KAAK;EAE/C,MAAM,mBAAmB,YAAqB;GAC5C,IAAI,CAAC,UAAU;GACf,IAAI,aAAa,SAAS;IAExB,SAAS,KAAK;IACd,SAAS,WAAW;IACpB;GACF;GACA,IAAI,SAAS,SAAS,MAAM;QACvB,SAAS,KAAK;EACrB;EAEA,MAAM,2BAA2B;GAE/B,gBAAgB,IAAI;EACtB;EAEA,gBAAgB;GACd,MAAM,SAAS,UAAU;GACzB,IAAI,CAAC,QAAQ;GAEb,WAAW,kBAAkB,QAAQ;IAAE,MAAM,MAAM;IAAM,QAAQ,MAAM;GAAO,CAAC;GAE/E,cAAc,OAAO,aAAa,kCAAkC,KAAK;GACzE,aAAa,mBAAmB,UAAU,kBAAkB;GAE5D,IAAI,OAAO,yBAAyB,aAAa;IAE/C,WAAW,IAAI,sBAAsB,YAAY;KAC/C,gBAAgB,QAAQ,EAAE,EAAE,kBAAkB,IAAI;IACpD,CAAC;IACD,SAAS,QAAQ,MAAM;GACzB,OACE,gBAAgB,IAAI;EAExB,CAAC;EAED,YACQ,MAAM,SACX,WAAW,UAAU,UAAU,MAAM,CACxC;EAEA,YACQ,MAAM,OACX,SAAS,UAAU,OAAO,IAAI,CACjC;EAEA,MAAM,SAAS,aAA4B,UAAU,QAAQ,QAAQ;EAErE,SAA2B,EAAE,MAAM,CAAC;EAEpC,sBAAsB;GACpB,UAAU,WAAW;GACrB,aAAa,sBAAsB,UAAU,kBAAkB;GAC/D,UAAU,KAAK;GACf,WAAW;EACb,CAAC;;GAxGC,OAAAA,UAAA,GAAAC,mBASE,UAAA;IARI,SAAA;IAAJ,KAAI;IACH,OAAKC,eAAEC,MAAA,GAAA,CAAG,CAAC,CAAC;IACZ,OAAKC,eAAA;KAAA,OAAA,GAAc,QAAA,KAAI;KAAA,QAAA,GAAiB,QAAA,KAAI;IAAA,CAAA;IAC7C,eAAY;IACX,eAAa;IACb,gBAAc;IACd,eAAa;IACb,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,OAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@veltra/ai",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"homepage": "https://github.com/cabinet-fe/ultra-ui#readme",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/cabinet-fe/ultra-ui/issues"
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"markstream-vue": "^2.0.
|
|
39
|
+
"markstream-vue": "^2.0.17"
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|
|
42
42
|
"@veltra/compositions": "^1.7.14",
|
|
@@ -44,6 +44,6 @@
|
|
|
44
44
|
"@veltra/icons": "^1.5.0",
|
|
45
45
|
"@veltra/styles": "^1.7.14",
|
|
46
46
|
"@veltra/utils": "^1.7.14",
|
|
47
|
-
"vue": ">=3.5.
|
|
47
|
+
"vue": ">=3.5.43"
|
|
48
48
|
}
|
|
49
49
|
}
|