zames_pro 1.2.0 → 2.0.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.
- package/dist/agent-loop.js +652 -0
- package/dist/agent-loop.js.map +1 -0
- package/dist/browser.js +573 -0
- package/dist/browser.js.map +1 -0
- package/dist/config.js +74 -0
- package/dist/config.js.map +1 -0
- package/dist/confirm.js +82 -0
- package/dist/confirm.js.map +1 -0
- package/dist/diff.js +38 -0
- package/dist/diff.js.map +1 -0
- package/dist/gitTools.js +219 -0
- package/dist/gitTools.js.map +1 -0
- package/dist/index.js +1365 -0
- package/dist/index.js.map +1 -0
- package/dist/input.js +524 -0
- package/dist/input.js.map +1 -0
- package/dist/markdown.js +48 -0
- package/dist/markdown.js.map +1 -0
- package/dist/self-review.js +281 -0
- package/dist/self-review.js.map +1 -0
- package/dist/sessions.js +123 -0
- package/dist/sessions.js.map +1 -0
- package/dist/spinner.js +138 -0
- package/dist/spinner.js.map +1 -0
- package/{src → dist}/system-prompt.js +10 -14
- package/dist/system-prompt.js.map +1 -0
- package/{src → dist}/theme.js +17 -19
- package/dist/theme.js.map +1 -0
- package/dist/tools.js +189 -0
- package/dist/tools.js.map +1 -0
- package/dist/transcript.js +51 -0
- package/dist/transcript.js.map +1 -0
- package/dist/types.js +4 -0
- package/dist/types.js.map +1 -0
- package/dist/undo.js +100 -0
- package/dist/undo.js.map +1 -0
- package/dist/web.js +220 -0
- package/dist/web.js.map +1 -0
- package/dist/xml-toolcall.js +68 -0
- package/dist/xml-toolcall.js.map +1 -0
- package/package.json +13 -5
- package/src/agent-loop.js +0 -575
- package/src/browser.js +0 -627
- package/src/config.js +0 -81
- package/src/confirm.js +0 -85
- package/src/diff.js +0 -32
- package/src/gitTools.js +0 -256
- package/src/index.js +0 -1461
- package/src/input.js +0 -447
- package/src/markdown.js +0 -49
- package/src/self-review.js +0 -319
- package/src/spinner.js +0 -144
- package/src/tools.js +0 -206
- package/src/transcript.js +0 -46
- package/src/undo.js +0 -101
- package/src/web.js +0 -266
- package/src/xml-toolcall.js +0 -69
package/src/input.js
DELETED
|
@@ -1,447 +0,0 @@
|
|
|
1
|
-
import { theme } from './theme.js'
|
|
2
|
-
import { renderMarkdown } from './markdown.js'
|
|
3
|
-
import { randomThinkingPhrase, stripEllipsis } from './spinner.js'
|
|
4
|
-
|
|
5
|
-
// Постоянная строка ввода внизу терминала + область статуса/вывода над ней.
|
|
6
|
-
//
|
|
7
|
-
// Зачем свой редактор:
|
|
8
|
-
// * строка ввода видна ВСЕГДА и не затирается выводом агента;
|
|
9
|
-
// * спиннер/статус и ответы печатаются ВЫШЕ строки ввода (printAbove);
|
|
10
|
-
// * перерисовка учитывает перенос по ширине терминала, поэтому нет
|
|
11
|
-
// дублирования строк при многострочном вводе;
|
|
12
|
-
// * многострочный ввод: Enter отправляет, Ctrl+J (и Shift+Enter в
|
|
13
|
-
// терминалах с расширенным протоколом) вставляют перевод строки.
|
|
14
|
-
//
|
|
15
|
-
// Все управляющие символы собираются из кодов, чтобы в файле не было
|
|
16
|
-
// «сырых» ESC/CR/LF в строковых литералах (см. AGENTS.md).
|
|
17
|
-
|
|
18
|
-
const NL = String.fromCharCode(10)
|
|
19
|
-
const CR = String.fromCharCode(13)
|
|
20
|
-
const ESC = String.fromCharCode(27)
|
|
21
|
-
const PASTE_START = ESC + '[200~'
|
|
22
|
-
const PASTE_END = ESC + '[201~'
|
|
23
|
-
|
|
24
|
-
const DOTS = ['.', '..', '...']
|
|
25
|
-
const HINT = theme.dim(' · Enter — отправить, Ctrl+J — новая строка, Esc — стоп')
|
|
26
|
-
|
|
27
|
-
function safeJson(v) {
|
|
28
|
-
try {
|
|
29
|
-
return JSON.stringify(v)
|
|
30
|
-
} catch {
|
|
31
|
-
return String(v)
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Видимая длина строки без ANSI-последовательностей.
|
|
36
|
-
export function visLen(s) {
|
|
37
|
-
s = String(s)
|
|
38
|
-
let n = 0
|
|
39
|
-
let i = 0
|
|
40
|
-
while (i < s.length) {
|
|
41
|
-
if (s.charCodeAt(i) === 27) {
|
|
42
|
-
i++
|
|
43
|
-
if (s[i] === '[') {
|
|
44
|
-
i++
|
|
45
|
-
while (i < s.length && !/[A-Za-z]/.test(s[i])) i++
|
|
46
|
-
i++
|
|
47
|
-
}
|
|
48
|
-
continue
|
|
49
|
-
}
|
|
50
|
-
n++
|
|
51
|
-
i++
|
|
52
|
-
}
|
|
53
|
-
return n
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Раскладка ввода на визуальные строки с учётом ширины терминала.
|
|
57
|
-
// Возвращает строки (с префиксом) и позицию курсора в визуальных координатах.
|
|
58
|
-
export function layoutInput(promptStr, buf, cursor, cols) {
|
|
59
|
-
const width = Math.max(20, Number(cols) || 80)
|
|
60
|
-
const promptW = visLen(promptStr)
|
|
61
|
-
const pad = ' '.repeat(promptW)
|
|
62
|
-
const chars = Array.from(String(buf))
|
|
63
|
-
const cur = Math.max(0, Math.min(Number(cursor) || 0, chars.length))
|
|
64
|
-
const rows = []
|
|
65
|
-
let i = 0
|
|
66
|
-
while (true) {
|
|
67
|
-
const first = rows.length === 0
|
|
68
|
-
const prefix = first ? promptStr : pad
|
|
69
|
-
const avail = Math.max(1, width - promptW)
|
|
70
|
-
let text = ''
|
|
71
|
-
let count = 0
|
|
72
|
-
const start = i
|
|
73
|
-
while (i < chars.length && chars[i] !== NL && count < avail) {
|
|
74
|
-
text += chars[i]
|
|
75
|
-
i++
|
|
76
|
-
count++
|
|
77
|
-
}
|
|
78
|
-
if (i < chars.length && chars[i] === NL) {
|
|
79
|
-
i++
|
|
80
|
-
rows.push({ prefix, text, start })
|
|
81
|
-
continue
|
|
82
|
-
}
|
|
83
|
-
rows.push({ prefix, text, start })
|
|
84
|
-
if (i >= chars.length) break
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
let cursorRow = 0
|
|
88
|
-
let cursorCol = promptW
|
|
89
|
-
for (let r = 0; r < rows.length; r++) {
|
|
90
|
-
const row = rows[r]
|
|
91
|
-
const len = Array.from(row.text).length
|
|
92
|
-
const from = row.start
|
|
93
|
-
const to = row.start + len
|
|
94
|
-
if (cur >= from && cur <= to) {
|
|
95
|
-
const next = rows[r + 1]
|
|
96
|
-
if (cur === to && next && next.start === to) continue
|
|
97
|
-
cursorRow = r
|
|
98
|
-
cursorCol = promptW + (cur - from)
|
|
99
|
-
break
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
return { rows, cursorRow, cursorCol }
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export class LineEditor {
|
|
106
|
-
constructor({ prompt = '> ' } = {}) {
|
|
107
|
-
this.promptStr = prompt
|
|
108
|
-
this.buf = ''
|
|
109
|
-
this.cursor = 0
|
|
110
|
-
this.statusText = ''
|
|
111
|
-
this.pendingText = null
|
|
112
|
-
this.rendered = false
|
|
113
|
-
this.cursorRowFromTop = 0
|
|
114
|
-
this.busy = false
|
|
115
|
-
this.onSubmit = null
|
|
116
|
-
this.onEscape = null
|
|
117
|
-
this.onCtrlC = null
|
|
118
|
-
this._inPaste = false
|
|
119
|
-
this._dotTimer = null
|
|
120
|
-
this._dotPhase = 0
|
|
121
|
-
this._thinkBase = ''
|
|
122
|
-
this._wasRaw = false
|
|
123
|
-
this._onData = (b) => this._handle(b)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
start() {
|
|
127
|
-
const stdin = process.stdin
|
|
128
|
-
if (!stdin.isTTY || !stdin.setRawMode) return false
|
|
129
|
-
this._wasRaw = stdin.isRaw
|
|
130
|
-
stdin.setRawMode(true)
|
|
131
|
-
stdin.resume()
|
|
132
|
-
process.stdout.write(ESC + '[?2004h')
|
|
133
|
-
stdin.on('data', this._onData)
|
|
134
|
-
this._writeBlock()
|
|
135
|
-
return true
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
dispose() {
|
|
139
|
-
const stdin = process.stdin
|
|
140
|
-
stdin.removeListener('data', this._onData)
|
|
141
|
-
process.stdout.write(ESC + '[?2004l')
|
|
142
|
-
this._stopDots()
|
|
143
|
-
if (this.rendered) this._eraseBlock()
|
|
144
|
-
if (stdin.setRawMode) stdin.setRawMode(this._wasRaw || false)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
setPrompt(str) {
|
|
148
|
-
this.promptStr = str
|
|
149
|
-
this._render()
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
clear() {
|
|
153
|
-
this.buf = ''
|
|
154
|
-
this.cursor = 0
|
|
155
|
-
this._render()
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// ---------- отрисовка ----------
|
|
159
|
-
|
|
160
|
-
_eraseBlock() {
|
|
161
|
-
if (!this.rendered) return
|
|
162
|
-
if (this.cursorRowFromTop > 0) {
|
|
163
|
-
process.stdout.write(ESC + '[' + this.cursorRowFromTop + 'A')
|
|
164
|
-
}
|
|
165
|
-
process.stdout.write(CR + ESC + '[J')
|
|
166
|
-
this.rendered = false
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
_writeBlock() {
|
|
170
|
-
const cols = process.stdout.columns || 80
|
|
171
|
-
let out = ''
|
|
172
|
-
let top = 0
|
|
173
|
-
if (this.statusText) {
|
|
174
|
-
out += this.statusText + NL
|
|
175
|
-
top = 1
|
|
176
|
-
}
|
|
177
|
-
const lay = layoutInput(this.promptStr, this.buf, this.cursor, cols)
|
|
178
|
-
out += lay.rows.map((r) => r.prefix + r.text).join(NL)
|
|
179
|
-
process.stdout.write(out)
|
|
180
|
-
const lastRow = lay.rows.length - 1
|
|
181
|
-
const up = lastRow - lay.cursorRow
|
|
182
|
-
if (up > 0) process.stdout.write(ESC + '[' + up + 'A')
|
|
183
|
-
process.stdout.write(CR)
|
|
184
|
-
if (lay.cursorCol > 0) process.stdout.write(ESC + '[' + lay.cursorCol + 'C')
|
|
185
|
-
this.rendered = true
|
|
186
|
-
this.cursorRowFromTop = top + lay.cursorRow
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
_render() {
|
|
190
|
-
this._eraseBlock()
|
|
191
|
-
this._writeBlock()
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Напечатать блок ВЫШЕ строки ввода и вернуть строку ввода на место.
|
|
195
|
-
printAbove(text) {
|
|
196
|
-
this._eraseBlock()
|
|
197
|
-
process.stdout.write(String(text) + NL)
|
|
198
|
-
this._writeBlock()
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// ---------- интерфейс, совместимый со спиннером ----------
|
|
202
|
-
|
|
203
|
-
setStatus(text) {
|
|
204
|
-
this.statusText = text || ''
|
|
205
|
-
this._render()
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
_stopDots() {
|
|
209
|
-
if (this._dotTimer) {
|
|
210
|
-
clearInterval(this._dotTimer)
|
|
211
|
-
this._dotTimer = null
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
_startThinking() {
|
|
216
|
-
if (this.pendingText) {
|
|
217
|
-
this.setStatus(theme.prompt('✎ ') + this.pendingText + HINT)
|
|
218
|
-
return
|
|
219
|
-
}
|
|
220
|
-
this._thinkBase = theme.brown(stripEllipsis(randomThinkingPhrase()))
|
|
221
|
-
this._dotPhase = 0
|
|
222
|
-
this.setStatus(this._thinkBase + DOTS[0] + HINT)
|
|
223
|
-
this._stopDots()
|
|
224
|
-
this._dotTimer = setInterval(() => {
|
|
225
|
-
this._dotPhase = (this._dotPhase + 1) % DOTS.length
|
|
226
|
-
this.setStatus(this._thinkBase + DOTS[this._dotPhase] + HINT)
|
|
227
|
-
}, 400)
|
|
228
|
-
if (this._dotTimer.unref) this._dotTimer.unref()
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
thinking() {
|
|
232
|
-
this._startThinking()
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
setPending(text) {
|
|
236
|
-
this.pendingText = text && String(text).length ? String(text) : null
|
|
237
|
-
if (this.pendingText) {
|
|
238
|
-
this._stopDots()
|
|
239
|
-
this.setStatus(theme.prompt('✎ ') + this.pendingText + HINT)
|
|
240
|
-
} else {
|
|
241
|
-
this._startThinking()
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
stop() {
|
|
246
|
-
this._stopDots()
|
|
247
|
-
this.setStatus('')
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
toolCall(name, args) {
|
|
251
|
-
const preview = safeJson(args).slice(0, 120)
|
|
252
|
-
this.printAbove(theme.tool('🔧 ' + name) + ' ' + theme.dim(preview))
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
toolResult(result) {
|
|
256
|
-
const text = typeof result === 'string' ? result : safeJson(result)
|
|
257
|
-
const preview = text.slice(0, 200).split(NL).join(' ↵ ')
|
|
258
|
-
this.printAbove(theme.toolResult(' → ' + preview))
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
assistant(msg) {
|
|
262
|
-
this.stop()
|
|
263
|
-
const rendered = renderMarkdown(msg)
|
|
264
|
-
this.printAbove(
|
|
265
|
-
NL + theme.assistant('● Ответ') + NL + rendered + NL + theme.dim('─'.repeat(60)),
|
|
266
|
-
)
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// ---------- ввод ----------
|
|
270
|
-
|
|
271
|
-
_submit() {
|
|
272
|
-
const text = this.buf
|
|
273
|
-
if (!text.trim()) {
|
|
274
|
-
this._render()
|
|
275
|
-
return
|
|
276
|
-
}
|
|
277
|
-
this.buf = ''
|
|
278
|
-
this.cursor = 0
|
|
279
|
-
this.pendingText = null
|
|
280
|
-
this._stopDots()
|
|
281
|
-
this.statusText = ''
|
|
282
|
-
this.printAbove(theme.user('❯ ') + text)
|
|
283
|
-
if (this.onSubmit) this.onSubmit(text)
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
_insert(text) {
|
|
287
|
-
const chars = Array.from(this.buf)
|
|
288
|
-
const ins = Array.from(String(text))
|
|
289
|
-
const next = chars.slice(0, this.cursor).concat(ins, chars.slice(this.cursor))
|
|
290
|
-
this.buf = next.join('')
|
|
291
|
-
this.cursor += ins.length
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
_backspace() {
|
|
295
|
-
if (this.cursor <= 0) return
|
|
296
|
-
const chars = Array.from(this.buf)
|
|
297
|
-
chars.splice(this.cursor - 1, 1)
|
|
298
|
-
this.buf = chars.join('')
|
|
299
|
-
this.cursor--
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
_delete() {
|
|
303
|
-
const chars = Array.from(this.buf)
|
|
304
|
-
if (this.cursor >= chars.length) return
|
|
305
|
-
chars.splice(this.cursor, 1)
|
|
306
|
-
this.buf = chars.join('')
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
_left() {
|
|
310
|
-
if (this.cursor > 0) this.cursor--
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
_right() {
|
|
314
|
-
if (this.cursor < Array.from(this.buf).length) this.cursor++
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
_home() {
|
|
318
|
-
const chars = Array.from(this.buf)
|
|
319
|
-
let i = this.cursor
|
|
320
|
-
while (i > 0 && chars[i - 1] !== NL) i--
|
|
321
|
-
this.cursor = i
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
_end() {
|
|
325
|
-
const chars = Array.from(this.buf)
|
|
326
|
-
let i = this.cursor
|
|
327
|
-
while (i < chars.length && chars[i] !== NL) i++
|
|
328
|
-
this.cursor = i
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
_up() {
|
|
332
|
-
const chars = Array.from(this.buf)
|
|
333
|
-
let start = this.cursor
|
|
334
|
-
while (start > 0 && chars[start - 1] !== NL) start--
|
|
335
|
-
if (start === 0) return
|
|
336
|
-
const col = this.cursor - start
|
|
337
|
-
const prevEnd = start - 1
|
|
338
|
-
let prevStart = prevEnd
|
|
339
|
-
while (prevStart > 0 && chars[prevStart - 1] !== NL) prevStart--
|
|
340
|
-
this.cursor = prevStart + Math.min(col, prevEnd - prevStart)
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
_down() {
|
|
344
|
-
const chars = Array.from(this.buf)
|
|
345
|
-
let end = this.cursor
|
|
346
|
-
while (end < chars.length && chars[end] !== NL) end++
|
|
347
|
-
if (end >= chars.length) return
|
|
348
|
-
let lineStart = this.cursor
|
|
349
|
-
while (lineStart > 0 && chars[lineStart - 1] !== NL) lineStart--
|
|
350
|
-
const col = this.cursor - lineStart
|
|
351
|
-
const nextStart = end + 1
|
|
352
|
-
let nextEnd = nextStart
|
|
353
|
-
while (nextEnd < chars.length && chars[nextEnd] !== NL) nextEnd++
|
|
354
|
-
this.cursor = nextStart + Math.min(col, nextEnd - nextStart)
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
_handle(data) {
|
|
358
|
-
let s = data.toString('utf-8')
|
|
359
|
-
|
|
360
|
-
if (!this._inPaste && s === ESC) {
|
|
361
|
-
if (this.onEscape) this.onEscape()
|
|
362
|
-
return
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
while (s.length) {
|
|
366
|
-
if (this._inPaste) {
|
|
367
|
-
const end = s.indexOf(PASTE_END)
|
|
368
|
-
if (end === -1) {
|
|
369
|
-
this._insert(s)
|
|
370
|
-
s = ''
|
|
371
|
-
} else {
|
|
372
|
-
this._insert(s.slice(0, end))
|
|
373
|
-
s = s.slice(end + PASTE_END.length)
|
|
374
|
-
this._inPaste = false
|
|
375
|
-
}
|
|
376
|
-
this._render()
|
|
377
|
-
continue
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
const pasteAt = s.indexOf(PASTE_START)
|
|
381
|
-
if (pasteAt !== -1) {
|
|
382
|
-
const before = s.slice(0, pasteAt)
|
|
383
|
-
s = s.slice(pasteAt + PASTE_START.length)
|
|
384
|
-
this._inPaste = true
|
|
385
|
-
if (before) {
|
|
386
|
-
this._insert(before)
|
|
387
|
-
this._render()
|
|
388
|
-
}
|
|
389
|
-
continue
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// Shift+Enter в терминалах с расширенным протоколом и Alt+Enter.
|
|
393
|
-
if (s.startsWith(ESC + '[13;2u')) { this._insert(NL); s = s.slice(7); this._render(); continue }
|
|
394
|
-
if (s.startsWith(ESC + '[27;2;13~')) { this._insert(NL); s = s.slice(10); this._render(); continue }
|
|
395
|
-
if (s.startsWith(ESC + NL)) { this._insert(NL); s = s.slice(2); this._render(); continue }
|
|
396
|
-
|
|
397
|
-
const ch = s[0]
|
|
398
|
-
const code = s.charCodeAt(0)
|
|
399
|
-
s = s.slice(1)
|
|
400
|
-
|
|
401
|
-
if (ch === CR) { this._submit(); continue }
|
|
402
|
-
if (code === 10) { this._insert(NL); this._render(); continue }
|
|
403
|
-
if (code === 3) { if (this.onCtrlC) this.onCtrlC(); continue }
|
|
404
|
-
if (code === 4) { if (!this.buf && this.onCtrlC) this.onCtrlC(); continue }
|
|
405
|
-
if (code === 1) { this._home(); this._render(); continue }
|
|
406
|
-
if (code === 5) { this._end(); this._render(); continue }
|
|
407
|
-
if (code === 21) { this.buf = ''; this.cursor = 0; this._render(); continue }
|
|
408
|
-
if (code === 127 || code === 8) { this._backspace(); this._render(); continue }
|
|
409
|
-
|
|
410
|
-
if (code === 27) {
|
|
411
|
-
if (s.startsWith('[D')) { this._left(); s = s.slice(2); this._render(); continue }
|
|
412
|
-
if (s.startsWith('[C')) { this._right(); s = s.slice(2); this._render(); continue }
|
|
413
|
-
if (s.startsWith('[A')) { this._up(); s = s.slice(2); this._render(); continue }
|
|
414
|
-
if (s.startsWith('[B')) { this._down(); s = s.slice(2); this._render(); continue }
|
|
415
|
-
if (s.startsWith('[H') || s.startsWith('[1~')) { this._home(); s = s.slice(s.startsWith('[1~') ? 3 : 2); this._render(); continue }
|
|
416
|
-
if (s.startsWith('[F') || s.startsWith('[4~')) { this._end(); s = s.slice(s.startsWith('[4~') ? 3 : 2); this._render(); continue }
|
|
417
|
-
if (s.startsWith('[3~')) { this._delete(); s = s.slice(3); this._render(); continue }
|
|
418
|
-
let j = 0
|
|
419
|
-
while (j < s.length && !/[A-Za-z~]/.test(s[j])) j++
|
|
420
|
-
s = s.slice(j + 1)
|
|
421
|
-
continue
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
if (code < 32) continue
|
|
425
|
-
this._insert(ch)
|
|
426
|
-
this._render()
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// Проверка раскладки: node src/input.js --selftest
|
|
432
|
-
export function selftest() {
|
|
433
|
-
const cases = [
|
|
434
|
-
{ p: '> ', b: 'hello', c: 5, w: 80 },
|
|
435
|
-
{ p: '> ', b: 'a' + NL + 'b', c: 3, w: 80 },
|
|
436
|
-
{ p: '> ', b: 'x'.repeat(200), c: 200, w: 40 },
|
|
437
|
-
{ p: '> ', b: '', c: 0, w: 40 },
|
|
438
|
-
]
|
|
439
|
-
for (const t of cases) {
|
|
440
|
-
const r = layoutInput(t.p, t.b, t.c, t.w)
|
|
441
|
-
console.log(JSON.stringify({ rows: r.rows.length, row: r.cursorRow, col: r.cursorCol }))
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
if (process.argv[1] && process.argv[1].endsWith('input.js') && process.argv.includes('--selftest')) {
|
|
446
|
-
selftest()
|
|
447
|
-
}
|
package/src/markdown.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { render } from 'markdansi'
|
|
2
|
-
import { highlight } from 'cli-highlight'
|
|
3
|
-
|
|
4
|
-
// Рендер Markdown-ответов модели в терминал через markdansi.
|
|
5
|
-
// GFM (заголовки, списки, таблицы, цитаты, task lists) + подсветка кода.
|
|
6
|
-
// Тема собрана из нашей спокойной палитры, без кислотных цветов.
|
|
7
|
-
|
|
8
|
-
const theme = {
|
|
9
|
-
heading: { color: '#9fb8d8', bold: true },
|
|
10
|
-
strong: { color: '#e6e0cc', bold: true },
|
|
11
|
-
emph: { color: '#cfc9b0', italic: true },
|
|
12
|
-
inlineCode: { color: '#9ecb9e' },
|
|
13
|
-
blockCode: { color: '#c9d1c9' },
|
|
14
|
-
code: { color: '#9ecb9e' },
|
|
15
|
-
link: { color: '#8fb8d8', underline: true },
|
|
16
|
-
quote: { color: '#8a8f9a', italic: true },
|
|
17
|
-
hr: { color: '#5b616e' },
|
|
18
|
-
listMarker: { color: '#8a9bb5' },
|
|
19
|
-
tableHeader: { color: '#9fb8d8', bold: true },
|
|
20
|
-
tableCell: { color: '#d0d0d0' },
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Подсветка кода через cli-highlight (транзитивно уже есть у нас).
|
|
24
|
-
function highlighter(code, lang) {
|
|
25
|
-
try {
|
|
26
|
-
return highlight(code, { language: lang || 'plaintext', ignoreIllegals: true })
|
|
27
|
-
} catch {
|
|
28
|
-
return code
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function renderMarkdown(text) {
|
|
33
|
-
if (!text) return ''
|
|
34
|
-
try {
|
|
35
|
-
return render(String(text), {
|
|
36
|
-
width: Math.min(process.stdout.columns || 80, 100),
|
|
37
|
-
theme,
|
|
38
|
-
highlighter,
|
|
39
|
-
codeBox: true,
|
|
40
|
-
tableBorder: 'unicode',
|
|
41
|
-
listIndent: 2,
|
|
42
|
-
hyperlinks: false,
|
|
43
|
-
})
|
|
44
|
-
} catch {
|
|
45
|
-
return String(text)
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export default renderMarkdown
|