wormclaude 1.0.21 → 1.0.23
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/ansi.js +166 -0
- package/dist/cli.js +171 -15
- package/dist/commands.js +1 -0
- package/dist/theme.js +1 -1
- package/package.json +1 -1
package/dist/ansi.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// Geçmiş öğelerini doğrudan terminal scrollback'ine yazmak için ANSI string üreticisi.
|
|
2
|
+
// ink <Static> resize'da her şeyi yeniden bastığından (kademe/çift banner), geçmiş artık
|
|
3
|
+
// stdout'a BİR KEZ yazılır; ink yalnız canlı footer'ı yönetir. Böylece doğal kaydırma +
|
|
4
|
+
// metin seçip kopyalama çalışır. Markdown + sözdizimi vurgusu burada ANSI'ye çevrilir.
|
|
5
|
+
import { theme } from './theme.js';
|
|
6
|
+
import { highlight } from './highlight.js';
|
|
7
|
+
import { t } from './i18n.js';
|
|
8
|
+
const RESET = '\x1b[0m';
|
|
9
|
+
function hexAnsi(hex) {
|
|
10
|
+
if (!hex)
|
|
11
|
+
return '';
|
|
12
|
+
const m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(hex);
|
|
13
|
+
if (!m)
|
|
14
|
+
return '';
|
|
15
|
+
return `\x1b[38;2;${parseInt(m[1], 16)};${parseInt(m[2], 16)};${parseInt(m[3], 16)}m`;
|
|
16
|
+
}
|
|
17
|
+
const paint = (s, hex, bold = false, dim = false, italic = false) => `${bold ? '\x1b[1m' : ''}${dim ? '\x1b[2m' : ''}${italic ? '\x1b[3m' : ''}${hexAnsi(hex)}${s}${RESET}`;
|
|
18
|
+
// ── Satır kaydırma (görsel genişliğe göre basit sarma) ──
|
|
19
|
+
function wrap(text, width) {
|
|
20
|
+
const out = [];
|
|
21
|
+
for (const para of String(text).split('\n')) {
|
|
22
|
+
if (!para) {
|
|
23
|
+
out.push('');
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
let line = '';
|
|
27
|
+
for (const word of para.split(' ')) {
|
|
28
|
+
const cand = line ? line + ' ' + word : word;
|
|
29
|
+
if (cand.length <= width) {
|
|
30
|
+
line = cand;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (line)
|
|
34
|
+
out.push(line);
|
|
35
|
+
if (word.length > width) {
|
|
36
|
+
let w = word;
|
|
37
|
+
while (w.length > width) {
|
|
38
|
+
out.push(w.slice(0, width));
|
|
39
|
+
w = w.slice(width);
|
|
40
|
+
}
|
|
41
|
+
line = w;
|
|
42
|
+
}
|
|
43
|
+
else
|
|
44
|
+
line = word;
|
|
45
|
+
}
|
|
46
|
+
out.push(line);
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
// ── Inline markdown → ANSI (**bold** `code` *italik* [t](u)) ──
|
|
51
|
+
function inlineAnsi(text) {
|
|
52
|
+
return text
|
|
53
|
+
.replace(/`([^`]+)`/g, (_m, c) => paint(c, theme.green))
|
|
54
|
+
.replace(/\*\*([^*]+)\*\*/g, (_m, c) => paint(c, undefined, true))
|
|
55
|
+
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_m, a, b) => `${a}${paint(' (' + b + ')', theme.redBright)}`);
|
|
56
|
+
}
|
|
57
|
+
const WORM_ROWS = [
|
|
58
|
+
'██╗ ██╗ ██████╗ ██████╗ ███╗ ███╗',
|
|
59
|
+
'██║ ██║██╔═══██╗██╔══██╗████╗ ████║',
|
|
60
|
+
'██║ █╗ ██║██║ ██║██████╔╝██╔████╔██║',
|
|
61
|
+
'██║███╗██║██║ ██║██╔══██╗██║╚██╔╝██║',
|
|
62
|
+
'╚███╔███╔╝╚██████╔╝██║ ██║██║ ╚═╝ ██║',
|
|
63
|
+
' ╚══╝╚══╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝',
|
|
64
|
+
];
|
|
65
|
+
const CLAUDE_ROWS = [
|
|
66
|
+
' ██████╗██╗ █████╗ ██╗ ██╗██████╗ ███████╗',
|
|
67
|
+
'██╔════╝██║ ██╔══██╗██║ ██║██╔══██╗██╔════╝',
|
|
68
|
+
'██║ ██║ ███████║██║ ██║██║ ██║█████╗ ',
|
|
69
|
+
'██║ ██║ ██╔══██║██║ ██║██║ ██║██╔══╝ ',
|
|
70
|
+
'╚██████╗███████╗██║ ██║╚██████╔╝██████╔╝███████╗',
|
|
71
|
+
' ╚═════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚══════╝',
|
|
72
|
+
];
|
|
73
|
+
/** Banner'ı ANSI olarak üretir (dar ekranda tek kelime, geniş ekranda yan yana). */
|
|
74
|
+
export function bannerAnsi(cols) {
|
|
75
|
+
if (cols < 46)
|
|
76
|
+
return paint('WORMCLAUDE', theme.red, true) + '\n' + paint(' ' + t('banner.subtitle'), theme.greyDim);
|
|
77
|
+
const rows = cols >= 88 ? WORM_ROWS.map((w, i) => w + CLAUDE_ROWS[i]) : [...WORM_ROWS, ...CLAUDE_ROWS];
|
|
78
|
+
return rows.map((r) => paint(r, theme.red, true)).join('\n') + '\n' + paint(' ' + t('banner.subtitle'), theme.greyDim);
|
|
79
|
+
}
|
|
80
|
+
// ── Markdown bloğu → ANSI (kod blokları sözdizimi-vurgulu) ──
|
|
81
|
+
function markdownAnsi(text, cols) {
|
|
82
|
+
const W = Math.max(20, cols - 2);
|
|
83
|
+
const lines = text.split('\n');
|
|
84
|
+
const out = [];
|
|
85
|
+
let inCode = false, fence = '', lang = '', code = [];
|
|
86
|
+
const fenceRe = /^ *(`{3,}|~{3,}) *(\w*)? *$/;
|
|
87
|
+
const flushCode = () => {
|
|
88
|
+
if (lang)
|
|
89
|
+
out.push(paint(' ' + lang, theme.greyDim));
|
|
90
|
+
for (const toks of highlight(code.join('\n'), lang)) {
|
|
91
|
+
out.push(paint('│ ', theme.greyDim) + (toks.length ? toks.map((tk) => paint(tk.text, tk.color)).join('') : ' '));
|
|
92
|
+
}
|
|
93
|
+
code = [];
|
|
94
|
+
lang = '';
|
|
95
|
+
fence = '';
|
|
96
|
+
};
|
|
97
|
+
for (const line of lines) {
|
|
98
|
+
if (inCode) {
|
|
99
|
+
const fm = line.match(fenceRe);
|
|
100
|
+
if (fm && fm[1][0] === fence[0] && fm[1].length >= fence.length) {
|
|
101
|
+
inCode = false;
|
|
102
|
+
flushCode();
|
|
103
|
+
}
|
|
104
|
+
else
|
|
105
|
+
code.push(line);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const fm = line.match(fenceRe);
|
|
109
|
+
if (fm) {
|
|
110
|
+
inCode = true;
|
|
111
|
+
fence = fm[1];
|
|
112
|
+
lang = (fm[2] || '').trim();
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const hm = line.match(/^ *(#{1,4}) +(.*)/);
|
|
116
|
+
const um = line.match(/^([ \t]*)([-*+]) +(.*)/);
|
|
117
|
+
const om = line.match(/^([ \t]*)(\d+)\. +(.*)/);
|
|
118
|
+
if (hm) {
|
|
119
|
+
const lvl = hm[1].length, inner = inlineAnsi(hm[2]);
|
|
120
|
+
out.push(lvl <= 2 ? paint(inner, theme.redBright, true) : lvl === 3 ? paint(inner, theme.white, true) : paint(inner, theme.grey, false, false, true));
|
|
121
|
+
}
|
|
122
|
+
else if (um || om) {
|
|
123
|
+
const [, ws, marker, itemText] = (um || om);
|
|
124
|
+
const prefix = om ? `${marker}. ` : '• ';
|
|
125
|
+
out.push(' '.repeat(ws.length) + paint(prefix, theme.grey) + inlineAnsi(itemText));
|
|
126
|
+
}
|
|
127
|
+
else if (line.trim() === '') {
|
|
128
|
+
out.push('');
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
for (const w of wrap(line, W))
|
|
132
|
+
out.push(inlineAnsi(w));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (inCode)
|
|
136
|
+
flushCode();
|
|
137
|
+
return out.join('\n');
|
|
138
|
+
}
|
|
139
|
+
/** Bir geçmiş öğesini scrollback'e yazılacak ANSI string'e çevirir (üstte 1 boş satır). */
|
|
140
|
+
export function itemAnsi(it, cols) {
|
|
141
|
+
const W = Math.max(24, cols - 2);
|
|
142
|
+
if (it.kind === 'banner')
|
|
143
|
+
return '\n' + bannerAnsi(cols);
|
|
144
|
+
if (it.kind === 'user') {
|
|
145
|
+
const inner = W - 4;
|
|
146
|
+
const top = paint('╭' + '─'.repeat(W - 2) + '╮', theme.greyDim);
|
|
147
|
+
const bot = paint('╰' + '─'.repeat(W - 2) + '╯', theme.greyDim);
|
|
148
|
+
const body = wrap('› ' + (it.text || ''), inner).map((ln) => paint('│ ', theme.greyDim) + paint(ln.padEnd(inner), theme.white) + paint(' │', theme.greyDim));
|
|
149
|
+
return '\n' + [top, ...body, bot].join('\n');
|
|
150
|
+
}
|
|
151
|
+
if (it.kind === 'assistant') {
|
|
152
|
+
const md = markdownAnsi(it.text || '', cols).split('\n');
|
|
153
|
+
return '\n' + md.map((ln, i) => (i === 0 ? paint('⏺ ', theme.redBright, true) + ln : ' ' + ln)).join('\n');
|
|
154
|
+
}
|
|
155
|
+
if (it.kind === 'tool') {
|
|
156
|
+
const n = (it.result || '').split('\n').length, chars = (it.result || '').length;
|
|
157
|
+
const head = paint('⏺ ', theme.redBright, true) + paint(it.label || '', theme.white);
|
|
158
|
+
const sub = paint(' ⎿ ', theme.greyDim) + (it.ok
|
|
159
|
+
? paint(`${n} ${t('common.lines') || 'satır'} (${chars})`, theme.grey)
|
|
160
|
+
: paint('✗ ' + (it.result || '').slice(0, 160), theme.errorRed));
|
|
161
|
+
return '\n' + head + '\n' + sub;
|
|
162
|
+
}
|
|
163
|
+
if (it.kind === 'note')
|
|
164
|
+
return '\n' + wrap(it.text || '', W).map((ln) => paint(ln, theme.greyDim)).join('\n');
|
|
165
|
+
return '';
|
|
166
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import React, { useState, useRef, useEffect } from 'react';
|
|
3
|
-
import { render, Box, Text,
|
|
3
|
+
import { render, Box, Text, useApp, useInput } from 'ink';
|
|
4
4
|
import TextInput from 'ink-text-input';
|
|
5
5
|
import * as path from 'node:path';
|
|
6
6
|
import { theme, VERSION } from './theme.js';
|
|
@@ -11,6 +11,7 @@ import { allToolSchemas, executeToolCalls, executeTool, toolLabel, setToolConfig
|
|
|
11
11
|
import { sanitizeError, sanitizeOutput } from './errorsan.js';
|
|
12
12
|
import { cleanModelText } from './textclean.js';
|
|
13
13
|
import { MarkdownDisplay } from './markdown.js';
|
|
14
|
+
import { highlight } from './highlight.js';
|
|
14
15
|
import { resolveAtMentions } from './atmention.js';
|
|
15
16
|
import { summarizeTools } from './toolSummary.js';
|
|
16
17
|
import { pickTipId, tipText } from './tips.js';
|
|
@@ -85,10 +86,19 @@ const _initHistory = () => {
|
|
|
85
86
|
sys.push({ role: 'system', content: _memCtx });
|
|
86
87
|
return sys;
|
|
87
88
|
};
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
89
|
+
// ALT-SCREEN (alternate screen) — ink normal buffer'da resize'da eski dinamik çerçeveyi
|
|
90
|
+
// silemeyip KASKAD yapıyor (scrollback reflow). Alt-screen tüm ekranı yönetir → resize'da
|
|
91
|
+
// temiz yeniden çizim, kaskad YOK. Kopyalama: alt-screen'de fare seçimi sınırlı olduğundan
|
|
92
|
+
// /kopyala komutu (OSC52 ile panoya) sağlanır. ?1049h alt-screen · ?1007h alternate-scroll.
|
|
93
|
+
try {
|
|
94
|
+
process.stdout.write('\x1b[?1049h\x1b[?1007h\x1b[2J\x1b[H');
|
|
95
|
+
}
|
|
96
|
+
catch { }
|
|
97
|
+
const _leaveAlt = () => { try {
|
|
98
|
+
process.stdout.write('\x1b[?1007l\x1b[?1049l');
|
|
99
|
+
}
|
|
100
|
+
catch { } };
|
|
101
|
+
process.on('exit', _leaveAlt);
|
|
92
102
|
function useDimensions() {
|
|
93
103
|
const [d, setD] = useState({
|
|
94
104
|
cols: process.stdout.columns || 80,
|
|
@@ -148,6 +158,91 @@ function wrapText(text, width) {
|
|
|
148
158
|
}
|
|
149
159
|
return out;
|
|
150
160
|
}
|
|
161
|
+
// Inline markdown (`code` **bold**) → segment'ler.
|
|
162
|
+
function inlineSegs(text, base) {
|
|
163
|
+
const segs = [];
|
|
164
|
+
const re = /(`[^`]+`|\*\*[^*]+\*\*)/g;
|
|
165
|
+
let last = 0;
|
|
166
|
+
let m;
|
|
167
|
+
while ((m = re.exec(text)) !== null) {
|
|
168
|
+
if (m.index > last)
|
|
169
|
+
segs.push({ text: text.slice(last, m.index), color: base });
|
|
170
|
+
const f = m[0];
|
|
171
|
+
if (f.startsWith('`'))
|
|
172
|
+
segs.push({ text: f.slice(1, -1), color: theme.green });
|
|
173
|
+
else
|
|
174
|
+
segs.push({ text: f.slice(2, -2), color: base, bold: true });
|
|
175
|
+
last = re.lastIndex;
|
|
176
|
+
}
|
|
177
|
+
if (last < text.length)
|
|
178
|
+
segs.push({ text: text.slice(last), color: base });
|
|
179
|
+
return segs.length ? segs : [{ text, color: base }];
|
|
180
|
+
}
|
|
181
|
+
// Markdown metnini segment-satırlarına çevirir; kod blokları sözdizimi-vurgulu (highlight()).
|
|
182
|
+
function mdToSegLines(text, W) {
|
|
183
|
+
const out = [];
|
|
184
|
+
const white = theme.white;
|
|
185
|
+
let inCode = false, fence = '', lang = '', code = [];
|
|
186
|
+
const fenceRe = /^ *(`{3,}|~{3,}) *(\w*)? *$/;
|
|
187
|
+
const flush = () => {
|
|
188
|
+
if (lang)
|
|
189
|
+
out.push([{ text: ' ' + lang, dim: true }]);
|
|
190
|
+
for (const toks of highlight(code.join('\n'), lang)) {
|
|
191
|
+
const segs = [{ text: '│ ', dim: true }];
|
|
192
|
+
if (toks.length)
|
|
193
|
+
for (const tk of toks)
|
|
194
|
+
segs.push({ text: tk.text, color: tk.color });
|
|
195
|
+
else
|
|
196
|
+
segs.push({ text: ' ' });
|
|
197
|
+
out.push(segs);
|
|
198
|
+
}
|
|
199
|
+
code = [];
|
|
200
|
+
lang = '';
|
|
201
|
+
fence = '';
|
|
202
|
+
};
|
|
203
|
+
for (const line of text.split('\n')) {
|
|
204
|
+
if (inCode) {
|
|
205
|
+
const fm = line.match(fenceRe);
|
|
206
|
+
if (fm && fm[1][0] === fence[0] && fm[1].length >= fence.length) {
|
|
207
|
+
inCode = false;
|
|
208
|
+
flush();
|
|
209
|
+
}
|
|
210
|
+
else
|
|
211
|
+
code.push(line);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const fm = line.match(fenceRe);
|
|
215
|
+
if (fm) {
|
|
216
|
+
inCode = true;
|
|
217
|
+
fence = fm[1];
|
|
218
|
+
lang = (fm[2] || '').trim();
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
const hm = line.match(/^ *(#{1,4}) +(.*)/);
|
|
222
|
+
const um = line.match(/^([ \t]*)([-*+]) +(.*)/);
|
|
223
|
+
const om = line.match(/^([ \t]*)(\d+)\. +(.*)/);
|
|
224
|
+
if (hm) {
|
|
225
|
+
const lvl = hm[1].length;
|
|
226
|
+
out.push([{ text: hm[2], color: lvl <= 2 ? theme.redBright : white, bold: lvl <= 3, italic: lvl > 3 }]);
|
|
227
|
+
}
|
|
228
|
+
else if (um || om) {
|
|
229
|
+
const [, ws, marker, itemText] = (um || om);
|
|
230
|
+
const prefix = om ? `${marker}. ` : '• ';
|
|
231
|
+
const wrapped = wrapText(itemText, Math.max(8, W - ws.length - prefix.length));
|
|
232
|
+
wrapped.forEach((w, i) => out.push([{ text: ' '.repeat(ws.length) + (i === 0 ? prefix : ' '), dim: true }, ...inlineSegs(w, white)]));
|
|
233
|
+
}
|
|
234
|
+
else if (line.trim() === '') {
|
|
235
|
+
out.push([]);
|
|
236
|
+
}
|
|
237
|
+
else {
|
|
238
|
+
for (const w of wrapText(line, W))
|
|
239
|
+
out.push(inlineSegs(w, white));
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
if (inCode)
|
|
243
|
+
flush();
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
151
246
|
// Tüm öğeleri görsel satır dizisine çevirir (kaydırma satır granülerliğinde).
|
|
152
247
|
function buildLines(items, cols) {
|
|
153
248
|
const lines = [];
|
|
@@ -171,7 +266,8 @@ function buildLines(items, cols) {
|
|
|
171
266
|
lines.push([{ text: '╰' + '─'.repeat(W - 2) + '╯', dim: true }]);
|
|
172
267
|
}
|
|
173
268
|
else if (it.kind === 'assistant') {
|
|
174
|
-
|
|
269
|
+
const seg = mdToSegLines(it.text, W - 2);
|
|
270
|
+
seg.forEach((segs, i) => lines.push(i === 0 ? [{ text: '⏺ ', color: redB, bold: true }, ...segs] : [{ text: ' ', dim: true }, ...segs]));
|
|
175
271
|
}
|
|
176
272
|
else if (it.kind === 'tool') {
|
|
177
273
|
const n = it.result.split('\n').length, chars = it.result.length;
|
|
@@ -440,9 +536,24 @@ function App() {
|
|
|
440
536
|
else if (key.downArrow)
|
|
441
537
|
setCmdSel((s) => (s + 1) % n);
|
|
442
538
|
});
|
|
443
|
-
//
|
|
444
|
-
//
|
|
445
|
-
|
|
539
|
+
// Geçmişte kaydırma: PageUp/PageDown her zaman; boş input'ta ok tuşları (fare tekerleği
|
|
540
|
+
// alternate-scroll ile ok yollar → tekerlekle kaydırma). Alt-screen olduğundan biz yönetiriz.
|
|
541
|
+
useInput((_inp, key) => {
|
|
542
|
+
if (!started || perm || ask || lang === null)
|
|
543
|
+
return;
|
|
544
|
+
if (key.pageUp)
|
|
545
|
+
setScroll((s) => s + 10);
|
|
546
|
+
else if (key.pageDown)
|
|
547
|
+
setScroll((s) => Math.max(0, s - 10));
|
|
548
|
+
else if (input === '' && !busy) {
|
|
549
|
+
if (key.upArrow)
|
|
550
|
+
setScroll((s) => s + 3);
|
|
551
|
+
else if (key.downArrow)
|
|
552
|
+
setScroll((s) => Math.max(0, s - 3));
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
// Yeni mesaj gelince en alta dön
|
|
556
|
+
useEffect(() => { setScroll(0); }, [items.length]);
|
|
446
557
|
// Başlangıçta güven seviyesini (Doğrulanmış Araştırmacı Programı) çek → StatusLine rozeti
|
|
447
558
|
useEffect(() => {
|
|
448
559
|
if (!started)
|
|
@@ -695,6 +806,31 @@ function App() {
|
|
|
695
806
|
v = filtered[Math.min(cmdSel, filtered.length - 1)].name;
|
|
696
807
|
}
|
|
697
808
|
const tok = v.split(' ')[0];
|
|
809
|
+
// /kopyala [hepsi] — son yanıtı (veya tüm sohbeti) panoya kopyalar (OSC52, fare gerekmez).
|
|
810
|
+
if (tok === '/kopyala' || tok === '/copy') {
|
|
811
|
+
const arg = v.slice(tok.length).trim().toLowerCase();
|
|
812
|
+
const en = getLang() === 'en';
|
|
813
|
+
const asst = items.filter((it) => it.kind === 'assistant').map((it) => it.text);
|
|
814
|
+
let text = '';
|
|
815
|
+
if (arg === 'all' || arg === 'hepsi') {
|
|
816
|
+
text = items.map((it) => it.kind === 'user' ? '> ' + it.text : it.kind === 'assistant' ? it.text : '').filter(Boolean).join('\n\n');
|
|
817
|
+
}
|
|
818
|
+
else
|
|
819
|
+
text = asst[asst.length - 1] || '';
|
|
820
|
+
if (!text) {
|
|
821
|
+
push({ kind: 'note', text: en ? 'Nothing to copy.' : 'Kopyalanacak içerik yok.' });
|
|
822
|
+
return;
|
|
823
|
+
}
|
|
824
|
+
try {
|
|
825
|
+
const b64 = Buffer.from(text, 'utf8').toString('base64');
|
|
826
|
+
process.stdout.write(`\x1b]52;c;${b64}\x07`);
|
|
827
|
+
push({ kind: 'note', text: en ? `✓ Copied to clipboard (${text.length} chars).` : `✓ Panoya kopyalandı (${text.length} karakter).` });
|
|
828
|
+
}
|
|
829
|
+
catch {
|
|
830
|
+
push({ kind: 'note', text: en ? 'Copy failed.' : 'Kopyalama başarısız.' });
|
|
831
|
+
}
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
698
834
|
// /agent ve /multi-agent — tek / çoklu alt-agent
|
|
699
835
|
if (tok === '/agent' || tok === '/multi-agent') {
|
|
700
836
|
const task = v.slice(tok.length).trim();
|
|
@@ -832,12 +968,32 @@ function App() {
|
|
|
832
968
|
React.createElement(Box, { flexGrow: 1 }),
|
|
833
969
|
React.createElement(StatusLine, { model: config.model, ctxTokens: 0 })));
|
|
834
970
|
}
|
|
835
|
-
return (React.createElement(Box, { flexDirection: "column", width: cols },
|
|
836
|
-
React.createElement(
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
971
|
+
return (React.createElement(Box, { flexDirection: "column", height: rows, width: cols },
|
|
972
|
+
React.createElement(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden" }, (() => {
|
|
973
|
+
// Alt-screen + satır-pencere pager: tüm öğeleri satıra çevir, pencere göster.
|
|
974
|
+
// (Kopyalama /kopyala komutuyla; resize'da kaskad olmaz.)
|
|
975
|
+
const all = buildLines(items, cols);
|
|
976
|
+
const reserve = busy ? 4 : 6;
|
|
977
|
+
const avail = Math.max(4, rows - reserve - (streaming ? 3 : 0) - 2);
|
|
978
|
+
const maxScroll = Math.max(0, all.length - avail);
|
|
979
|
+
const off = Math.min(scroll, maxScroll);
|
|
980
|
+
const startLine = Math.max(0, all.length - avail - off);
|
|
981
|
+
const view = all.slice(startLine, startLine + avail);
|
|
982
|
+
return (React.createElement(React.Fragment, null,
|
|
983
|
+
startLine > 0 ? React.createElement(Text, { color: theme.greyDim },
|
|
984
|
+
" \u2191 ",
|
|
985
|
+
startLine,
|
|
986
|
+
" sat\u0131r \u00B7 PageUp") : null,
|
|
987
|
+
view.map((segs, i) => (React.createElement(Text, { key: i }, segs.length === 0 ? ' ' : segs.map((s, j) => (React.createElement(Text, { key: j, color: s.dim ? theme.greyDim : s.color, bold: s.bold, italic: s.italic }, linkify(s.text))))))),
|
|
988
|
+
off > 0 ? React.createElement(Text, { color: theme.greyDim },
|
|
989
|
+
" \u2193 ",
|
|
990
|
+
off,
|
|
991
|
+
" sat\u0131r \u00B7 PageDown / Son") : null,
|
|
992
|
+
streaming && off === 0 ? (React.createElement(Box, { flexDirection: "row" },
|
|
993
|
+
React.createElement(Text, { color: theme.redBright, bold: true }, "\u23FA "),
|
|
994
|
+
React.createElement(Box, { flexDirection: "column" },
|
|
995
|
+
React.createElement(Text, { color: theme.white }, streaming)))) : null));
|
|
996
|
+
})()),
|
|
841
997
|
perm ? (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.red, paddingX: 1, marginTop: 1 },
|
|
842
998
|
React.createElement(Text, { color: theme.redBright, bold: true }, t('perm.title', perm.label)),
|
|
843
999
|
permMode === 'feedback' ? (React.createElement(React.Fragment, null,
|
package/dist/commands.js
CHANGED
|
@@ -18,6 +18,7 @@ import { programText, tier } from './program.js';
|
|
|
18
18
|
export const COMMANDS = [
|
|
19
19
|
{ name: '/help', desc: 'komutları ve ipuçlarını göster' },
|
|
20
20
|
{ name: '/clear', desc: 'sohbeti ve geçmişi temizle' },
|
|
21
|
+
{ name: '/kopyala', desc: 'son yanıtı panoya kopyala (/kopyala hepsi · tüm sohbet)' },
|
|
21
22
|
{ name: '/compact', desc: 'geçmişi modelle özetleyip bağlamı küçült' },
|
|
22
23
|
{ name: '/context', desc: 'bağlam / token kullanımını göster' },
|
|
23
24
|
{ name: '/cost', desc: 'oturum token tahminini göster' },
|
package/dist/theme.js
CHANGED