@vibe-forge/client 0.7.4 → 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-DXs6SvQX.js → arc-BjI8Mzf5.js} +1 -1
  3. package/dist/assets/{blockDiagram-c4efeb88-h-xVkbzT.js → blockDiagram-c4efeb88-By4JL1RU.js} +1 -1
  4. package/dist/assets/{c4Diagram-c83219d4-DEumwLCr.js → c4Diagram-c83219d4-frpxdNO6.js} +1 -1
  5. package/dist/assets/channel-Da5T54-_.js +1 -0
  6. package/dist/assets/{classDiagram-beda092f-Dh_6VL8e.js → classDiagram-beda092f-sGBIwOiO.js} +1 -1
  7. package/dist/assets/{classDiagram-v2-2358418a-D9hG_V5y.js → classDiagram-v2-2358418a-JfASkQqT.js} +1 -1
  8. package/dist/assets/clone-BfjbcwWs.js +1 -0
  9. package/dist/assets/{createText-1719965b-DGO5tdKk.js → createText-1719965b-C6SwHZ-r.js} +1 -1
  10. package/dist/assets/{edges-96097737-63FzeZDk.js → edges-96097737-z-Dp4FKF.js} +1 -1
  11. package/dist/assets/{erDiagram-0228fc6a-jN2RzBTN.js → erDiagram-0228fc6a-5fIyCTtw.js} +1 -1
  12. package/dist/assets/{flowDb-c6c81e3f-CvND0Kz-.js → flowDb-c6c81e3f-DuDOLffh.js} +1 -1
  13. package/dist/assets/{flowDiagram-50d868cf-jtMtLi5z.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-Dic1wweO.js → flowchart-elk-definition-6af322e1-wpZusdN_.js} +1 -1
  16. package/dist/assets/{ganttDiagram-a2739b55-BLbYj7ru.js → ganttDiagram-a2739b55-aw70jAEI.js} +1 -1
  17. package/dist/assets/{gitGraphDiagram-82fe8481-Dm4ee53U.js → gitGraphDiagram-82fe8481-DhJVtfJF.js} +1 -1
  18. package/dist/assets/{graph-BnzAin3i.js → graph-Dp5XlF1F.js} +1 -1
  19. package/dist/assets/{index-5325376f-gU7GGRnq.js → index-5325376f-C7cRw1io.js} +1 -1
  20. package/dist/assets/{index-BRIfON-w.css → index-DHL1Qu5o.css} +1 -1
  21. package/dist/assets/index-DqioMim6.js +557 -0
  22. package/dist/assets/{infoDiagram-8eee0895-BI_1UH70.js → infoDiagram-8eee0895-B9VmKQm_.js} +1 -1
  23. package/dist/assets/{journeyDiagram-c64418c1-Xc6td0Nk.js → journeyDiagram-c64418c1-BTKwOAU-.js} +1 -1
  24. package/dist/assets/{layout-PHWoi3a3.js → layout-XtAsDaFY.js} +1 -1
  25. package/dist/assets/{line-BJPgSD92.js → line-1nd8Xc89.js} +1 -1
  26. package/dist/assets/{linear-DYKGy-mG.js → linear-BBztVBp6.js} +1 -1
  27. package/dist/assets/{mermaid.core-H3QJi-7A.js → mermaid.core-DaqQ11eY.js} +4 -4
  28. package/dist/assets/{mindmap-definition-8da855dc-UC--JAZa.js → mindmap-definition-8da855dc-DYdtyQbX.js} +1 -1
  29. package/dist/assets/{pieDiagram-a8764435-BTI_-cYX.js → pieDiagram-a8764435-CO9FnqSm.js} +1 -1
  30. package/dist/assets/{quadrantDiagram-1e28029f-C4Gf_SaX.js → quadrantDiagram-1e28029f-Cs-iTCZ-.js} +1 -1
  31. package/dist/assets/{requirementDiagram-08caed73-BKwfGAsO.js → requirementDiagram-08caed73-Diwrdq_y.js} +1 -1
  32. package/dist/assets/{sankeyDiagram-a04cb91d-DTp2p2pD.js → sankeyDiagram-a04cb91d-DjxNZwMs.js} +1 -1
  33. package/dist/assets/{sequenceDiagram-c5b8d532-CLuNEegU.js → sequenceDiagram-c5b8d532-CWawhoyM.js} +1 -1
  34. package/dist/assets/{stateDiagram-1ecb1508-BUofUUM6.js → stateDiagram-1ecb1508-Bow7IRrW.js} +1 -1
  35. package/dist/assets/{stateDiagram-v2-c2b004d7-BATuZH_y.js → stateDiagram-v2-c2b004d7-BJqu9_Fj.js} +1 -1
  36. package/dist/assets/{styles-b4e223ce-CVO41uVV.js → styles-b4e223ce-F2FDTYdm.js} +1 -1
  37. package/dist/assets/{styles-ca3715f6-fFE_-gsH.js → styles-ca3715f6-DJITgKSs.js} +1 -1
  38. package/dist/assets/{styles-d45a18b0-BeG4Dd2L.js → styles-d45a18b0-DMSpafXP.js} +1 -1
  39. package/dist/assets/{svgDrawCommon-b86b1483-D6PZVIuy.js → svgDrawCommon-b86b1483-3_yd3bB_.js} +1 -1
  40. package/dist/assets/{timeline-definition-faaaa080-CTFMc2GO.js → timeline-definition-faaaa080-CV5umgp5.js} +1 -1
  41. package/dist/assets/{xychartDiagram-f5964ef8-wWcw3yKn.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 +8 -3
  69. package/src/components/chat/sender/Sender.tsx +71 -22
  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 +14 -1
  133. package/src/resources/locales/zh.json +14 -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-Hxo8SEEx.js +0 -1
  139. package/dist/assets/clone-Dd_kUYh5.js +0 -1
  140. package/dist/assets/flowDiagram-v2-4f6560a1-CmztIxNZ.js +0 -1
  141. package/dist/assets/index-Cw-fkktx.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,21 +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)
150
154
  : []
151
155
  const assetWarnings = sessionInfo != null && sessionInfo.type === 'init'
152
- ? (sessionInfo.assetDiagnostics ?? []).filter(diagnostic => diagnostic.status === 'skipped')
156
+ ? (sessionInfo.assetDiagnostics ?? []).filter((diagnostic: SessionAssetDiagnostic) =>
157
+ diagnostic.status === 'skipped'
158
+ )
159
+ : []
160
+ const selectionWarnings = sessionInfo != null && sessionInfo.type === 'init'
161
+ ? (sessionInfo.selectionWarnings ?? [])
153
162
  : []
154
163
  const toolCascaderOptions: SenderToolOption[] = groupedTools.map(group => ({
155
164
  value: group.key,
@@ -174,6 +183,19 @@ export function Sender({
174
183
  const [historyIndex, setHistoryIndex] = useState(-1)
175
184
  const [draft, setDraft] = useState('')
176
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
+
177
199
  const readFileAsDataUrl = (file: File) => {
178
200
  return new Promise<string>((resolve, reject) => {
179
201
  const reader = new FileReader()
@@ -243,7 +265,7 @@ export function Sender({
243
265
  if (input.trim() !== '') {
244
266
  content.push({ type: 'text', text: input.trim() })
245
267
  }
246
- content.push(...pendingImages.map(img => ({
268
+ content.push(...pendingImages.map((img): ChatMessageContent => ({
247
269
  type: 'image',
248
270
  url: img.url,
249
271
  name: img.name,
@@ -524,19 +546,19 @@ export function Sender({
524
546
  if (sessionInfo?.type === 'init') {
525
547
  const info = sessionInfo
526
548
  if (charBeforeCursor === '/') {
527
- items = (info.slashCommands != null ? info.slashCommands : []).map(cmd => ({
549
+ items = (info.slashCommands != null ? info.slashCommands : []).map((cmd: string) => ({
528
550
  label: `/${cmd}`,
529
551
  value: cmd,
530
552
  icon: 'terminal'
531
553
  }))
532
554
  } else if (charBeforeCursor === '@') {
533
- items = (info.agents != null ? info.agents : []).map(agent => ({
555
+ items = (info.agents != null ? info.agents : []).map((agent: string) => ({
534
556
  label: `@${agent}`,
535
557
  value: agent,
536
558
  icon: 'smart_toy'
537
559
  }))
538
560
  } else if (charBeforeCursor === '#') {
539
- items = (info.tools != null ? info.tools : []).map(tool => ({
561
+ items = (info.tools != null ? info.tools : []).map((tool: string) => ({
540
562
  label: `#${tool}`,
541
563
  value: tool,
542
564
  icon: 'check_box'
@@ -581,7 +603,7 @@ export function Sender({
581
603
  <div className='interaction-question' style={{ fontWeight: 'bold' }}>
582
604
  {interactionRequest.payload.question}
583
605
  </div>
584
- {interactionRequest.payload.options?.map((option) => (
606
+ {interactionRequest.payload.options?.map((option: NonNullable<AskUserQuestionParams['options']>[number]) => (
585
607
  <Button
586
608
  key={option.label}
587
609
  block
@@ -692,13 +714,40 @@ export function Sender({
692
714
 
693
715
  {sessionInfo != null && sessionInfo.type === 'init' && (
694
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
+ )}
695
744
  {assetWarnings.length > 0 && (
696
745
  <Tooltip
697
746
  placement='topLeft'
698
- title={(
747
+ title={
699
748
  <div className='asset-warning-tooltip'>
700
749
  <div className='asset-warning-tooltip__title'>{t('chat.assetWarningsTitle')}</div>
701
- {assetWarnings.slice(0, 5).map((warning) => (
750
+ {assetWarnings.slice(0, 5).map((warning: SessionAssetDiagnostic) => (
702
751
  <div key={warning.assetId} className='asset-warning-tooltip__item'>
703
752
  <code>{warning.assetId}</code>
704
753
  <span>{warning.reason}</span>
@@ -710,7 +759,7 @@ export function Sender({
710
759
  </div>
711
760
  )}
712
761
  </div>
713
- )}
762
+ }
714
763
  >
715
764
  <div className='info-item asset-warning-item'>
716
765
  <span className='info-item-leading'>
@@ -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'