@huyooo/ai-chat-frontend-vue 0.2.13 → 0.2.14
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/README.md +85 -77
- package/dist/_baseUniq-Berj5otS.js +477 -0
- package/dist/arc-D4b-De5i.js +83 -0
- package/dist/architecture-U656AL7Q-B1R4o7wt.js +5 -0
- package/dist/architectureDiagram-VXUJARFQ-0H1LzSrf.js +4668 -0
- package/dist/blockDiagram-VD42YOAC-Dd6DMjLf.js +2265 -0
- package/dist/c4Diagram-YG6GDRKO-c6mpIonb.js +1581 -0
- package/dist/channel-CQSBI1fo.js +5 -0
- package/dist/chunk-4BX2VUAB-CfWC7lTG.js +9 -0
- package/dist/chunk-55IACEB6-joM8m2Zv.js +8 -0
- package/dist/chunk-B4BG7PRW-DN94Y3mS.js +1376 -0
- package/dist/chunk-DI55MBZ5-CPRGoFPG.js +1382 -0
- package/dist/chunk-FMBD7UC4-dUKl2TcN.js +19 -0
- package/dist/chunk-QN33PNHL-o3WmqquO.js +20 -0
- package/dist/chunk-QZHKN3VN-Btf62lRF.js +15 -0
- package/dist/chunk-TZMSLE5B-QCPpnm8Y.js +64 -0
- package/dist/classDiagram-2ON5EDUG-ERF9RE9K.js +16 -0
- package/dist/classDiagram-v2-WZHVMYZB-ERF9RE9K.js +16 -0
- package/dist/clone-B_B1yyxj.js +8 -0
- package/dist/components/ChatPanel.vue.d.ts +11 -7
- package/dist/components/ChatPanel.vue.d.ts.map +1 -1
- package/dist/components/common/SettingsPanel.vue.d.ts +10 -1
- package/dist/components/common/SettingsPanel.vue.d.ts.map +1 -1
- package/dist/components/common/ToggleSwitch.vue.d.ts.map +1 -1
- package/dist/components/input/ChatInput.vue.d.ts +4 -1
- package/dist/components/input/ChatInput.vue.d.ts.map +1 -1
- package/dist/components/input/DropdownSelector.vue.d.ts +10 -0
- package/dist/components/input/DropdownSelector.vue.d.ts.map +1 -1
- package/dist/components/message/MessageBubble.vue.d.ts +2 -2
- package/dist/components/message/MessageBubble.vue.d.ts.map +1 -1
- package/dist/components/message/PartsRenderer.vue.d.ts +6 -0
- package/dist/components/message/PartsRenderer.vue.d.ts.map +1 -1
- package/dist/components/message/parts/CollapsibleCard.vue.d.ts.map +1 -1
- package/dist/components/message/parts/ImagePart.vue.d.ts.map +1 -1
- package/dist/components/message/parts/SearchPart.vue.d.ts.map +1 -1
- package/dist/components/message/parts/TextPart.vue.d.ts +3 -1
- package/dist/components/message/parts/TextPart.vue.d.ts.map +1 -1
- package/dist/components/message/parts/ToolCallPart.vue.d.ts +9 -4
- package/dist/components/message/parts/ToolCallPart.vue.d.ts.map +1 -1
- package/dist/components/message/parts/index.d.ts +5 -1
- package/dist/components/message/parts/index.d.ts.map +1 -1
- package/dist/components/message/parts/visual-predicate.d.ts +7 -0
- package/dist/components/message/parts/visual-predicate.d.ts.map +1 -0
- package/dist/components/message/parts/visual-render.d.ts +2 -0
- package/dist/components/message/parts/visual-render.d.ts.map +1 -0
- package/dist/components/message/parts/visual.d.ts +3 -0
- package/dist/components/message/parts/visual.d.ts.map +1 -0
- package/dist/composables/useChat.d.ts +34 -10
- package/dist/composables/useChat.d.ts.map +1 -1
- package/dist/composables/useImageUpload.d.ts +7 -0
- package/dist/composables/useImageUpload.d.ts.map +1 -1
- package/dist/cose-bilkent-S5V4N54A-C-CkEnIH.js +2608 -0
- package/dist/cytoscape.esm-CjI2IsL8.js +18735 -0
- package/dist/dagre-6UL2VRFP-BbmZysl9.js +446 -0
- package/dist/defaultLocale-D7EN2tov.js +171 -0
- package/dist/diagram-PSM6KHXK-CMnope5e.js +533 -0
- package/dist/diagram-QEK2KX5R-Bc87skpo.js +221 -0
- package/dist/diagram-S2PKOQOG-BtkzC5-o.js +143 -0
- package/dist/erDiagram-Q2GNP2WA-CwQLdjh9.js +842 -0
- package/dist/flowDiagram-NV44I4VS-8ehMD0M4.js +1627 -0
- package/dist/ganttDiagram-JELNMOA3-dLX8Pbvy.js +2670 -0
- package/dist/gitGraph-F6HP7TQM-BBk5qfnx.js +5 -0
- package/dist/gitGraphDiagram-NY62KEGX-DG_fvWrq.js +712 -0
- package/dist/graph-B7BnGhuR.js +381 -0
- package/dist/index-Cr3tTH72.js +83883 -0
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -56433
- package/dist/info-NVLQJR56-eZr6nVr1.js +5 -0
- package/dist/infoDiagram-WHAUD3N6-BcVYTft_.js +24 -0
- package/dist/init-DjUOC4st.js +16 -0
- package/dist/journeyDiagram-XKPGCS4Q-D4C72gYB.js +834 -0
- package/dist/kanban-definition-3W4ZIXB7-BuSjAlm6.js +721 -0
- package/dist/layout-LjYblgUu.js +1441 -0
- package/dist/linear-DtPqj295.js +259 -0
- package/dist/mermaid-parser.core-DdOZxjrw.js +15189 -0
- package/dist/min-DYbgfxu0.js +38 -0
- package/dist/mindmap-definition-VGOIOE7T-BZxsr4M_.js +787 -0
- package/dist/ordinal-DfAQgscy.js +61 -0
- package/dist/packet-BFZMPI3H-XfNBjRzf.js +5 -0
- package/dist/pie-7BOR55EZ-pJDdOv19.js +5 -0
- package/dist/pieDiagram-ADFJNKIX-DDwfWBNn.js +161 -0
- package/dist/quadrantDiagram-AYHSOK5B-Ddpxuto0.js +1024 -0
- package/dist/radar-NHE76QYJ-jE07d-4Z.js +5 -0
- package/dist/requirementDiagram-UZGBJVZJ-B_TLWQ-N.js +852 -0
- package/dist/sankeyDiagram-TZEHDZUN-C1TWrUpG.js +810 -0
- package/dist/sequenceDiagram-WL72ISMW-B8pqb5YX.js +2518 -0
- package/dist/stateDiagram-FKZM4ZOC-DwNT1ej6.js +263 -0
- package/dist/stateDiagram-v2-4FDKWEC3-CZQUitzd.js +16 -0
- package/dist/style.css +1 -1
- package/dist/timeline-definition-IT6M3QCI-BGdS1wvf.js +799 -0
- package/dist/treemap-KMMF4GRG-B_eEU0Q4.js +5 -0
- package/dist/types/index.d.ts +36 -37
- package/dist/types/index.d.ts.map +1 -1
- package/dist/xychartDiagram-PRI3JC2R-DtwXQqN4.js +1340 -0
- package/package.json +3 -3
- package/src/components/ChatPanel.vue +75 -13
- package/src/components/common/SettingsPanel.vue +169 -0
- package/src/components/input/ChatInput.vue +49 -34
- package/src/components/input/DropdownSelector.vue +175 -18
- package/src/components/message/MessageBubble.vue +61 -40
- package/src/components/message/PartsRenderer.vue +106 -27
- package/src/components/message/parts/CollapsibleCard.vue +8 -3
- package/src/components/message/parts/ImagePart.vue +0 -1
- package/src/components/message/parts/SearchPart.vue +2 -0
- package/src/components/message/parts/TextPart.vue +753 -11
- package/src/components/message/parts/ToolCallPart.vue +315 -246
- package/src/components/message/parts/index.ts +5 -1
- package/src/components/message/parts/visual-predicate.ts +43 -0
- package/src/components/message/parts/visual-render.ts +19 -0
- package/src/components/message/parts/visual.ts +12 -0
- package/src/composables/useChat.ts +560 -92
- package/src/composables/useImageUpload.ts +115 -11
- package/src/index.ts +6 -8
- package/src/styles.css +124 -0
- package/src/types/index.ts +52 -44
- package/dist/components/message/blocks/CodeBlock.vue.d.ts +0 -11
- package/dist/components/message/blocks/CodeBlock.vue.d.ts.map +0 -1
- package/dist/components/message/blocks/TextBlock.vue.d.ts +0 -7
- package/dist/components/message/blocks/TextBlock.vue.d.ts.map +0 -1
- package/dist/components/message/blocks/index.d.ts +0 -6
- package/dist/components/message/blocks/index.d.ts.map +0 -1
- package/dist/components/message/parts/ToolResultPart.vue.d.ts +0 -14
- package/dist/components/message/parts/ToolResultPart.vue.d.ts.map +0 -1
- package/dist/components/message/tool-results/DefaultToolResult.vue.d.ts +0 -4
- package/dist/components/message/tool-results/DefaultToolResult.vue.d.ts.map +0 -1
- package/dist/components/message/tool-results/SearchResults.vue.d.ts +0 -4
- package/dist/components/message/tool-results/SearchResults.vue.d.ts.map +0 -1
- package/dist/components/message/tool-results/WeatherCard.vue.d.ts +0 -4
- package/dist/components/message/tool-results/WeatherCard.vue.d.ts.map +0 -1
- package/dist/components/message/tool-results/index.d.ts +0 -7
- package/dist/components/message/tool-results/index.d.ts.map +0 -1
- package/src/components/message/ContentRenderer.vue +0 -61
- package/src/components/message/ToolResultRenderer.vue +0 -27
- package/src/components/message/blocks/CodeBlock.vue +0 -113
- package/src/components/message/blocks/TextBlock.vue +0 -21
- package/src/components/message/blocks/index.ts +0 -6
- package/src/components/message/parts/ToolResultPart.vue +0 -78
- package/src/components/message/tool-results/DefaultToolResult.vue +0 -45
- package/src/components/message/tool-results/SearchResults.vue +0 -133
- package/src/components/message/tool-results/WeatherCard.vue +0 -139
- package/src/components/message/tool-results/index.ts +0 -7
|
@@ -7,65 +7,83 @@
|
|
|
7
7
|
:spinning="status === 'running'"
|
|
8
8
|
>
|
|
9
9
|
<template #header-actions>
|
|
10
|
-
<CopyButton :text="
|
|
10
|
+
<CopyButton :text="argsText" title="复制" />
|
|
11
11
|
</template>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
|
|
13
|
+
<!-- 参数内容 -->
|
|
14
|
+
<!-- ⚠️ 注意:<pre> 会原样渲染模板里的换行/缩进;因此 command/text 直接用 v-text,避免顶部出现“大空白” -->
|
|
15
|
+
<pre v-if="argsType === 'json'" class="tool-args json"><code v-html="highlightedJson"></code></pre>
|
|
16
|
+
<pre v-else class="tool-args" :class="argsType" v-text="argsText"></pre>
|
|
17
|
+
|
|
18
|
+
<!-- 输出面板(stdout/stderr) -->
|
|
19
|
+
<div v-if="hasOutput" class="output-panel">
|
|
20
|
+
<div class="output-header">
|
|
21
|
+
<div class="output-tabs">
|
|
22
|
+
<button
|
|
23
|
+
class="tab"
|
|
24
|
+
:class="{ active: activeStream === 'stdout' }"
|
|
25
|
+
type="button"
|
|
26
|
+
@click="activeStream = 'stdout'"
|
|
27
|
+
>
|
|
28
|
+
正常输出
|
|
29
|
+
</button>
|
|
30
|
+
<button
|
|
31
|
+
class="tab"
|
|
32
|
+
:class="{ active: activeStream === 'stderr' }"
|
|
33
|
+
type="button"
|
|
34
|
+
@click="activeStream = 'stderr'"
|
|
35
|
+
>
|
|
36
|
+
异常/告警输出
|
|
37
|
+
</button>
|
|
18
38
|
</div>
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
<span class="result-label">结果:</span>
|
|
26
|
-
<span class="result-name"></span>
|
|
39
|
+
<div class="output-actions">
|
|
40
|
+
<button class="btn btn-save" type="button" @click="saveLog">
|
|
41
|
+
<Icon icon="lucide:download" width="14" />
|
|
42
|
+
保存日志
|
|
43
|
+
</button>
|
|
44
|
+
<CopyButton :text="activeOutputText" title="复制输出" />
|
|
27
45
|
</div>
|
|
28
|
-
<code class="result-content">{{ formattedResult }}</code>
|
|
29
46
|
</div>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
</
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
47
|
+
<pre class="output-body chat-scrollbar"><code>{{ activeOutputText || '(无输出)' }}</code></pre>
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<!-- 底部操作区 -->
|
|
51
|
+
<div class="tool-footer">
|
|
52
|
+
<!-- 左侧:模式选择 -->
|
|
53
|
+
<DropdownSelector
|
|
54
|
+
:value="currentMode"
|
|
55
|
+
:options="modeOptions"
|
|
56
|
+
@select="handleModeChange"
|
|
57
|
+
/>
|
|
58
|
+
|
|
59
|
+
<!-- 右侧:状态或按钮 -->
|
|
60
|
+
<div class="tool-actions">
|
|
61
|
+
<!-- pending:跳过/运行按钮 -->
|
|
62
|
+
<template v-if="status === 'pending'">
|
|
63
|
+
<button class="btn btn-skip" @click="handleSkip">跳过</button>
|
|
64
|
+
<button class="btn btn-run" @click="handleRun">
|
|
65
|
+
运行
|
|
66
|
+
<Icon icon="lucide:arrow-right" width="14" />
|
|
67
|
+
</button>
|
|
68
|
+
</template>
|
|
69
|
+
|
|
70
|
+
<!-- running:取消按钮 -->
|
|
71
|
+
<template v-else-if="status === 'running'">
|
|
72
|
+
<span class="status-text running">
|
|
73
|
+
<Icon icon="lucide:loader-2" width="14" class="spinning" />
|
|
74
|
+
运行中
|
|
75
|
+
</span>
|
|
76
|
+
<button class="btn btn-cancel" @click="handleCancel">
|
|
77
|
+
<Icon icon="lucide:x" width="14" />
|
|
78
|
+
取消
|
|
79
|
+
</button>
|
|
80
|
+
</template>
|
|
81
|
+
|
|
82
|
+
<!-- 其他状态:只显示状态 -->
|
|
83
|
+
<span v-else :class="['status-text', status]">
|
|
84
|
+
<Icon :icon="statusDisplayIcon" width="14" />
|
|
85
|
+
{{ statusText }}
|
|
86
|
+
</span>
|
|
69
87
|
</div>
|
|
70
88
|
</div>
|
|
71
89
|
</CollapsibleCard>
|
|
@@ -77,24 +95,27 @@ import { Icon } from '@iconify/vue'
|
|
|
77
95
|
import DropdownSelector from '../../input/DropdownSelector.vue'
|
|
78
96
|
import CollapsibleCard from './CollapsibleCard.vue'
|
|
79
97
|
import CopyButton from '../../common/CopyButton.vue'
|
|
98
|
+
import { highlightCode } from '@huyooo/ai-chat-shared'
|
|
80
99
|
import type { ChatAdapter } from '../../../adapter'
|
|
81
100
|
import type { AutoRunConfig, AutoRunMode } from '@huyooo/ai-chat-bridge-electron/renderer'
|
|
82
101
|
import type { StepsExpandedType } from '../../../types'
|
|
83
102
|
|
|
84
|
-
|
|
103
|
+
const props = withDefaults(defineProps<{
|
|
85
104
|
id: string
|
|
86
105
|
name: string
|
|
87
106
|
args?: Record<string, unknown>
|
|
88
107
|
status: 'pending' | 'running' | 'done' | 'error' | 'cancelled' | 'skipped'
|
|
89
|
-
|
|
108
|
+
output?: {
|
|
109
|
+
stdout?: string
|
|
110
|
+
stderr?: string
|
|
111
|
+
}
|
|
90
112
|
expandedType?: StepsExpandedType
|
|
91
|
-
// 通过 props 传递,不使用 inject
|
|
92
113
|
adapter?: ChatAdapter
|
|
114
|
+
/** 取消工具调用(优先使用上层传入,确保能同步更新 UI 状态) */
|
|
115
|
+
onCancelToolCall?: (toolCallId: string) => void
|
|
93
116
|
autoRunConfig?: AutoRunConfig
|
|
94
117
|
onSaveConfig?: (config: AutoRunConfig) => Promise<void>
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const props = withDefaults(defineProps<Props>(), {
|
|
118
|
+
}>(), {
|
|
98
119
|
expandedType: 'auto',
|
|
99
120
|
})
|
|
100
121
|
|
|
@@ -104,25 +125,27 @@ const modeOptions = [
|
|
|
104
125
|
{ value: 'run-everything', label: '自动执行' },
|
|
105
126
|
]
|
|
106
127
|
|
|
107
|
-
// 当前模式:直接从配置读取
|
|
108
128
|
const currentMode = computed(() => props.autoRunConfig?.mode ?? 'run-everything')
|
|
109
129
|
|
|
110
|
-
//
|
|
111
|
-
const
|
|
130
|
+
// 参数类型:'json' | 'command' | 'text'
|
|
131
|
+
const argsType = computed(() => {
|
|
112
132
|
if (props.name === 'execute_command' && props.args?.command) {
|
|
113
|
-
|
|
114
|
-
const parts = cmd.trim().split(/\s+/)
|
|
115
|
-
return parts.slice(0, 3).join(' ') + (parts.length > 3 ? '...' : '')
|
|
133
|
+
return 'command'
|
|
116
134
|
}
|
|
117
|
-
|
|
135
|
+
if (props.args && Object.keys(props.args).length > 0) {
|
|
136
|
+
return 'json'
|
|
137
|
+
}
|
|
138
|
+
return 'text'
|
|
118
139
|
})
|
|
119
140
|
|
|
120
|
-
|
|
141
|
+
// 参数文本(格式化后的)
|
|
142
|
+
const argsText = computed(() => {
|
|
121
143
|
if (props.name === 'execute_command' && props.args?.command) {
|
|
122
144
|
return String(props.args.command)
|
|
123
145
|
}
|
|
124
146
|
if (props.args && Object.keys(props.args).length > 0) {
|
|
125
147
|
try {
|
|
148
|
+
// JSON 格式化:2 空格缩进
|
|
126
149
|
return JSON.stringify(props.args, null, 2)
|
|
127
150
|
} catch {
|
|
128
151
|
return String(props.args)
|
|
@@ -131,64 +154,62 @@ const commandText = computed(() => {
|
|
|
131
154
|
return props.name
|
|
132
155
|
})
|
|
133
156
|
|
|
134
|
-
//
|
|
135
|
-
const
|
|
136
|
-
if (
|
|
137
|
-
|
|
138
|
-
try {
|
|
139
|
-
return JSON.stringify(props.result, null, 2)
|
|
140
|
-
} catch {
|
|
141
|
-
return String(props.result)
|
|
157
|
+
// JSON 高亮后的 HTML
|
|
158
|
+
const highlightedJson = computed(() => {
|
|
159
|
+
if (argsType.value === 'json') {
|
|
160
|
+
return highlightCode(argsText.value, 'json')
|
|
142
161
|
}
|
|
162
|
+
return ''
|
|
143
163
|
})
|
|
144
164
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
165
|
+
const hasOutput = computed(() => {
|
|
166
|
+
return !!(props.output?.stdout || props.output?.stderr)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
const activeStream = ref<'stdout' | 'stderr'>('stdout')
|
|
170
|
+
|
|
171
|
+
const activeOutputText = computed(() => {
|
|
172
|
+
return activeStream.value === 'stdout'
|
|
173
|
+
? (props.output?.stdout ?? '')
|
|
174
|
+
: (props.output?.stderr ?? '')
|
|
175
|
+
})
|
|
151
176
|
|
|
152
|
-
|
|
177
|
+
// 折叠状态
|
|
178
|
+
const expanded = ref(
|
|
179
|
+
props.expandedType === 'open' ? true :
|
|
180
|
+
props.expandedType === 'close' ? false :
|
|
181
|
+
props.status === 'pending' || props.status === 'running'
|
|
182
|
+
)
|
|
153
183
|
|
|
154
|
-
// auto 模式下:状态变化时自动折叠/展开
|
|
155
184
|
watch(() => props.status, (newStatus) => {
|
|
156
185
|
if (props.expandedType === 'auto') {
|
|
157
186
|
expanded.value = newStatus === 'pending' || newStatus === 'running'
|
|
158
187
|
}
|
|
159
188
|
})
|
|
160
189
|
|
|
161
|
-
//
|
|
162
|
-
const statusIcon = computed(() => {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
pending: 'var(--chat-text-muted, #888)',
|
|
180
|
-
cancelled: 'var(--chat-warning, #f59e0b)',
|
|
181
|
-
skipped: 'var(--chat-text-muted, #888)',
|
|
182
|
-
}
|
|
183
|
-
return colors[props.status] ?? 'var(--chat-text-muted, #888)'
|
|
184
|
-
})
|
|
190
|
+
// 状态相关
|
|
191
|
+
const statusIcon = computed(() => ({
|
|
192
|
+
error: 'lucide:x-circle',
|
|
193
|
+
done: 'lucide:check-circle',
|
|
194
|
+
running: 'lucide:loader-2',
|
|
195
|
+
pending: 'lucide:clock',
|
|
196
|
+
cancelled: 'lucide:ban',
|
|
197
|
+
skipped: 'lucide:skip-forward',
|
|
198
|
+
}[props.status] ?? 'lucide:clock'))
|
|
199
|
+
|
|
200
|
+
const statusIconColor = computed(() => ({
|
|
201
|
+
error: 'var(--chat-error, #ef4444)',
|
|
202
|
+
done: 'var(--chat-success, #22c55e)',
|
|
203
|
+
running: 'var(--chat-accent, #3b82f6)',
|
|
204
|
+
pending: 'var(--chat-text-muted, #888)',
|
|
205
|
+
cancelled: 'var(--chat-warning, #f59e0b)',
|
|
206
|
+
skipped: 'var(--chat-text-muted, #888)',
|
|
207
|
+
}[props.status] ?? 'var(--chat-text-muted, #888)'))
|
|
185
208
|
|
|
186
209
|
const statusDisplayIcon = computed(() => {
|
|
187
210
|
const icons: Record<string, string> = {
|
|
188
211
|
done: 'lucide:check-circle-2',
|
|
189
212
|
error: 'lucide:x-circle',
|
|
190
|
-
running: 'lucide:loader-2',
|
|
191
|
-
pending: 'lucide:clock',
|
|
192
213
|
cancelled: 'lucide:ban',
|
|
193
214
|
skipped: 'lucide:skip-forward',
|
|
194
215
|
}
|
|
@@ -199,8 +220,6 @@ const statusText = computed(() => {
|
|
|
199
220
|
const texts: Record<string, string> = {
|
|
200
221
|
done: '成功',
|
|
201
222
|
error: '错误',
|
|
202
|
-
running: '运行中',
|
|
203
|
-
pending: '等待中',
|
|
204
223
|
cancelled: '已取消',
|
|
205
224
|
skipped: '已跳过',
|
|
206
225
|
}
|
|
@@ -208,169 +227,169 @@ const statusText = computed(() => {
|
|
|
208
227
|
})
|
|
209
228
|
|
|
210
229
|
const title = computed(() => {
|
|
211
|
-
const
|
|
230
|
+
const suffix = {
|
|
212
231
|
pending: ' - 等待确认',
|
|
213
232
|
running: ' - 执行中...',
|
|
214
233
|
error: ' - 执行失败',
|
|
215
234
|
done: ' - 执行完成',
|
|
216
235
|
cancelled: ' - 已取消',
|
|
217
236
|
skipped: ' - 已跳过',
|
|
218
|
-
}
|
|
219
|
-
return props.name +
|
|
237
|
+
}[props.status] ?? ''
|
|
238
|
+
return props.name + suffix
|
|
220
239
|
})
|
|
221
240
|
|
|
222
|
-
|
|
241
|
+
// 操作
|
|
223
242
|
async function handleModeChange(value: string) {
|
|
224
243
|
if (!props.onSaveConfig || !props.autoRunConfig) return
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
// 直接修改引用类型的属性
|
|
228
|
-
props.autoRunConfig.mode = value as AutoRunMode
|
|
229
|
-
// 保存到数据库
|
|
230
|
-
await props.onSaveConfig(props.autoRunConfig)
|
|
231
|
-
} catch (error) {
|
|
232
|
-
console.error('[ToolCallPart] 保存配置失败:', error)
|
|
233
|
-
}
|
|
244
|
+
props.autoRunConfig.mode = value as AutoRunMode
|
|
245
|
+
await props.onSaveConfig(props.autoRunConfig)
|
|
234
246
|
}
|
|
235
247
|
|
|
236
|
-
/** 跳过执行:只拒绝当前工具,AI 继续思考 */
|
|
237
248
|
async function handleSkip() {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
try {
|
|
241
|
-
// 只拒绝工具执行,不取消整个请求
|
|
242
|
-
// AI 会收到"用户跳过了此工具"的反馈,然后可能:
|
|
243
|
-
// 1. 尝试其他工具/方法
|
|
244
|
-
// 2. 直接回复用户
|
|
245
|
-
// 3. 询问用户想要什么
|
|
246
|
-
await props.adapter.respondToolApproval(props.id, false)
|
|
247
|
-
} catch (error) {
|
|
248
|
-
console.error('[ToolCallPart] 跳过失败:', error)
|
|
249
|
-
}
|
|
249
|
+
await props.adapter?.respondToolApproval?.(props.id, false)
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
/** 运行执行 */
|
|
253
252
|
async function handleRun() {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
await props.adapter.respondToolApproval(props.id, true)
|
|
258
|
-
} catch (error) {
|
|
259
|
-
console.error('[ToolCallPart] 运行失败:', error)
|
|
260
|
-
}
|
|
253
|
+
await props.adapter?.respondToolApproval?.(props.id, true)
|
|
261
254
|
}
|
|
262
255
|
|
|
263
|
-
/** 取消执行 */
|
|
264
256
|
function handleCancel() {
|
|
257
|
+
if (props.onCancelToolCall) {
|
|
258
|
+
props.onCancelToolCall(props.id)
|
|
259
|
+
return
|
|
260
|
+
}
|
|
265
261
|
props.adapter?.cancel?.()
|
|
266
262
|
}
|
|
267
263
|
|
|
268
|
-
|
|
264
|
+
function saveLog() {
|
|
265
|
+
const stdout = props.output?.stdout ?? ''
|
|
266
|
+
const stderr = props.output?.stderr ?? ''
|
|
267
|
+
const content =
|
|
268
|
+
`# tool: ${props.name}\n` +
|
|
269
|
+
`# id: ${props.id}\n` +
|
|
270
|
+
`# time: ${new Date().toISOString()}\n\n` +
|
|
271
|
+
`===== STDOUT =====\n${stdout}\n\n` +
|
|
272
|
+
`===== STDERR =====\n${stderr}\n`
|
|
273
|
+
|
|
274
|
+
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' })
|
|
275
|
+
const url = URL.createObjectURL(blob)
|
|
276
|
+
const a = document.createElement('a')
|
|
277
|
+
a.href = url
|
|
278
|
+
a.download = `tool-${props.name}-${props.id}.log`
|
|
279
|
+
document.body.appendChild(a)
|
|
280
|
+
a.click()
|
|
281
|
+
document.body.removeChild(a)
|
|
282
|
+
URL.revokeObjectURL(url)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// 模式切换时自动执行
|
|
269
286
|
watch(currentMode, (newMode, oldMode) => {
|
|
270
287
|
if (oldMode === 'manual' && newMode === 'run-everything' && props.status === 'pending') {
|
|
271
288
|
handleRun()
|
|
272
289
|
}
|
|
273
290
|
})
|
|
274
|
-
|
|
275
291
|
</script>
|
|
276
292
|
|
|
277
293
|
<style scoped>
|
|
278
|
-
.tool-
|
|
279
|
-
|
|
280
|
-
|
|
294
|
+
.tool-args {
|
|
295
|
+
margin: 0 0 12px 0;
|
|
296
|
+
padding: 0.75em 1em;
|
|
297
|
+
background: var(--chat-bg, #1e1e1e);
|
|
298
|
+
border: 1px solid var(--chat-border, rgba(255, 255, 255, 0.08));
|
|
299
|
+
border-radius: 8px;
|
|
300
|
+
font-size: 13px;
|
|
301
|
+
line-height: 1.6;
|
|
302
|
+
color: var(--chat-text-muted, #999);
|
|
303
|
+
white-space: pre-wrap;
|
|
304
|
+
word-break: break-word;
|
|
305
|
+
overflow-x: auto;
|
|
281
306
|
}
|
|
282
307
|
|
|
283
|
-
|
|
284
|
-
|
|
308
|
+
/* 让 <code> 继承父级字体(避免默认 monospace 影响 text 类型的展示) */
|
|
309
|
+
.tool-args code {
|
|
310
|
+
font-family: inherit;
|
|
311
|
+
font-size: inherit;
|
|
285
312
|
}
|
|
286
313
|
|
|
287
|
-
|
|
288
|
-
.
|
|
289
|
-
|
|
290
|
-
margin-bottom: 8px;
|
|
291
|
-
padding: 0;
|
|
292
|
-
cursor: default;
|
|
293
|
-
user-select: none;
|
|
314
|
+
/* JSON 格式:使用等宽字体,更好的可读性,支持横向滚动 */
|
|
315
|
+
.tool-args.json {
|
|
316
|
+
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
|
|
294
317
|
font-size: 12px;
|
|
295
|
-
line-height: 1.
|
|
318
|
+
line-height: 1.8;
|
|
319
|
+
white-space: pre;
|
|
320
|
+
word-break: normal;
|
|
321
|
+
overflow-x: auto;
|
|
296
322
|
}
|
|
297
323
|
|
|
298
|
-
|
|
299
|
-
.
|
|
300
|
-
|
|
301
|
-
.result-label:hover,
|
|
302
|
-
.command-name:hover,
|
|
303
|
-
.result-name:hover {
|
|
304
|
-
background: transparent !important;
|
|
324
|
+
/* JSON 横向滚动条样式(与全局 chat-scrollbar 对齐) */
|
|
325
|
+
.tool-args.json::-webkit-scrollbar {
|
|
326
|
+
height: 6px;
|
|
305
327
|
}
|
|
306
328
|
|
|
307
|
-
.
|
|
308
|
-
|
|
309
|
-
padding: 10px;
|
|
310
|
-
/* 浅色主题下 rgba(0,0,0,0.3) 会显得“脏黑”,这里统一走 code token */
|
|
311
|
-
background: var(--chat-code-bg, rgba(0, 0, 0, 0.3));
|
|
312
|
-
border: 1px solid var(--chat-border, #333);
|
|
313
|
-
border-radius: 6px;
|
|
314
|
-
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
|
|
315
|
-
font-size: 13px;
|
|
316
|
-
color: var(--chat-code-text, var(--chat-text, #ccc));
|
|
317
|
-
white-space: pre-wrap;
|
|
318
|
-
word-break: break-all;
|
|
319
|
-
overflow-x: auto;
|
|
329
|
+
.tool-args.json::-webkit-scrollbar-track {
|
|
330
|
+
background: transparent;
|
|
320
331
|
}
|
|
321
332
|
|
|
322
|
-
.tool-
|
|
323
|
-
|
|
333
|
+
.tool-args.json::-webkit-scrollbar-thumb {
|
|
334
|
+
background: var(--chat-scrollbar, rgba(255, 255, 255, 0.2));
|
|
335
|
+
border-radius: 3px;
|
|
324
336
|
}
|
|
325
337
|
|
|
326
|
-
.
|
|
327
|
-
|
|
328
|
-
color: var(--chat-text-muted, #888);
|
|
329
|
-
font-weight: 500;
|
|
338
|
+
.tool-args.json::-webkit-scrollbar-thumb:hover {
|
|
339
|
+
background: var(--chat-scrollbar-hover, rgba(255, 255, 255, 0.3));
|
|
330
340
|
}
|
|
331
341
|
|
|
332
|
-
|
|
333
|
-
.
|
|
334
|
-
|
|
342
|
+
/* JSON 语法高亮样式(使用 CSS 变量,跟随主题) */
|
|
343
|
+
.tool-args.json :deep(.hljs-attr),
|
|
344
|
+
.tool-args.json :deep(.hljs-attribute) {
|
|
345
|
+
color: var(--chat-hljs-attr, #79c0ff); /* 键名 */
|
|
335
346
|
}
|
|
336
347
|
|
|
337
|
-
.
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
348
|
+
.tool-args.json :deep(.hljs-string),
|
|
349
|
+
.tool-args.json :deep(.hljs-template-variable) {
|
|
350
|
+
color: var(--chat-hljs-string, #a5d6ff); /* 字符串值 */
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.tool-args.json :deep(.hljs-number) {
|
|
354
|
+
color: var(--chat-hljs-number, #79c0ff); /* 数字 */
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.tool-args.json :deep(.hljs-literal),
|
|
358
|
+
.tool-args.json :deep(.hljs-null) {
|
|
359
|
+
color: var(--chat-hljs-literal, #79c0ff); /* true, false, null */
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
.tool-args.json :deep(.hljs-punctuation) {
|
|
363
|
+
color: var(--chat-hljs-punctuation, #e6edf3); /* 括号、逗号等 */
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/* 命令格式:使用等宽字体 */
|
|
367
|
+
.tool-args.command {
|
|
344
368
|
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
|
|
369
|
+
font-size: 12px;
|
|
370
|
+
line-height: 1.6;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/* 普通文本:保持默认字体 */
|
|
374
|
+
.tool-args.text {
|
|
345
375
|
font-size: 13px;
|
|
346
|
-
|
|
347
|
-
white-space: pre-wrap;
|
|
348
|
-
word-break: break-all;
|
|
349
|
-
overflow-x: auto;
|
|
376
|
+
line-height: 1.5;
|
|
350
377
|
}
|
|
351
378
|
|
|
352
|
-
.tool-
|
|
379
|
+
.tool-footer {
|
|
353
380
|
display: flex;
|
|
354
381
|
align-items: center;
|
|
355
382
|
justify-content: space-between;
|
|
356
383
|
gap: 12px;
|
|
357
|
-
padding-top: 12px;
|
|
358
|
-
border-top: 1px solid var(--chat-border, #333);
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
.footer-left {
|
|
362
|
-
flex-shrink: 0;
|
|
363
|
-
position: relative;
|
|
364
|
-
z-index: 10;
|
|
365
384
|
}
|
|
366
385
|
|
|
367
|
-
.
|
|
386
|
+
.tool-actions {
|
|
368
387
|
display: flex;
|
|
369
388
|
align-items: center;
|
|
370
|
-
gap:
|
|
389
|
+
gap: 8px;
|
|
371
390
|
}
|
|
372
391
|
|
|
373
|
-
.
|
|
392
|
+
.status-text {
|
|
374
393
|
display: flex;
|
|
375
394
|
align-items: center;
|
|
376
395
|
gap: 6px;
|
|
@@ -378,29 +397,11 @@ watch(currentMode, (newMode, oldMode) => {
|
|
|
378
397
|
font-weight: 500;
|
|
379
398
|
}
|
|
380
399
|
|
|
381
|
-
.
|
|
382
|
-
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
.
|
|
386
|
-
color: #ef4444;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
.execution-status.running {
|
|
390
|
-
color: var(--chat-accent, #3b82f6);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
.execution-status.pending {
|
|
394
|
-
color: var(--chat-text-muted, #888);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
.execution-status.cancelled {
|
|
398
|
-
color: var(--chat-warning, #f59e0b);
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
.execution-status.skipped {
|
|
402
|
-
color: var(--chat-text-muted, #888);
|
|
403
|
-
}
|
|
400
|
+
.status-text.done { color: var(--chat-success, #22c55e); }
|
|
401
|
+
.status-text.error { color: var(--chat-error, #ef4444); }
|
|
402
|
+
.status-text.running { color: var(--chat-accent, #3b82f6); }
|
|
403
|
+
.status-text.cancelled { color: var(--chat-warning, #f59e0b); }
|
|
404
|
+
.status-text.skipped { color: var(--chat-text-muted, #888); }
|
|
404
405
|
|
|
405
406
|
.spinning {
|
|
406
407
|
animation: spin 1s linear infinite;
|
|
@@ -411,11 +412,6 @@ watch(currentMode, (newMode, oldMode) => {
|
|
|
411
412
|
to { transform: rotate(360deg); }
|
|
412
413
|
}
|
|
413
414
|
|
|
414
|
-
.action-buttons {
|
|
415
|
-
display: flex;
|
|
416
|
-
gap: 8px;
|
|
417
|
-
}
|
|
418
|
-
|
|
419
415
|
.btn {
|
|
420
416
|
padding: 6px 12px;
|
|
421
417
|
border-radius: 6px;
|
|
@@ -434,7 +430,6 @@ watch(currentMode, (newMode, oldMode) => {
|
|
|
434
430
|
color: var(--chat-text, #ccc);
|
|
435
431
|
border: 1px solid var(--chat-border, #333);
|
|
436
432
|
}
|
|
437
|
-
|
|
438
433
|
.btn-skip:hover {
|
|
439
434
|
background: var(--chat-muted-hover, #333);
|
|
440
435
|
}
|
|
@@ -443,7 +438,6 @@ watch(currentMode, (newMode, oldMode) => {
|
|
|
443
438
|
background: var(--chat-accent, #3b82f6);
|
|
444
439
|
color: #fff;
|
|
445
440
|
}
|
|
446
|
-
|
|
447
441
|
.btn-run:hover {
|
|
448
442
|
background: var(--chat-accent-hover, #2563eb);
|
|
449
443
|
}
|
|
@@ -453,10 +447,85 @@ watch(currentMode, (newMode, oldMode) => {
|
|
|
453
447
|
color: var(--chat-text-muted, #888);
|
|
454
448
|
border: 1px solid var(--chat-border, #333);
|
|
455
449
|
}
|
|
456
|
-
|
|
457
450
|
.btn-cancel:hover {
|
|
458
451
|
background: rgba(239, 68, 68, 0.1);
|
|
459
452
|
color: #ef4444;
|
|
460
453
|
border-color: #ef4444;
|
|
461
454
|
}
|
|
455
|
+
|
|
456
|
+
.output-panel {
|
|
457
|
+
margin: 0 0 12px 0;
|
|
458
|
+
border: 1px solid var(--chat-border, rgba(255, 255, 255, 0.08));
|
|
459
|
+
border-radius: 8px;
|
|
460
|
+
background: var(--chat-bg, #1e1e1e);
|
|
461
|
+
overflow: hidden;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.output-header {
|
|
465
|
+
display: flex;
|
|
466
|
+
align-items: center;
|
|
467
|
+
justify-content: space-between;
|
|
468
|
+
gap: 12px;
|
|
469
|
+
padding: 8px 10px;
|
|
470
|
+
border-bottom: 1px solid var(--chat-border, rgba(255, 255, 255, 0.08));
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
.output-tabs {
|
|
474
|
+
display: flex;
|
|
475
|
+
gap: 6px;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.tab {
|
|
479
|
+
padding: 4px 8px;
|
|
480
|
+
border-radius: 6px;
|
|
481
|
+
border: 1px solid var(--chat-border, #333);
|
|
482
|
+
background: var(--chat-muted, #2a2a2a);
|
|
483
|
+
color: var(--chat-text-muted, #999);
|
|
484
|
+
font-size: 11px;
|
|
485
|
+
cursor: pointer;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.tab.active {
|
|
489
|
+
background: rgba(59, 130, 246, 0.15);
|
|
490
|
+
border-color: rgba(59, 130, 246, 0.35);
|
|
491
|
+
color: var(--chat-text, #ccc);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.output-actions {
|
|
495
|
+
display: flex;
|
|
496
|
+
align-items: center;
|
|
497
|
+
gap: 8px;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.btn-save {
|
|
501
|
+
padding: 6px 10px;
|
|
502
|
+
border-radius: 6px;
|
|
503
|
+
font-size: 12px;
|
|
504
|
+
font-weight: 500;
|
|
505
|
+
cursor: pointer;
|
|
506
|
+
border: 1px solid var(--chat-border, #333);
|
|
507
|
+
background: transparent;
|
|
508
|
+
color: var(--chat-text-muted, #888);
|
|
509
|
+
display: flex;
|
|
510
|
+
align-items: center;
|
|
511
|
+
gap: 6px;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
.btn-save:hover {
|
|
515
|
+
background: var(--chat-muted-hover, #333);
|
|
516
|
+
color: var(--chat-text, #ccc);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
.output-body {
|
|
520
|
+
margin: 0;
|
|
521
|
+
padding: 10px 12px;
|
|
522
|
+
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
|
|
523
|
+
font-size: 12px;
|
|
524
|
+
line-height: 1.6;
|
|
525
|
+
color: var(--chat-text-muted, #999);
|
|
526
|
+
white-space: pre-wrap;
|
|
527
|
+
word-break: break-word;
|
|
528
|
+
max-height: 260px;
|
|
529
|
+
overflow: auto;
|
|
530
|
+
}
|
|
462
531
|
</style>
|