@vibe-forge/client 0.7.3 → 0.8.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 (141) hide show
  1. package/cli.cjs +16 -17
  2. package/dist/assets/{arc-3Inn4weF.js → arc-BjI8Mzf5.js} +1 -1
  3. package/dist/assets/{blockDiagram-c4efeb88-CDUTPZC5.js → blockDiagram-c4efeb88-By4JL1RU.js} +1 -1
  4. package/dist/assets/{c4Diagram-c83219d4-C6HJlK_i.js → c4Diagram-c83219d4-frpxdNO6.js} +1 -1
  5. package/dist/assets/channel-Da5T54-_.js +1 -0
  6. package/dist/assets/{classDiagram-beda092f-BbTSEeM-.js → classDiagram-beda092f-sGBIwOiO.js} +1 -1
  7. package/dist/assets/{classDiagram-v2-2358418a-XUN9iLKI.js → classDiagram-v2-2358418a-JfASkQqT.js} +1 -1
  8. package/dist/assets/clone-BfjbcwWs.js +1 -0
  9. package/dist/assets/{createText-1719965b-DPPzBI6W.js → createText-1719965b-C6SwHZ-r.js} +1 -1
  10. package/dist/assets/{edges-96097737-B2PDevbO.js → edges-96097737-z-Dp4FKF.js} +1 -1
  11. package/dist/assets/{erDiagram-0228fc6a-CDx-zkVG.js → erDiagram-0228fc6a-5fIyCTtw.js} +1 -1
  12. package/dist/assets/{flowDb-c6c81e3f-AXQhNDwJ.js → flowDb-c6c81e3f-DuDOLffh.js} +1 -1
  13. package/dist/assets/{flowDiagram-50d868cf-DIFzlLzz.js → flowDiagram-50d868cf-CqfJoZYm.js} +1 -1
  14. package/dist/assets/flowDiagram-v2-4f6560a1-B25RT9lb.js +1 -0
  15. package/dist/assets/{flowchart-elk-definition-6af322e1-CBunCaHi.js → flowchart-elk-definition-6af322e1-wpZusdN_.js} +1 -1
  16. package/dist/assets/{ganttDiagram-a2739b55-DPMS1mgp.js → ganttDiagram-a2739b55-aw70jAEI.js} +1 -1
  17. package/dist/assets/{gitGraphDiagram-82fe8481-lsQFHHkQ.js → gitGraphDiagram-82fe8481-DhJVtfJF.js} +1 -1
  18. package/dist/assets/{graph-BlL8UTXV.js → graph-Dp5XlF1F.js} +1 -1
  19. package/dist/assets/{index-5325376f-D5mAuW3d.js → index-5325376f-C7cRw1io.js} +1 -1
  20. package/dist/assets/{index-fcJ9v94I.css → index-DHL1Qu5o.css} +1 -1
  21. package/dist/assets/index-DqioMim6.js +557 -0
  22. package/dist/assets/{infoDiagram-8eee0895-CQ5qoRoz.js → infoDiagram-8eee0895-B9VmKQm_.js} +1 -1
  23. package/dist/assets/{journeyDiagram-c64418c1-COrg8_ZP.js → journeyDiagram-c64418c1-BTKwOAU-.js} +1 -1
  24. package/dist/assets/{layout-DjdlpleO.js → layout-XtAsDaFY.js} +1 -1
  25. package/dist/assets/{line-DC6oQAQt.js → line-1nd8Xc89.js} +1 -1
  26. package/dist/assets/{linear-CRrvE1Sj.js → linear-BBztVBp6.js} +1 -1
  27. package/dist/assets/{mermaid.core-Chovhfjq.js → mermaid.core-DaqQ11eY.js} +4 -4
  28. package/dist/assets/{mindmap-definition-8da855dc-CGLpitPv.js → mindmap-definition-8da855dc-DYdtyQbX.js} +1 -1
  29. package/dist/assets/{pieDiagram-a8764435-BuuRwI2Y.js → pieDiagram-a8764435-CO9FnqSm.js} +1 -1
  30. package/dist/assets/{quadrantDiagram-1e28029f-3Aap76t1.js → quadrantDiagram-1e28029f-Cs-iTCZ-.js} +1 -1
  31. package/dist/assets/{requirementDiagram-08caed73-IN9gT6dQ.js → requirementDiagram-08caed73-Diwrdq_y.js} +1 -1
  32. package/dist/assets/{sankeyDiagram-a04cb91d-BocKz01i.js → sankeyDiagram-a04cb91d-DjxNZwMs.js} +1 -1
  33. package/dist/assets/{sequenceDiagram-c5b8d532-BW9zy30M.js → sequenceDiagram-c5b8d532-CWawhoyM.js} +1 -1
  34. package/dist/assets/{stateDiagram-1ecb1508-BjmUkr7N.js → stateDiagram-1ecb1508-Bow7IRrW.js} +1 -1
  35. package/dist/assets/{stateDiagram-v2-c2b004d7-CWKpgiA4.js → stateDiagram-v2-c2b004d7-BJqu9_Fj.js} +1 -1
  36. package/dist/assets/{styles-b4e223ce-B5ZUhrVa.js → styles-b4e223ce-F2FDTYdm.js} +1 -1
  37. package/dist/assets/{styles-ca3715f6-Dg6_iMfO.js → styles-ca3715f6-DJITgKSs.js} +1 -1
  38. package/dist/assets/{styles-d45a18b0-BntmyMbR.js → styles-d45a18b0-DMSpafXP.js} +1 -1
  39. package/dist/assets/{svgDrawCommon-b86b1483-CdnpTDnc.js → svgDrawCommon-b86b1483-3_yd3bB_.js} +1 -1
  40. package/dist/assets/{timeline-definition-faaaa080-D3aQK7FD.js → timeline-definition-faaaa080-CV5umgp5.js} +1 -1
  41. package/dist/assets/{xychartDiagram-f5964ef8-BkxgYbPP.js → xychartDiagram-f5964ef8-DhVTgtev.js} +1 -1
  42. package/dist/index.html +2 -2
  43. package/package.json +10 -8
  44. package/src/App.tsx +1 -1
  45. package/src/api/base.ts +7 -7
  46. package/src/api/benchmark.ts +7 -3
  47. package/src/api/config.ts +2 -1
  48. package/src/api.ts +1 -1
  49. package/src/components/ArchiveView.tsx +1 -1
  50. package/src/components/ConfigView.tsx +18 -6
  51. package/src/components/MarkdownContent.tsx +1 -1
  52. package/src/components/Sidebar.tsx +2 -2
  53. package/src/components/automation-view/RuleFormPanel.tsx +7 -5
  54. package/src/components/automation-view/TaskList.tsx +8 -5
  55. package/src/components/automation-view/TriggerList.tsx +25 -15
  56. package/src/components/automation-view/types.ts +1 -1
  57. package/src/components/benchmark-view/BenchmarkCasePanel.tsx +94 -94
  58. package/src/components/benchmark-view/BenchmarkSidebar.scss +8 -6
  59. package/src/components/benchmark-view/BenchmarkSidebar.tsx +43 -30
  60. package/src/components/benchmark-view/index.tsx +4 -2
  61. package/src/components/benchmark-view/types.ts +3 -2
  62. package/src/components/benchmark-view/utils.ts +1 -2
  63. package/src/components/chat/ChatHeader.tsx +1 -1
  64. package/src/components/chat/ChatHistoryView.tsx +3 -2
  65. package/src/components/chat/CurrentTodoList.tsx +2 -3
  66. package/src/components/chat/messages/MessageItem.tsx +15 -14
  67. package/src/components/chat/messages/message-utils.ts +1 -1
  68. package/src/components/chat/sender/Sender.scss +53 -3
  69. package/src/components/chat/sender/Sender.tsx +98 -18
  70. package/src/components/chat/session-timeline-panel/git-graph.ts +8 -1
  71. package/src/components/chat/session-timeline-panel/index.scss +2 -2
  72. package/src/components/chat/tools/DefaultTool.tsx +3 -3
  73. package/src/components/chat/tools/adapter-claude/BashTool.tsx +2 -2
  74. package/src/components/chat/tools/adapter-claude/GlobTool.tsx +2 -2
  75. package/src/components/chat/tools/adapter-claude/GrepTool.tsx +2 -2
  76. package/src/components/chat/tools/adapter-claude/LSTool.tsx +4 -4
  77. package/src/components/chat/tools/adapter-claude/ReadTool.scss +1 -2
  78. package/src/components/chat/tools/adapter-claude/ReadTool.tsx +3 -3
  79. package/src/components/chat/tools/adapter-claude/TodoTool.tsx +1 -1
  80. package/src/components/chat/tools/adapter-claude/WriteTool.tsx +2 -2
  81. package/src/components/chat/tools/adapter-claude/components/FileList.scss +4 -2
  82. package/src/components/chat/tools/core/ToolCallBox.scss +34 -35
  83. package/src/components/chat/tools/core/ToolGroup.tsx +5 -5
  84. package/src/components/chat/tools/plugin-chrome-devtools/ChromeDevtoolsTool.tsx +1 -1
  85. package/src/components/chat/tools/task/GetTaskInfoTool.tsx +1 -1
  86. package/src/components/chat/tools/task/StartTasksTool.tsx +2 -2
  87. package/src/components/chat/tools/task/components/TaskRow.tsx +4 -4
  88. package/src/components/chat/tools/task/components/TaskToolCard.tsx +4 -4
  89. package/src/components/config/ConfigAboutSection.tsx +1 -1
  90. package/src/components/config/ConfigSectionForm.tsx +2 -1
  91. package/src/components/config/ConfigSectionPanel.tsx +1 -1
  92. package/src/components/config/ConfigShortcutInput.scss +1 -1
  93. package/src/components/config/ConfigSourceSwitch.tsx +1 -1
  94. package/src/components/config/configSchema.ts +16 -1
  95. package/src/components/config/index.tsx +1 -1
  96. package/src/components/config/record-editors/McpServersRecordEditor.tsx +125 -123
  97. package/src/components/config/record-editors/ModelServicesRecordEditor.tsx +138 -136
  98. package/src/components/config/record-editors/RecordJsonEditor.tsx +31 -29
  99. package/src/components/config/record-editors/index.tsx +1 -1
  100. package/src/components/knowledge-base/components/EmptyState.tsx +1 -1
  101. package/src/components/knowledge-base/components/EntitiesTab.tsx +2 -2
  102. package/src/components/knowledge-base/components/EntityItem.tsx +1 -1
  103. package/src/components/knowledge-base/components/EntityList.tsx +1 -1
  104. package/src/components/knowledge-base/components/FilterBar.tsx +2 -2
  105. package/src/components/knowledge-base/components/FlowsTab.tsx +1 -1
  106. package/src/components/knowledge-base/components/KnowledgeList.tsx +1 -1
  107. package/src/components/knowledge-base/components/MetaList.tsx +1 -1
  108. package/src/components/knowledge-base/components/RuleItem.tsx +1 -1
  109. package/src/components/knowledge-base/components/RuleList.tsx +1 -1
  110. package/src/components/knowledge-base/components/RulesTab.tsx +3 -3
  111. package/src/components/knowledge-base/components/SectionHeader.tsx +1 -1
  112. package/src/components/knowledge-base/components/SkillsTab.tsx +3 -3
  113. package/src/components/knowledge-base/components/SpecItem.tsx +1 -1
  114. package/src/components/knowledge-base/components/SpecList.tsx +1 -1
  115. package/src/components/knowledge-base/components/TabContent.tsx +1 -1
  116. package/src/components/knowledge-base/components/TabLabel.tsx +1 -1
  117. package/src/components/sidebar/SessionItem.scss +0 -1
  118. package/src/components/sidebar/SessionItem.tsx +1 -1
  119. package/src/hooks/chat/model-selector.ts +115 -121
  120. package/src/hooks/chat/use-chat-adapter.ts +3 -3
  121. package/src/hooks/chat/use-chat-interaction.ts +1 -1
  122. package/src/hooks/chat/use-chat-model-adapter-selection.tsx +549 -0
  123. package/src/hooks/chat/use-chat-models.tsx +7 -2
  124. package/src/hooks/chat/use-chat-permission-mode.ts +5 -1
  125. package/src/hooks/chat/use-chat-session-messages.ts +2 -2
  126. package/src/hooks/chat/use-chat-session.ts +14 -12
  127. package/src/hooks/chat/use-chat-view.ts +1 -1
  128. package/src/hooks/use-app-preferences.ts +14 -4
  129. package/src/hooks/use-session-subscription.ts +17 -6
  130. package/src/hooks/useQueryParams.ts +8 -6
  131. package/src/resources/adapters.ts +8 -2
  132. package/src/resources/locales/en.json +17 -1
  133. package/src/resources/locales/zh.json +17 -1
  134. package/src/routes/ChatRoute.scss +5 -1
  135. package/src/runtime-config.ts +17 -13
  136. package/src/utils/shortcutUtils.ts +1 -1
  137. package/vite.config.ts +5 -0
  138. package/dist/assets/channel-DpfFJ11i.js +0 -1
  139. package/dist/assets/clone-Dpf8N0T0.js +0 -1
  140. package/dist/assets/flowDiagram-v2-4f6560a1-B3kWo3j-.js +0 -1
  141. package/dist/assets/index-BPyYnHE3.js +0 -557
@@ -7,7 +7,8 @@ import { useTranslation } from 'react-i18next'
7
7
  import useSWR from 'swr'
8
8
 
9
9
  import type { PermissionMode } from '#~/hooks/chat/use-chat-permission-mode'
10
- import type { AskUserQuestionParams, ChatMessageContent, SessionInfo, SessionStatus } from '@vibe-forge/core'
10
+ import type { SessionInfo } from '@vibe-forge/types'
11
+ import type { AskUserQuestionParams, ChatMessageContent, SessionStatus } from '@vibe-forge/core'
11
12
  import { isShortcutMatch } from '../../../utils/shortcutUtils'
12
13
  import type { CompletionItem } from './CompletionMenu'
13
14
  import { CompletionMenu } from './CompletionMenu'
@@ -35,6 +36,9 @@ interface PendingImage {
35
36
  mimeType?: string
36
37
  }
37
38
 
39
+ type SessionAssetDiagnostic = NonNullable<Extract<SessionInfo, { type: 'init' }>['assetDiagnostics']>[number]
40
+ type SessionSelectionWarning = NonNullable<Extract<SessionInfo, { type: 'init' }>['selectionWarnings']>[number]
41
+
38
42
  interface SenderToolGroup {
39
43
  key: 'chrome-devtools' | 'system'
40
44
  label: string
@@ -135,18 +139,26 @@ export function Sender({
135
139
 
136
140
  const isThinking = sessionStatus === 'running'
137
141
  const groupedTools: SenderToolGroup[] = sessionInfo != null && sessionInfo.type === 'init'
138
- ? [
139
- {
140
- key: 'chrome-devtools',
141
- label: t('chat.toolGroupChromeDevtools'),
142
- tools: sessionInfo.tools.filter(tool => tool.startsWith('mcp__ChromeDevtools__'))
143
- },
144
- {
145
- key: 'system',
146
- label: t('chat.toolGroupSystem'),
147
- tools: sessionInfo.tools.filter(tool => !tool.startsWith('mcp__ChromeDevtools__'))
148
- }
149
- ].filter(group => group.tools.length > 0)
142
+ ? ([
143
+ {
144
+ key: 'chrome-devtools',
145
+ label: t('chat.toolGroupChromeDevtools'),
146
+ tools: sessionInfo.tools.filter((tool: string) => tool.startsWith('mcp__ChromeDevtools__'))
147
+ },
148
+ {
149
+ key: 'system',
150
+ label: t('chat.toolGroupSystem'),
151
+ tools: sessionInfo.tools.filter((tool: string) => !tool.startsWith('mcp__ChromeDevtools__'))
152
+ }
153
+ ] satisfies SenderToolGroup[]).filter((group): group is SenderToolGroup => group.tools.length > 0)
154
+ : []
155
+ const assetWarnings = sessionInfo != null && sessionInfo.type === 'init'
156
+ ? (sessionInfo.assetDiagnostics ?? []).filter((diagnostic: SessionAssetDiagnostic) =>
157
+ diagnostic.status === 'skipped'
158
+ )
159
+ : []
160
+ const selectionWarnings = sessionInfo != null && sessionInfo.type === 'init'
161
+ ? (sessionInfo.selectionWarnings ?? [])
150
162
  : []
151
163
  const toolCascaderOptions: SenderToolOption[] = groupedTools.map(group => ({
152
164
  value: group.key,
@@ -171,6 +183,19 @@ export function Sender({
171
183
  const [historyIndex, setHistoryIndex] = useState(-1)
172
184
  const [draft, setDraft] = useState('')
173
185
 
186
+ const formatSelectionWarning = (warning: SessionSelectionWarning) => {
187
+ const reason = warning.reason === 'excluded'
188
+ ? t('chat.selectionWarningReasonExcluded')
189
+ : t('chat.selectionWarningReasonNotIncluded')
190
+
191
+ return t('chat.selectionWarningFallback', {
192
+ adapter: warning.adapter,
193
+ requestedModel: warning.requestedModel,
194
+ resolvedModel: warning.resolvedModel,
195
+ reason
196
+ })
197
+ }
198
+
174
199
  const readFileAsDataUrl = (file: File) => {
175
200
  return new Promise<string>((resolve, reject) => {
176
201
  const reader = new FileReader()
@@ -240,7 +265,7 @@ export function Sender({
240
265
  if (input.trim() !== '') {
241
266
  content.push({ type: 'text', text: input.trim() })
242
267
  }
243
- content.push(...pendingImages.map(img => ({
268
+ content.push(...pendingImages.map((img): ChatMessageContent => ({
244
269
  type: 'image',
245
270
  url: img.url,
246
271
  name: img.name,
@@ -521,19 +546,19 @@ export function Sender({
521
546
  if (sessionInfo?.type === 'init') {
522
547
  const info = sessionInfo
523
548
  if (charBeforeCursor === '/') {
524
- items = (info.slashCommands != null ? info.slashCommands : []).map(cmd => ({
549
+ items = (info.slashCommands != null ? info.slashCommands : []).map((cmd: string) => ({
525
550
  label: `/${cmd}`,
526
551
  value: cmd,
527
552
  icon: 'terminal'
528
553
  }))
529
554
  } else if (charBeforeCursor === '@') {
530
- items = (info.agents != null ? info.agents : []).map(agent => ({
555
+ items = (info.agents != null ? info.agents : []).map((agent: string) => ({
531
556
  label: `@${agent}`,
532
557
  value: agent,
533
558
  icon: 'smart_toy'
534
559
  }))
535
560
  } else if (charBeforeCursor === '#') {
536
- items = (info.tools != null ? info.tools : []).map(tool => ({
561
+ items = (info.tools != null ? info.tools : []).map((tool: string) => ({
537
562
  label: `#${tool}`,
538
563
  value: tool,
539
564
  icon: 'check_box'
@@ -578,7 +603,7 @@ export function Sender({
578
603
  <div className='interaction-question' style={{ fontWeight: 'bold' }}>
579
604
  {interactionRequest.payload.question}
580
605
  </div>
581
- {interactionRequest.payload.options?.map((option) => (
606
+ {interactionRequest.payload.options?.map((option: NonNullable<AskUserQuestionParams['options']>[number]) => (
582
607
  <Button
583
608
  key={option.label}
584
609
  block
@@ -689,6 +714,61 @@ export function Sender({
689
714
 
690
715
  {sessionInfo != null && sessionInfo.type === 'init' && (
691
716
  <div className='session-info-toolbar'>
717
+ {selectionWarnings.length > 0 && (
718
+ <Tooltip
719
+ placement='topLeft'
720
+ title={
721
+ <div className='asset-warning-tooltip'>
722
+ <div className='asset-warning-tooltip__title'>{t('chat.selectionWarningsTitle')}</div>
723
+ {selectionWarnings.slice(0, 5).map((warning: SessionSelectionWarning, index: number) => (
724
+ <div key={`${warning.adapter}:${warning.requestedModel}:${index}`} className='asset-warning-tooltip__item'>
725
+ <span>{formatSelectionWarning(warning)}</span>
726
+ </div>
727
+ ))}
728
+ {selectionWarnings.length > 5 && (
729
+ <div className='asset-warning-tooltip__more'>
730
+ {t('chat.assetWarningsMore', { count: selectionWarnings.length - 5 })}
731
+ </div>
732
+ )}
733
+ </div>
734
+ }
735
+ >
736
+ <div className='info-item asset-warning-item'>
737
+ <span className='info-item-leading'>
738
+ <span className='material-symbols-rounded'>warning</span>
739
+ </span>
740
+ <span className='info-text'>{t('chat.selectionWarningsCount', { count: selectionWarnings.length })}</span>
741
+ </div>
742
+ </Tooltip>
743
+ )}
744
+ {assetWarnings.length > 0 && (
745
+ <Tooltip
746
+ placement='topLeft'
747
+ title={
748
+ <div className='asset-warning-tooltip'>
749
+ <div className='asset-warning-tooltip__title'>{t('chat.assetWarningsTitle')}</div>
750
+ {assetWarnings.slice(0, 5).map((warning: SessionAssetDiagnostic) => (
751
+ <div key={warning.assetId} className='asset-warning-tooltip__item'>
752
+ <code>{warning.assetId}</code>
753
+ <span>{warning.reason}</span>
754
+ </div>
755
+ ))}
756
+ {assetWarnings.length > 5 && (
757
+ <div className='asset-warning-tooltip__more'>
758
+ {t('chat.assetWarningsMore', { count: assetWarnings.length - 5 })}
759
+ </div>
760
+ )}
761
+ </div>
762
+ }
763
+ >
764
+ <div className='info-item asset-warning-item'>
765
+ <span className='info-item-leading'>
766
+ <span className='material-symbols-rounded'>warning</span>
767
+ </span>
768
+ <span className='info-text'>{t('chat.assetWarningsCount', { count: assetWarnings.length })}</span>
769
+ </div>
770
+ </Tooltip>
771
+ )}
692
772
  <Cascader
693
773
  open={showToolsList}
694
774
  options={toolCascaderOptions}
@@ -1,4 +1,11 @@
1
- import type { Task, TimelineDiagram, TimelineEvent, TimelineEventType, TimelineInteraction, TimelineInteractionPayload } from './types'
1
+ import type {
2
+ Task,
3
+ TimelineDiagram,
4
+ TimelineEvent,
5
+ TimelineEventType,
6
+ TimelineInteraction,
7
+ TimelineInteractionPayload
8
+ } from './types'
2
9
  import { parseTime, sanitizeId } from './utils'
3
10
 
4
11
  interface MermaidLabels {
@@ -19,10 +19,10 @@
19
19
 
20
20
  .session-timeline-mermaid__interactive {
21
21
  cursor: pointer;
22
- transition: filter 0.2s ease, opacity 0.2s ease;
22
+ transition: filter .2s ease, opacity .2s ease;
23
23
  }
24
24
 
25
25
  .session-timeline-mermaid__interactive:hover {
26
26
  filter: drop-shadow(0 0 6px var(--primary-color));
27
- opacity: 0.9;
27
+ opacity: .9;
28
28
  }
@@ -1,9 +1,9 @@
1
- import type { ChatMessageContent } from '@vibe-forge/core'
2
- import { useTranslation } from 'react-i18next'
3
1
  import { CodeBlock } from '#~/components/CodeBlock'
4
2
  import { MarkdownContent } from '#~/components/MarkdownContent'
5
- import { ToolCallBox } from './core/ToolCallBox'
6
3
  import { safeJsonStringify, toSerializable } from '#~/utils/safe-serialize'
4
+ import type { ChatMessageContent } from '@vibe-forge/core'
5
+ import { useTranslation } from 'react-i18next'
6
+ import { ToolCallBox } from './core/ToolCallBox'
7
7
 
8
8
  interface StructuredTextBlock {
9
9
  type: 'text'
@@ -1,10 +1,10 @@
1
1
  import './BashTool.scss'
2
+ import { CodeBlock } from '#~/components/CodeBlock'
3
+ import { safeJsonStringify } from '#~/utils/safe-serialize'
2
4
  import type { ToolInputs } from '@vibe-forge/core'
3
5
  import React, { useState } from 'react'
4
6
  import { useTranslation } from 'react-i18next'
5
- import { CodeBlock } from '#~/components/CodeBlock'
6
7
  import { ToolCallBox } from '../core/ToolCallBox'
7
- import { safeJsonStringify } from '#~/utils/safe-serialize'
8
8
  import { defineToolRender } from '../defineToolRender'
9
9
 
10
10
  export const BashTool = defineToolRender(({ item, resultItem }) => {
@@ -1,8 +1,8 @@
1
1
  import './GlobTool.scss'
2
- import React, { useMemo } from 'react'
2
+ import { safeJsonStringify } from '#~/utils/safe-serialize'
3
3
  import type { ToolInputs } from '@vibe-forge/core'
4
+ import React, { useMemo } from 'react'
4
5
  import { ToolCallBox } from '../core/ToolCallBox'
5
- import { safeJsonStringify } from '#~/utils/safe-serialize'
6
6
  import { defineToolRender } from '../defineToolRender'
7
7
  import { FileList } from './components/FileList'
8
8
  import { normalizeResultLines } from './utils'
@@ -1,8 +1,8 @@
1
1
  import './GrepTool.scss'
2
- import React, { useMemo } from 'react'
3
2
  import { CodeBlock } from '#~/components/CodeBlock'
4
- import { ToolCallBox } from '../core/ToolCallBox'
5
3
  import { safeJsonStringify } from '#~/utils/safe-serialize'
4
+ import React, { useMemo } from 'react'
5
+ import { ToolCallBox } from '../core/ToolCallBox'
6
6
  import { defineToolRender } from '../defineToolRender'
7
7
  import { FileList } from './components/FileList'
8
8
  import { normalizeResultLines } from './utils'
@@ -1,10 +1,10 @@
1
1
  import './LsTool.scss'
2
- import React, { useMemo } from 'react'
3
- import type { ToolInputs } from '@vibe-forge/core'
4
- import { defineToolRender } from '../defineToolRender'
5
2
  import { CodeBlock } from '#~/components/CodeBlock'
6
- import { ToolCallBox } from '../core/ToolCallBox'
7
3
  import { safeJsonStringify } from '#~/utils/safe-serialize'
4
+ import type { ToolInputs } from '@vibe-forge/core'
5
+ import React, { useMemo } from 'react'
6
+ import { ToolCallBox } from '../core/ToolCallBox'
7
+ import { defineToolRender } from '../defineToolRender'
8
8
  import { FileList } from './components/FileList'
9
9
 
10
10
  export const LsTool = defineToolRender(({ item, resultItem }) => {
@@ -1,2 +1 @@
1
- .read-tool {
2
- }
1
+ .read-tool {}
@@ -1,10 +1,10 @@
1
1
  import './ReadTool.scss'
2
+ import { CodeBlock } from '#~/components/CodeBlock'
3
+ import { safeJsonStringify } from '#~/utils/safe-serialize'
4
+ import type { ToolInputs } from '@vibe-forge/core'
2
5
  import React from 'react'
3
6
  import { useTranslation } from 'react-i18next'
4
- import type { ToolInputs } from '@vibe-forge/core'
5
- import { CodeBlock } from '#~/components/CodeBlock'
6
7
  import { ToolCallBox } from '../core/ToolCallBox'
7
- import { safeJsonStringify } from '#~/utils/safe-serialize'
8
8
  import { defineToolRender } from '../defineToolRender'
9
9
  import { getFileInfo, getLanguageFromPath } from './utils'
10
10
 
@@ -1,7 +1,7 @@
1
1
  import './TodoTool.scss'
2
+ import type { ToolInputs } from '@vibe-forge/core'
2
3
  import React from 'react'
3
4
  import { useTranslation } from 'react-i18next'
4
- import type { ToolInputs } from '@vibe-forge/core'
5
5
 
6
6
  import { ToolCallBox } from '../core/ToolCallBox'
7
7
  import { defineToolRender } from '../defineToolRender'
@@ -1,7 +1,7 @@
1
1
  import './WriteTool.scss'
2
- import React from 'react'
3
- import type { ToolInputs } from '@vibe-forge/core'
4
2
  import { CodeBlock } from '#~/components/CodeBlock'
3
+ import type { ToolInputs } from '@vibe-forge/core'
4
+ import React from 'react'
5
5
  import { ToolCallBox } from '../core/ToolCallBox'
6
6
  import { defineToolRender } from '../defineToolRender'
7
7
  import { getFileInfo, getLanguageFromPath } from './utils'
@@ -1,5 +1,7 @@
1
1
  .file-list-container {
2
- font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Roboto Mono', 'Consolas', 'Courier New', monospace;
2
+ font-family:
3
+ 'SF Mono', 'Monaco', 'Cascadia Code', 'Roboto Mono', 'Consolas',
4
+ 'Courier New', monospace;
3
5
  font-size: 12px;
4
6
  line-height: 1.4;
5
7
  border-radius: 6px;
@@ -16,7 +18,7 @@
16
18
  white-space: nowrap;
17
19
  overflow: hidden;
18
20
  text-overflow: ellipsis;
19
- transition: background-color 0.2s ease;
21
+ transition: background-color .2s ease;
20
22
 
21
23
  &:hover {
22
24
  background-color: var(--tag-bg);
@@ -133,44 +133,43 @@
133
133
  border-radius: 0;
134
134
  }
135
135
 
136
- .tool-result-structured {
137
- display: flex;
138
- flex-direction: column;
139
- gap: 12px;
140
- padding: 12px;
141
- }
142
-
143
- .tool-result-text {
144
- font-size: 13px;
145
- color: var(--text-color);
146
- }
147
-
148
- .tool-result-text-content {
149
- white-space: pre-wrap;
150
- line-height: 1.6;
151
- }
152
-
153
- .tool-result-image-wrapper {
154
- display: flex;
155
- flex-direction: column;
156
- gap: 6px;
157
- }
158
-
159
- .tool-result-image {
160
- max-width: 100%;
161
- border-radius: 6px;
162
- border: 1px solid var(--border-color);
163
- background: var(--bg-color);
164
- }
165
-
166
- .tool-result-image-caption {
167
- font-size: 12px;
168
- color: var(--sub-text-color);
169
- }
136
+ .tool-result-structured {
137
+ display: flex;
138
+ flex-direction: column;
139
+ gap: 12px;
140
+ padding: 12px;
141
+ }
142
+
143
+ .tool-result-text {
144
+ font-size: 13px;
145
+ color: var(--text-color);
146
+ }
147
+
148
+ .tool-result-text-content {
149
+ white-space: pre-wrap;
150
+ line-height: 1.6;
151
+ }
152
+
153
+ .tool-result-image-wrapper {
154
+ display: flex;
155
+ flex-direction: column;
156
+ gap: 6px;
157
+ }
158
+
159
+ .tool-result-image {
160
+ max-width: 100%;
161
+ border-radius: 6px;
162
+ border: 1px solid var(--border-color);
163
+ background: var(--bg-color);
164
+ }
165
+
166
+ .tool-result-image-caption {
167
+ font-size: 12px;
168
+ color: var(--sub-text-color);
169
+ }
170
170
  }
171
171
  }
172
172
 
173
-
174
173
  .tool-header-content {
175
174
  display: flex;
176
175
  align-items: center;
@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'
6
6
  import { MessageFooter } from '../../messages/MessageFooter'
7
7
  import { ToolRenderer } from './ToolRenderer'
8
8
 
9
- type ToolGroupProps = {
9
+ interface ToolGroupProps {
10
10
  items: {
11
11
  item: Extract<ChatMessageContent, { type: 'tool_use' }>
12
12
  resultItem?: Extract<ChatMessageContent, { type: 'tool_result' }>
@@ -112,10 +112,10 @@ const areToolGroupPropsEqual = (prev: ToolGroupProps, next: ToolGroupProps) => {
112
112
  }
113
113
  if (prev.footer == null && next.footer == null) return true
114
114
  if (prev.footer == null || next.footer == null) return false
115
- return prev.footer.originalMessage === next.footer.originalMessage
116
- && prev.footer.createdAt === next.footer.createdAt
117
- && prev.footer.model === next.footer.model
118
- && prev.footer.usage === next.footer.usage
115
+ return prev.footer.originalMessage === next.footer.originalMessage &&
116
+ prev.footer.createdAt === next.footer.createdAt &&
117
+ prev.footer.model === next.footer.model &&
118
+ prev.footer.usage === next.footer.usage
119
119
  }
120
120
 
121
121
  export const ToolGroup = React.memo(ToolGroupComponent, areToolGroupPropsEqual)
@@ -3,8 +3,8 @@ import React from 'react'
3
3
  import { useTranslation } from 'react-i18next'
4
4
 
5
5
  import { CodeBlock } from '#~/components/CodeBlock'
6
- import { ToolCallBox } from '../core/ToolCallBox'
7
6
  import { safeJsonStringify } from '#~/utils/safe-serialize'
7
+ import { ToolCallBox } from '../core/ToolCallBox'
8
8
  import { defineToolRender } from '../defineToolRender'
9
9
 
10
10
  const formatToolName = (name: string) => {
@@ -1,8 +1,8 @@
1
1
  import './GetTaskInfoTool.scss'
2
2
 
3
+ import type { ToolInputs } from '@vibe-forge/core'
3
4
  import React, { useMemo } from 'react'
4
5
  import { useTranslation } from 'react-i18next'
5
- import type { ToolInputs } from '@vibe-forge/core'
6
6
 
7
7
  import { ToolCallBox } from '../core/ToolCallBox'
8
8
  import { defineToolRender } from '../defineToolRender'
@@ -1,8 +1,8 @@
1
1
  import './StartTasksTool.scss'
2
2
 
3
+ import type { ToolInputs } from '@vibe-forge/core'
3
4
  import React, { useMemo } from 'react'
4
5
  import { useTranslation } from 'react-i18next'
5
- import type { ToolInputs } from '@vibe-forge/core'
6
6
 
7
7
  import { ToolCallBox } from '../core/ToolCallBox'
8
8
  import { defineToolRender } from '../defineToolRender'
@@ -54,7 +54,7 @@ export const StartTasksTool = defineToolRender(({ item, resultItem }) => {
54
54
  content={
55
55
  <div className='tool-content'>
56
56
  <div className='start-tasks-tool__list'>
57
- {tasks.map((task, idx) => {
57
+ {tasks.map((task: StartTask, idx: number) => {
58
58
  const { description, type, name, adapter, background } = task
59
59
  const { status, taskId, logs = [] } = taskResults?.[idx] ?? {}
60
60
  const metaChips = [
@@ -3,10 +3,10 @@ import './TaskRow.scss'
3
3
  import React, { useEffect, useMemo, useRef, useState } from 'react'
4
4
  import { useNavigate } from 'react-router-dom'
5
5
 
6
- import type { ChatMessage, WSEvent } from '@vibe-forge/core'
6
+ import type { ChatMessage, ChatMessageContent, WSEvent } from '@vibe-forge/core'
7
7
 
8
- import { connectionManager } from '#~/connectionManager.js'
9
8
  import { MarkdownContent } from '#~/components/MarkdownContent'
9
+ import { connectionManager } from '#~/connectionManager.js'
10
10
 
11
11
  export interface TaskRowProps {
12
12
  description?: string
@@ -135,7 +135,7 @@ export function TaskRow({
135
135
  )}
136
136
  {chips.length > 0 && (
137
137
  <div className='task-row__meta'>
138
- {chips.map((item) => (
138
+ {chips.map((item: string) => (
139
139
  <span className='task-row__meta-chip' key={item}>{item}</span>
140
140
  ))}
141
141
  </div>
@@ -156,7 +156,7 @@ function extractMessageText(message: ChatMessage): string {
156
156
  }
157
157
  if (Array.isArray(message.content)) {
158
158
  return message.content
159
- .map((c) => (c.type === 'text' ? c.text : ''))
159
+ .map((c: ChatMessageContent) => (c.type === 'text' ? c.text : ''))
160
160
  .join('')
161
161
  }
162
162
  return ''
@@ -3,10 +3,10 @@ import './TaskToolCard.scss'
3
3
  import React, { useEffect, useMemo, useRef, useState } from 'react'
4
4
  import { useNavigate } from 'react-router-dom'
5
5
 
6
- import type { ChatMessage, WSEvent } from '@vibe-forge/core'
6
+ import type { ChatMessage, ChatMessageContent, WSEvent } from '@vibe-forge/core'
7
7
 
8
- import { connectionManager } from '#~/connectionManager.js'
9
8
  import { CodeBlock } from '#~/components/CodeBlock'
9
+ import { connectionManager } from '#~/connectionManager.js'
10
10
 
11
11
  export interface TaskToolCardProps {
12
12
  description?: string
@@ -149,7 +149,7 @@ export function TaskToolCard({
149
149
  )}
150
150
  {chips.length > 0 && (
151
151
  <div className='task-tool-card__meta'>
152
- {chips.map((item) => (
152
+ {chips.map((item: string) => (
153
153
  <span className='task-tool-card__meta-chip' key={item}>{item}</span>
154
154
  ))}
155
155
  </div>
@@ -172,7 +172,7 @@ function extractMessageText(message: ChatMessage): string {
172
172
  }
173
173
  if (Array.isArray(message.content)) {
174
174
  return message.content
175
- .map((c) => (c.type === 'text' ? c.text : ''))
175
+ .map((c: ChatMessageContent) => (c.type === 'text' ? c.text : ''))
176
176
  .join('')
177
177
  }
178
178
  return ''
@@ -1,6 +1,6 @@
1
1
  import './ConfigAboutSection.scss'
2
2
 
3
- import type { AboutInfo } from '@vibe-forge/core'
3
+ import type { AboutInfo } from '@vibe-forge/types'
4
4
  import { useTranslation } from 'react-i18next'
5
5
 
6
6
  export const AboutSection = ({ value }: { value?: AboutInfo }) => {
@@ -50,7 +50,7 @@ export const SectionForm = ({
50
50
  if (fields.length === 0) {
51
51
  return <Empty description={t('common.noData')} image={null} />
52
52
  }
53
- const directRecordSections = new Set(['modelServices', 'channels', 'adapters', 'plugins', 'mcp'])
53
+ const directRecordSections = new Set(['models', 'modelServices', 'channels', 'adapters', 'plugins', 'mcp'])
54
54
 
55
55
  const modelServiceEntries = Object.entries(mergedModelServices)
56
56
  const modelServiceOptions: Array<{ value: string; label: ReactNode }> = modelServiceEntries.map(([key, entry]) => {
@@ -97,6 +97,7 @@ export const SectionForm = ({
97
97
  const notificationEventOrder = ['completed', 'failed', 'terminated', 'waiting_input']
98
98
 
99
99
  const getRecordKeyPlaceholder = (field: FieldSpec) => {
100
+ if (sectionKey === 'models') return t('config.editor.newModelSelectorName')
100
101
  if (sectionKey === 'modelServices') return t('config.editor.newModelServiceName')
101
102
  if (sectionKey === 'channels') return t('config.editor.newChannelName')
102
103
  if (sectionKey === 'adapters') return t('config.editor.newAdapterName')
@@ -3,8 +3,8 @@ import '../ConfigView.scss'
3
3
  import type { ReactNode } from 'react'
4
4
 
5
5
  import { SectionForm } from './ConfigSectionForm'
6
- import type { TranslationFn } from './configUtils'
7
6
  import type { FieldSpec } from './configSchema'
7
+ import type { TranslationFn } from './configUtils'
8
8
 
9
9
  export function ConfigSectionPanel({
10
10
  sectionKey,
@@ -7,5 +7,5 @@
7
7
  .config-shortcut-input .ant-input {
8
8
  text-align: center;
9
9
  font-weight: 600;
10
- letter-spacing: 0.5px;
10
+ letter-spacing: .5px;
11
11
  }
@@ -6,7 +6,7 @@ import type { ConfigSource } from '@vibe-forge/core'
6
6
  export function ConfigSourceSwitch({
7
7
  value,
8
8
  onChange,
9
- options,
9
+ options
10
10
  }: {
11
11
  value: ConfigSource
12
12
  onChange: (value: ConfigSource) => void
@@ -79,7 +79,13 @@ export const configSchema: Record<string, FieldSpec[]> = {
79
79
  { path: ['permissions', 'deny'], type: 'string[]', defaultValue: [], icon: 'block', group: 'permissions' },
80
80
  { path: ['permissions', 'ask'], type: 'string[]', defaultValue: [], icon: 'help', group: 'permissions' },
81
81
  { path: ['env'], type: 'record', recordKind: 'keyValue', defaultValue: {}, icon: 'terminal', group: 'env' },
82
- { path: ['notifications', 'disabled'], type: 'boolean', defaultValue: false, icon: 'notifications', group: 'items' },
82
+ {
83
+ path: ['notifications', 'disabled'],
84
+ type: 'boolean',
85
+ defaultValue: false,
86
+ icon: 'notifications',
87
+ group: 'items'
88
+ },
83
89
  { path: ['notifications', 'volume'], type: 'number', defaultValue: 100, icon: 'volume_down', group: 'items' },
84
90
  {
85
91
  path: ['notifications', 'events', 'completed', 'title'],
@@ -251,6 +257,15 @@ export const configSchema: Record<string, FieldSpec[]> = {
251
257
  },
252
258
  { path: ['customInstructions'], type: 'multiline', defaultValue: '', icon: 'description' }
253
259
  ],
260
+ models: [
261
+ {
262
+ path: [],
263
+ type: 'record',
264
+ recordKind: 'json',
265
+ defaultValue: {},
266
+ icon: 'tune'
267
+ }
268
+ ],
254
269
  modelServices: [
255
270
  {
256
271
  path: [],
@@ -1,5 +1,5 @@
1
1
  export { AboutSection } from './ConfigAboutSection'
2
2
  export { DisplayValue } from './ConfigDisplayValue'
3
+ export { SectionForm } from './ConfigSectionForm'
3
4
  export { ConfigSectionPanel } from './ConfigSectionPanel'
4
5
  export { ConfigSourceSwitch } from './ConfigSourceSwitch'
5
- export { SectionForm } from './ConfigSectionForm'