@raolin2025/claude-code-node 2.3.2 → 2.3.4
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.
- package/package.json +1 -1
- package/src/core/cli.js +61 -27
package/package.json
CHANGED
package/src/core/cli.js
CHANGED
|
@@ -136,8 +136,8 @@ function buildBanner({ model, permissionMode, session, maxTokens }) {
|
|
|
136
136
|
const top = `╭${leftLabel}${'─'.repeat(inner - leftLabel.length)}╮`
|
|
137
137
|
const bottom = `╰${'─'.repeat(inner)}╯`
|
|
138
138
|
|
|
139
|
-
const col1 =
|
|
140
|
-
const col2 =
|
|
139
|
+
const col1 = 24 // 左侧机器人列(19字符+边距)
|
|
140
|
+
const col2 = 50 // 标题列
|
|
141
141
|
const col3 = inner - col1 - col2 - 2 // 信息列
|
|
142
142
|
|
|
143
143
|
// ANSI 颜色
|
|
@@ -145,47 +145,68 @@ function buildBanner({ model, permissionMode, session, maxTokens }) {
|
|
|
145
145
|
const CYAN = '\x1b[36m'
|
|
146
146
|
const RESET = '\x1b[0m'
|
|
147
147
|
|
|
148
|
-
// 像素风格 CC 机器人(
|
|
148
|
+
// 像素风格 CC 机器人(19字符宽,带颜色边框)
|
|
149
149
|
const robotRaw = [
|
|
150
|
-
'
|
|
151
|
-
'
|
|
152
|
-
'
|
|
153
|
-
'
|
|
154
|
-
'
|
|
155
|
-
'
|
|
156
|
-
'
|
|
157
|
-
'
|
|
158
|
-
'
|
|
159
|
-
'
|
|
150
|
+
' ┌───────────────┐ ',
|
|
151
|
+
' │ ██ ██ │ ',
|
|
152
|
+
' │ ██████ │ ',
|
|
153
|
+
' │ ██ ██ │ ',
|
|
154
|
+
' │ │ ',
|
|
155
|
+
' │ ███████ │ ',
|
|
156
|
+
' │ ██ ██ │ ',
|
|
157
|
+
' │ ██████ │ ',
|
|
158
|
+
' │ ██ ██ ██ │ ',
|
|
159
|
+
' └───────────────┘ ',
|
|
160
160
|
]
|
|
161
161
|
|
|
162
162
|
// 颜色化:边框蓝,眼睛/嘴巴青
|
|
163
163
|
const colorize = (line) =>
|
|
164
164
|
line
|
|
165
|
-
.replace(/[
|
|
165
|
+
.replace(/[┌└─╭╮┐┘│]/g, BLUE + '$&' + RESET)
|
|
166
166
|
.replace(/[█]/g, CYAN + '$&' + RESET)
|
|
167
167
|
|
|
168
168
|
const robotColored = robotRaw.map(colorize)
|
|
169
169
|
|
|
170
|
-
//
|
|
171
|
-
const
|
|
172
|
-
const
|
|
170
|
+
// 机器人列:先去掉 ANSI(保证 pad 长度准确),再填充
|
|
171
|
+
const stripAnsi = (s) => s.replace(/\x1b\[[0-9;]*m/g, '')
|
|
172
|
+
const robotPlain = robotColored.map(line => stripAnsi(line))
|
|
173
|
+
|
|
174
|
+
// 在 col1 内居中(按最大可见长度 21 计算,短的自动靠右)
|
|
175
|
+
const visibleWidth = 21
|
|
176
|
+
const totalCol1 = 24
|
|
173
177
|
const leftPad = Math.floor((totalCol1 - visibleWidth) / 2)
|
|
174
178
|
const rightPad = totalCol1 - visibleWidth - leftPad
|
|
175
|
-
const robotLines =
|
|
179
|
+
const robotLines = robotPlain.map(line => ' '.repeat(leftPad) + line + ' '.repeat(rightPad))
|
|
180
|
+
|
|
181
|
+
// 辅助函数:字符串填充(忽略 ANSI 颜色码计算真实长度,正确处理嵌入在字符串中的 ANSI)
|
|
182
|
+
const realLen = (s) => stripAnsi(s).length
|
|
176
183
|
|
|
177
|
-
// 辅助函数:字符串填充
|
|
178
184
|
const pad = (s, w, align = 'left') => {
|
|
179
|
-
|
|
180
|
-
|
|
185
|
+
const rlen = realLen(s)
|
|
186
|
+
if (rlen >= w) {
|
|
187
|
+
// 需要截断,但保留 ANSI 序列
|
|
188
|
+
const result = stripAnsi(s).substring(0, w)
|
|
189
|
+
// 把截断后的字符对应的 ANSI 序列加回去(简化处理:只保留前导 ANSI)
|
|
190
|
+
const leadingAnsi = s.match(/^(\x1b\[[0-9;]*m)*/)?.[0] || ''
|
|
191
|
+
return leadingAnsi + result
|
|
192
|
+
}
|
|
193
|
+
const spaces = w - rlen
|
|
194
|
+
const afterAnsi = stripAnsi(s) // 纯文本
|
|
195
|
+
let result
|
|
181
196
|
if (align === 'center') {
|
|
182
197
|
const l = Math.floor(spaces / 2)
|
|
183
|
-
|
|
198
|
+
result = ' '.repeat(l) + afterAnsi + ' '.repeat(spaces - l)
|
|
199
|
+
} else if (align === 'right') {
|
|
200
|
+
result = ' '.repeat(spaces) + afterAnsi
|
|
201
|
+
} else {
|
|
202
|
+
result = afterAnsi + ' '.repeat(spaces)
|
|
184
203
|
}
|
|
185
|
-
|
|
204
|
+
// 加回前导 ANSI
|
|
205
|
+
const leadingAnsi = s.match(/^(\x1b\[[0-9;]*m)*/)?.[0] || ''
|
|
206
|
+
return leadingAnsi + result
|
|
186
207
|
}
|
|
187
208
|
|
|
188
|
-
// 标题列(居中,行数匹配 robotLines
|
|
209
|
+
// 标题列(居中,行数匹配 robotLines,内容放在中间行)
|
|
189
210
|
const baseTitleLines = [
|
|
190
211
|
pad('AI Code Agent', col2, 'center'),
|
|
191
212
|
pad('Node.js Edition', col2, 'center'),
|
|
@@ -193,12 +214,19 @@ function buildBanner({ model, permissionMode, session, maxTokens }) {
|
|
|
193
214
|
pad('─'.repeat(col2 - 2), col2, 'center'),
|
|
194
215
|
pad('/help — commands · /exit — quit', col2, 'center')
|
|
195
216
|
]
|
|
217
|
+
// 内容从第2行开始(让分隔线与机器人中间横线对齐)
|
|
218
|
+
const titleStart = 1
|
|
196
219
|
const titleLines = []
|
|
197
220
|
for (let i = 0; i < robotLines.length; i++) {
|
|
198
|
-
|
|
221
|
+
const ti = i - titleStart
|
|
222
|
+
if (ti >= 0 && ti < baseTitleLines.length) {
|
|
223
|
+
titleLines.push(baseTitleLines[ti])
|
|
224
|
+
} else {
|
|
225
|
+
titleLines.push(pad('', col2, 'center'))
|
|
226
|
+
}
|
|
199
227
|
}
|
|
200
228
|
|
|
201
|
-
//
|
|
229
|
+
// 信息列(右对齐,内容放在中间行)
|
|
202
230
|
const sessionId = session?.id || '??????'
|
|
203
231
|
const baseInfoLines = [
|
|
204
232
|
pad(`Turns: ${session?.state?.turnCount ?? 0} • Tools: 0`, col3, 'right'),
|
|
@@ -207,9 +235,15 @@ function buildBanner({ model, permissionMode, session, maxTokens }) {
|
|
|
207
235
|
pad(`Budget: 0 / ${maxTokens ?? 200000}`, col3, 'right'),
|
|
208
236
|
pad(`Session: ${sessionId?.toString().slice(-6)}`, col3, 'right')
|
|
209
237
|
]
|
|
238
|
+
const infoStart = 1
|
|
210
239
|
const infoLines = []
|
|
211
240
|
for (let i = 0; i < robotLines.length; i++) {
|
|
212
|
-
|
|
241
|
+
const ti = i - infoStart
|
|
242
|
+
if (ti >= 0 && ti < baseInfoLines.length) {
|
|
243
|
+
infoLines.push(baseInfoLines[ti])
|
|
244
|
+
} else {
|
|
245
|
+
infoLines.push(pad('', col3, 'right'))
|
|
246
|
+
}
|
|
213
247
|
}
|
|
214
248
|
|
|
215
249
|
// 构建每行:│ col1 │ col2 │ col3 │
|