@vibe-forge/client 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/dist/assets/{arc-CCXV7u3V.js → arc-M4HYfcHs.js} +1 -1
  2. package/dist/assets/{blockDiagram-c4efeb88-Bm52FmvT.js → blockDiagram-c4efeb88-CUrDjrxj.js} +1 -1
  3. package/dist/assets/{c4Diagram-c83219d4-C8tTEpcK.js → c4Diagram-c83219d4-BMEtqlFp.js} +1 -1
  4. package/dist/assets/channel-Cj3Cp2OJ.js +1 -0
  5. package/dist/assets/{classDiagram-beda092f-CNAIBAH1.js → classDiagram-beda092f-BOmDJ0Ml.js} +1 -1
  6. package/dist/assets/{classDiagram-v2-2358418a-BHeZAVdc.js → classDiagram-v2-2358418a-BODzX2MB.js} +1 -1
  7. package/dist/assets/clone-B7Q9B1dS.js +1 -0
  8. package/dist/assets/{createText-1719965b-BS2hLG8t.js → createText-1719965b-B9Dd8zcR.js} +1 -1
  9. package/dist/assets/{cssMode-WHcTFAOU.js → cssMode-DLxG92Ot.js} +1 -1
  10. package/dist/assets/{edges-96097737-C07f4iWA.js → edges-96097737-CuZFd43m.js} +1 -1
  11. package/dist/assets/{erDiagram-0228fc6a-BytsAWUs.js → erDiagram-0228fc6a-8g9lu2-Z.js} +1 -1
  12. package/dist/assets/{flowDb-c6c81e3f-CQJkOpAs.js → flowDb-c6c81e3f-BlBS1tdN.js} +1 -1
  13. package/dist/assets/{flowDiagram-50d868cf-CD5Tng2S.js → flowDiagram-50d868cf-u6mWflpF.js} +1 -1
  14. package/dist/assets/flowDiagram-v2-4f6560a1-G3v545eF.js +1 -0
  15. package/dist/assets/{flowchart-elk-definition-6af322e1-ylso-GWH.js → flowchart-elk-definition-6af322e1-BDqI2NFr.js} +1 -1
  16. package/dist/assets/{freemarker2-U_9Jyyr3.js → freemarker2-tVtpTMPu.js} +1 -1
  17. package/dist/assets/{ganttDiagram-a2739b55-Cg98bJx5.js → ganttDiagram-a2739b55-CDQjx9Wu.js} +1 -1
  18. package/dist/assets/{gitGraphDiagram-82fe8481-7Yp4hz0N.js → gitGraphDiagram-82fe8481-DUHFKRVA.js} +1 -1
  19. package/dist/assets/{graph-Ig3nvzvL.js → graph-2HKPi5B_.js} +1 -1
  20. package/dist/assets/{handlebars-DQyCBwHe.js → handlebars-D00tgNd8.js} +1 -1
  21. package/dist/assets/{html-CNC2AT5k.js → html-B-TDzBiR.js} +1 -1
  22. package/dist/assets/{htmlMode-DlATk4xW.js → htmlMode-ClycqSTM.js} +1 -1
  23. package/dist/assets/{index-5325376f-C4zed9sb.js → index-5325376f-DPrJpRQ-.js} +1 -1
  24. package/dist/assets/{index-Dbx0JG0p.js → index-CAHZZEoo.js} +319 -323
  25. package/dist/assets/{index-DRLsOoqb.css → index-Di7lePfb.css} +1 -1
  26. package/dist/assets/{infoDiagram-8eee0895-C8oSBaFs.js → infoDiagram-8eee0895-Co5tS1I5.js} +1 -1
  27. package/dist/assets/{javascript-9wv9uKW4.js → javascript-zbkwarmb.js} +1 -1
  28. package/dist/assets/{journeyDiagram-c64418c1-D5kJldvy.js → journeyDiagram-c64418c1-k_qioHgy.js} +1 -1
  29. package/dist/assets/{jsonMode-45dv39mU.js → jsonMode-C3CSpzBF.js} +1 -1
  30. package/dist/assets/{layout-DYNFLnIl.js → layout-CjOXKxvs.js} +1 -1
  31. package/dist/assets/{line-1gvOYQYZ.js → line-C-XnQrKR.js} +1 -1
  32. package/dist/assets/{linear-DHGm6Zdw.js → linear-C7MMERzS.js} +1 -1
  33. package/dist/assets/{liquid-BGoxrdXO.js → liquid-5G37EU6K.js} +1 -1
  34. package/dist/assets/{lspLanguageFeatures-CpCCXhrd.js → lspLanguageFeatures-zaDMuhCE.js} +1 -1
  35. package/dist/assets/{mdx-Dc2iMbEw.js → mdx-Bc-LY0gi.js} +1 -1
  36. package/dist/assets/{mermaid.core-Cq2bBFF1.js → mermaid.core-CechbHof.js} +4 -4
  37. package/dist/assets/{mindmap-definition-8da855dc-y5l6GRVh.js → mindmap-definition-8da855dc-ejftCDGb.js} +1 -1
  38. package/dist/assets/{pieDiagram-a8764435-PNROcv9y.js → pieDiagram-a8764435-DY__X3Qj.js} +1 -1
  39. package/dist/assets/{python-DjYAge7h.js → python-vK2Ff2J5.js} +1 -1
  40. package/dist/assets/{quadrantDiagram-1e28029f-CFJ3VPpp.js → quadrantDiagram-1e28029f-azIZCv_2.js} +1 -1
  41. package/dist/assets/{razor-OIY8fx_i.js → razor-BipjBJKu.js} +1 -1
  42. package/dist/assets/{requirementDiagram-08caed73-BpzDIINS.js → requirementDiagram-08caed73-C4EB0Xs2.js} +1 -1
  43. package/dist/assets/{sankeyDiagram-a04cb91d-CH69-iIn.js → sankeyDiagram-a04cb91d-PNhR6YWu.js} +1 -1
  44. package/dist/assets/{sequenceDiagram-c5b8d532-yBBEeVFU.js → sequenceDiagram-c5b8d532-4c-qV-Ri.js} +1 -1
  45. package/dist/assets/{stateDiagram-1ecb1508-BvF4sign.js → stateDiagram-1ecb1508-CnURumPE.js} +1 -1
  46. package/dist/assets/{stateDiagram-v2-c2b004d7-BeyT7Ghx.js → stateDiagram-v2-c2b004d7-DR2qHTPg.js} +1 -1
  47. package/dist/assets/{styles-b4e223ce-C58zxmK6.js → styles-b4e223ce-B2PWXT_i.js} +1 -1
  48. package/dist/assets/{styles-ca3715f6-DCE5sFi5.js → styles-ca3715f6-DEhgVF5H.js} +1 -1
  49. package/dist/assets/{styles-d45a18b0-CG-C1aM8.js → styles-d45a18b0-DyzccA5F.js} +1 -1
  50. package/dist/assets/{svgDrawCommon-b86b1483-F-K8GeDd.js → svgDrawCommon-b86b1483-C_1tMhxp.js} +1 -1
  51. package/dist/assets/{timeline-definition-faaaa080-DPv4uqVX.js → timeline-definition-faaaa080-FdaC0dQH.js} +1 -1
  52. package/dist/assets/{tsMode-BtU8ZELV.js → tsMode-CrMC5T3_.js} +1 -1
  53. package/dist/assets/{typescript-CJHgISWo.js → typescript-CRfPu8v7.js} +1 -1
  54. package/dist/assets/{xml-C_TJw4Bi.js → xml-jlRvQfFI.js} +1 -1
  55. package/dist/assets/{xychartDiagram-f5964ef8-BmXlhBzX.js → xychartDiagram-f5964ef8-sxjv75h9.js} +1 -1
  56. package/dist/assets/{yaml-BujeJOJ6.js → yaml-B47_IHOH.js} +1 -1
  57. package/dist/index.html +2 -2
  58. package/package.json +10 -10
  59. package/src/components/chat/ChatHistoryView.tsx +92 -14
  60. package/src/components/chat/messages/MessageItem.scss +10 -0
  61. package/src/components/chat/messages/MessageItem.tsx +5 -1
  62. package/src/components/chat/messages/MessageStatusNotice.scss +163 -0
  63. package/src/components/chat/messages/MessageStatusNotice.tsx +48 -0
  64. package/src/components/chat/messages/build-chat-history-status-notices.ts +138 -0
  65. package/src/components/chat/sender/@components/sender-body/SenderBody.tsx +0 -24
  66. package/src/components/chat/sender/@core/build-sender-controller-result.ts +0 -6
  67. package/src/components/chat/sender/@hooks/use-sender-controller.ts +0 -2
  68. package/src/components/chat/sender/@types/sender-props.ts +0 -3
  69. package/src/components/chat/sender/Sender.scss +0 -58
  70. package/src/components/chat/sender/Sender.tsx +0 -2
  71. package/src/components/chat/tools/DefaultTool.tsx +74 -207
  72. package/src/components/chat/tools/adapter-claude/ClaudeEditDiff.tsx +30 -0
  73. package/src/components/chat/tools/adapter-claude/GenericClaudeTool.scss +128 -0
  74. package/src/components/chat/tools/adapter-claude/GenericClaudeTool.tsx +133 -0
  75. package/src/components/chat/tools/adapter-claude/claude-tool-edit-builders.ts +102 -0
  76. package/src/components/chat/tools/adapter-claude/claude-tool-field-sections.tsx +168 -0
  77. package/src/components/chat/tools/adapter-claude/claude-tool-operation-builders.ts +135 -0
  78. package/src/components/chat/tools/adapter-claude/claude-tool-presentation.ts +61 -0
  79. package/src/components/chat/tools/adapter-claude/claude-tool-shared.ts +185 -0
  80. package/src/components/chat/tools/adapter-claude/claude-tool-summary.ts +76 -0
  81. package/src/components/chat/tools/adapter-claude/claude-tool-system-builders.ts +125 -0
  82. package/src/components/chat/tools/adapter-claude/claude-tool-task-builders.ts +148 -0
  83. package/src/components/chat/tools/adapter-claude/index.ts +24 -15
  84. package/src/components/chat/tools/core/ToolCallBox.scss +344 -36
  85. package/src/components/chat/tools/core/ToolCallBox.tsx +35 -13
  86. package/src/components/chat/tools/core/ToolDiffViewer.scss +138 -0
  87. package/src/components/chat/tools/core/ToolDiffViewer.tsx +180 -0
  88. package/src/components/chat/tools/core/ToolGroup.scss +52 -74
  89. package/src/components/chat/tools/core/ToolGroup.tsx +20 -26
  90. package/src/components/chat/tools/core/ToolRenderer.tsx +3 -3
  91. package/src/components/chat/tools/core/ToolResultContent.tsx +66 -0
  92. package/src/components/chat/tools/core/ToolSummaryHeader.tsx +67 -0
  93. package/src/components/chat/tools/core/tool-content-presence.ts +57 -0
  94. package/src/components/chat/tools/core/tool-display.ts +192 -0
  95. package/src/components/chat/tools/core/tool-result-content-utils.ts +171 -0
  96. package/src/components/chat/tools/core/tool-summary.ts +194 -0
  97. package/src/components/chat/tools/plugin-chrome-devtools/ChromeDevtoolsTool.tsx +66 -53
  98. package/src/components/chat/tools/task/GetTaskInfoTool.tsx +26 -9
  99. package/src/components/chat/tools/task/ListTasksTool.tsx +22 -9
  100. package/src/components/chat/tools/task/StartTasksTool.tsx +22 -9
  101. package/src/hooks/chat/interaction-state.ts +29 -9
  102. package/src/hooks/chat/use-chat-scroll.ts +2 -2
  103. package/src/hooks/chat/use-chat-session-messages.ts +24 -18
  104. package/src/hooks/chat/use-chat-session.ts +2 -2
  105. package/src/resources/locales/en.json +81 -0
  106. package/src/resources/locales/zh.json +81 -0
  107. package/src/routes/ChatRoute.tsx +40 -15
  108. package/src/utils/strip-ansi.ts +26 -0
  109. package/dist/assets/channel-gq_WMRvv.js +0 -1
  110. package/dist/assets/clone-XxGY7A5N.js +0 -1
  111. package/dist/assets/flowDiagram-v2-4f6560a1-DIBOANLV.js +0 -1
@@ -76,6 +76,16 @@
76
76
  max-width: 100%;
77
77
  }
78
78
  }
79
+
80
+ &.is-targeted {
81
+ scroll-margin-top: 96px;
82
+
83
+ .bubble {
84
+ outline: 2px solid rgba(59, 130, 246, .35);
85
+ outline-offset: 6px;
86
+ border-radius: 12px;
87
+ }
88
+ }
79
89
  }
80
90
 
81
91
  .message-side-actions {
@@ -25,6 +25,7 @@ interface MessageItemProps {
25
25
  msg: ChatMessage
26
26
  isFirstInGroup: boolean
27
27
  originalMessage: ChatMessage
28
+ isTargeted: boolean
28
29
  sessionId?: string
29
30
  sessionInfo?: SessionInfo | null
30
31
  isSessionBusy: boolean
@@ -47,6 +48,7 @@ function MessageItemComponent({
47
48
  msg,
48
49
  isFirstInGroup,
49
50
  originalMessage,
51
+ isTargeted,
50
52
  sessionId,
51
53
  sessionInfo,
52
54
  isSessionBusy,
@@ -402,7 +404,8 @@ function MessageItemComponent({
402
404
  id={anchorId}
403
405
  className={`${isUser ? 'chat-message-user' : 'chat-message-assistant'} ${isEditing ? 'is-editing' : ''} ${
404
406
  !isFirstInGroup ? 'consecutive' : ''
405
- } ${isActionsVisible ? 'is-actions-visible' : ''}`}
407
+ } ${isActionsVisible ? 'is-actions-visible' : ''} ${isTargeted ? 'is-targeted' : ''}`}
408
+ data-message-id={originalMessage.id}
406
409
  onPointerEnter={handleActionsPointerEnter}
407
410
  onPointerLeave={handleActionsPointerLeave}
408
411
  >
@@ -448,6 +451,7 @@ function MessageItemComponent({
448
451
  const areMessageItemPropsEqual = (prev: MessageItemProps, next: MessageItemProps) => {
449
452
  return prev.anchorId === next.anchorId &&
450
453
  prev.isFirstInGroup === next.isFirstInGroup &&
454
+ prev.isTargeted === next.isTargeted &&
451
455
  prev.isSessionBusy === next.isSessionBusy &&
452
456
  prev.isEditing === next.isEditing &&
453
457
  prev.sessionId === next.sessionId &&
@@ -0,0 +1,163 @@
1
+ .message-status-notice {
2
+ width: 100%;
3
+
4
+ &__card {
5
+ width: 100%;
6
+ padding: 7px 12px;
7
+ border: 0;
8
+ border-radius: 0;
9
+ background: transparent;
10
+ box-shadow: none;
11
+ }
12
+
13
+ &__icon {
14
+ font-size: 14px;
15
+ line-height: 1;
16
+ flex-shrink: 0;
17
+ }
18
+
19
+ &__content {
20
+ display: flex;
21
+ flex-direction: column;
22
+ gap: 4px;
23
+ min-width: 0;
24
+ flex: 1;
25
+ }
26
+
27
+ &__header {
28
+ display: flex;
29
+ align-items: flex-start;
30
+ justify-content: space-between;
31
+ gap: 6px;
32
+ min-width: 0;
33
+ }
34
+
35
+ &__title-row {
36
+ display: flex;
37
+ align-items: flex-start;
38
+ gap: 6px;
39
+ min-width: 0;
40
+ flex-wrap: wrap;
41
+ }
42
+
43
+ &__title {
44
+ font-size: 12px;
45
+ line-height: 1.3;
46
+ font-weight: 500;
47
+ }
48
+
49
+ &__badge,
50
+ &__meta {
51
+ display: inline-flex;
52
+ align-items: center;
53
+ padding: 0;
54
+ border-radius: 0;
55
+ font-size: 10px;
56
+ line-height: 1.2;
57
+ white-space: nowrap;
58
+ }
59
+
60
+ &__badge {
61
+ opacity: .72;
62
+ }
63
+
64
+ &__meta {
65
+ font-family:
66
+ ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono,
67
+ Courier New, monospace;
68
+ opacity: .8;
69
+ }
70
+
71
+ &__message {
72
+ color: var(--text-color);
73
+ font-size: 12px;
74
+ line-height: 1.45;
75
+ word-break: break-word;
76
+ }
77
+
78
+ &__detail {
79
+ color: var(--sub-text-color, #6b7280);
80
+ font-size: 11px;
81
+ line-height: 1.35;
82
+ word-break: break-word;
83
+ }
84
+
85
+ &__actions {
86
+ display: flex;
87
+ align-items: center;
88
+ gap: 6px;
89
+ padding-top: 1px;
90
+ }
91
+
92
+ &--error {
93
+ .message-status-notice__icon,
94
+ .message-status-notice__title {
95
+ color: #a16262;
96
+ }
97
+
98
+ .message-status-notice__badge,
99
+ .message-status-notice__meta {
100
+ color: #b07a7a;
101
+ }
102
+ }
103
+
104
+ &--warning {
105
+ .message-status-notice__icon,
106
+ .message-status-notice__title {
107
+ color: #9a7b4f;
108
+ }
109
+
110
+ .message-status-notice__badge,
111
+ .message-status-notice__meta {
112
+ color: #aa8b62;
113
+ }
114
+ }
115
+ }
116
+
117
+ html.dark {
118
+ .message-status-notice {
119
+ &--error {
120
+ .message-status-notice__icon,
121
+ .message-status-notice__title,
122
+ .message-status-notice__badge,
123
+ .message-status-notice__meta {
124
+ color: #d9b5b5;
125
+ }
126
+
127
+ .message-status-notice__badge,
128
+ .message-status-notice__meta {
129
+ background: transparent;
130
+ }
131
+
132
+ .message-status-notice__message {
133
+ color: #f3f4f6;
134
+ }
135
+
136
+ .message-status-notice__detail {
137
+ color: #cba0a0;
138
+ }
139
+ }
140
+
141
+ &--warning {
142
+ .message-status-notice__icon,
143
+ .message-status-notice__title,
144
+ .message-status-notice__badge,
145
+ .message-status-notice__meta {
146
+ color: #d7c399;
147
+ }
148
+
149
+ .message-status-notice__badge,
150
+ .message-status-notice__meta {
151
+ background: transparent;
152
+ }
153
+
154
+ .message-status-notice__message {
155
+ color: #f3f4f6;
156
+ }
157
+
158
+ .message-status-notice__detail {
159
+ color: #c8b27c;
160
+ }
161
+ }
162
+ }
163
+ }
@@ -0,0 +1,48 @@
1
+ import './MessageStatusNotice.scss'
2
+
3
+ import { Button } from 'antd'
4
+ import { useTranslation } from 'react-i18next'
5
+
6
+ import type { ChatHistoryStatusNotice } from './build-chat-history-status-notices'
7
+
8
+ export function MessageStatusNotice({
9
+ notice,
10
+ onRetryConnection
11
+ }: {
12
+ notice: ChatHistoryStatusNotice
13
+ onRetryConnection: () => void
14
+ }) {
15
+ const { t } = useTranslation()
16
+
17
+ return (
18
+ <div className={`message-status-notice message-status-notice--${notice.tone} ${notice.isMock ? 'is-mock' : ''}`}>
19
+ <div className='message-status-notice__card' role='status' aria-live='polite'>
20
+ <div className='message-status-notice__content'>
21
+ <div className='message-status-notice__header'>
22
+ <div className='message-status-notice__title-row'>
23
+ <span className='material-symbols-rounded message-status-notice__icon'>{notice.icon}</span>
24
+ <div className='message-status-notice__title'>{notice.title}</div>
25
+ {notice.isMock && (
26
+ <span className='message-status-notice__badge'>{t('chat.debugMockLabel')}</span>
27
+ )}
28
+ </div>
29
+ {notice.meta != null && notice.meta !== '' && (
30
+ <span className='message-status-notice__meta'>{notice.meta}</span>
31
+ )}
32
+ </div>
33
+ <div className='message-status-notice__message'>{notice.message}</div>
34
+ {notice.detail != null && notice.detail !== '' && (
35
+ <div className='message-status-notice__detail'>{notice.detail}</div>
36
+ )}
37
+ {notice.action === 'retry-connection' && (
38
+ <div className='message-status-notice__actions'>
39
+ <Button size='small' onClick={onRetryConnection}>
40
+ {t('chat.retryConnection')}
41
+ </Button>
42
+ </div>
43
+ )}
44
+ </div>
45
+ </div>
46
+ </div>
47
+ )
48
+ }
@@ -0,0 +1,138 @@
1
+ import type { ChatErrorState } from '#~/hooks/chat/interaction-state'
2
+
3
+ type Translate = (key: string, options?: Record<string, unknown>) => string
4
+
5
+ export interface ChatHistoryStatusNotice {
6
+ action?: 'retry-connection'
7
+ detail?: string
8
+ icon: string
9
+ id: string
10
+ isMock?: boolean
11
+ message: string
12
+ meta?: string
13
+ tone: 'error' | 'warning'
14
+ title: string
15
+ }
16
+
17
+ const createModelUnavailableNotice = (t: Translate, isMock = false): ChatHistoryStatusNotice => ({
18
+ icon: 'settings_suggest',
19
+ id: isMock ? 'mock-model-unavailable' : 'model-unavailable',
20
+ isMock,
21
+ message: t('chat.modelConfigRequired'),
22
+ detail: t('chat.modelConfigRequiredHelp'),
23
+ tone: 'warning',
24
+ title: t('chat.modelConfigRequiredTitle')
25
+ })
26
+
27
+ const createConnectionNotice = (
28
+ t: Translate,
29
+ state: ChatErrorState,
30
+ isMock = false
31
+ ): ChatHistoryStatusNotice => {
32
+ const isClosed = state.reason === 'closed'
33
+
34
+ return {
35
+ action: isMock ? undefined : 'retry-connection',
36
+ detail: isClosed ? t('chat.connectionClosedHelp') : t('chat.connectionErrorHelp'),
37
+ icon: isClosed ? 'wifi_off' : 'cloud_off',
38
+ id: isMock
39
+ ? isClosed ? 'mock-connection-closed' : 'mock-connection-error'
40
+ : isClosed
41
+ ? 'connection-closed'
42
+ : 'connection-error',
43
+ isMock,
44
+ message: state.message,
45
+ tone: 'error',
46
+ title: isClosed ? t('chat.connectionClosedTitle') : t('chat.connectionErrorTitle')
47
+ }
48
+ }
49
+
50
+ const createSessionNotice = (
51
+ t: Translate,
52
+ state: ChatErrorState,
53
+ isMock = false
54
+ ): ChatHistoryStatusNotice => ({
55
+ detail: t('chat.sessionErrorHelp'),
56
+ icon: 'error',
57
+ id: isMock ? 'mock-session-error' : 'session-error',
58
+ isMock,
59
+ message: state.message,
60
+ meta: state.code != null && state.code !== ''
61
+ ? t('chat.sessionErrorCode', { code: state.code })
62
+ : undefined,
63
+ tone: 'error',
64
+ title: t('chat.sessionErrorTitle')
65
+ })
66
+
67
+ export const buildChatHistoryStatusNotices = ({
68
+ errorState,
69
+ isDebugMode,
70
+ modelUnavailable,
71
+ t
72
+ }: {
73
+ errorState?: ChatErrorState | null
74
+ isDebugMode: boolean
75
+ modelUnavailable: boolean
76
+ t: Translate
77
+ }) => {
78
+ const notices: ChatHistoryStatusNotice[] = []
79
+ const activeScenarios = new Set<string>()
80
+
81
+ if (modelUnavailable) {
82
+ notices.push(createModelUnavailableNotice(t))
83
+ activeScenarios.add('model-unavailable')
84
+ }
85
+
86
+ if (errorState != null && errorState.message.trim() !== '') {
87
+ if (errorState.kind === 'session') {
88
+ notices.push(createSessionNotice(t, errorState))
89
+ activeScenarios.add('session-error')
90
+ } else {
91
+ notices.push(createConnectionNotice(t, errorState))
92
+ activeScenarios.add(errorState.reason === 'closed' ? 'connection-closed' : 'connection-error')
93
+ }
94
+ }
95
+
96
+ if (!isDebugMode) {
97
+ return notices
98
+ }
99
+
100
+ const mockNotices: Array<{ notice: ChatHistoryStatusNotice; scenario: string }> = [
101
+ {
102
+ scenario: 'connection-error',
103
+ notice: createConnectionNotice(t, {
104
+ kind: 'connection',
105
+ message: t('chat.debugMockConnectionErrorMessage'),
106
+ reason: 'error'
107
+ }, true)
108
+ },
109
+ {
110
+ scenario: 'connection-closed',
111
+ notice: createConnectionNotice(t, {
112
+ kind: 'connection',
113
+ message: t('chat.debugMockConnectionClosedMessage'),
114
+ reason: 'closed'
115
+ }, true)
116
+ },
117
+ {
118
+ scenario: 'session-error',
119
+ notice: createSessionNotice(t, {
120
+ kind: 'session',
121
+ message: t('chat.debugMockSessionErrorMessage'),
122
+ code: 'session_timeout'
123
+ }, true)
124
+ },
125
+ {
126
+ scenario: 'model-unavailable',
127
+ notice: createModelUnavailableNotice(t, true)
128
+ }
129
+ ]
130
+
131
+ for (const mock of mockNotices) {
132
+ if (!activeScenarios.has(mock.scenario)) {
133
+ notices.push(mock.notice)
134
+ }
135
+ }
136
+
137
+ return notices
138
+ }
@@ -1,11 +1,9 @@
1
- import { Button } from 'antd'
2
1
  import type { MutableRefObject } from 'react'
3
2
  import { useTranslation } from 'react-i18next'
4
3
 
5
4
  import type { SessionInfo } from '@vibe-forge/types'
6
5
 
7
6
  import { ContextFilePicker } from '#~/components/workspace/ContextFilePicker'
8
- import type { ChatErrorBannerState } from '#~/hooks/chat/interaction-state'
9
7
 
10
8
  import type {
11
9
  SenderToolbarData,
@@ -25,8 +23,6 @@ export function SenderBody({
25
23
  isInlineEdit,
26
24
  isBusy,
27
25
  modelUnavailable,
28
- errorBanner,
29
- onRetryConnection,
30
26
  pendingImages,
31
27
  pendingFiles,
32
28
  onRemovePendingImage,
@@ -52,8 +48,6 @@ export function SenderBody({
52
48
  isInlineEdit: boolean
53
49
  isBusy: boolean
54
50
  modelUnavailable?: boolean
55
- errorBanner?: ChatErrorBannerState | null
56
- onRetryConnection?: () => void
57
51
  pendingImages: Parameters<typeof SenderAttachments>[0]['pendingImages']
58
52
  pendingFiles: Parameters<typeof SenderAttachments>[0]['pendingFiles']
59
53
  onRemovePendingImage: (id: string) => void
@@ -81,27 +75,9 @@ export function SenderBody({
81
75
  onConfirmContextPicker: (files: PendingContextFile[]) => void
82
76
  }) {
83
77
  const { t } = useTranslation()
84
- const errorTitle = errorBanner?.kind === 'session'
85
- ? t('chat.sessionErrorTitle')
86
- : t('chat.connectionErrorTitle')
87
78
 
88
79
  return (
89
80
  <div className={`chat-input-container ${isInlineEdit ? 'chat-input-container--inline-edit' : ''}`.trim()}>
90
- {!isInlineEdit && errorBanner != null && errorBanner.message.trim() !== '' && (
91
- <div className='connection-error-banner'>
92
- <div className='connection-error-content'>
93
- <span className='material-symbols-rounded'>error</span>
94
- <div className='connection-error-copy'>
95
- <div className='connection-error-title'>{errorTitle}</div>
96
- <div className='connection-error-message'>{errorBanner.message}</div>
97
- </div>
98
- </div>
99
- {errorBanner.kind === 'connection' && onRetryConnection != null && (
100
- <Button size='small' onClick={onRetryConnection}>{t('chat.retryConnection')}</Button>
101
- )}
102
- </div>
103
- )}
104
- {!isInlineEdit && modelUnavailable && <div className='model-unavailable'>{t('chat.modelConfigRequired')}</div>}
105
81
  <SenderAttachments
106
82
  pendingImages={pendingImages}
107
83
  pendingFiles={pendingFiles}
@@ -49,7 +49,6 @@ export const buildSenderControllerResult = ({
49
49
  attachments,
50
50
  completion,
51
51
  composer,
52
- errorBanner,
53
52
  focusRestore,
54
53
  handleKeyDown,
55
54
  hideSender,
@@ -59,7 +58,6 @@ export const buildSenderControllerResult = ({
59
58
  isInlineEdit,
60
59
  isThinking,
61
60
  modelUnavailable,
62
- onRetryConnection,
63
61
  permissionContext,
64
62
  placeholder,
65
63
  editorRef,
@@ -68,7 +66,6 @@ export const buildSenderControllerResult = ({
68
66
  attachments: SenderControllerAttachments
69
67
  completion: SenderControllerCompletion
70
68
  composer: SenderControllerComposer
71
- errorBanner?: SenderProps['errorBanner']
72
69
  focusRestore: { queueEditorFocusRestore: () => void }
73
70
  handleKeyDown: (event: KeyboardEvent) => void
74
71
  hideSender: boolean
@@ -78,7 +75,6 @@ export const buildSenderControllerResult = ({
78
75
  isInlineEdit: boolean
79
76
  isThinking: boolean
80
77
  modelUnavailable?: boolean
81
- onRetryConnection?: (() => void) | undefined
82
78
  permissionContext?: {
83
79
  deniedTools?: string[]
84
80
  reasons?: string[]
@@ -102,8 +98,6 @@ export const buildSenderControllerResult = ({
102
98
  handleKeyDown,
103
99
  interactionRequest,
104
100
  interactionResponse,
105
- errorBanner,
106
- onRetryConnection,
107
101
  modelUnavailable,
108
102
  placeholder,
109
103
  onInputChange: completion.handleInputChange,
@@ -174,7 +174,6 @@ export const useSenderController = (props: SenderProps) => {
174
174
  attachments,
175
175
  completion,
176
176
  composer,
177
- errorBanner: props.errorBanner,
178
177
  focusRestore,
179
178
  handleKeyDown,
180
179
  hideSender,
@@ -184,7 +183,6 @@ export const useSenderController = (props: SenderProps) => {
184
183
  isInlineEdit,
185
184
  isThinking,
186
185
  modelUnavailable: props.modelUnavailable,
187
- onRetryConnection: props.onRetryConnection,
188
186
  permissionContext,
189
187
  editorRef,
190
188
  placeholder: props.placeholder ?? props.interactionRequest?.payload.question ?? t('chat.inputPlaceholder'),
@@ -3,7 +3,6 @@ import type { ReactNode } from 'react'
3
3
  import type { AskUserQuestionParams, ChatMessageContent, SessionStatus } from '@vibe-forge/core'
4
4
  import type { SessionInfo } from '@vibe-forge/types'
5
5
 
6
- import type { ChatErrorBannerState } from '#~/hooks/chat/interaction-state'
7
6
  import type { ChatEffort } from '#~/hooks/chat/use-chat-effort'
8
7
  import type { ModelSelectMenuGroup, ModelSelectOption } from '#~/hooks/chat/use-chat-model-adapter-selection'
9
8
  import type { PermissionMode } from '#~/hooks/chat/use-chat-permission-mode'
@@ -19,8 +18,6 @@ export interface SenderProps {
19
18
  onInterrupt: () => void
20
19
  onClear?: () => void
21
20
  sessionInfo?: SessionInfo | null
22
- errorBanner?: ChatErrorBannerState | null
23
- onRetryConnection?: () => void
24
21
  interactionRequest?: { id: string; payload: AskUserQuestionParams } | null
25
22
  onInteractionResponse?: (id: string, data: string | string[]) => void
26
23
  placeholder?: string
@@ -27,64 +27,6 @@
27
27
  .chat-input-monaco {
28
28
  color: var(--text-color);
29
29
  }
30
-
31
- .model-unavailable {
32
- margin-bottom: 6px;
33
- padding: 6px 8px;
34
- border-radius: 8px;
35
- border: 1px dashed var(--border-color);
36
- background-color: var(--tag-bg, #f9fafb);
37
- color: var(--placeholder-color);
38
- font-size: 12px;
39
- }
40
-
41
- .connection-error-banner {
42
- display: flex;
43
- align-items: center;
44
- justify-content: space-between;
45
- gap: 12px;
46
- margin-bottom: 8px;
47
- padding: 10px 12px;
48
- border-radius: 10px;
49
- border: 1px solid rgba(220, 38, 38, .2);
50
- background: linear-gradient(
51
- 135deg,
52
- rgba(254, 242, 242, .95),
53
- rgba(255, 255, 255, .92)
54
- );
55
- }
56
-
57
- .connection-error-content {
58
- display: flex;
59
- align-items: flex-start;
60
- gap: 10px;
61
- min-width: 0;
62
-
63
- .material-symbols-rounded {
64
- color: #dc2626;
65
- font-size: 18px;
66
- line-height: 1;
67
- margin-top: 1px;
68
- }
69
- }
70
-
71
- .connection-error-copy {
72
- min-width: 0;
73
- }
74
-
75
- .connection-error-title {
76
- color: #991b1b;
77
- font-size: 12px;
78
- font-weight: 600;
79
- }
80
-
81
- .connection-error-message {
82
- margin-top: 2px;
83
- color: #7f1d1d;
84
- font-size: 12px;
85
- line-height: 1.4;
86
- word-break: break-word;
87
- }
88
30
  }
89
31
 
90
32
  .chat-input-container--inline-edit {
@@ -33,8 +33,6 @@ export function Sender(props: SenderProps) {
33
33
  isInlineEdit={controller.isInlineEdit}
34
34
  isBusy={controller.isBusy}
35
35
  modelUnavailable={controller.modelUnavailable}
36
- errorBanner={controller.errorBanner}
37
- onRetryConnection={controller.onRetryConnection}
38
36
  pendingImages={controller.composer.pendingImages}
39
37
  pendingFiles={controller.composer.pendingFiles}
40
38
  onRemovePendingImage={(id) =>