@vibe-forge/client 0.3.0 → 0.4.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.
Files changed (158) hide show
  1. package/cli.cjs +1 -1
  2. package/dist/assets/{arc-CwMXUVsq.js → arc-DgIxeTMg.js} +1 -1
  3. package/dist/assets/{blockDiagram-c4efeb88-CGxJV7KJ.js → blockDiagram-c4efeb88-CEAob3X9.js} +1 -1
  4. package/dist/assets/{c4Diagram-c83219d4-BKhin7cY.js → c4Diagram-c83219d4-DwIxpDKd.js} +1 -1
  5. package/dist/assets/channel-DhtnrNJ6.js +1 -0
  6. package/dist/assets/{classDiagram-beda092f-BASmn22R.js → classDiagram-beda092f-Cz1q8u_0.js} +1 -1
  7. package/dist/assets/{classDiagram-v2-2358418a-BUk9rNBX.js → classDiagram-v2-2358418a-CImgTuwd.js} +1 -1
  8. package/dist/assets/clone-7bHB6YkC.js +1 -0
  9. package/dist/assets/{createText-1719965b-2XqnWjQY.js → createText-1719965b-C1_HJcCc.js} +1 -1
  10. package/dist/assets/devicon-BWlTeAUU.woff +0 -0
  11. package/dist/assets/devicon-CirD-cQx.ttf +0 -0
  12. package/dist/assets/devicon-Dg8iWy0i.svg +1211 -0
  13. package/dist/assets/devicon-TqfHp33-.eot +0 -0
  14. package/dist/assets/{edges-96097737-B7e32Jeg.js → edges-96097737-BU8qStzd.js} +1 -1
  15. package/dist/assets/{erDiagram-0228fc6a-CCR2or72.js → erDiagram-0228fc6a-DNA1Fz2L.js} +1 -1
  16. package/dist/assets/{flowDb-c6c81e3f-B72HWT9x.js → flowDb-c6c81e3f-DjiCStMN.js} +1 -1
  17. package/dist/assets/{flowDiagram-50d868cf-WOi0KARY.js → flowDiagram-50d868cf-CSDi0-RD.js} +1 -1
  18. package/dist/assets/flowDiagram-v2-4f6560a1-_13Sz5Wh.js +1 -0
  19. package/dist/assets/{flowchart-elk-definition-6af322e1-i_Yd0LCE.js → flowchart-elk-definition-6af322e1-DrhIMas7.js} +1 -1
  20. package/dist/assets/{ganttDiagram-a2739b55-CFH9zF14.js → ganttDiagram-a2739b55-CTZnUP5z.js} +1 -1
  21. package/dist/assets/{gitGraphDiagram-82fe8481-DglKfMze.js → gitGraphDiagram-82fe8481-COOW7jTi.js} +1 -1
  22. package/dist/assets/{graph-BKbBNGPf.js → graph-CIkpD4Kx.js} +1 -1
  23. package/dist/assets/{index-5325376f-BK7F9nSl.js → index-5325376f-aVVRRTIu.js} +1 -1
  24. package/dist/assets/index-D1giUI7r.css +1 -0
  25. package/dist/assets/index-DRSI_ZIL.js +514 -0
  26. package/dist/assets/{infoDiagram-8eee0895-BLFL77_D.js → infoDiagram-8eee0895-DQpZ1LVD.js} +1 -1
  27. package/dist/assets/{journeyDiagram-c64418c1-CS9XctDL.js → journeyDiagram-c64418c1-DoKguIuk.js} +1 -1
  28. package/dist/assets/{layout-By3JZZGt.js → layout-Tnmha8Nh.js} +1 -1
  29. package/dist/assets/{line-9GUsXbwv.js → line-BQR2SOyl.js} +1 -1
  30. package/dist/assets/{linear-DzGV4E9N.js → linear-DlG0eemV.js} +1 -1
  31. package/dist/assets/{mermaid.core-CG3Ib42Q.js → mermaid.core-BnwYO0He.js} +6 -6
  32. package/dist/assets/{mindmap-definition-8da855dc-WQ3LPKJU.js → mindmap-definition-8da855dc-BllYwDID.js} +1 -1
  33. package/dist/assets/{pieDiagram-a8764435-DHVIUZiN.js → pieDiagram-a8764435-DwCkhPVc.js} +1 -1
  34. package/dist/assets/{quadrantDiagram-1e28029f-C3G9Ye8-.js → quadrantDiagram-1e28029f-c40GKTU0.js} +1 -1
  35. package/dist/assets/{requirementDiagram-08caed73-C9ES1D5G.js → requirementDiagram-08caed73-DnQp2Tk6.js} +1 -1
  36. package/dist/assets/{sankeyDiagram-a04cb91d-B4BKXclQ.js → sankeyDiagram-a04cb91d-CnJrs13b.js} +1 -1
  37. package/dist/assets/{sequenceDiagram-c5b8d532-DrgEb25G.js → sequenceDiagram-c5b8d532-1YBwnpKu.js} +1 -1
  38. package/dist/assets/{stateDiagram-1ecb1508-CF1XWARJ.js → stateDiagram-1ecb1508-BFBxQ6Fh.js} +1 -1
  39. package/dist/assets/{stateDiagram-v2-c2b004d7-IO3i3yXv.js → stateDiagram-v2-c2b004d7-Dmechvv2.js} +1 -1
  40. package/dist/assets/{styles-b4e223ce-DACN9aSc.js → styles-b4e223ce-DWWfWX8O.js} +1 -1
  41. package/dist/assets/{styles-ca3715f6-bekm2WLP.js → styles-ca3715f6-CKKvZxaU.js} +1 -1
  42. package/dist/assets/{styles-d45a18b0-OzTDVBb8.js → styles-d45a18b0-dKMOUh9p.js} +1 -1
  43. package/dist/assets/{svgDrawCommon-b86b1483-BWroJerr.js → svgDrawCommon-b86b1483-CBgjChPM.js} +1 -1
  44. package/dist/assets/{timeline-definition-faaaa080-CCfRNigO.js → timeline-definition-faaaa080-NCt-HHmb.js} +1 -1
  45. package/dist/assets/{xychartDiagram-f5964ef8-C3cbfVqN.js → xychartDiagram-f5964ef8-BJhXS4dG.js} +1 -1
  46. package/dist/index.html +2 -7
  47. package/index.html +0 -5
  48. package/package.json +11 -6
  49. package/src/App.tsx +2 -0
  50. package/src/api/README.md +26 -0
  51. package/src/api/automation.ts +88 -0
  52. package/src/api/base.ts +54 -0
  53. package/src/api/benchmark.ts +45 -0
  54. package/src/api/config.ts +24 -0
  55. package/src/api/knowledge.ts +72 -0
  56. package/src/api/projects.ts +15 -0
  57. package/src/api/sessions.ts +82 -0
  58. package/src/api/types.ts +20 -0
  59. package/src/api.ts +44 -269
  60. package/src/components/AutomationView/AutomationView.scss +5 -1
  61. package/src/components/AutomationView/RuleFormPanel.tsx +3 -2
  62. package/src/components/AutomationView/TaskList.scss +4 -6
  63. package/src/components/AutomationView/TaskList.tsx +2 -1
  64. package/src/components/AutomationView/TriggerList.scss +4 -1
  65. package/src/components/BenchmarkView/BenchmarkCasePanel.scss +267 -0
  66. package/src/components/BenchmarkView/BenchmarkCasePanel.tsx +309 -0
  67. package/src/components/BenchmarkView/BenchmarkSidebar.scss +182 -0
  68. package/src/components/BenchmarkView/BenchmarkSidebar.tsx +262 -0
  69. package/src/components/BenchmarkView/BenchmarkView.scss +78 -0
  70. package/src/components/BenchmarkView/index.tsx +197 -0
  71. package/src/components/BenchmarkView/types.ts +10 -0
  72. package/src/components/BenchmarkView/utils.ts +21 -0
  73. package/src/components/Chat.tsx +37 -29
  74. package/src/components/{chat/CodeBlock.tsx → CodeBlock.tsx} +3 -1
  75. package/src/components/ConfigView.tsx +7 -0
  76. package/src/components/{chat/MarkdownContent.tsx → MarkdownContent.tsx} +1 -1
  77. package/src/components/NavRail.tsx +7 -0
  78. package/src/components/chat/ChatHeader.scss +37 -19
  79. package/src/components/chat/ChatHeader.tsx +6 -9
  80. package/src/components/chat/ChatHistoryView.tsx +89 -45
  81. package/src/components/chat/CurrentTodoList.tsx +10 -9
  82. package/src/components/chat/{MessageItem.scss → Messages/MessageItem.scss} +14 -0
  83. package/src/components/chat/{MessageItem.tsx → Messages/MessageItem.tsx} +30 -8
  84. package/src/components/chat/{messageUtils.ts → Messages/message-utils.ts} +1 -1
  85. package/src/components/chat/{Sender.scss → Sender/Sender.scss} +80 -0
  86. package/src/components/chat/{Sender.tsx → Sender/Sender.tsx} +161 -5
  87. package/src/components/chat/tools/DefaultTool.tsx +184 -21
  88. package/src/components/chat/tools/adapter-claude/BashTool.scss +67 -51
  89. package/src/components/chat/tools/adapter-claude/BashTool.tsx +83 -49
  90. package/src/components/chat/tools/adapter-claude/GlobTool.scss +0 -79
  91. package/src/components/chat/tools/adapter-claude/GlobTool.tsx +16 -36
  92. package/src/components/chat/tools/adapter-claude/GrepTool.scss +0 -87
  93. package/src/components/chat/tools/adapter-claude/GrepTool.tsx +22 -41
  94. package/src/components/chat/tools/adapter-claude/LSTool.scss +0 -79
  95. package/src/components/chat/tools/adapter-claude/LSTool.tsx +15 -15
  96. package/src/components/chat/tools/adapter-claude/ReadTool.scss +0 -55
  97. package/src/components/chat/tools/adapter-claude/ReadTool.tsx +20 -42
  98. package/src/components/chat/tools/adapter-claude/TodoTool.scss +8 -23
  99. package/src/components/chat/tools/adapter-claude/TodoTool.tsx +24 -11
  100. package/src/components/chat/tools/adapter-claude/WriteTool.scss +21 -69
  101. package/src/components/chat/tools/adapter-claude/WriteTool.tsx +22 -58
  102. package/src/components/chat/tools/adapter-claude/index.ts +4 -10
  103. package/src/components/chat/tools/adapter-claude/utils.ts +54 -0
  104. package/src/components/chat/tools/core/ToolCallBox.scss +356 -0
  105. package/src/components/chat/{ToolGroup.tsx → tools/core/ToolGroup.tsx} +26 -7
  106. package/src/components/chat/{ToolRenderer.tsx → tools/core/ToolRenderer.tsx} +6 -4
  107. package/src/components/chat/tools/plugin-chrome-devtools/ChromeDevtoolsTool.scss +11 -0
  108. package/src/components/chat/tools/plugin-chrome-devtools/ChromeDevtoolsTool.tsx +75 -0
  109. package/src/components/chat/tools/plugin-chrome-devtools/index.ts +45 -0
  110. package/src/components/chat/tools/task/GetTaskInfoTool.scss +2 -27
  111. package/src/components/chat/tools/task/GetTaskInfoTool.tsx +48 -38
  112. package/src/components/chat/tools/task/ListTasksTool.scss +3 -28
  113. package/src/components/chat/tools/task/ListTasksTool.tsx +11 -8
  114. package/src/components/chat/tools/task/StartTasksTool.scss +3 -28
  115. package/src/components/chat/tools/task/StartTasksTool.tsx +14 -17
  116. package/src/components/chat/tools/task/components/TaskRow.scss +105 -0
  117. package/src/components/chat/tools/task/components/TaskRow.tsx +163 -0
  118. package/src/components/chat/tools/task/components/TaskToolCard.scss +15 -15
  119. package/src/components/chat/tools/task/components/TaskToolCard.tsx +8 -6
  120. package/src/components/config/ConfigSectionForm.tsx +12 -1
  121. package/src/components/config/channelDefinitions.ts +6 -0
  122. package/src/components/config/configSchema.ts +10 -1
  123. package/src/components/config/recordEditors/ChannelRecordEditor.scss +1 -0
  124. package/src/components/config/recordEditors/ChannelRecordEditor.tsx +397 -0
  125. package/src/components/config/recordEditors/index.tsx +1 -0
  126. package/src/components/knowledge-base/components/RuleItem.tsx +1 -1
  127. package/src/components/knowledge-base/components/SpecItem.tsx +1 -1
  128. package/src/hooks/chat/use-chat-interaction.ts +26 -0
  129. package/src/{components/chat/useChatModels.tsx → hooks/chat/use-chat-models.tsx} +46 -15
  130. package/src/hooks/chat/use-chat-permission-mode.ts +47 -0
  131. package/src/hooks/chat/use-chat-scroll.ts +51 -0
  132. package/src/hooks/chat/use-chat-session-actions.ts +147 -0
  133. package/src/hooks/chat/use-chat-session-messages.ts +250 -0
  134. package/src/hooks/chat/use-chat-session.ts +57 -0
  135. package/src/hooks/chat/use-chat-view.ts +39 -0
  136. package/src/main.tsx +10 -13
  137. package/src/resources/locales/en.json +66 -0
  138. package/src/resources/locales/zh.json +66 -0
  139. package/src/runtime-config.ts +52 -0
  140. package/src/vite-env.d.ts +11 -0
  141. package/src/ws.ts +5 -3
  142. package/vite.config.ts +12 -4
  143. package/dist/assets/channel-jbCEHqbG.js +0 -1
  144. package/dist/assets/clone-CCRKqS4L.js +0 -1
  145. package/dist/assets/flowDiagram-v2-4f6560a1-Baslbgn4.js +0 -1
  146. package/dist/assets/index-B0qfCb1G.css +0 -1
  147. package/dist/assets/index-CNo75dYr.js +0 -497
  148. package/src/components/chat/ToolCallBox.scss +0 -137
  149. package/src/components/chat/useChatSession.ts +0 -370
  150. /package/src/components/{chat/CodeBlock.scss → CodeBlock.scss} +0 -0
  151. /package/src/components/chat/{MessageFooter.tsx → Messages/MessageFooter.tsx} +0 -0
  152. /package/src/components/chat/{CompletionMenu.scss → Sender/CompletionMenu.scss} +0 -0
  153. /package/src/components/chat/{CompletionMenu.tsx → Sender/CompletionMenu.tsx} +0 -0
  154. /package/src/components/chat/{ThinkingStatus.scss → Sender/ThinkingStatus.scss} +0 -0
  155. /package/src/components/chat/{ThinkingStatus.tsx → Sender/ThinkingStatus.tsx} +0 -0
  156. /package/src/components/chat/{ToolCallBox.tsx → tools/core/ToolCallBox.tsx} +0 -0
  157. /package/src/components/chat/{ToolGroup.scss → tools/core/ToolGroup.scss} +0 -0
  158. /package/src/{components/chat/safeSerialize.ts → utils/safe-serialize.ts} +0 -0
@@ -1,42 +1,20 @@
1
- import React, { useMemo } from 'react'
2
1
  import './GlobTool.scss'
3
- import { useTranslation } from 'react-i18next'
4
-
2
+ import React, { useMemo } from 'react'
3
+ import type { ToolInputs } from '@vibe-forge/core'
4
+ import { ToolCallBox } from '../core/ToolCallBox'
5
+ import { safeJsonStringify } from '#~/utils/safe-serialize'
5
6
  import { defineToolRender } from '../defineToolRender'
6
- import { ToolCallBox } from '../../ToolCallBox'
7
- import { safeJsonStringify } from '../../safeSerialize'
8
7
  import { FileList } from './components/FileList'
8
+ import { normalizeResultLines } from './utils'
9
9
 
10
10
  export const GlobTool = defineToolRender(({ item, resultItem }) => {
11
- const { t } = useTranslation()
12
- const input = (item.input != null ? item.input : {}) as { pattern?: string; path?: string }
11
+ const input = (item.input != null ? item.input : {}) as ToolInputs['adapter:claude-code:Glob']
13
12
  const pattern = (input.pattern != null && input.pattern !== '') ? input.pattern : '*'
14
13
  const path = input.path
15
14
 
16
15
  const fileCount = useMemo(() => {
17
16
  if (!resultItem) return null
18
- const content = resultItem.content
19
- let lines: string[] = []
20
-
21
- if (typeof content === 'string') {
22
- if (content.trim().startsWith('[') && content.trim().endsWith(']')) {
23
- try {
24
- const parsed = JSON.parse(content)
25
- if (Array.isArray(parsed)) {
26
- lines = parsed.map(String)
27
- } else {
28
- lines = content.split('\n')
29
- }
30
- } catch (e) {
31
- lines = content.split('\n')
32
- }
33
- } else {
34
- lines = content.split('\n')
35
- }
36
- } else if (Array.isArray(content)) {
37
- lines = content.map(String)
38
- }
39
-
17
+ const lines = normalizeResultLines(resultItem.content)
40
18
  const count = lines.filter(line => line.trim() !== '').length
41
19
  return count
42
20
  }, [resultItem])
@@ -46,20 +24,22 @@ export const GlobTool = defineToolRender(({ item, resultItem }) => {
46
24
  <ToolCallBox
47
25
  header={
48
26
  <div className='tool-header-content'>
49
- <span className='material-symbols-rounded'>search</span>
50
- <span className='command-name'>Glob</span>
51
- <span className='pattern'>{pattern}</span>
27
+ <span className='material-symbols-rounded tool-header-icon'>search</span>
28
+ <span className='tool-header-title'>Glob</span>
29
+ <span className='tool-header-secondary'>{pattern}</span>
52
30
  {fileCount !== null && (
53
- <span className='file-count'>({fileCount} files)</span>
31
+ <span className='tool-header-chip'>{fileCount} files</span>
54
32
  )}
55
33
  </div>
56
34
  }
57
35
  content={
58
36
  <div className='tool-content'>
59
37
  {path && (
60
- <div className='input-details'>
61
- <span className='label'>Path:</span>
62
- <span className='value'>{path}</span>
38
+ <div className='tool-input-grid'>
39
+ <div className='tool-input-item'>
40
+ <span className='tool-input-label'>Path</span>
41
+ <span className='tool-input-value'>{path}</span>
42
+ </div>
63
43
  </div>
64
44
  )}
65
45
  {resultItem
@@ -1,91 +1,4 @@
1
1
  .grep-tool {
2
- .tool-header-content {
3
- display: flex;
4
- align-items: center;
5
- width: 100%;
6
- height: 100%;
7
- overflow: hidden;
8
-
9
- .material-symbols-rounded {
10
- font-size: 18px;
11
- display: flex;
12
- align-items: center;
13
- justify-content: center;
14
- color: var(--sub-text-color);
15
- height: 20px;
16
- width: 18px;
17
- margin-right: 8px;
18
- position: relative;
19
- top: 1px;
20
- }
21
-
22
- .command-name {
23
- font-size: 13px;
24
- font-weight: 500;
25
- color: var(--text-color);
26
- white-space: nowrap;
27
- line-height: 20px;
28
- display: flex;
29
- align-items: center;
30
- margin-right: 8px;
31
- }
32
-
33
- .pattern {
34
- font-size: 12px;
35
- color: var(--sub-text-color);
36
- white-space: nowrap;
37
- overflow: hidden;
38
- text-overflow: ellipsis;
39
- display: flex;
40
- align-items: center;
41
- min-width: 0;
42
- flex: 1;
43
- line-height: 20px;
44
- }
45
-
46
- .file-count {
47
- font-size: 12px;
48
- color: var(--sub-text-color);
49
- white-space: nowrap;
50
- margin-left: 8px;
51
- flex-shrink: 0;
52
- }
53
- }
54
-
55
- .input-details-grid {
56
- display: flex;
57
- flex-wrap: wrap;
58
- gap: 12px;
59
- padding: 8px 12px;
60
- border-bottom: 1px solid var(--border-color);
61
-
62
- .input-detail-item {
63
- display: flex;
64
- align-items: center;
65
- font-size: 12px;
66
- color: var(--sub-text-color);
67
-
68
- .label {
69
- font-weight: 500;
70
- margin-right: 6px;
71
- }
72
-
73
- .value {
74
- font-family: var(--font-mono);
75
- background: var(--bg-color-hover);
76
- padding: 2px 4px;
77
- border-radius: 4px;
78
- }
79
- }
80
- }
81
-
82
- .tool-placeholder {
83
- padding: 8px 12px;
84
- color: var(--sub-text-color);
85
- font-size: 12px;
86
- font-style: italic;
87
- }
88
-
89
2
  .file-list-container {
90
3
  border-radius: 0;
91
4
  border-left: none;
@@ -1,15 +1,13 @@
1
- import React, { useMemo } from 'react'
2
1
  import './GrepTool.scss'
3
- import { useTranslation } from 'react-i18next'
4
-
2
+ import React, { useMemo } from 'react'
3
+ import { CodeBlock } from '#~/components/CodeBlock'
4
+ import { ToolCallBox } from '../core/ToolCallBox'
5
+ import { safeJsonStringify } from '#~/utils/safe-serialize'
5
6
  import { defineToolRender } from '../defineToolRender'
6
- import { CodeBlock } from '../../CodeBlock'
7
- import { ToolCallBox } from '../../ToolCallBox'
8
- import { safeJsonStringify } from '../../safeSerialize'
9
7
  import { FileList } from './components/FileList'
8
+ import { normalizeResultLines } from './utils'
10
9
 
11
10
  export const GrepTool = defineToolRender(({ item, resultItem }) => {
12
- const { t } = useTranslation()
13
11
  const input = (item.input != null ? item.input : {}) as {
14
12
  pattern?: string
15
13
  path?: string
@@ -23,28 +21,7 @@ export const GrepTool = defineToolRender(({ item, resultItem }) => {
23
21
 
24
22
  const fileCount = useMemo(() => {
25
23
  if (!resultItem) return null
26
- const content = resultItem.content
27
- let lines: string[] = []
28
-
29
- if (typeof content === 'string') {
30
- if (content.trim().startsWith('[') && content.trim().endsWith(']')) {
31
- try {
32
- const parsed = JSON.parse(content)
33
- if (Array.isArray(parsed)) {
34
- lines = parsed.map(String)
35
- } else {
36
- lines = content.split('\n')
37
- }
38
- } catch (e) {
39
- lines = content.split('\n')
40
- }
41
- } else {
42
- lines = content.split('\n')
43
- }
44
- } else if (Array.isArray(content)) {
45
- lines = content.map(String)
46
- }
47
-
24
+ const lines = normalizeResultLines(resultItem.content)
48
25
  const count = lines.filter(line => line.trim() !== '').length
49
26
  return count
50
27
  }, [resultItem])
@@ -54,30 +31,34 @@ export const GrepTool = defineToolRender(({ item, resultItem }) => {
54
31
  <ToolCallBox
55
32
  header={
56
33
  <div className='tool-header-content'>
57
- <span className='material-symbols-rounded'>find_in_page</span>
58
- <span className='command-name'>Grep</span>
59
- <span className='pattern'>{pattern}</span>
34
+ <span className='material-symbols-rounded tool-header-icon'>find_in_page</span>
35
+ <span className='tool-header-title'>Grep</span>
36
+ <span className='tool-header-secondary'>{pattern}</span>
60
37
  {fileCount !== null && (
61
- <span className='file-count'>({fileCount} matches)</span>
38
+ <span className='tool-header-chip'>{fileCount} matches</span>
62
39
  )}
63
40
  </div>
64
41
  }
65
42
  content={
66
43
  <div className='tool-content'>
67
- {(path || fileGlob) && (
68
- <div className='input-details-grid'>
44
+ {(path || fileGlob || outputMode) && (
45
+ <div className='tool-input-grid'>
69
46
  {path && (
70
- <div className='input-detail-item'>
71
- <span className='label'>Path:</span>
72
- <span className='value'>{path}</span>
47
+ <div className='tool-input-item'>
48
+ <span className='tool-input-label'>Path</span>
49
+ <span className='tool-input-value'>{path}</span>
73
50
  </div>
74
51
  )}
75
52
  {fileGlob && (
76
- <div className='input-detail-item'>
77
- <span className='label'>Glob:</span>
78
- <span className='value'>{fileGlob}</span>
53
+ <div className='tool-input-item'>
54
+ <span className='tool-input-label'>Glob</span>
55
+ <span className='tool-input-value'>{fileGlob}</span>
79
56
  </div>
80
57
  )}
58
+ <div className='tool-input-item'>
59
+ <span className='tool-input-label'>Mode</span>
60
+ <span className='tool-input-value'>{outputMode}</span>
61
+ </div>
81
62
  </div>
82
63
  )}
83
64
  {resultItem
@@ -1,83 +1,4 @@
1
1
  .ls-tool {
2
- .tool-header-content {
3
- display: flex;
4
- align-items: center;
5
- width: 100%;
6
- height: 100%;
7
- overflow: hidden;
8
-
9
- .material-symbols-rounded {
10
- font-size: 18px;
11
- display: flex;
12
- align-items: center;
13
- justify-content: center;
14
- color: var(--sub-text-color);
15
- height: 20px;
16
- width: 18px;
17
- margin-right: 8px;
18
- position: relative;
19
- top: 1px;
20
- }
21
-
22
- .command-name {
23
- font-size: 13px;
24
- font-weight: 500;
25
- color: var(--text-color);
26
- white-space: nowrap;
27
- line-height: 20px;
28
- display: flex;
29
- align-items: center;
30
- margin-right: 8px;
31
- }
32
-
33
- .path {
34
- font-size: 12px;
35
- color: var(--sub-text-color);
36
- white-space: nowrap;
37
- overflow: hidden;
38
- text-overflow: ellipsis;
39
- display: flex;
40
- align-items: center;
41
- min-width: 0;
42
- flex: 1;
43
- line-height: 20px;
44
- }
45
-
46
- .file-count {
47
- font-size: 12px;
48
- color: var(--sub-text-color);
49
- white-space: nowrap;
50
- margin-left: 8px;
51
- flex-shrink: 0;
52
- }
53
- }
54
-
55
- .input-details {
56
- padding: 8px 12px;
57
- font-size: 12px;
58
- color: var(--sub-text-color);
59
- border-bottom: 1px solid var(--border-color);
60
-
61
- .label {
62
- font-weight: 500;
63
- margin-right: 8px;
64
- }
65
-
66
- code {
67
- font-family: var(--font-mono);
68
- background: var(--bg-color-hover);
69
- padding: 2px 4px;
70
- border-radius: 4px;
71
- }
72
- }
73
-
74
- .tool-placeholder {
75
- padding: 8px 12px;
76
- color: var(--sub-text-color);
77
- font-size: 12px;
78
- font-style: italic;
79
- }
80
-
81
2
  .file-list-container {
82
3
  border-radius: 0;
83
4
  border: none;
@@ -1,16 +1,14 @@
1
- import React, { useMemo } from 'react'
2
1
  import './LsTool.scss'
3
- import { useTranslation } from 'react-i18next'
4
-
2
+ import React, { useMemo } from 'react'
3
+ import type { ToolInputs } from '@vibe-forge/core'
5
4
  import { defineToolRender } from '../defineToolRender'
6
- import { CodeBlock } from '../../CodeBlock'
7
- import { ToolCallBox } from '../../ToolCallBox'
8
- import { safeJsonStringify } from '../../safeSerialize'
5
+ import { CodeBlock } from '#~/components/CodeBlock'
6
+ import { ToolCallBox } from '../core/ToolCallBox'
7
+ import { safeJsonStringify } from '#~/utils/safe-serialize'
9
8
  import { FileList } from './components/FileList'
10
9
 
11
10
  export const LsTool = defineToolRender(({ item, resultItem }) => {
12
- const { t } = useTranslation()
13
- const input = (item.input != null ? item.input : {}) as { path?: string; ignore?: string[] }
11
+ const input = (item.input != null ? item.input : {}) as ToolInputs['adapter:claude-code:LS']
14
12
  const path = (input.path != null && input.path !== '') ? input.path : 'current directory'
15
13
  const ignore = input.ignore
16
14
 
@@ -43,20 +41,22 @@ export const LsTool = defineToolRender(({ item, resultItem }) => {
43
41
  <ToolCallBox
44
42
  header={
45
43
  <div className='tool-header-content'>
46
- <span className='material-symbols-rounded'>folder_open</span>
47
- <span className='command-name'>LS</span>
48
- <span className='path'>{path}</span>
44
+ <span className='material-symbols-rounded tool-header-icon'>folder_open</span>
45
+ <span className='tool-header-title'>LS</span>
46
+ <span className='tool-header-secondary'>{path}</span>
49
47
  {fileCount !== null && (
50
- <span className='file-count'>({fileCount} files)</span>
48
+ <span className='tool-header-chip'>{fileCount} files</span>
51
49
  )}
52
50
  </div>
53
51
  }
54
52
  content={
55
53
  <div className='tool-content'>
56
54
  {ignore && ignore.length > 0 && (
57
- <div className='input-details'>
58
- <span className='label'>Ignore:</span>
59
- <code>{JSON.stringify(ignore)}</code>
55
+ <div className='tool-input-grid'>
56
+ <div className='tool-input-item'>
57
+ <span className='tool-input-label'>Ignore</span>
58
+ <span className='tool-input-value'>{JSON.stringify(ignore)}</span>
59
+ </div>
60
60
  </div>
61
61
  )}
62
62
 
@@ -1,57 +1,2 @@
1
1
  .read-tool {
2
- .tool-header-content {
3
- display: flex;
4
- align-items: center;
5
- width: 100%;
6
- height: 100%;
7
- overflow: hidden;
8
-
9
- .material-symbols-rounded {
10
- font-size: 18px;
11
- display: flex;
12
- align-items: center;
13
- justify-content: center;
14
- color: var(--sub-text-color);
15
- height: 20px;
16
- width: 18px;
17
- margin-right: 8px;
18
- position: relative;
19
- top: 1px;
20
- }
21
-
22
- .file-name {
23
- font-size: 13px;
24
- font-weight: 500;
25
- color: var(--text-color);
26
- white-space: nowrap;
27
- line-height: 20px;
28
- display: flex;
29
- align-items: center;
30
- }
31
-
32
- .file-path {
33
- font-size: 12px;
34
- color: var(--sub-text-color);
35
- white-space: nowrap;
36
- overflow: hidden;
37
- text-overflow: ellipsis;
38
- display: flex;
39
- align-items: center;
40
- min-width: 0;
41
- flex: 1;
42
- line-height: 20px;
43
-
44
- &::before {
45
- content: '—';
46
- margin: 0 8px;
47
- color: var(--border-color);
48
- font-weight: 300;
49
- }
50
- }
51
- .reading-placeholder {
52
- padding: 8px;
53
- color: var(--sub-text-color);
54
- font-size: 12px;
55
- }
56
- }
57
2
  }
@@ -1,43 +1,20 @@
1
- import React from 'react'
2
1
  import './ReadTool.scss'
2
+ import React from 'react'
3
3
  import { useTranslation } from 'react-i18next'
4
-
4
+ import type { ToolInputs } from '@vibe-forge/core'
5
+ import { CodeBlock } from '#~/components/CodeBlock'
6
+ import { ToolCallBox } from '../core/ToolCallBox'
7
+ import { safeJsonStringify } from '#~/utils/safe-serialize'
5
8
  import { defineToolRender } from '../defineToolRender'
6
- import { CodeBlock } from '../../CodeBlock'
7
- import { ToolCallBox } from '../../ToolCallBox'
8
- import { safeJsonStringify } from '../../safeSerialize'
9
+ import { getFileInfo, getLanguageFromPath } from './utils'
9
10
 
10
11
  export const ReadTool = defineToolRender(({ item, resultItem }) => {
11
12
  const { t } = useTranslation()
12
- const input = (item.input != null ? item.input : {}) as { file_path?: string }
13
- const filePath = (input.file_path != null && input.file_path !== '') ? input.file_path : 'unknown file'
14
- const lastPart = filePath.split('/').pop()
15
- const fileName = (lastPart != null && lastPart !== '') ? lastPart : filePath
16
- const dirPath = filePath.includes('/') ? filePath.substring(0, filePath.lastIndexOf('/')) : ''
17
-
18
- const getLanguage = (path: string) => {
19
- const extPart = path.split('.').pop()
20
- const ext = (extPart != null && extPart !== '') ? extPart.toLowerCase() : ''
21
- const langMap: Record<string, string> = {
22
- 'js': 'javascript',
23
- 'jsx': 'jsx',
24
- 'ts': 'typescript',
25
- 'tsx': 'tsx',
26
- 'py': 'python',
27
- 'md': 'markdown',
28
- 'json': 'json',
29
- 'scss': 'scss',
30
- 'css': 'css',
31
- 'html': 'html',
32
- 'sh': 'bash',
33
- 'yml': 'yaml',
34
- 'yaml': 'yaml',
35
- 'sql': 'sql'
36
- }
37
- return langMap[ext] || 'text'
38
- }
39
-
40
- const language = getLanguage(filePath)
13
+ const input = (item.input != null ? item.input : {}) as ToolInputs['adapter:claude-code:Read']
14
+ const { filePath } = getFileInfo(input.file_path)
15
+ const language = getLanguageFromPath(filePath)
16
+ const offset = input.offset
17
+ const limit = input.limit
41
18
 
42
19
  const cleanContent = (content: any): string => {
43
20
  if (typeof content !== 'string') return safeJsonStringify(content, 2)
@@ -53,24 +30,25 @@ export const ReadTool = defineToolRender(({ item, resultItem }) => {
53
30
  <ToolCallBox
54
31
  header={
55
32
  <div className='tool-header-content'>
56
- <span className='material-symbols-rounded'>description</span>
57
- <span className='command-name'>{t('chat.tools.read')}</span>
58
- <span className='file-name'>{fileName}</span>
33
+ <span className='material-symbols-rounded tool-header-icon'>visibility</span>
34
+ <span className='tool-header-primary tool-header-mono tool-header-row-text'>{filePath}</span>
35
+ {(typeof offset === 'number' && Number.isFinite(offset)) && (
36
+ <span className='tool-header-chip'>{t('chat.tools.offset')}:{offset}</span>
37
+ )}
38
+ {(typeof limit === 'number' && Number.isFinite(limit)) && (
39
+ <span className='tool-header-chip'>{t('chat.tools.limit')}:{limit}</span>
40
+ )}
59
41
  </div>
60
42
  }
61
43
  content={
62
44
  <div className='tool-content'>
63
- {dirPath && (
64
- <div className='file-path'>
65
- {dirPath}
66
- </div>
67
- )}
68
45
  {resultItem
69
46
  ? (
70
47
  <div className='result-content'>
71
48
  <CodeBlock
72
49
  code={cleanContent(resultItem.content)}
73
50
  lang={language}
51
+ hideHeader={true}
74
52
  />
75
53
  </div>
76
54
  )
@@ -1,34 +1,15 @@
1
1
  .todo-tool {
2
- .todo-header {
3
- display: flex;
4
- align-items: center;
5
- gap: 8px;
6
-
7
- .status-icon {
8
- font-size: 18px;
9
- }
10
-
11
- .todo-title {
12
- font-size: 13px;
13
- font-weight: 500;
14
- }
15
- }
16
-
17
- .tool-content {
18
- padding: 4px 0;
19
- }
20
-
21
2
  .todo-item {
22
3
  display: flex;
23
4
  align-items: flex-start;
24
5
  gap: 8px;
25
6
  margin-bottom: 2px;
26
- padding: 6px 12px;
7
+ padding: 6px 4px;
27
8
  border-radius: 0;
28
9
  transition: background-color .2s;
29
10
 
30
11
  &:hover {
31
- background-color: rgba(0, 0, 0, .04);
12
+ background-color: var(--bg-color-hover);
32
13
  }
33
14
 
34
15
  &:last-child {
@@ -43,13 +24,13 @@
43
24
  }
44
25
 
45
26
  .status-icon {
46
- color: #10b981;
27
+ color: var(--success-color);
47
28
  }
48
29
  }
49
30
 
50
31
  &.in_progress {
51
32
  .status-icon {
52
- color: #3b82f6;
33
+ color: var(--primary-color);
53
34
  }
54
35
  }
55
36
 
@@ -59,6 +40,10 @@
59
40
  }
60
41
  }
61
42
 
43
+ .status-icon {
44
+ font-size: 16px;
45
+ }
46
+
62
47
  .todo-info {
63
48
  display: flex;
64
49
  flex-direction: column;
@@ -1,29 +1,42 @@
1
1
  import './TodoTool.scss'
2
2
  import React from 'react'
3
3
  import { useTranslation } from 'react-i18next'
4
+ import type { ToolInputs } from '@vibe-forge/core'
4
5
 
6
+ import { ToolCallBox } from '../core/ToolCallBox'
5
7
  import { defineToolRender } from '../defineToolRender'
6
- import { ToolCallBox } from '../../ToolCallBox'
7
8
 
8
- interface TodoItem {
9
- content: string
10
- status: 'pending' | 'in_progress' | 'completed'
11
- activeForm?: string
12
- }
9
+ type TodoItem = ToolInputs['adapter:claude-code:TodoWrite']['todos'][number]
13
10
 
14
11
  export const TodoTool = defineToolRender(({ item }) => {
15
12
  const { t } = useTranslation()
16
- const input = (item.input != null ? item.input : {}) as { todos?: TodoItem[] }
17
- const todos = input.todos ?? []
13
+ const input = (item.input != null ? item.input : {}) as Partial<ToolInputs['adapter:claude-code:TodoWrite']>
14
+ const todos = (input.todos ?? []) as TodoItem[]
15
+ const totalCount = todos.length
16
+ const completedCount = todos.filter(todo => todo.status === 'completed').length
17
+ const inProgressCount = todos.filter(todo => todo.status === 'in_progress').length
18
+ const pendingCount = totalCount - completedCount - inProgressCount
18
19
 
19
20
  return (
20
21
  <div className='tool-group todo-tool'>
21
22
  <ToolCallBox
22
23
  defaultExpanded={true}
23
24
  header={
24
- <div className='todo-header'>
25
- <span className='material-symbols-rounded status-icon'>task_alt</span>
26
- <span className='todo-title'>{t('chat.tools.todo')}</span>
25
+ <div className='tool-header-content'>
26
+ <span className='material-symbols-rounded tool-header-icon'>task_alt</span>
27
+ <span className='tool-header-title'>{t('chat.tools.todo')}</span>
28
+ {totalCount > 0 && (
29
+ <span className='tool-header-chip'>{totalCount} total</span>
30
+ )}
31
+ {inProgressCount > 0 && (
32
+ <span className='tool-header-chip'>{inProgressCount} doing</span>
33
+ )}
34
+ {pendingCount > 0 && (
35
+ <span className='tool-header-chip'>{pendingCount} todo</span>
36
+ )}
37
+ {completedCount > 0 && (
38
+ <span className='tool-header-chip'>{completedCount} done</span>
39
+ )}
27
40
  </div>
28
41
  }
29
42
  content={