@vibe-forge/client 0.10.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{arc-C1rWFTer.js → arc-M4HYfcHs.js} +1 -1
- package/dist/assets/{blockDiagram-c4efeb88-DlZ9x70F.js → blockDiagram-c4efeb88-CUrDjrxj.js} +1 -1
- package/dist/assets/{c4Diagram-c83219d4-BKKxi__y.js → c4Diagram-c83219d4-BMEtqlFp.js} +1 -1
- package/dist/assets/channel-Cj3Cp2OJ.js +1 -0
- package/dist/assets/{classDiagram-beda092f-CVGPySZq.js → classDiagram-beda092f-BOmDJ0Ml.js} +1 -1
- package/dist/assets/{classDiagram-v2-2358418a-7kp8GVVj.js → classDiagram-v2-2358418a-BODzX2MB.js} +1 -1
- package/dist/assets/clone-B7Q9B1dS.js +1 -0
- package/dist/assets/{createText-1719965b-Dykv8kT9.js → createText-1719965b-B9Dd8zcR.js} +1 -1
- package/dist/assets/{cssMode-B59COYVW.js → cssMode-DLxG92Ot.js} +1 -1
- package/dist/assets/{edges-96097737-CkZ1ZBro.js → edges-96097737-CuZFd43m.js} +1 -1
- package/dist/assets/{erDiagram-0228fc6a-281ADcRp.js → erDiagram-0228fc6a-8g9lu2-Z.js} +1 -1
- package/dist/assets/{flowDb-c6c81e3f-BQjX_flP.js → flowDb-c6c81e3f-BlBS1tdN.js} +1 -1
- package/dist/assets/{flowDiagram-50d868cf-DMHZTjES.js → flowDiagram-50d868cf-u6mWflpF.js} +1 -1
- package/dist/assets/flowDiagram-v2-4f6560a1-G3v545eF.js +1 -0
- package/dist/assets/{flowchart-elk-definition-6af322e1-CI3yz4z8.js → flowchart-elk-definition-6af322e1-BDqI2NFr.js} +1 -1
- package/dist/assets/{freemarker2-DWnWjibn.js → freemarker2-tVtpTMPu.js} +1 -1
- package/dist/assets/{ganttDiagram-a2739b55-B3IING9L.js → ganttDiagram-a2739b55-CDQjx9Wu.js} +1 -1
- package/dist/assets/{gitGraphDiagram-82fe8481-CnArIr_T.js → gitGraphDiagram-82fe8481-DUHFKRVA.js} +1 -1
- package/dist/assets/{graph-BZ1F0Yve.js → graph-2HKPi5B_.js} +1 -1
- package/dist/assets/{handlebars-C1QH9qTz.js → handlebars-D00tgNd8.js} +1 -1
- package/dist/assets/{html-D1NkqHjC.js → html-B-TDzBiR.js} +1 -1
- package/dist/assets/{htmlMode-DAZCE_rA.js → htmlMode-ClycqSTM.js} +1 -1
- package/dist/assets/{index-5325376f-Da9zSHjA.js → index-5325376f-DPrJpRQ-.js} +1 -1
- package/dist/assets/{index-C0vjF3D0.js → index-CAHZZEoo.js} +319 -323
- package/dist/assets/{index-vzEbM21t.css → index-Di7lePfb.css} +1 -1
- package/dist/assets/{infoDiagram-8eee0895-DYbFvRM7.js → infoDiagram-8eee0895-Co5tS1I5.js} +1 -1
- package/dist/assets/{javascript-CoMjGRHa.js → javascript-zbkwarmb.js} +1 -1
- package/dist/assets/{journeyDiagram-c64418c1-Boebox0b.js → journeyDiagram-c64418c1-k_qioHgy.js} +1 -1
- package/dist/assets/{jsonMode-D__gAvuz.js → jsonMode-C3CSpzBF.js} +1 -1
- package/dist/assets/{layout-CTcHNbHp.js → layout-CjOXKxvs.js} +1 -1
- package/dist/assets/{line-4AwinCz2.js → line-C-XnQrKR.js} +1 -1
- package/dist/assets/{linear-CeSMLzJW.js → linear-C7MMERzS.js} +1 -1
- package/dist/assets/{liquid-DZF6egdE.js → liquid-5G37EU6K.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-6K4lv5S2.js → lspLanguageFeatures-zaDMuhCE.js} +1 -1
- package/dist/assets/{mdx-Cnt4ka6w.js → mdx-Bc-LY0gi.js} +1 -1
- package/dist/assets/{mermaid.core-B0yG5s4D.js → mermaid.core-CechbHof.js} +4 -4
- package/dist/assets/{mindmap-definition-8da855dc-KJEvXMKj.js → mindmap-definition-8da855dc-ejftCDGb.js} +1 -1
- package/dist/assets/{pieDiagram-a8764435-17nFAXPJ.js → pieDiagram-a8764435-DY__X3Qj.js} +1 -1
- package/dist/assets/{python-DA3TtjDv.js → python-vK2Ff2J5.js} +1 -1
- package/dist/assets/{quadrantDiagram-1e28029f-Dt4vubi-.js → quadrantDiagram-1e28029f-azIZCv_2.js} +1 -1
- package/dist/assets/{razor-CWDJgvX_.js → razor-BipjBJKu.js} +1 -1
- package/dist/assets/{requirementDiagram-08caed73-H6aDyDK-.js → requirementDiagram-08caed73-C4EB0Xs2.js} +1 -1
- package/dist/assets/{sankeyDiagram-a04cb91d-DxsVtbjI.js → sankeyDiagram-a04cb91d-PNhR6YWu.js} +1 -1
- package/dist/assets/{sequenceDiagram-c5b8d532-BHa148XJ.js → sequenceDiagram-c5b8d532-4c-qV-Ri.js} +1 -1
- package/dist/assets/{stateDiagram-1ecb1508-DgwBm8LO.js → stateDiagram-1ecb1508-CnURumPE.js} +1 -1
- package/dist/assets/{stateDiagram-v2-c2b004d7-BK7IQLVc.js → stateDiagram-v2-c2b004d7-DR2qHTPg.js} +1 -1
- package/dist/assets/{styles-b4e223ce-DzW27Bc-.js → styles-b4e223ce-B2PWXT_i.js} +1 -1
- package/dist/assets/{styles-ca3715f6-Dex2GiLT.js → styles-ca3715f6-DEhgVF5H.js} +1 -1
- package/dist/assets/{styles-d45a18b0-B6fGtDKS.js → styles-d45a18b0-DyzccA5F.js} +1 -1
- package/dist/assets/{svgDrawCommon-b86b1483-B4HYgfV5.js → svgDrawCommon-b86b1483-C_1tMhxp.js} +1 -1
- package/dist/assets/{timeline-definition-faaaa080--QSbWb25.js → timeline-definition-faaaa080-FdaC0dQH.js} +1 -1
- package/dist/assets/{tsMode-ZM7ocZCH.js → tsMode-CrMC5T3_.js} +1 -1
- package/dist/assets/{typescript-CKWDmBCc.js → typescript-CRfPu8v7.js} +1 -1
- package/dist/assets/{xml-DuEUAzPi.js → xml-jlRvQfFI.js} +1 -1
- package/dist/assets/{xychartDiagram-f5964ef8-D09Zkv2K.js → xychartDiagram-f5964ef8-sxjv75h9.js} +1 -1
- package/dist/assets/{yaml-DL7QPRYk.js → yaml-B47_IHOH.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +10 -10
- package/src/components/chat/ChatHistoryView.tsx +22 -13
- package/src/components/chat/messages/MessageStatusNotice.scss +163 -0
- package/src/components/chat/messages/MessageStatusNotice.tsx +48 -0
- package/src/components/chat/messages/build-chat-history-status-notices.ts +138 -0
- package/src/components/chat/sender/@components/sender-body/SenderBody.tsx +0 -24
- package/src/components/chat/sender/@core/build-sender-controller-result.ts +0 -6
- package/src/components/chat/sender/@hooks/use-sender-controller.ts +0 -2
- package/src/components/chat/sender/@types/sender-props.ts +0 -3
- package/src/components/chat/sender/Sender.scss +0 -58
- package/src/components/chat/sender/Sender.tsx +0 -2
- package/src/components/chat/tools/DefaultTool.tsx +74 -207
- package/src/components/chat/tools/adapter-claude/ClaudeEditDiff.tsx +30 -0
- package/src/components/chat/tools/adapter-claude/GenericClaudeTool.scss +128 -0
- package/src/components/chat/tools/adapter-claude/GenericClaudeTool.tsx +133 -0
- package/src/components/chat/tools/adapter-claude/claude-tool-edit-builders.ts +102 -0
- package/src/components/chat/tools/adapter-claude/claude-tool-field-sections.tsx +168 -0
- package/src/components/chat/tools/adapter-claude/claude-tool-operation-builders.ts +135 -0
- package/src/components/chat/tools/adapter-claude/claude-tool-presentation.ts +61 -0
- package/src/components/chat/tools/adapter-claude/claude-tool-shared.ts +185 -0
- package/src/components/chat/tools/adapter-claude/claude-tool-summary.ts +76 -0
- package/src/components/chat/tools/adapter-claude/claude-tool-system-builders.ts +125 -0
- package/src/components/chat/tools/adapter-claude/claude-tool-task-builders.ts +148 -0
- package/src/components/chat/tools/adapter-claude/index.ts +24 -15
- package/src/components/chat/tools/core/ToolCallBox.scss +344 -36
- package/src/components/chat/tools/core/ToolCallBox.tsx +35 -13
- package/src/components/chat/tools/core/ToolDiffViewer.scss +138 -0
- package/src/components/chat/tools/core/ToolDiffViewer.tsx +180 -0
- package/src/components/chat/tools/core/ToolGroup.scss +52 -74
- package/src/components/chat/tools/core/ToolGroup.tsx +25 -40
- package/src/components/chat/tools/core/ToolRenderer.tsx +3 -3
- package/src/components/chat/tools/core/ToolResultContent.tsx +66 -0
- package/src/components/chat/tools/core/ToolSummaryHeader.tsx +67 -0
- package/src/components/chat/tools/core/tool-content-presence.ts +57 -0
- package/src/components/chat/tools/core/tool-display.ts +192 -0
- package/src/components/chat/tools/core/tool-result-content-utils.ts +171 -0
- package/src/components/chat/tools/core/tool-summary.ts +194 -0
- package/src/components/chat/tools/plugin-chrome-devtools/ChromeDevtoolsTool.tsx +66 -53
- package/src/components/chat/tools/task/GetTaskInfoTool.tsx +26 -9
- package/src/components/chat/tools/task/ListTasksTool.tsx +22 -9
- package/src/components/chat/tools/task/StartTasksTool.tsx +22 -9
- package/src/hooks/chat/interaction-state.ts +29 -9
- package/src/hooks/chat/use-chat-scroll.ts +2 -2
- package/src/hooks/chat/use-chat-session-messages.ts +24 -18
- package/src/hooks/chat/use-chat-session.ts +2 -2
- package/src/resources/locales/en.json +81 -0
- package/src/resources/locales/zh.json +81 -0
- package/src/routes/ChatRoute.tsx +24 -27
- package/src/utils/strip-ansi.ts +26 -0
- package/dist/assets/channel-F1aqMANO.js +0 -1
- package/dist/assets/clone-B-GCuXNo.js +0 -1
- package/dist/assets/flowDiagram-v2-4f6560a1-C5FzdVl1.js +0 -1
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import './ToolDiffViewer.scss'
|
|
2
|
+
|
|
3
|
+
import { DiffEditor, loader } from '@monaco-editor/react'
|
|
4
|
+
import { Tooltip } from 'antd'
|
|
5
|
+
import type { editor as MonacoEditorNamespace } from 'monaco-editor'
|
|
6
|
+
import * as monacoApi from 'monaco-editor'
|
|
7
|
+
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
|
|
8
|
+
import React, { useEffect, useMemo, useState } from 'react'
|
|
9
|
+
|
|
10
|
+
import { TOOL_TOOLTIP_PROPS } from './tool-display'
|
|
11
|
+
|
|
12
|
+
const DIFF_LINE_HEIGHT = 18
|
|
13
|
+
const MIN_DIFF_HEIGHT = 96
|
|
14
|
+
const MAX_DIFF_HEIGHT = 360
|
|
15
|
+
|
|
16
|
+
const monacoRuntime = globalThis as typeof globalThis & {
|
|
17
|
+
MonacoEnvironment?: {
|
|
18
|
+
getWorker: () => Worker
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (monacoRuntime.MonacoEnvironment == null) {
|
|
23
|
+
monacoRuntime.MonacoEnvironment = {
|
|
24
|
+
getWorker: () => new EditorWorker()
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
loader.config({ monaco: monacoApi })
|
|
29
|
+
|
|
30
|
+
const getThemeName = () => (document.documentElement.classList.contains('dark') ? 'vs-dark' : 'vs')
|
|
31
|
+
|
|
32
|
+
const getEditorHeight = (original: string, modified: string) => {
|
|
33
|
+
const lineCount = Math.max(
|
|
34
|
+
original === '' ? 1 : original.split('\n').length,
|
|
35
|
+
modified === '' ? 1 : modified.split('\n').length
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return Math.min(MAX_DIFF_HEIGHT, Math.max(MIN_DIFF_HEIGHT, lineCount * DIFF_LINE_HEIGHT + 28))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const getModeIcon = (mode: 'split' | 'inline') => (
|
|
42
|
+
<span className='material-symbols-rounded'>
|
|
43
|
+
{mode === 'split' ? 'splitscreen_right' : 'view_agenda'}
|
|
44
|
+
</span>
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
export interface ToolDiffMetaItem {
|
|
48
|
+
icon?: string
|
|
49
|
+
label: string
|
|
50
|
+
value?: string
|
|
51
|
+
tone?: 'default' | 'success' | 'muted'
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function useMonacoTheme() {
|
|
55
|
+
const [themeName, setThemeName] = useState(getThemeName)
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
const observer = new MutationObserver(() => {
|
|
59
|
+
setThemeName(getThemeName())
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
observer.observe(document.documentElement, {
|
|
63
|
+
attributes: true,
|
|
64
|
+
attributeFilter: ['class']
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return () => {
|
|
68
|
+
observer.disconnect()
|
|
69
|
+
}
|
|
70
|
+
}, [])
|
|
71
|
+
|
|
72
|
+
return themeName
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function ToolDiffViewer({
|
|
76
|
+
original,
|
|
77
|
+
modified,
|
|
78
|
+
language,
|
|
79
|
+
metaItems = [],
|
|
80
|
+
splitLabel,
|
|
81
|
+
inlineLabel
|
|
82
|
+
}: {
|
|
83
|
+
original: string
|
|
84
|
+
modified: string
|
|
85
|
+
language?: string
|
|
86
|
+
metaItems?: ToolDiffMetaItem[]
|
|
87
|
+
splitLabel: string
|
|
88
|
+
inlineLabel: string
|
|
89
|
+
}) {
|
|
90
|
+
const [viewMode, setViewMode] = useState<'split' | 'inline'>('split')
|
|
91
|
+
const themeName = useMonacoTheme()
|
|
92
|
+
const height = useMemo(() => getEditorHeight(original, modified), [modified, original])
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div className='tool-diff-viewer'>
|
|
96
|
+
<div className='tool-diff-viewer__toolbar'>
|
|
97
|
+
<div className='tool-diff-viewer__legend'>
|
|
98
|
+
{metaItems.map(item => (
|
|
99
|
+
<span
|
|
100
|
+
key={`${item.label}-${item.value ?? ''}`}
|
|
101
|
+
className={`tool-diff-viewer__meta-item ${
|
|
102
|
+
item.tone != null ? `tool-diff-viewer__meta-item--${item.tone}` : ''
|
|
103
|
+
}`}
|
|
104
|
+
>
|
|
105
|
+
{item.icon != null && item.icon !== '' && (
|
|
106
|
+
<span className='material-symbols-rounded'>{item.icon}</span>
|
|
107
|
+
)}
|
|
108
|
+
<span className='tool-diff-viewer__meta-label'>{item.label}</span>
|
|
109
|
+
{item.value != null && item.value !== '' && (
|
|
110
|
+
<span className='tool-diff-viewer__meta-value'>{item.value}</span>
|
|
111
|
+
)}
|
|
112
|
+
</span>
|
|
113
|
+
))}
|
|
114
|
+
{language != null && language !== '' && (
|
|
115
|
+
<span className='tool-diff-viewer__lang'>{language}</span>
|
|
116
|
+
)}
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div className='tool-diff-viewer__mode-switch' role='tablist' aria-label='Diff view mode'>
|
|
120
|
+
{(['split', 'inline'] as const).map(mode => {
|
|
121
|
+
const label = mode === 'split' ? splitLabel : inlineLabel
|
|
122
|
+
return (
|
|
123
|
+
<Tooltip key={mode} title={label} {...TOOL_TOOLTIP_PROPS}>
|
|
124
|
+
<button
|
|
125
|
+
type='button'
|
|
126
|
+
className={`tool-diff-viewer__mode-button ${viewMode === mode ? 'is-active' : ''}`}
|
|
127
|
+
aria-label={label}
|
|
128
|
+
aria-pressed={viewMode === mode}
|
|
129
|
+
onClick={() => setViewMode(mode)}
|
|
130
|
+
>
|
|
131
|
+
{getModeIcon(mode)}
|
|
132
|
+
</button>
|
|
133
|
+
</Tooltip>
|
|
134
|
+
)
|
|
135
|
+
})}
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<div
|
|
140
|
+
className='tool-diff-viewer__editor'
|
|
141
|
+
style={{ height: `${height}px` }}
|
|
142
|
+
aria-label='tool diff viewer'
|
|
143
|
+
>
|
|
144
|
+
<DiffEditor
|
|
145
|
+
original={original}
|
|
146
|
+
modified={modified}
|
|
147
|
+
originalLanguage={language ?? 'text'}
|
|
148
|
+
modifiedLanguage={language ?? 'text'}
|
|
149
|
+
theme={themeName}
|
|
150
|
+
loading={null}
|
|
151
|
+
options={{
|
|
152
|
+
automaticLayout: true,
|
|
153
|
+
contextmenu: false,
|
|
154
|
+
diffCodeLens: false,
|
|
155
|
+
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace',
|
|
156
|
+
fontSize: 12,
|
|
157
|
+
glyphMargin: false,
|
|
158
|
+
hideUnchangedRegions: { enabled: false },
|
|
159
|
+
lineDecorationsWidth: 8,
|
|
160
|
+
lineHeight: DIFF_LINE_HEIGHT,
|
|
161
|
+
minimap: { enabled: false },
|
|
162
|
+
originalEditable: false,
|
|
163
|
+
overviewRulerBorder: false,
|
|
164
|
+
readOnly: true,
|
|
165
|
+
renderIndicators: true,
|
|
166
|
+
renderMarginRevertIcon: false,
|
|
167
|
+
renderOverviewRuler: false,
|
|
168
|
+
renderSideBySide: viewMode === 'split',
|
|
169
|
+
scrollBeyondLastLine: false,
|
|
170
|
+
scrollbar: {
|
|
171
|
+
alwaysConsumeMouseWheel: false,
|
|
172
|
+
useShadows: false
|
|
173
|
+
},
|
|
174
|
+
wordWrap: 'on'
|
|
175
|
+
} satisfies MonacoEditorNamespace.IDiffEditorConstructionOptions}
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
)
|
|
180
|
+
}
|
|
@@ -11,114 +11,97 @@
|
|
|
11
11
|
.tool-group-wrapper {
|
|
12
12
|
margin: 0;
|
|
13
13
|
|
|
14
|
-
&.single-item {
|
|
15
|
-
// No special style for single item
|
|
16
|
-
}
|
|
17
|
-
|
|
18
14
|
&.card-style {
|
|
19
|
-
border: 1px solid var(--border-color);
|
|
20
|
-
border-radius: 8px;
|
|
21
|
-
background-color: var(--bg-color);
|
|
22
|
-
overflow: hidden;
|
|
23
|
-
|
|
24
15
|
.tool-group-header {
|
|
25
16
|
display: flex;
|
|
26
17
|
align-items: center;
|
|
27
|
-
justify-content:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
18
|
+
justify-content: flex-start;
|
|
19
|
+
gap: 4px;
|
|
20
|
+
padding: 1px 0;
|
|
21
|
+
background: transparent;
|
|
31
22
|
cursor: pointer;
|
|
32
23
|
user-select: none;
|
|
33
|
-
|
|
24
|
+
color: var(--sub-text-color);
|
|
25
|
+
transition: color .2s ease;
|
|
34
26
|
|
|
35
27
|
.header-left {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
font-size: 13px;
|
|
28
|
+
min-width: 0;
|
|
29
|
+
flex: 0 1 auto;
|
|
30
|
+
font-size: 12px;
|
|
40
31
|
font-weight: 500;
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
color: var(--sub-text-color);
|
|
46
|
-
}
|
|
32
|
+
line-height: 1.45;
|
|
33
|
+
white-space: nowrap;
|
|
34
|
+
overflow: hidden;
|
|
35
|
+
text-overflow: ellipsis;
|
|
47
36
|
}
|
|
48
37
|
|
|
49
38
|
.expand-icon {
|
|
50
|
-
font-size:
|
|
51
|
-
|
|
39
|
+
font-size: 16px;
|
|
40
|
+
flex-shrink: 0;
|
|
41
|
+
transition: transform .22s ease, opacity .18s ease;
|
|
42
|
+
transform-origin: center;
|
|
43
|
+
|
|
44
|
+
&.is-expanded {
|
|
45
|
+
transform: rotate(90deg);
|
|
46
|
+
}
|
|
52
47
|
}
|
|
53
48
|
|
|
54
49
|
&:hover {
|
|
55
|
-
|
|
50
|
+
color: var(--text-color);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.tool-group-list-shell {
|
|
55
|
+
display: grid;
|
|
56
|
+
grid-template-rows: 0fr;
|
|
57
|
+
opacity: 0;
|
|
58
|
+
visibility: hidden;
|
|
59
|
+
pointer-events: none;
|
|
60
|
+
transition:
|
|
61
|
+
grid-template-rows .22s ease,
|
|
62
|
+
opacity .18s ease,
|
|
63
|
+
visibility 0s linear .22s;
|
|
64
|
+
|
|
65
|
+
&.expanded {
|
|
66
|
+
grid-template-rows: 1fr;
|
|
67
|
+
opacity: 1;
|
|
68
|
+
visibility: visible;
|
|
69
|
+
pointer-events: auto;
|
|
70
|
+
transition: grid-template-rows .22s ease, opacity .18s ease;
|
|
56
71
|
}
|
|
57
72
|
}
|
|
58
73
|
|
|
59
74
|
.tool-group-list {
|
|
75
|
+
min-height: 0;
|
|
76
|
+
overflow: hidden;
|
|
60
77
|
display: flex;
|
|
61
78
|
flex-direction: column;
|
|
62
|
-
gap:
|
|
63
|
-
padding: 0;
|
|
64
|
-
border-top: 1px solid var(--border-color);
|
|
65
|
-
background-color: var(--bg-color);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.tool-group-last-item {
|
|
69
|
-
background-color: var(--bg-color);
|
|
70
|
-
}
|
|
79
|
+
gap: 2px;
|
|
80
|
+
padding: 4px 0 0;
|
|
71
81
|
|
|
72
|
-
// Shared style reset for all tool groups inside the card
|
|
73
|
-
.tool-group-list, .tool-group-last-item {
|
|
74
82
|
.tool-group {
|
|
75
83
|
margin: 0;
|
|
76
84
|
border: none;
|
|
77
85
|
border-radius: 0;
|
|
78
|
-
background
|
|
79
|
-
overflow: visible;
|
|
86
|
+
background: transparent;
|
|
87
|
+
overflow: visible;
|
|
80
88
|
|
|
81
89
|
.tool-call-box {
|
|
82
90
|
margin: 0;
|
|
83
91
|
border: none;
|
|
84
92
|
border-radius: 0;
|
|
93
|
+
background: transparent;
|
|
85
94
|
|
|
86
95
|
.tool-call-header {
|
|
87
|
-
|
|
88
|
-
background-color: transparent;
|
|
89
|
-
|
|
90
|
-
&:hover {
|
|
91
|
-
text-shadow: 0 0 .5px currentColor;
|
|
92
|
-
}
|
|
96
|
+
background: transparent;
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
.tool-call-body {
|
|
96
|
-
|
|
97
|
-
background-color: transparent;
|
|
100
|
+
background: transparent;
|
|
98
101
|
}
|
|
99
102
|
}
|
|
100
103
|
}
|
|
101
104
|
}
|
|
102
|
-
|
|
103
|
-
// Timeline gap style
|
|
104
|
-
.tool-group-list .tool-group {
|
|
105
|
-
position: relative;
|
|
106
|
-
margin-bottom: 12px; // Increased gap between items
|
|
107
|
-
border-bottom: none; // Remove border as we use gap + line
|
|
108
|
-
|
|
109
|
-
&::after {
|
|
110
|
-
content: '';
|
|
111
|
-
position: absolute;
|
|
112
|
-
left: 19px;
|
|
113
|
-
bottom: -11px; // Adjusted for larger margin
|
|
114
|
-
height: 10px; // Increased length for larger gap
|
|
115
|
-
width: 2px;
|
|
116
|
-
background-color: var(--sub-text-color);
|
|
117
|
-
z-index: 99;
|
|
118
|
-
border-radius: 4px;
|
|
119
|
-
opacity: .5;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
105
|
}
|
|
123
106
|
}
|
|
124
107
|
|
|
@@ -148,14 +131,9 @@
|
|
|
148
131
|
}
|
|
149
132
|
|
|
150
133
|
html.dark .tool-group-container .tool-group-wrapper.card-style {
|
|
151
|
-
background-color: #1f2937; // Darker bg for card
|
|
152
134
|
.tool-group-header {
|
|
153
|
-
background-color: #374151;
|
|
154
135
|
&:hover {
|
|
155
|
-
|
|
136
|
+
color: #e5e7eb;
|
|
156
137
|
}
|
|
157
138
|
}
|
|
158
|
-
.tool-group-list, .tool-group-last-item {
|
|
159
|
-
background-color: #1f2937;
|
|
160
|
-
}
|
|
161
139
|
}
|
|
@@ -7,6 +7,7 @@ import { useSearchParams } from 'react-router-dom'
|
|
|
7
7
|
import { MessageContextMenu } from '../../messages/MessageContextMenu'
|
|
8
8
|
import { MessageFooter } from '../../messages/MessageFooter'
|
|
9
9
|
import { ToolRenderer } from './ToolRenderer'
|
|
10
|
+
import { getToolGroupSummaryText } from './tool-summary'
|
|
10
11
|
|
|
11
12
|
interface ToolGroupProps {
|
|
12
13
|
anchorId: string
|
|
@@ -38,15 +39,14 @@ function ToolGroupComponent({
|
|
|
38
39
|
const isDebugMode = searchParams.get('debug') === 'true'
|
|
39
40
|
const [expanded, setExpanded] = useState(false)
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
if (items.length === 0) return null
|
|
43
|
+
|
|
44
|
+
const summaryText = getToolGroupSummaryText(items, t)
|
|
43
45
|
const shouldForceExpand = targetToolUseId != null &&
|
|
44
46
|
targetToolUseId !== '' &&
|
|
45
|
-
|
|
47
|
+
items.some(item => item.item.id === targetToolUseId)
|
|
46
48
|
const isExpanded = expanded || shouldForceExpand
|
|
47
49
|
|
|
48
|
-
if (items.length === 0) return null
|
|
49
|
-
|
|
50
50
|
// If only one item, just render it directly (wrapped in container for footer)
|
|
51
51
|
if (items.length === 1) {
|
|
52
52
|
return (
|
|
@@ -96,48 +96,33 @@ function ToolGroupComponent({
|
|
|
96
96
|
>
|
|
97
97
|
<div id={anchorId} className='tool-group-container'>
|
|
98
98
|
<div className='tool-group-wrapper card-style'>
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
<
|
|
105
|
-
|
|
99
|
+
<div
|
|
100
|
+
className='tool-group-header'
|
|
101
|
+
aria-expanded={isExpanded}
|
|
102
|
+
onClick={() => setExpanded(!expanded)}
|
|
103
|
+
>
|
|
104
|
+
<div className='header-left'>
|
|
105
|
+
<span>{summaryText}</span>
|
|
106
|
+
</div>
|
|
107
|
+
<span className={`material-symbols-rounded expand-icon ${isExpanded ? 'is-expanded' : ''}`}>
|
|
108
|
+
chevron_right
|
|
109
|
+
</span>
|
|
106
110
|
</div>
|
|
107
|
-
<span className='material-symbols-rounded expand-icon'>
|
|
108
|
-
{isExpanded ? 'expand_less' : 'expand_more'}
|
|
109
|
-
</span>
|
|
110
|
-
</div>
|
|
111
111
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
{
|
|
115
|
-
|
|
112
|
+
<div
|
|
113
|
+
className={`tool-group-list-shell ${isExpanded ? 'expanded' : 'collapsed'}`}
|
|
114
|
+
aria-hidden={!isExpanded}
|
|
115
|
+
>
|
|
116
|
+
<div className='tool-group-list'>
|
|
117
|
+
{items.map((it, idx) => (
|
|
116
118
|
<ToolRenderer
|
|
119
|
+
key={it.item.id || idx}
|
|
117
120
|
item={it.item}
|
|
118
121
|
resultItem={it.resultItem}
|
|
119
122
|
/>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
</div>
|
|
123
|
-
)}
|
|
124
|
-
|
|
125
|
-
{
|
|
126
|
-
/* Always show the last item, but if expanded, it's just part of the list visually.
|
|
127
|
-
If collapsed, it appears "below" the header.
|
|
128
|
-
Actually, to make it look like "part of the list", we should just put it in the flow.
|
|
129
|
-
|
|
130
|
-
When collapsed: Header + Last Item
|
|
131
|
-
When expanded: Header + Other Items + Last Item
|
|
132
|
-
*/
|
|
133
|
-
}
|
|
134
|
-
<div className='tool-group-last-item' data-tool-use-id={lastItem.item.id}>
|
|
135
|
-
<ToolRenderer
|
|
136
|
-
item={lastItem.item}
|
|
137
|
-
resultItem={lastItem.resultItem}
|
|
138
|
-
/>
|
|
123
|
+
))}
|
|
124
|
+
</div>
|
|
139
125
|
</div>
|
|
140
|
-
|
|
141
126
|
</div>
|
|
142
127
|
|
|
143
128
|
{footer && isDebugMode && (
|
|
@@ -3,7 +3,7 @@ import React from 'react'
|
|
|
3
3
|
import type { ChatMessageContent } from '@vibe-forge/core'
|
|
4
4
|
|
|
5
5
|
import { DefaultTool } from '../DefaultTool'
|
|
6
|
-
import {
|
|
6
|
+
import { GenericClaudeTool, adapterClaudeToolRenders, isClaudeToolName } from '../adapter-claude'
|
|
7
7
|
import { chromeDevtoolsToolRenders } from '../plugin-chrome-devtools'
|
|
8
8
|
import { taskToolRenders } from '../task'
|
|
9
9
|
|
|
@@ -28,7 +28,7 @@ export function ToolRenderer({
|
|
|
28
28
|
}) {
|
|
29
29
|
const toolName = item.name
|
|
30
30
|
const foundRenderer = TOOL_RENDERERS[toolName] ?? ToolRenderer.findRendererByInput(item)
|
|
31
|
-
const Renderer = foundRenderer ?? DefaultTool
|
|
31
|
+
const Renderer = foundRenderer ?? (isClaudeToolName(toolName) ? GenericClaudeTool : DefaultTool)
|
|
32
32
|
return <Renderer item={item} resultItem={resultItem} />
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -40,7 +40,7 @@ ToolRenderer.findRendererByInput = (item: Extract<ChatMessageContent, { type: 't
|
|
|
40
40
|
'command' in input &&
|
|
41
41
|
(('description' in input && input.description != null) || ('reason' in input && input.reason != null))
|
|
42
42
|
) {
|
|
43
|
-
return
|
|
43
|
+
return GenericClaudeTool
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
return null
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { CodeBlock } from '#~/components/CodeBlock'
|
|
2
|
+
import { MarkdownContent } from '#~/components/MarkdownContent'
|
|
3
|
+
import { safeJsonStringify } from '#~/utils/safe-serialize'
|
|
4
|
+
|
|
5
|
+
import { getStringList, getStructuredBlocks, looksLikeMarkdown } from './tool-result-content-utils'
|
|
6
|
+
|
|
7
|
+
export function ToolResultContent({
|
|
8
|
+
content,
|
|
9
|
+
preferMarkdown = false
|
|
10
|
+
}: {
|
|
11
|
+
content: unknown
|
|
12
|
+
preferMarkdown?: boolean
|
|
13
|
+
}) {
|
|
14
|
+
const structuredBlocks = getStructuredBlocks(content)
|
|
15
|
+
if (structuredBlocks != null) {
|
|
16
|
+
return (
|
|
17
|
+
<div className='tool-result-structured'>
|
|
18
|
+
{structuredBlocks.map((block, index) => (
|
|
19
|
+
block.type === 'text'
|
|
20
|
+
? (
|
|
21
|
+
<div className='tool-result-text' key={`text-${index}`}>
|
|
22
|
+
{block.format === 'markdown'
|
|
23
|
+
? <MarkdownContent content={block.text} />
|
|
24
|
+
: <div className='tool-result-text-content'>{block.text}</div>}
|
|
25
|
+
</div>
|
|
26
|
+
)
|
|
27
|
+
: (
|
|
28
|
+
<div className='tool-result-image-wrapper' key={`image-${index}`}>
|
|
29
|
+
<img
|
|
30
|
+
className='tool-result-image'
|
|
31
|
+
src={block.src}
|
|
32
|
+
alt={block.alt ?? ''}
|
|
33
|
+
width={block.width}
|
|
34
|
+
height={block.height}
|
|
35
|
+
/>
|
|
36
|
+
{block.title != null && block.title !== '' && (
|
|
37
|
+
<div className='tool-result-image-caption'>{block.title}</div>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
)
|
|
41
|
+
))}
|
|
42
|
+
</div>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const stringList = getStringList(content)
|
|
47
|
+
if (stringList != null) {
|
|
48
|
+
return (
|
|
49
|
+
<div className='tool-result-list'>
|
|
50
|
+
{stringList.map(item => (
|
|
51
|
+
<div className='tool-result-list-item' key={item}>{item}</div>
|
|
52
|
+
))}
|
|
53
|
+
</div>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (typeof content === 'string') {
|
|
58
|
+
if (content.startsWith('```') || (preferMarkdown && looksLikeMarkdown(content))) {
|
|
59
|
+
return <MarkdownContent content={content} />
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return <CodeBlock code={content} lang='text' />
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return <CodeBlock code={safeJsonStringify(content, 2)} lang='json' />
|
|
66
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Tooltip } from 'antd'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
import { TOOL_TOOLTIP_PROPS } from './tool-display'
|
|
5
|
+
|
|
6
|
+
export function ToolSummaryHeader({
|
|
7
|
+
icon,
|
|
8
|
+
title,
|
|
9
|
+
target,
|
|
10
|
+
targetTitle,
|
|
11
|
+
meta,
|
|
12
|
+
metaTitle,
|
|
13
|
+
expanded = false,
|
|
14
|
+
collapsible = false,
|
|
15
|
+
targetMonospace = false
|
|
16
|
+
}: {
|
|
17
|
+
icon?: React.ReactNode
|
|
18
|
+
title: React.ReactNode
|
|
19
|
+
target?: React.ReactNode
|
|
20
|
+
targetTitle?: string
|
|
21
|
+
meta?: React.ReactNode
|
|
22
|
+
metaTitle?: string
|
|
23
|
+
expanded?: boolean
|
|
24
|
+
collapsible?: boolean
|
|
25
|
+
targetMonospace?: boolean
|
|
26
|
+
}) {
|
|
27
|
+
const hasTarget = target != null && target !== ''
|
|
28
|
+
const targetNode = hasTarget
|
|
29
|
+
? (
|
|
30
|
+
<span className={`tool-summary-header__target ${targetMonospace ? 'tool-summary-header__target--mono' : ''}`}>
|
|
31
|
+
{target}
|
|
32
|
+
</span>
|
|
33
|
+
)
|
|
34
|
+
: null
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div className='tool-summary-header'>
|
|
38
|
+
<div className='tool-summary-header__lead'>
|
|
39
|
+
<span className='tool-summary-header__action'>
|
|
40
|
+
{icon != null && (
|
|
41
|
+
<span className='tool-summary-header__icon'>{icon}</span>
|
|
42
|
+
)}
|
|
43
|
+
<span className='tool-summary-header__title'>{title}</span>
|
|
44
|
+
</span>
|
|
45
|
+
{hasTarget && (
|
|
46
|
+
targetTitle != null && targetTitle !== ''
|
|
47
|
+
? (
|
|
48
|
+
<Tooltip title={targetTitle} {...TOOL_TOOLTIP_PROPS}>
|
|
49
|
+
{targetNode}
|
|
50
|
+
</Tooltip>
|
|
51
|
+
)
|
|
52
|
+
: targetNode
|
|
53
|
+
)}
|
|
54
|
+
{collapsible && (
|
|
55
|
+
<span className={`material-symbols-rounded tool-summary-header__toggle ${expanded ? 'is-expanded' : ''}`}>
|
|
56
|
+
expand_more
|
|
57
|
+
</span>
|
|
58
|
+
)}
|
|
59
|
+
</div>
|
|
60
|
+
{meta != null && (
|
|
61
|
+
<span className='tool-summary-header__meta' title={metaTitle}>
|
|
62
|
+
{meta}
|
|
63
|
+
</span>
|
|
64
|
+
)}
|
|
65
|
+
</div>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { toSerializable } from '#~/utils/safe-serialize'
|
|
2
|
+
|
|
3
|
+
import { getStringList, getStructuredBlocks } from './tool-result-content-utils'
|
|
4
|
+
|
|
5
|
+
const parseStructuredInput = (value: unknown) => {
|
|
6
|
+
if (typeof value !== 'string') {
|
|
7
|
+
return value
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const trimmed = value.trim()
|
|
11
|
+
if (
|
|
12
|
+
(trimmed.startsWith('{') && trimmed.endsWith('}')) ||
|
|
13
|
+
(trimmed.startsWith('[') && trimmed.endsWith(']'))
|
|
14
|
+
) {
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(trimmed)
|
|
17
|
+
} catch {
|
|
18
|
+
return value
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return value
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const hasMeaningfulToolValue = (value: unknown): boolean => {
|
|
26
|
+
const parsed = parseStructuredInput(toSerializable(value))
|
|
27
|
+
|
|
28
|
+
if (parsed == null) {
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (typeof parsed === 'string') {
|
|
33
|
+
return parsed.trim() !== ''
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (typeof parsed === 'number' || typeof parsed === 'boolean') {
|
|
37
|
+
return true
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (Array.isArray(parsed)) {
|
|
41
|
+
return parsed.some(item => hasMeaningfulToolValue(item))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (typeof parsed !== 'object') {
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (getStructuredBlocks(parsed) != null) {
|
|
49
|
+
return true
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (getStringList(parsed) != null) {
|
|
53
|
+
return true
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return Object.values(parsed as Record<string, unknown>).some(item => hasMeaningfulToolValue(item))
|
|
57
|
+
}
|