@vibe-forge/client 0.2.0-alpha.9 → 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 (166) hide show
  1. package/cli.cjs +1 -1
  2. package/dist/assets/{arc-CybT1Fs2.js → arc-DgIxeTMg.js} +1 -1
  3. package/dist/assets/{blockDiagram-c4efeb88-BY5Aoa-D.js → blockDiagram-c4efeb88-CEAob3X9.js} +1 -1
  4. package/dist/assets/{c4Diagram-c83219d4-F42hTbzS.js → c4Diagram-c83219d4-DwIxpDKd.js} +1 -1
  5. package/dist/assets/channel-DhtnrNJ6.js +1 -0
  6. package/dist/assets/{classDiagram-beda092f-D-tIPp-3.js → classDiagram-beda092f-Cz1q8u_0.js} +1 -1
  7. package/dist/assets/{classDiagram-v2-2358418a-J57aCe6u.js → classDiagram-v2-2358418a-CImgTuwd.js} +1 -1
  8. package/dist/assets/clone-7bHB6YkC.js +1 -0
  9. package/dist/assets/{createText-1719965b-ByfEqOF-.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-CMEArkOa.js → edges-96097737-BU8qStzd.js} +1 -1
  15. package/dist/assets/{erDiagram-0228fc6a-Cf8mX2aj.js → erDiagram-0228fc6a-DNA1Fz2L.js} +1 -1
  16. package/dist/assets/{flowDb-c6c81e3f-DG6WKyo7.js → flowDb-c6c81e3f-DjiCStMN.js} +1 -1
  17. package/dist/assets/{flowDiagram-50d868cf-CstUxz-w.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--4CRoQ-H.js → flowchart-elk-definition-6af322e1-DrhIMas7.js} +1 -1
  20. package/dist/assets/{ganttDiagram-a2739b55-DYgHcKd-.js → ganttDiagram-a2739b55-CTZnUP5z.js} +1 -1
  21. package/dist/assets/{gitGraphDiagram-82fe8481-DDSVpfsd.js → gitGraphDiagram-82fe8481-COOW7jTi.js} +1 -1
  22. package/dist/assets/{graph-CRWF39gX.js → graph-CIkpD4Kx.js} +1 -1
  23. package/dist/assets/{index-5325376f-W1hft795.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-D4SHcix6.js → infoDiagram-8eee0895-DQpZ1LVD.js} +1 -1
  27. package/dist/assets/{journeyDiagram-c64418c1-MWgCkVoE.js → journeyDiagram-c64418c1-DoKguIuk.js} +1 -1
  28. package/dist/assets/{layout-C88ObkCf.js → layout-Tnmha8Nh.js} +1 -1
  29. package/dist/assets/{line-C7WAYMt5.js → line-BQR2SOyl.js} +1 -1
  30. package/dist/assets/{linear-C4msxfcU.js → linear-DlG0eemV.js} +1 -1
  31. package/dist/assets/{mermaid.core-Cabag9SZ.js → mermaid.core-BnwYO0He.js} +6 -6
  32. package/dist/assets/{mindmap-definition-8da855dc-CeS8ETXx.js → mindmap-definition-8da855dc-BllYwDID.js} +1 -1
  33. package/dist/assets/{pieDiagram-a8764435-BvjyKnq5.js → pieDiagram-a8764435-DwCkhPVc.js} +1 -1
  34. package/dist/assets/{quadrantDiagram-1e28029f-DzYvpbNM.js → quadrantDiagram-1e28029f-c40GKTU0.js} +1 -1
  35. package/dist/assets/{requirementDiagram-08caed73-DHIoDbyo.js → requirementDiagram-08caed73-DnQp2Tk6.js} +1 -1
  36. package/dist/assets/{sankeyDiagram-a04cb91d-BFSGnQGs.js → sankeyDiagram-a04cb91d-CnJrs13b.js} +1 -1
  37. package/dist/assets/{sequenceDiagram-c5b8d532-_LM3BJ5-.js → sequenceDiagram-c5b8d532-1YBwnpKu.js} +1 -1
  38. package/dist/assets/{stateDiagram-1ecb1508-DwORjOzl.js → stateDiagram-1ecb1508-BFBxQ6Fh.js} +1 -1
  39. package/dist/assets/{stateDiagram-v2-c2b004d7-B4cAWWz1.js → stateDiagram-v2-c2b004d7-Dmechvv2.js} +1 -1
  40. package/dist/assets/{styles-b4e223ce-D_rmV3B_.js → styles-b4e223ce-DWWfWX8O.js} +1 -1
  41. package/dist/assets/{styles-ca3715f6-BFx4VuFc.js → styles-ca3715f6-CKKvZxaU.js} +1 -1
  42. package/dist/assets/{styles-d45a18b0-BE3106vL.js → styles-d45a18b0-dKMOUh9p.js} +1 -1
  43. package/dist/assets/{svgDrawCommon-b86b1483-DwDTO1op.js → svgDrawCommon-b86b1483-CBgjChPM.js} +1 -1
  44. package/dist/assets/{timeline-definition-faaaa080-C4b8qUQZ.js → timeline-definition-faaaa080-NCt-HHmb.js} +1 -1
  45. package/dist/assets/{xychartDiagram-f5964ef8-BRJ9Z4u-.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 -241
  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 +13 -1
  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/NewSessionGuide.scss +35 -13
  86. package/src/components/chat/NewSessionGuide.tsx +20 -10
  87. package/src/components/chat/{Sender.scss → Sender/Sender.scss} +80 -0
  88. package/src/components/chat/{Sender.tsx → Sender/Sender.tsx} +161 -5
  89. package/src/components/chat/tools/DefaultTool.tsx +184 -21
  90. package/src/components/chat/tools/adapter-claude/BashTool.scss +67 -51
  91. package/src/components/chat/tools/adapter-claude/BashTool.tsx +83 -49
  92. package/src/components/chat/tools/adapter-claude/GlobTool.scss +0 -79
  93. package/src/components/chat/tools/adapter-claude/GlobTool.tsx +16 -36
  94. package/src/components/chat/tools/adapter-claude/GrepTool.scss +0 -87
  95. package/src/components/chat/tools/adapter-claude/GrepTool.tsx +22 -41
  96. package/src/components/chat/tools/adapter-claude/LSTool.scss +0 -79
  97. package/src/components/chat/tools/adapter-claude/LSTool.tsx +15 -15
  98. package/src/components/chat/tools/adapter-claude/ReadTool.scss +0 -55
  99. package/src/components/chat/tools/adapter-claude/ReadTool.tsx +20 -42
  100. package/src/components/chat/tools/adapter-claude/TodoTool.scss +8 -23
  101. package/src/components/chat/tools/adapter-claude/TodoTool.tsx +24 -11
  102. package/src/components/chat/tools/adapter-claude/WriteTool.scss +21 -69
  103. package/src/components/chat/tools/adapter-claude/WriteTool.tsx +22 -58
  104. package/src/components/chat/tools/adapter-claude/index.ts +4 -10
  105. package/src/components/chat/tools/adapter-claude/utils.ts +54 -0
  106. package/src/components/chat/tools/core/ToolCallBox.scss +356 -0
  107. package/src/components/chat/{ToolGroup.tsx → tools/core/ToolGroup.tsx} +26 -7
  108. package/src/components/chat/{ToolRenderer.tsx → tools/core/ToolRenderer.tsx} +6 -4
  109. package/src/components/chat/tools/plugin-chrome-devtools/ChromeDevtoolsTool.scss +11 -0
  110. package/src/components/chat/tools/plugin-chrome-devtools/ChromeDevtoolsTool.tsx +75 -0
  111. package/src/components/chat/tools/plugin-chrome-devtools/index.ts +45 -0
  112. package/src/components/chat/tools/task/GetTaskInfoTool.scss +2 -27
  113. package/src/components/chat/tools/task/GetTaskInfoTool.tsx +48 -38
  114. package/src/components/chat/tools/task/ListTasksTool.scss +3 -28
  115. package/src/components/chat/tools/task/ListTasksTool.tsx +11 -8
  116. package/src/components/chat/tools/task/StartTasksTool.scss +3 -28
  117. package/src/components/chat/tools/task/StartTasksTool.tsx +14 -17
  118. package/src/components/chat/tools/task/components/TaskRow.scss +105 -0
  119. package/src/components/chat/tools/task/components/TaskRow.tsx +163 -0
  120. package/src/components/chat/tools/task/components/TaskToolCard.scss +15 -15
  121. package/src/components/chat/tools/task/components/TaskToolCard.tsx +8 -6
  122. package/src/components/config/AppSettingsPanel.tsx +33 -0
  123. package/src/components/config/ConfigSectionForm.tsx +12 -1
  124. package/src/components/config/channelDefinitions.ts +6 -0
  125. package/src/components/config/configSchema.ts +10 -1
  126. package/src/components/config/recordEditors/ChannelRecordEditor.scss +1 -0
  127. package/src/components/config/recordEditors/ChannelRecordEditor.tsx +397 -0
  128. package/src/components/config/recordEditors/index.tsx +1 -0
  129. package/src/components/knowledge-base/KnowledgeBaseView.tsx +51 -3
  130. package/src/components/knowledge-base/components/RuleItem.tsx +79 -0
  131. package/src/components/knowledge-base/components/RuleList.scss +5 -0
  132. package/src/components/knowledge-base/components/RuleList.tsx +70 -0
  133. package/src/components/knowledge-base/components/RulesTab.tsx +32 -7
  134. package/src/components/knowledge-base/components/SpecItem.tsx +1 -1
  135. package/src/hooks/chat/use-chat-interaction.ts +26 -0
  136. package/src/{components/chat/useChatModels.tsx → hooks/chat/use-chat-models.tsx} +65 -16
  137. package/src/hooks/chat/use-chat-permission-mode.ts +47 -0
  138. package/src/hooks/chat/use-chat-scroll.ts +51 -0
  139. package/src/hooks/chat/use-chat-session-actions.ts +147 -0
  140. package/src/hooks/chat/use-chat-session-messages.ts +250 -0
  141. package/src/hooks/chat/use-chat-session.ts +57 -0
  142. package/src/hooks/chat/use-chat-view.ts +39 -0
  143. package/src/main.tsx +10 -13
  144. package/src/resources/locales/en.json +73 -0
  145. package/src/resources/locales/zh.json +73 -0
  146. package/src/runtime-config.ts +52 -0
  147. package/src/store/index.ts +2 -0
  148. package/src/vite-env.d.ts +11 -0
  149. package/src/ws.ts +5 -3
  150. package/vite.config.ts +12 -4
  151. package/dist/assets/channel-DrWdSpqV.js +0 -1
  152. package/dist/assets/clone-D0cC8LLB.js +0 -1
  153. package/dist/assets/flowDiagram-v2-4f6560a1-Bf_DH7dp.js +0 -1
  154. package/dist/assets/index-CNMzWvKV.js +0 -497
  155. package/dist/assets/index-PEmISxiy.css +0 -1
  156. package/src/components/chat/ToolCallBox.scss +0 -137
  157. package/src/components/chat/useChatSession.ts +0 -370
  158. /package/src/components/{chat/CodeBlock.scss → CodeBlock.scss} +0 -0
  159. /package/src/components/chat/{MessageFooter.tsx → Messages/MessageFooter.tsx} +0 -0
  160. /package/src/components/chat/{CompletionMenu.scss → Sender/CompletionMenu.scss} +0 -0
  161. /package/src/components/chat/{CompletionMenu.tsx → Sender/CompletionMenu.tsx} +0 -0
  162. /package/src/components/chat/{ThinkingStatus.scss → Sender/ThinkingStatus.scss} +0 -0
  163. /package/src/components/chat/{ThinkingStatus.tsx → Sender/ThinkingStatus.tsx} +0 -0
  164. /package/src/components/chat/{ToolCallBox.tsx → tools/core/ToolCallBox.tsx} +0 -0
  165. /package/src/components/chat/{ToolGroup.scss → tools/core/ToolGroup.scss} +0 -0
  166. /package/src/{components/chat/safeSerialize.ts → utils/safe-serialize.ts} +0 -0
@@ -2,10 +2,11 @@ import './GetTaskInfoTool.scss'
2
2
 
3
3
  import React, { useMemo } from 'react'
4
4
  import { useTranslation } from 'react-i18next'
5
+ import type { ToolInputs } from '@vibe-forge/core'
5
6
 
7
+ import { ToolCallBox } from '../core/ToolCallBox'
6
8
  import { defineToolRender } from '../defineToolRender'
7
- import { ToolCallBox } from '../../ToolCallBox'
8
- import { TaskToolCard } from './components/TaskToolCard'
9
+ import { TaskRow } from './components/TaskRow'
9
10
 
10
11
  interface TaskResult {
11
12
  taskId: string
@@ -22,7 +23,7 @@ interface TaskResult {
22
23
  export const GetTaskInfoTool = defineToolRender(({ item, resultItem }) => {
23
24
  const { t } = useTranslation()
24
25
 
25
- const input = (item.input != null ? item.input : {}) as { taskId?: string }
26
+ const input = (item.input != null ? item.input : {}) as Partial<ToolInputs['GetTaskInfo']>
26
27
  const inputTaskId = input.taskId
27
28
 
28
29
  const taskResult = useMemo(() => {
@@ -34,18 +35,23 @@ export const GetTaskInfoTool = defineToolRender(({ item, resultItem }) => {
34
35
  return parsed[0] ?? null
35
36
  }, [resultItem?.content])
36
37
 
37
- const logs = taskResult?.logs ?? []
38
- const metaChips = taskResult ? (() => {
39
- const { exitCode, ...cardMeta } = taskResult
40
- return [
41
- cardMeta.adapter,
42
- cardMeta.type === 'default' ? undefined : cardMeta.type,
43
- cardMeta.name,
44
- cardMeta.status,
45
- exitCode != null ? t('chat.tools.taskExitCode', { code: exitCode }) : undefined,
46
- cardMeta.background === false ? t('chat.tools.startTasksForeground') : cardMeta.background === true ? t('chat.tools.startTasksBackground') : undefined
47
- ]
48
- })() : []
38
+ const metaChips = taskResult
39
+ ? (() => {
40
+ const { exitCode, ...cardMeta } = taskResult
41
+ return [
42
+ cardMeta.adapter,
43
+ cardMeta.type === 'default' ? undefined : cardMeta.type,
44
+ cardMeta.name,
45
+ cardMeta.status,
46
+ exitCode != null ? t('chat.tools.taskExitCode', { code: exitCode }) : undefined,
47
+ cardMeta.background === false
48
+ ? t('chat.tools.startTasksForeground')
49
+ : cardMeta.background === true
50
+ ? t('chat.tools.startTasksBackground')
51
+ : undefined
52
+ ]
53
+ })()
54
+ : []
49
55
  const titleFallback = t('chat.tools.task')
50
56
 
51
57
  return (
@@ -53,33 +59,37 @@ export const GetTaskInfoTool = defineToolRender(({ item, resultItem }) => {
53
59
  <ToolCallBox
54
60
  defaultExpanded={true}
55
61
  header={
56
- <div className='get-task-info-tool__header'>
57
- <span className='material-symbols-rounded get-task-info-tool__icon'>info</span>
58
- <span className='get-task-info-tool__title'>{t('chat.tools.getTaskInfo')}</span>
59
- <span className='get-task-info-tool__count'>{taskResult ? 1 : 0}</span>
62
+ <div className='tool-header-content'>
63
+ <span className='material-symbols-rounded tool-header-icon'>info</span>
64
+ <span className='tool-header-title'>{t('chat.tools.getTaskInfo')}</span>
65
+ <span className='tool-header-chip'>{taskResult ? 1 : 0}</span>
60
66
  </div>
61
67
  }
62
68
  content={
63
69
  <div className='tool-content'>
64
- {taskResult ? (
65
- (() => {
66
- const { taskId, exitCode, ...cardProps } = taskResult
67
- return (
68
- <TaskToolCard
69
- {...cardProps}
70
- logs={logs}
71
- sessionId={taskId || inputTaskId}
72
- titleFallback={titleFallback}
73
- metaChips={metaChips}
74
- showExecutionIcon={false}
75
- />
76
- )
77
- })()
78
- ) : (
79
- <div className='get-task-info-tool__empty'>
80
- {t('chat.tools.startTasksEmpty')}
81
- </div>
82
- )}
70
+ {taskResult
71
+ ? (
72
+ (() => {
73
+ const { taskId, exitCode, ...cardProps } = taskResult
74
+ return (
75
+ <TaskRow
76
+ description={cardProps.description}
77
+ status={cardProps.status}
78
+ background={cardProps.background}
79
+ sessionId={taskId || inputTaskId}
80
+ logs={cardProps.logs}
81
+ titleFallback={titleFallback}
82
+ metaChips={metaChips}
83
+ showExecutionIcon={false}
84
+ />
85
+ )
86
+ })()
87
+ )
88
+ : (
89
+ <div className='get-task-info-tool__empty'>
90
+ {t('chat.tools.startTasksEmpty')}
91
+ </div>
92
+ )}
83
93
  </div>
84
94
  }
85
95
  />
@@ -13,40 +13,15 @@
13
13
  );
14
14
  }
15
15
 
16
- &__header {
17
- display: flex;
18
- align-items: center;
19
- gap: 8px;
20
- }
21
-
22
- &__icon {
23
- font-size: 16px;
24
- }
25
-
26
- &__title {
27
- font-weight: 600;
28
- }
29
-
30
- &__count {
31
- margin-left: 4px;
32
- padding: 1px 6px;
33
- border: 1px solid var(--border-color);
34
- border-radius: 999px;
35
- font-size: 11px;
36
- font-weight: 600;
37
- color: var(--sub-text-color);
38
- background: color-mix(in srgb, var(--bg-color) 70%, transparent 30%);
39
- }
40
-
41
16
  &__list {
42
- padding: 10px 12px;
17
+ padding: 0;
43
18
  display: flex;
44
19
  flex-direction: column;
45
- gap: 8px;
20
+ gap: 0;
46
21
  }
47
22
 
48
23
  &__empty {
49
- padding: 10px 12px;
24
+ padding: 8px 10px;
50
25
  border: 1px dashed var(--border-color);
51
26
  border-radius: 8px;
52
27
  font-size: 12px;
@@ -3,9 +3,9 @@ import './ListTasksTool.scss'
3
3
  import React, { useMemo } from 'react'
4
4
  import { useTranslation } from 'react-i18next'
5
5
 
6
- import { ToolCallBox } from '../../ToolCallBox'
6
+ import { ToolCallBox } from '../core/ToolCallBox'
7
7
  import { defineToolRender } from '../defineToolRender'
8
- import { TaskToolCard } from './components/TaskToolCard'
8
+ import { TaskRow } from './components/TaskRow'
9
9
 
10
10
  interface TaskResult {
11
11
  taskId: string
@@ -35,10 +35,10 @@ export const ListTasksTool = defineToolRender(({ resultItem }) => {
35
35
  <ToolCallBox
36
36
  defaultExpanded={true}
37
37
  header={
38
- <div className='list-tasks-tool__header'>
39
- <span className='material-symbols-rounded list-tasks-tool__icon'>list_alt</span>
40
- <span className='list-tasks-tool__title'>{t('chat.tools.listTasks')}</span>
41
- <span className='list-tasks-tool__count'>{taskResults.length}</span>
38
+ <div className='tool-header-content'>
39
+ <span className='material-symbols-rounded tool-header-icon'>list_alt</span>
40
+ <span className='tool-header-title'>{t('chat.tools.listTasks')}</span>
41
+ <span className='tool-header-chip'>{taskResults.length}</span>
42
42
  </div>
43
43
  }
44
44
  content={
@@ -59,10 +59,13 @@ export const ListTasksTool = defineToolRender(({ resultItem }) => {
59
59
  : undefined
60
60
  ]
61
61
  return (
62
- <TaskToolCard
62
+ <TaskRow
63
63
  key={taskId}
64
- {...cardProps}
64
+ description={cardProps.description}
65
+ status={cardProps.status}
66
+ background={cardProps.background}
65
67
  sessionId={taskId}
68
+ logs={cardProps.logs}
66
69
  titleFallback={t('chat.tools.task')}
67
70
  metaChips={metaChips}
68
71
  showExecutionIcon={false}
@@ -13,40 +13,15 @@
13
13
  );
14
14
  }
15
15
 
16
- &__header {
17
- display: flex;
18
- align-items: center;
19
- gap: 8px;
20
- }
21
-
22
- &__icon {
23
- font-size: 16px;
24
- }
25
-
26
- &__title {
27
- font-weight: 600;
28
- }
29
-
30
- &__count {
31
- margin-left: 4px;
32
- padding: 1px 6px;
33
- border: 1px solid var(--border-color);
34
- border-radius: 999px;
35
- font-size: 11px;
36
- font-weight: 600;
37
- color: var(--sub-text-color);
38
- background: color-mix(in srgb, var(--bg-color) 70%, transparent 30%);
39
- }
40
-
41
16
  &__list {
42
- padding: 10px 12px;
17
+ padding: 0;
43
18
  display: flex;
44
19
  flex-direction: column;
45
- gap: 8px;
20
+ gap: 0;
46
21
  }
47
22
 
48
23
  &__empty {
49
- padding: 10px 12px;
24
+ padding: 8px 10px;
50
25
  border: 1px dashed var(--border-color);
51
26
  border-radius: 8px;
52
27
  font-size: 12px;
@@ -2,18 +2,13 @@ import './StartTasksTool.scss'
2
2
 
3
3
  import React, { useMemo } from 'react'
4
4
  import { useTranslation } from 'react-i18next'
5
+ import type { ToolInputs } from '@vibe-forge/core'
5
6
 
6
- import { ToolCallBox } from '../../ToolCallBox'
7
+ import { ToolCallBox } from '../core/ToolCallBox'
7
8
  import { defineToolRender } from '../defineToolRender'
8
- import { TaskToolCard } from './components/TaskToolCard'
9
+ import { TaskRow } from './components/TaskRow'
9
10
 
10
- interface StartTask {
11
- description?: string
12
- type?: 'default' | 'spec' | 'entity'
13
- name?: string
14
- adapter?: string
15
- background?: boolean
16
- }
11
+ type StartTask = ToolInputs['StartTasks']['tasks'][number]
17
12
 
18
13
  interface TaskResult {
19
14
  taskId: string
@@ -30,7 +25,7 @@ interface TaskResult {
30
25
  export const StartTasksTool = defineToolRender(({ item, resultItem }) => {
31
26
  const { t } = useTranslation()
32
27
 
33
- const input = (item.input != null ? item.input : {}) as { tasks?: StartTask[] }
28
+ const input = (item.input != null ? item.input : {}) as Partial<ToolInputs['StartTasks']>
34
29
  const tasks = Array.isArray(input.tasks) ? input.tasks : []
35
30
 
36
31
  const parsedResult = useMemo(() => {
@@ -50,10 +45,10 @@ export const StartTasksTool = defineToolRender(({ item, resultItem }) => {
50
45
  <ToolCallBox
51
46
  defaultExpanded={true}
52
47
  header={
53
- <div className='start-tasks-tool__header'>
54
- <span className='material-symbols-rounded start-tasks-tool__icon'>playlist_add</span>
55
- <span className='start-tasks-tool__title'>{t('chat.tools.startTasks')}</span>
56
- <span className='start-tasks-tool__count'>{tasks.length}</span>
48
+ <div className='tool-header-content'>
49
+ <span className='material-symbols-rounded tool-header-icon'>playlist_add</span>
50
+ <span className='tool-header-title'>{t('chat.tools.startTasks')}</span>
51
+ <span className='tool-header-chip'>{tasks.length}</span>
57
52
  </div>
58
53
  }
59
54
  content={
@@ -62,7 +57,6 @@ export const StartTasksTool = defineToolRender(({ item, resultItem }) => {
62
57
  {tasks.map((task, idx) => {
63
58
  const { description, type, name, adapter, background } = task
64
59
  const { status, taskId, logs = [] } = taskResults?.[idx] ?? {}
65
- const cardProps = { description, status, logs, adapter, type, name, background }
66
60
  const metaChips = [
67
61
  adapter,
68
62
  type === 'default' ? undefined : type,
@@ -70,10 +64,13 @@ export const StartTasksTool = defineToolRender(({ item, resultItem }) => {
70
64
  ]
71
65
 
72
66
  return (
73
- <TaskToolCard
67
+ <TaskRow
74
68
  key={idx}
75
- {...cardProps}
69
+ description={description}
70
+ status={status}
71
+ background={background}
76
72
  sessionId={taskId}
73
+ logs={logs}
77
74
  titleFallback={t('chat.tools.startTasks')}
78
75
  metaChips={metaChips}
79
76
  showExecutionIcon={true}
@@ -0,0 +1,105 @@
1
+ .task-row {
2
+ display: flex;
3
+ flex-direction: column;
4
+ align-items: stretch;
5
+ gap: 6px;
6
+ min-width: 0;
7
+ padding: 6px 8px;
8
+ border-bottom: 1px solid var(--border-color);
9
+ }
10
+
11
+ .task-row:last-child {
12
+ border-bottom: none;
13
+ }
14
+
15
+ .task-row__header {
16
+ display: flex;
17
+ align-items: center;
18
+ gap: 8px;
19
+ min-width: 0;
20
+ }
21
+
22
+ .task-row__left {
23
+ display: flex;
24
+ align-items: center;
25
+ gap: 6px;
26
+ flex-shrink: 0;
27
+ }
28
+
29
+ .task-row__status-icon {
30
+ font-size: 16px;
31
+ color: var(--sub-text-color);
32
+ }
33
+
34
+ .task-row__status-icon.running {
35
+ color: var(--primary-color);
36
+ }
37
+
38
+ .task-row__status-icon.completed {
39
+ color: var(--success-color);
40
+ }
41
+
42
+ .task-row__status-icon.failed {
43
+ color: var(--danger-color);
44
+ }
45
+
46
+ .task-row__execution-icon {
47
+ font-size: 16px;
48
+ color: var(--sub-text-color);
49
+ }
50
+
51
+ .task-row__execution-icon--foreground {
52
+ color: var(--primary-color);
53
+ }
54
+
55
+ .task-row__execution-icon--background {
56
+ color: var(--sub-text-color);
57
+ }
58
+
59
+ .task-row__title {
60
+ min-width: 0;
61
+ flex: 1;
62
+ overflow: hidden;
63
+ text-overflow: ellipsis;
64
+ white-space: nowrap;
65
+ font-size: 12px;
66
+ color: var(--text-color);
67
+ }
68
+
69
+ .task-row__session-link {
70
+ border: none;
71
+ background: transparent;
72
+ padding: 0;
73
+ margin: 0;
74
+ cursor: pointer;
75
+ font-size: 11px;
76
+ color: var(--primary-color);
77
+ font-family: var(--font-mono);
78
+ flex-shrink: 0;
79
+ }
80
+
81
+ .task-row__session-link:hover {
82
+ text-shadow: 0 0 .5px currentColor;
83
+ }
84
+
85
+ .task-row__meta {
86
+ display: flex;
87
+ align-items: center;
88
+ gap: 6px;
89
+ flex-shrink: 0;
90
+ }
91
+
92
+ .task-row__meta-chip {
93
+ padding: 1px 6px;
94
+ border: 1px solid var(--border-color);
95
+ border-radius: 999px;
96
+ font-size: 10px;
97
+ font-weight: 600;
98
+ color: var(--sub-text-color);
99
+ background: var(--bg-color);
100
+ white-space: nowrap;
101
+ }
102
+
103
+ .task-row__logs {
104
+ width: 100%;
105
+ }
@@ -0,0 +1,163 @@
1
+ import './TaskRow.scss'
2
+
3
+ import React, { useEffect, useMemo, useRef, useState } from 'react'
4
+ import { useNavigate } from 'react-router-dom'
5
+
6
+ import type { ChatMessage, WSEvent } from '@vibe-forge/core'
7
+
8
+ import { connectionManager } from '#~/connectionManager.js'
9
+ import { MarkdownContent } from '#~/components/MarkdownContent'
10
+
11
+ export interface TaskRowProps {
12
+ description?: string
13
+ status?: 'running' | 'completed' | 'failed'
14
+ logs?: string[] | null
15
+ sessionId?: string | null
16
+ titleFallback?: string
17
+ metaChips?: Array<string | null | undefined>
18
+ showExecutionIcon?: boolean
19
+ background?: boolean
20
+ foregroundLabel?: string
21
+ backgroundLabel?: string
22
+ }
23
+
24
+ function parseTaskDescription(description?: string): { title: string } {
25
+ if (!description) {
26
+ return { title: '' }
27
+ }
28
+ const lines = description.split('\n')
29
+ const title = lines[0] || ''
30
+ return { title }
31
+ }
32
+
33
+ function getStatusIcon(status?: string): string {
34
+ switch (status) {
35
+ case 'running':
36
+ return 'play_circle'
37
+ case 'completed':
38
+ return 'check_circle'
39
+ case 'failed':
40
+ return 'error'
41
+ default:
42
+ return 'help_outline'
43
+ }
44
+ }
45
+
46
+ export function TaskRow({
47
+ description,
48
+ status,
49
+ logs,
50
+ sessionId,
51
+ titleFallback,
52
+ metaChips,
53
+ showExecutionIcon,
54
+ background,
55
+ foregroundLabel,
56
+ backgroundLabel
57
+ }: TaskRowProps) {
58
+ const navigate = useNavigate()
59
+ const [liveLogs, setLiveLogs] = useState<string[]>(logs ?? [])
60
+ const [liveStatus, setLiveStatus] = useState(status)
61
+ const seenMessageIdsRef = useRef<Set<string>>(new Set())
62
+ const { title } = parseTaskDescription(description)
63
+ const resolvedTitle = title || titleFallback || ''
64
+ const chips = (metaChips ?? []).filter((item): item is string => Boolean(item))
65
+ const isForeground = background === false
66
+ const executionClass = isForeground
67
+ ? 'task-row__execution-icon--foreground'
68
+ : 'task-row__execution-icon--background'
69
+ const executionLabel = isForeground ? foregroundLabel : backgroundLabel
70
+ const displaySessionId = sessionId != null && sessionId !== ''
71
+ const logText = useMemo(() => (liveLogs?.join('\n') ?? '').trim(), [liveLogs])
72
+
73
+ useEffect(() => {
74
+ setLiveLogs(logs ?? [])
75
+ }, [logs])
76
+
77
+ useEffect(() => {
78
+ setLiveStatus(status)
79
+ }, [status])
80
+
81
+ useEffect(() => {
82
+ if (!sessionId) return
83
+ const cleanup = connectionManager.connect(sessionId, {
84
+ onMessage(data: WSEvent) {
85
+ if (data.type === 'message') {
86
+ const message = data.message as ChatMessage
87
+ if (message.id && seenMessageIdsRef.current.has(message.id)) {
88
+ return
89
+ }
90
+ if (message.id) {
91
+ seenMessageIdsRef.current.add(message.id)
92
+ }
93
+ const text = extractMessageText(message)
94
+ if (text !== '') {
95
+ setLiveLogs((prev) => [...prev, text])
96
+ }
97
+ } else if (data.type === 'session_updated' && data.session?.id === sessionId) {
98
+ const updatedStatus = data.session?.status as typeof status | undefined
99
+ if (updatedStatus) {
100
+ setLiveStatus(updatedStatus)
101
+ }
102
+ }
103
+ }
104
+ })
105
+ return () => {
106
+ cleanup?.()
107
+ }
108
+ }, [sessionId])
109
+
110
+ return (
111
+ <div className='task-row'>
112
+ <div className='task-row__header'>
113
+ <div className='task-row__left'>
114
+ <span className={`material-symbols-rounded task-row__status-icon ${liveStatus ?? 'help_outline'}`}>
115
+ {getStatusIcon(liveStatus)}
116
+ </span>
117
+ {showExecutionIcon && (
118
+ <span
119
+ className={`material-symbols-rounded task-row__execution-icon ${executionClass}`}
120
+ title={executionLabel}
121
+ >
122
+ {isForeground ? 'desktop_windows' : 'schedule'}
123
+ </span>
124
+ )}
125
+ </div>
126
+ <span className='task-row__title'>{resolvedTitle}</span>
127
+ {displaySessionId && (
128
+ <button
129
+ type='button'
130
+ className='task-row__session-link'
131
+ onClick={() => navigate(`/session/${sessionId}`)}
132
+ >
133
+ {sessionId}
134
+ </button>
135
+ )}
136
+ {chips.length > 0 && (
137
+ <div className='task-row__meta'>
138
+ {chips.map((item) => (
139
+ <span className='task-row__meta-chip' key={item}>{item}</span>
140
+ ))}
141
+ </div>
142
+ )}
143
+ </div>
144
+ {logText !== '' && (
145
+ <div className='task-row__logs'>
146
+ <MarkdownContent content={logText} />
147
+ </div>
148
+ )}
149
+ </div>
150
+ )
151
+ }
152
+
153
+ function extractMessageText(message: ChatMessage): string {
154
+ if (typeof message.content === 'string') {
155
+ return message.content
156
+ }
157
+ if (Array.isArray(message.content)) {
158
+ return message.content
159
+ .map((c) => (c.type === 'text' ? c.text : ''))
160
+ .join('')
161
+ }
162
+ return ''
163
+ }
@@ -1,8 +1,8 @@
1
1
  .task-tool-card {
2
2
  display: grid;
3
- grid-template-columns: 24px minmax(0, 1fr);
4
- gap: 12px;
5
- padding: 12px;
3
+ grid-template-columns: 20px minmax(0, 1fr);
4
+ gap: 10px;
5
+ padding: 10px;
6
6
  border: 1px solid var(--border-color);
7
7
  border-radius: 8px;
8
8
  background: color-mix(
@@ -14,12 +14,12 @@
14
14
  &__left {
15
15
  display: flex;
16
16
  flex-direction: column;
17
- gap: 8px;
17
+ gap: 6px;
18
18
  align-items: center;
19
19
  }
20
20
 
21
21
  &__status-icon {
22
- font-size: 20px;
22
+ font-size: 18px;
23
23
  color: var(--sub-text-color);
24
24
  transition: color .2s ease;
25
25
 
@@ -41,7 +41,7 @@
41
41
  }
42
42
 
43
43
  &__execution-icon {
44
- font-size: 20px;
44
+ font-size: 18px;
45
45
  color: var(--sub-text-color);
46
46
  transition: color .2s ease;
47
47
 
@@ -69,15 +69,15 @@
69
69
  display: flex;
70
70
  justify-content: space-between;
71
71
  align-items: flex-start;
72
- margin-bottom: 6px;
72
+ margin-bottom: 4px;
73
73
  }
74
74
 
75
75
  &__title {
76
- font-size: 14px;
76
+ font-size: 13px;
77
77
  font-weight: 600;
78
78
  color: var(--text-color);
79
79
  flex: 1;
80
- margin-right: 12px;
80
+ margin-right: 8px;
81
81
  }
82
82
 
83
83
  &__session-id {
@@ -103,24 +103,24 @@
103
103
  }
104
104
 
105
105
  &__content {
106
- font-size: 13px;
106
+ font-size: 12px;
107
107
  line-height: 1.5;
108
108
  color: var(--text-color);
109
- margin-bottom: 8px;
109
+ margin-bottom: 6px;
110
110
  }
111
111
 
112
112
  &__meta {
113
- margin: 6px 0;
113
+ margin: 4px 0;
114
114
  display: flex;
115
115
  flex-wrap: wrap;
116
- gap: 6px;
116
+ gap: 4px;
117
117
  }
118
118
 
119
119
  &__meta-chip {
120
- padding: 2px 6px;
120
+ padding: 1px 6px;
121
121
  border: 1px solid var(--border-color);
122
122
  border-radius: 999px;
123
- font-size: 11px;
123
+ font-size: 10px;
124
124
  color: var(--sub-text-color);
125
125
  background: var(--bg-color);
126
126
  }