@vyr/service-chat 0.0.34

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.
@@ -0,0 +1,72 @@
1
+ <template>
2
+ <div class="executor-header">
3
+ <svg width="28" height="28" viewBox="0 0 24 24" class="loading-icon" :class="{ spinning }">
4
+ <circle cx="12" cy="12" r="10" stroke="#0078d4" stroke-width="2" fill="none" stroke-dasharray="30 30" />
5
+ <circle cx="12" cy="6" r="2" fill="#0078d4" />
6
+ </svg>
7
+ <span class="header-title">{{ title }}</span>
8
+ <span class="status-badge">
9
+ {{ language.get('executor.header.status', { completed, total }) }}
10
+ </span>
11
+ </div>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { language } from '../../locale/Language'
16
+
17
+ interface Props {
18
+ title?: string
19
+ spinning?: boolean
20
+ completed: number
21
+ total: number
22
+ }
23
+
24
+ defineProps<Props>()
25
+ </script>
26
+
27
+ <style scoped>
28
+ @keyframes spin {
29
+ 0% {
30
+ transform: rotate(0);
31
+ }
32
+
33
+ 100% {
34
+ transform: rotate(360deg);
35
+ }
36
+ }
37
+
38
+ .executor-header {
39
+ display: flex;
40
+ align-items: center;
41
+ gap: 12px;
42
+ margin-bottom: 20px;
43
+ padding-bottom: 16px;
44
+ border-bottom: 2px solid #3c3c3d;
45
+ }
46
+
47
+ .loading-icon {
48
+ transform: rotate(96deg);
49
+ transition: all 0.3s ease;
50
+ }
51
+
52
+ .loading-icon.spinning {
53
+ animation: spin 2s linear infinite;
54
+ }
55
+
56
+ .header-title {
57
+ font-size: 18px;
58
+ font-weight: 600;
59
+ color: #cccccc;
60
+ letter-spacing: 0.5px;
61
+ }
62
+
63
+ .status-badge {
64
+ background: #0078d4;
65
+ color: #ffffff;
66
+ padding: 4px 12px;
67
+ border-radius: 30px;
68
+ font-size: 12px;
69
+ font-weight: 500;
70
+ margin-left: auto;
71
+ }
72
+ </style>
@@ -0,0 +1,104 @@
1
+ <template>
2
+ <div class="gear-container">
3
+ <svg viewBox="200 200 624 624" version="1.1" xmlns="http://www.w3.org/2000/svg" :width="size" :height="size"
4
+ :class="{ spinning: animate }">
5
+ <path
6
+ d="M512 225.21821213a26.07107163 26.07107163 0 0 1 26.07107163 26.07107163v78.21321487a26.07107163 26.07107163 0 1 1-52.14214326 0V251.28928375a26.07107163 26.07107163 0 0 1 26.07107163-26.07107162z"
7
+ :fill="colors[0]" />
8
+ <path
9
+ d="M655.39089394 263.64045393a26.07107163 26.07107163 0 0 1 9.54852998 35.61960161l-39.10660743 67.75219738a26.1362493 26.1362493 0 0 1-43.37574543 2.93299557 26.07107163 26.07107163 0 0 1-1.79238618-29.00406719l39.10660745-67.78478622A26.07107163 26.07107163 0 0 1 655.39089394 263.67304277z"
10
+ :fill="colors[1]" />
11
+ <path
12
+ d="M760.35954607 368.60910606a26.07107163 26.07107163 0 0 1-9.54852999 35.61960161l-67.71960855 39.10660745a26.07107163 26.07107163 0 0 1-26.07107161-45.16813161l67.71960854-39.10660743a26.07107163 26.07107163 0 0 1 35.61960161 9.54852998z"
13
+ :fill="colors[2]" />
14
+ <path
15
+ d="M798.78178787 512a26.07107163 26.07107163 0 0 1-26.07107163 26.07107163h-78.21321487a26.07107163 26.07107163 0 0 1 0-52.14214326H772.71071625a26.07107163 26.07107163 0 0 1 26.07107162 26.07107163z"
16
+ :fill="colors[3]" />
17
+ <path
18
+ d="M760.35954607 655.39089394a26.07107163 26.07107163 0 0 1-35.61960161 9.54852998l-67.71960854-39.10660743a26.10366046 26.10366046 0 0 1 6.19187951-47.90559412 26.07107163 26.07107163 0 0 1 19.8791921 2.73746251l67.71960855 39.10660745a26.07107163 26.07107163 0 0 1 9.54852999 35.61960161z"
19
+ :fill="colors[4]" />
20
+ <path
21
+ d="M655.39089394 760.35954607a26.07107163 26.07107163 0 0 1-35.61960161-9.54852999l-39.10660745-67.71960855a26.07107163 26.07107163 0 0 1 45.16813161-26.07107161l39.10660743 67.71960854a26.07107163 26.07107163 0 0 1-9.54852998 35.61960161z"
22
+ :fill="colors[5]" />
23
+ <path
24
+ d="M512 798.78178787a26.07107163 26.07107163 0 0 1-26.07107163-26.07107163v-78.21321487a26.07107163 26.07107163 0 0 1 52.14214326 0V772.71071625a26.07107163 26.07107163 0 0 1-26.07107163 26.07107162z"
25
+ :fill="colors[6]" />
26
+ <path
27
+ d="M368.60910606 760.35954607a26.07107163 26.07107163 0 0 1-9.54852998-35.61960161l39.10660743-67.71960854a26.07107163 26.07107163 0 1 1 45.16813161 26.07107161l-39.10660745 67.71960855a26.07107163 26.07107163 0 0 1-35.61960161 9.54852999z"
28
+ :fill="colors[5]" />
29
+ <path
30
+ d="M263.64045393 655.39089394a26.07107163 26.07107163 0 0 1 9.54852999-35.61960161l67.75219738-39.10660745a26.07107163 26.07107163 0 0 1 26.07107162 45.16813161l-67.78478622 39.10660743A26.07107163 26.07107163 0 0 1 263.67304277 655.39089394z"
31
+ :fill="colors[4]" />
32
+ <path
33
+ d="M225.21821213 512a26.07107163 26.07107163 0 0 1 26.07107163-26.07107163h78.21321487a26.07107163 26.07107163 0 0 1 0 52.14214326H251.28928375a26.07107163 26.07107163 0 0 1-26.07107162-26.07107163z"
34
+ :fill="colors[3]" />
35
+ <path
36
+ d="M263.64045393 368.60910606a26.07107163 26.07107163 0 0 1 35.61960161-9.54852998l67.75219738 39.10660743a26.07107163 26.07107163 0 1 1-26.07107162 45.16813161l-67.78478622-39.10660745A26.07107163 26.07107163 0 0 1 263.67304277 368.60910606z"
37
+ :fill="colors[2]" />
38
+ <path
39
+ d="M368.60910606 263.64045393a26.07107163 26.07107163 0 0 1 35.61960161 9.54852999l39.10660745 67.75219738a26.07107163 26.07107163 0 0 1-45.16813161 26.07107162l-39.10660743-67.78478622A26.07107163 26.07107163 0 0 1 368.60910606 263.67304277z"
40
+ :fill="colors[1]" />
41
+ </svg>
42
+ </div>
43
+ </template>
44
+
45
+ <script setup lang="ts">
46
+
47
+ interface Props {
48
+ size?: number
49
+ animate?: boolean
50
+ text?: string
51
+ colors?: string[]
52
+ }
53
+
54
+ const props = withDefaults(defineProps<Props>(), {
55
+ size: 48,
56
+ animate: true,
57
+ colors: () => [
58
+ '#0078d4', // 最深
59
+ '#1e4a6b',
60
+ '#2a5f8a',
61
+ '#3575a8',
62
+ '#408ac7',
63
+ '#4ba0e6',
64
+ '#56b5ff' // 最浅
65
+ ]
66
+ })
67
+ </script>
68
+
69
+ <style scoped>
70
+ @keyframes spin {
71
+ 100% {
72
+ transform: rotate(360deg);
73
+ }
74
+ }
75
+
76
+ .gear-container {
77
+ display: flex;
78
+ flex-direction: column;
79
+ align-items: center;
80
+ justify-content: center;
81
+ padding: 32px 16px;
82
+ min-height: 150px;
83
+ border-radius: 0 0 12px 12px;
84
+ }
85
+
86
+ .gear-container svg {
87
+ transition: opacity 0.2s ease;
88
+ }
89
+
90
+ .gear-container svg.spinning {
91
+ animation: spin 3s linear infinite;
92
+ }
93
+
94
+ .gear-container svg:hover {
95
+ opacity: 1;
96
+ }
97
+
98
+ .gear-text {
99
+ margin-top: 12px;
100
+ font-size: 11px;
101
+ color: #6b6b6d;
102
+ letter-spacing: 0.5px;
103
+ }
104
+ </style>
@@ -0,0 +1,243 @@
1
+ <template>
2
+ <div class="param-section" :class="[type, statusClass]">
3
+ <div class="param-header">
4
+ <span class="param-title">
5
+ <svg v-if="type === 'input'" viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14">
6
+ <path d="M20 12H4M12 4v16" stroke-width="2" stroke-linecap="round" />
7
+ <path d="M16 8l4 4-4 4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
8
+ </svg>
9
+ <svg v-else-if="type === 'output'" viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14">
10
+ <rect x="4" y="4" width="16" height="16" rx="2" stroke-width="2" />
11
+ <path d="M8 8h8v8H8z" stroke-width="2" stroke-linecap="round" />
12
+ <path v-if="state === 'success'" d="M20 6L9 17l-5-5" stroke-width="2" stroke="#2e7d32" stroke-linecap="round"
13
+ stroke-linejoin="round" />
14
+ <circle v-else-if="state === 'failed'" cx="12" cy="12" r="10" stroke="#f44336" stroke-width="2"
15
+ fill="none" />
16
+ <path v-else-if="state === 'running'" d="M12 8v4M12 16h.01" stroke="#f44336" stroke-width="2"
17
+ stroke-linecap="round" />
18
+ </svg>
19
+ <svg v-else-if="type === 'error'" viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14">
20
+ <rect x="4" y="4" width="16" height="16" rx="2" stroke-width="2" />
21
+ <path d="M8 8h8v8H8z" stroke-width="2" stroke-linecap="round" />
22
+ <circle cx="12" cy="12" r="10" stroke="#f44336" stroke-width="2" fill="none" />
23
+ <path d="M12 8v4M12 16h.01" stroke="#f44336" stroke-width="2" stroke-linecap="round" />
24
+ </svg>
25
+ {{ title }}
26
+ </span>
27
+ <div class="param-actions">
28
+ <slot name="actions">
29
+ <button v-if="!hideCopy" class="copy-btn" @click="handleCopy">
30
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14">
31
+ <path d="M16 3H5C4.46957 3 3.96086 3.21071 3.58579 3.58579C3.21071 3.96086 3 4.46957 3 5V16"
32
+ stroke-width="2" />
33
+ <rect x="8" y="8" width="13" height="13" rx="2" stroke-width="2" />
34
+ </svg>
35
+ </button>
36
+ </slot>
37
+ </div>
38
+ </div>
39
+ <div v-if="loading" class="loading-placeholder">
40
+ <GearLoading :size="32" :show-text="false" />
41
+ </div>
42
+ <vyr-scroll v-else class="param-scroll" :padding="8" :margin="8">
43
+ <pre class="json-viewer"><code>{{ formattedContent }}</code></pre>
44
+ </vyr-scroll>
45
+ </div>
46
+ </template>
47
+
48
+ <script setup lang="ts">
49
+ import { computed } from 'vue'
50
+ import { TaskState, ParamSectionType } from './types'
51
+ import { VyrScroll } from '@vyr/design'
52
+ import GearLoading from './GearLoading.vue'
53
+
54
+ interface Props {
55
+ type: ParamSectionType
56
+ title: string
57
+ content?: any
58
+ state?: TaskState
59
+ loading?: boolean
60
+ hideCopy?: boolean
61
+ formatter?: (content: any) => string
62
+ }
63
+
64
+ const props = withDefaults(defineProps<Props>(), {
65
+ content: null,
66
+ state: undefined,
67
+ loading: false,
68
+ hideCopy: false,
69
+ formatter: undefined
70
+ })
71
+
72
+ const emit = defineEmits<{
73
+ (e: 'copy'): void
74
+ }>()
75
+
76
+ const statusClass = computed(() => {
77
+ if (!props.state) return ''
78
+ return props.state
79
+ })
80
+
81
+ const formattedContent = computed(() => {
82
+ if (props.formatter) {
83
+ return props.formatter(props.content)
84
+ }
85
+ if (props.content === undefined || props.content === null) return 'null'
86
+ if (typeof props.content === 'string') {
87
+ try {
88
+ const parsed = JSON.parse(props.content)
89
+ return JSON.stringify(parsed, null, 2)
90
+ } catch {
91
+ return props.content
92
+ }
93
+ }
94
+ try {
95
+ return JSON.stringify(props.content, null, 2)
96
+ } catch {
97
+ return String(props.content)
98
+ }
99
+ })
100
+
101
+ const handleCopy = () => {
102
+ emit('copy')
103
+ }
104
+ </script>
105
+
106
+ <style scoped>
107
+ .param-section {
108
+ border-radius: 12px;
109
+ border: 1px solid #3c3c3d;
110
+ overflow: hidden;
111
+ }
112
+
113
+ .param-section.running .param-header {
114
+ background: #2a2d2e;
115
+ }
116
+
117
+ .param-section.running .param-title {
118
+ color: #0078d4;
119
+ }
120
+
121
+ .param-section.failed .param-header {
122
+ border-left: 4px solid #f44336;
123
+ }
124
+
125
+ .param-section.failed .param-title {
126
+ color: #f44336;
127
+ }
128
+
129
+ .param-section.success .param-header {
130
+ border-left: 4px solid #2e7d32;
131
+ }
132
+
133
+ .param-section.success .param-title {
134
+ color: #2e7d32;
135
+ }
136
+
137
+ .param-header {
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: space-between;
141
+ padding: 12px 16px;
142
+ background: #2a2d2e;
143
+ border-bottom: 1px solid #3c3c3d;
144
+ }
145
+
146
+ .param-title {
147
+ font-size: 12px;
148
+ color: #cccccc;
149
+ font-weight: 500;
150
+ letter-spacing: 0.3px;
151
+ display: flex;
152
+ align-items: center;
153
+ gap: 6px;
154
+ }
155
+
156
+ .param-title svg {
157
+ opacity: 0.7;
158
+ }
159
+
160
+ .param-actions {
161
+ display: flex;
162
+ gap: 8px;
163
+ }
164
+
165
+ .copy-btn {
166
+ background: transparent;
167
+ border: none;
168
+ cursor: pointer;
169
+ padding: 4px;
170
+ color: #6b6b6d;
171
+ display: flex;
172
+ align-items: center;
173
+ justify-content: center;
174
+ border-radius: 4px;
175
+ transition: all 0.2s ease;
176
+ }
177
+
178
+ .copy-btn:hover {
179
+ background: #3c3c3d;
180
+ color: #0078d4;
181
+ }
182
+
183
+ .loading-placeholder {
184
+ min-height: 100px;
185
+ display: flex;
186
+ align-items: center;
187
+ justify-content: center;
188
+ }
189
+
190
+ .param-scroll{
191
+ max-height: 300px;
192
+ }
193
+
194
+ .json-viewer {
195
+ font-family: 'Consolas', 'Monaco', 'Menlo', 'Courier New', monospace;
196
+ font-size: 12px;
197
+ line-height: 1.6;
198
+ color: #cccccc;
199
+ white-space: pre-wrap;
200
+ word-wrap: break-word;
201
+ }
202
+
203
+ /* 自定义滚动条 */
204
+ .json-viewer::-webkit-scrollbar {
205
+ width: 8px;
206
+ height: 8px;
207
+ }
208
+
209
+ .json-viewer::-webkit-scrollbar-track {
210
+ background: #2a2d2e;
211
+ border-radius: 4px;
212
+ }
213
+
214
+ .json-viewer::-webkit-scrollbar-thumb {
215
+ background: #4b4b4d;
216
+ border-radius: 4px;
217
+ }
218
+
219
+ .json-viewer::-webkit-scrollbar-thumb:hover {
220
+ background: #6b6b6d;
221
+ }
222
+
223
+ /* JSON 语法高亮 */
224
+ .json-viewer .string {
225
+ color: #ce9178;
226
+ }
227
+
228
+ .json-viewer .number {
229
+ color: #b5cea8;
230
+ }
231
+
232
+ .json-viewer .boolean {
233
+ color: #569cd6;
234
+ }
235
+
236
+ .json-viewer .null {
237
+ color: #569cd6;
238
+ }
239
+
240
+ .json-viewer .key {
241
+ color: #9cdcfe;
242
+ }
243
+ </style>
@@ -0,0 +1,262 @@
1
+ <template>
2
+ <div class="tool-item" :data-state="task.state" :class="{ expanded }">
3
+ <!-- 任务主行 -->
4
+ <div class="tool-main" @click="handleToggle">
5
+ <div class="state-indicator" :class="`state-${task.state}`"></div>
6
+ <span class="tool-name">{{ task.toolName }}</span>
7
+ <span class="state-tag" :class="getStateTagClass(task.state)">
8
+ {{ getStateTag(task.state) }}
9
+ </span>
10
+ <div class="tool-actions">
11
+ <span v-if="task.args" class="param-preview" :title="paramPreview" @click.stop="handleToggle">
12
+ {{ paramPreview }}
13
+ </span>
14
+ <button class="expand-btn" :class="{ expanded }" @click.stop="handleToggle">
15
+ <svg width="16" height="16" viewBox="0 0 16 16">
16
+ <path d="M4 6L8 10L12 6" stroke="#6b6b6d" stroke-width="1.5" fill="none"
17
+ stroke-linecap="round" />
18
+ </svg>
19
+ </button>
20
+ </div>
21
+ </div>
22
+
23
+ <!-- 参数详情区域 -->
24
+ <div v-if="expanded" class="param-detail">
25
+ <!-- 输入参数 -->
26
+ <ParamSection type="input" :title="language.get('executor.param.input')" :content="task.args"
27
+ @copy="handleCopy('input')" />
28
+
29
+ <!-- 输出/错误参数 -->
30
+ <ParamSection v-if="task.state === 'running'" type="output" :title="language.get('executor.param.output')"
31
+ :loading="true" :hide-copy="true" />
32
+ <ParamSection v-else-if="task.state === 'failed'" type="error" :title="language.get('executor.param.error')"
33
+ :content="task.result" :state="task.state" @copy="handleCopy('error')" />
34
+ <ParamSection v-else-if="task.state === 'success'" type="output"
35
+ :title="language.get('executor.param.output')" :content="task.result" :state="task.state"
36
+ :formatter="formatter" @copy="handleCopy('output')" />
37
+ </div>
38
+ </div>
39
+ </template>
40
+
41
+ <script setup lang="ts">
42
+ import { computed } from 'vue'
43
+ import { language } from '../../locale/Language'
44
+ import { TaskItem } from './types'
45
+ import { useExecutor } from './useExecutor'
46
+ import ParamSection from './ParamSection.vue'
47
+
48
+ interface Props {
49
+ task: TaskItem
50
+ expanded?: boolean
51
+ }
52
+
53
+ const props = defineProps<Props>()
54
+
55
+ const emit = defineEmits<{
56
+ (e: 'toggle', taskId: string | number): void
57
+ (e: 'copy', type: string, taskId: string | number): void
58
+ }>()
59
+
60
+ const { getParamPreview, getStateTag, getStateTagClass, formatJSON } = useExecutor({ tasks: [] })
61
+
62
+ const paramPreview = computed(() => {
63
+ return getParamPreview(props.task.args)
64
+ })
65
+
66
+ const formatter = (conetnt: any) => {
67
+ if (typeof conetnt?.data === 'string' && conetnt.data.startsWith('{')) {
68
+ try {
69
+ conetnt.data = JSON.parse(conetnt.data)
70
+ } catch (error) {
71
+ console.log(error)
72
+ }
73
+ }
74
+
75
+ return conetnt
76
+ }
77
+
78
+ const handleToggle = () => {
79
+ emit('toggle', props.task.toolCallId)
80
+ }
81
+
82
+ const handleCopy = (type: string) => {
83
+ emit('copy', type, props.task.toolCallId)
84
+ }
85
+ </script>
86
+
87
+ <style scoped>
88
+ .tool-item {
89
+ display: flex;
90
+ flex-direction: column;
91
+ border-radius: 12px;
92
+ transition: all 0.2s ease;
93
+ background: transparent;
94
+ border: 1px solid transparent;
95
+ }
96
+
97
+ .tool-item:hover,
98
+ .tool-item.expanded {
99
+ border-color: #3c3c3d;
100
+ }
101
+
102
+ /* 工具主行 */
103
+ .tool-main {
104
+ display: flex;
105
+ align-items: center;
106
+ gap: 12px;
107
+ padding: 16px;
108
+ border-radius: 12px;
109
+ cursor: pointer;
110
+ transition: all 0.2s ease;
111
+ }
112
+
113
+ .tool-main:hover {
114
+ background: #2a2d2e;
115
+ }
116
+
117
+ /* 状态指示器 */
118
+ .state-indicator {
119
+ width: 10px;
120
+ height: 10px;
121
+ border-radius: 50%;
122
+ transition: all 0.2s ease;
123
+ flex-shrink: 0;
124
+ }
125
+
126
+ @keyframes pulse {
127
+ 0% {
128
+ box-shadow: 0 0 0 0 rgba(0, 120, 212, 0.7);
129
+ }
130
+
131
+ 70% {
132
+ box-shadow: 0 0 0 10px rgba(0, 120, 212, 0);
133
+ }
134
+
135
+ 100% {
136
+ box-shadow: 0 0 0 0 rgba(0, 120, 212, 0);
137
+ }
138
+ }
139
+
140
+ .state-running {
141
+ background: #0078d4;
142
+ animation: pulse 1.5s infinite;
143
+ box-shadow: 0 0 0 rgba(0, 120, 212, 0.4);
144
+ }
145
+
146
+ .state-success {
147
+ background: #2e7d32;
148
+ }
149
+
150
+ .state-failed {
151
+ background: #f44336;
152
+ }
153
+
154
+ /* 工具名称 */
155
+ .tool-name {
156
+ font-weight: 500;
157
+ color: #cccccc;
158
+ flex: 1;
159
+ font-size: 14px;
160
+ white-space: nowrap;
161
+ overflow: hidden;
162
+ text-overflow: ellipsis;
163
+ }
164
+
165
+ /* 状态标签 */
166
+ .state-tag {
167
+ font-size: 11px;
168
+ padding: 4px 8px;
169
+ border-radius: 12px;
170
+ font-weight: 500;
171
+ margin-right: 8px;
172
+ }
173
+
174
+ .state-tag.running {
175
+ background: #1a3b4d;
176
+ color: #0078d4;
177
+ }
178
+
179
+ .state-tag.success {
180
+ background: #1e3a2a;
181
+ color: #2e7d32;
182
+ }
183
+
184
+ .state-tag.failed {
185
+ background: #4d2a2a;
186
+ color: #f44336;
187
+ }
188
+
189
+ /* 工具操作区 */
190
+ .tool-actions {
191
+ display: flex;
192
+ align-items: center;
193
+ gap: 8px;
194
+ flex-shrink: 0;
195
+ }
196
+
197
+ /* 参数预览 */
198
+ .param-preview {
199
+ font-size: 11px;
200
+ color: #9b9b9b;
201
+ background: transparent;
202
+ padding: 0;
203
+ max-width: 200px;
204
+ white-space: nowrap;
205
+ overflow: hidden;
206
+ text-overflow: ellipsis;
207
+ font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
208
+ cursor: pointer;
209
+ transition: color 0.2s ease;
210
+ line-height: 1.5;
211
+ }
212
+
213
+ .param-preview:hover {
214
+ color: #0078d4;
215
+ }
216
+
217
+ /* 展开按钮 */
218
+ .expand-btn {
219
+ background: transparent;
220
+ border: none;
221
+ cursor: pointer;
222
+ padding: 4px;
223
+ display: flex;
224
+ align-items: center;
225
+ justify-content: center;
226
+ border-radius: 4px;
227
+ transition: all 0.2s ease;
228
+ }
229
+
230
+ .expand-btn:hover {
231
+ background: #3c3c3d;
232
+ }
233
+
234
+ .expand-btn svg {
235
+ transition: transform 0.3s ease;
236
+ }
237
+
238
+ .expand-btn.expanded svg {
239
+ transform: rotate(180deg);
240
+ }
241
+
242
+ /* 参数详情区域 */
243
+ .param-detail {
244
+ margin: 12px 16px;
245
+ display: grid;
246
+ grid-template-columns: 1fr 1fr;
247
+ gap: 16px;
248
+ animation: slideDown 0.3s ease;
249
+ }
250
+
251
+ @keyframes slideDown {
252
+ from {
253
+ opacity: 0;
254
+ transform: translateY(-10px);
255
+ }
256
+
257
+ to {
258
+ opacity: 1;
259
+ transform: translateY(0);
260
+ }
261
+ }
262
+ </style>
@@ -0,0 +1,27 @@
1
+ // 任务状态类型
2
+ export type TaskState = 'running' | 'success' | 'failed'
3
+
4
+ // 任务项接口
5
+ export interface TaskItem {
6
+ toolCallId: string | number
7
+ toolName: string
8
+ state: TaskState
9
+ args: Record<string, any> | any[] | string | number | boolean | null
10
+ result?: Record<string, any> | any[] | string | number | boolean | null
11
+ }
12
+
13
+ // 参数区域类型
14
+ export type ParamSectionType = 'input' | 'output' | 'error'
15
+
16
+ // 组件Props接口
17
+ export interface ExecutorProps {
18
+ tasks: TaskItem[]
19
+ title?: string
20
+ }
21
+
22
+ // 事件发射接口
23
+ export interface ExecutorEmits {
24
+ (e: 'copy:success', type: ParamSectionType, taskId: string | number): void
25
+ (e: 'copy:error', type: ParamSectionType, taskId: string | number): void
26
+ (e: 'task:toggle', taskId: string | number, expanded: boolean): void
27
+ }