@stevezhou/sisu 0.1.8 → 0.1.10
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/logo.js +3 -18
- package/dist/pager/app.js +14 -9
- package/dist/pager/render.js +29 -30
- package/dist/pager/text.js +40 -0
- package/dist/pager/theme.js +35 -11
- package/package.json +1 -1
package/dist/logo.js
CHANGED
|
@@ -18,6 +18,7 @@ Object.defineProperty(exports, "sisuTreeArt", { enumerable: true, get: function
|
|
|
18
18
|
Object.defineProperty(exports, "sisuTreeLines", { enumerable: true, get: function () { return tree_1.sisuTreeLines; } });
|
|
19
19
|
const mark_2 = require("./mark");
|
|
20
20
|
const mobius_1 = require("./mobius");
|
|
21
|
+
const theme_1 = require("./pager/theme");
|
|
21
22
|
const RESET = '\x1b[0m';
|
|
22
23
|
const INK = '\x1b[38;2;220;214;204m';
|
|
23
24
|
const INK_DIM = '\x1b[38;2;140;132;120m';
|
|
@@ -33,24 +34,8 @@ function sisuMobiusArt(columns = 80, phase = 0, color = false) {
|
|
|
33
34
|
function sisuWordmark() {
|
|
34
35
|
return ['思溯', 'SISU'].join('\n');
|
|
35
36
|
}
|
|
36
|
-
/** Terminal cells, not JS string length — CJK and ∞ must sit on the grid. */
|
|
37
37
|
function displayWidth(text) {
|
|
38
|
-
|
|
39
|
-
for (const ch of stripAnsi(text)) {
|
|
40
|
-
const code = ch.codePointAt(0) ?? 0;
|
|
41
|
-
width += isWide(code) ? 2 : 1;
|
|
42
|
-
}
|
|
43
|
-
return width;
|
|
44
|
-
}
|
|
45
|
-
function isWide(code) {
|
|
46
|
-
return ((code >= 0x1100 && code <= 0x115f) ||
|
|
47
|
-
(code >= 0x2e80 && code <= 0xa4cf && code !== 0x303f) ||
|
|
48
|
-
(code >= 0xac00 && code <= 0xd7a3) ||
|
|
49
|
-
(code >= 0xf900 && code <= 0xfaff) ||
|
|
50
|
-
(code >= 0xfe10 && code <= 0xfe19) ||
|
|
51
|
-
(code >= 0xfe30 && code <= 0xfe6f) ||
|
|
52
|
-
(code >= 0xff00 && code <= 0xff60) ||
|
|
53
|
-
(code >= 0xffe0 && code <= 0xffe6));
|
|
38
|
+
return (0, theme_1.visibleWidth)(text);
|
|
54
39
|
}
|
|
55
40
|
function paint(text, prefix, color) {
|
|
56
41
|
if (!color || !text)
|
|
@@ -67,7 +52,7 @@ function lockup(color) {
|
|
|
67
52
|
const inf = infinityMark(color);
|
|
68
53
|
const name = paint('思溯', INK, color);
|
|
69
54
|
const latin = paint('SiSu', INK_DIM, color);
|
|
70
|
-
return [`${inf} ${name}`,
|
|
55
|
+
return [`${inf} ${name}`, latin];
|
|
71
56
|
}
|
|
72
57
|
function padLeft(text, n) {
|
|
73
58
|
return `${' '.repeat(Math.max(0, n))}${text}`;
|
package/dist/pager/app.js
CHANGED
|
@@ -11,18 +11,23 @@ const logo_1 = require("../logo");
|
|
|
11
11
|
const render_1 = require("./render");
|
|
12
12
|
const ALT_ENTER = '\x1b[?1049h\x1b[?25l';
|
|
13
13
|
const ALT_LEAVE = '\x1b[?1049l\x1b[?25h';
|
|
14
|
-
function
|
|
14
|
+
function shortConversationId(id) {
|
|
15
|
+
const conv = (id || '').trim();
|
|
16
|
+
if (!conv || conv === 'new')
|
|
17
|
+
return '';
|
|
18
|
+
return conv.length > 12 ? conv.slice(0, 8) : conv;
|
|
19
|
+
}
|
|
20
|
+
function formatChromeStatus(email, quota, conversationId = '') {
|
|
15
21
|
const who = (email || '').trim();
|
|
22
|
+
const conv = shortConversationId(conversationId);
|
|
16
23
|
if (!who) {
|
|
17
|
-
|
|
18
|
-
return conv && conv !== 'new' ? `sisu · not signed in · ${conv}` : 'sisu · not signed in';
|
|
24
|
+
return conv ? `sisu · not signed in · ${conv}` : 'sisu · not signed in';
|
|
19
25
|
}
|
|
20
26
|
const parts = [who];
|
|
21
27
|
const quotaText = (quota || '').trim();
|
|
22
28
|
if (quotaText && quotaText !== 'quota unavailable')
|
|
23
29
|
parts.push(quotaText);
|
|
24
|
-
|
|
25
|
-
if (conv && conv !== 'new')
|
|
30
|
+
if (conv)
|
|
26
31
|
parts.push(conv);
|
|
27
32
|
return parts.join(' · ');
|
|
28
33
|
}
|
|
@@ -106,7 +111,7 @@ async function runPager(io, transport, options = {}) {
|
|
|
106
111
|
const paint = (phase = logo_1.SISU_STILL_PHASE) => {
|
|
107
112
|
if (!running)
|
|
108
113
|
return;
|
|
109
|
-
io.write(`\x1b[H${(0, render_1.renderPager)(state, cols, rows, theme, { phase })}`);
|
|
114
|
+
io.write(`\x1b[H${(0, render_1.renderPager)(state, cols, rows, theme, { phase })}\x1b[J`);
|
|
110
115
|
};
|
|
111
116
|
const playIntro = async () => {
|
|
112
117
|
const frames = Math.max(2, options.introFrames ?? 32);
|
|
@@ -211,24 +216,24 @@ async function runPager(io, transport, options = {}) {
|
|
|
211
216
|
paint();
|
|
212
217
|
return;
|
|
213
218
|
}
|
|
214
|
-
state =
|
|
219
|
+
state = { ...state, statusLine: 'Opening browser to sign in…' };
|
|
215
220
|
paint();
|
|
216
221
|
try {
|
|
217
222
|
const email = await options.login((line) => {
|
|
218
223
|
if (!running)
|
|
219
224
|
return;
|
|
220
|
-
state =
|
|
225
|
+
state = { ...state, statusLine: line };
|
|
221
226
|
paint();
|
|
222
227
|
});
|
|
223
228
|
if (!running)
|
|
224
229
|
return;
|
|
225
230
|
chromeEmail = email;
|
|
226
231
|
state = withChrome(state);
|
|
227
|
-
state = pushEntry(state, 'status', `logged in as ${email}`);
|
|
228
232
|
}
|
|
229
233
|
catch (err) {
|
|
230
234
|
if (!running)
|
|
231
235
|
return;
|
|
236
|
+
state = withChrome(state);
|
|
232
237
|
state = pushEntry(state, 'status', err instanceof Error ? err.message : String(err));
|
|
233
238
|
}
|
|
234
239
|
paint();
|
package/dist/pager/render.js
CHANGED
|
@@ -7,25 +7,13 @@ const mobius_1 = require("../mobius");
|
|
|
7
7
|
const model_1 = require("./model");
|
|
8
8
|
const theme_1 = require("./theme");
|
|
9
9
|
Object.defineProperty(exports, "stripAnsi", { enumerable: true, get: function () { return theme_1.stripAnsi; } });
|
|
10
|
+
const text_1 = require("./text");
|
|
10
11
|
const PROMPT_PREFIX = '› ';
|
|
11
12
|
const PROMPT_BOX_ROWS = 2;
|
|
12
13
|
const STATUS_ROWS = 1;
|
|
13
14
|
const MARK_WIDTH = 2;
|
|
14
15
|
function wrapPlain(text, width) {
|
|
15
|
-
|
|
16
|
-
if (!text)
|
|
17
|
-
return [''];
|
|
18
|
-
const out = [];
|
|
19
|
-
for (const paragraph of text.split('\n')) {
|
|
20
|
-
if (!paragraph) {
|
|
21
|
-
out.push('');
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
for (let i = 0; i < paragraph.length; i += max) {
|
|
25
|
-
out.push(paragraph.slice(i, i + max));
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return out;
|
|
16
|
+
return (0, text_1.wrapCells)(text, width);
|
|
29
17
|
}
|
|
30
18
|
function lineCount(text) {
|
|
31
19
|
if (!text)
|
|
@@ -43,22 +31,25 @@ function paintKind(kind, body, theme) {
|
|
|
43
31
|
return theme.dim(body);
|
|
44
32
|
return theme.text(body);
|
|
45
33
|
}
|
|
46
|
-
function
|
|
47
|
-
if (kind === '
|
|
48
|
-
return
|
|
49
|
-
|
|
50
|
-
return 'tool ';
|
|
51
|
-
return '';
|
|
34
|
+
function shownText(entry) {
|
|
35
|
+
if (entry.kind === 'assistant')
|
|
36
|
+
return (0, text_1.visibleAssistantText)(entry.text);
|
|
37
|
+
return entry.text;
|
|
52
38
|
}
|
|
53
39
|
function entryBodyLines(entry, wrapWidth) {
|
|
40
|
+
const text = shownText(entry);
|
|
54
41
|
if (entry.folded) {
|
|
55
|
-
const n = lineCount(
|
|
42
|
+
const n = lineCount(text);
|
|
56
43
|
const unit = n === 1 ? 'line' : 'lines';
|
|
57
44
|
return wrapPlain(`${entry.kind} · ${n} ${unit}`, wrapWidth);
|
|
58
45
|
}
|
|
59
|
-
if (
|
|
46
|
+
if (entry.kind === 'user')
|
|
47
|
+
return ['You', ...wrapPlain(text || ' ', wrapWidth)];
|
|
48
|
+
if (entry.kind === 'tool')
|
|
49
|
+
return wrapPlain(text ? `tool ${text}` : 'tool', wrapWidth);
|
|
50
|
+
if (!text)
|
|
60
51
|
return [''];
|
|
61
|
-
return wrapPlain(
|
|
52
|
+
return wrapPlain(text, wrapWidth);
|
|
62
53
|
}
|
|
63
54
|
function layoutScrollback(state, cols, theme) {
|
|
64
55
|
const wrapWidth = Math.max(1, cols - MARK_WIDTH);
|
|
@@ -68,6 +59,10 @@ function layoutScrollback(state, cols, theme) {
|
|
|
68
59
|
const entry = state.entries[i];
|
|
69
60
|
const body = entryBodyLines(entry, wrapWidth);
|
|
70
61
|
const selected = i === state.selected && state.entries.length > 0;
|
|
62
|
+
if (i > 0) {
|
|
63
|
+
lines.push('');
|
|
64
|
+
entryOf.push(i);
|
|
65
|
+
}
|
|
71
66
|
for (let j = 0; j < body.length; j += 1) {
|
|
72
67
|
const mark = selected && j === 0 ? theme.accent('▸ ') : ' ';
|
|
73
68
|
lines.push(`${mark}${paintKind(entry.kind, body[j], theme)}`);
|
|
@@ -156,13 +151,17 @@ function renderPager(state, cols, rows, themeName = 'dark', view = {}) {
|
|
|
156
151
|
const laid = layoutScrollback(state, width, theme);
|
|
157
152
|
const lastEntry = Math.max(0, state.entries.length - 1);
|
|
158
153
|
const visibleScroll = windowAroundSelected(laid.lines, laid.entryOf, state.selected, scrollBudget, lastEntry);
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
154
|
+
const shownWelcome = welcome.slice(0, welcomeTake);
|
|
155
|
+
const extra = Math.max(0, welcomeTake + scrollBudget - shownWelcome.length - visibleScroll.length);
|
|
156
|
+
const padTop = isIdleWelcome(state) && visibleScroll.length === 0 ? Math.floor(extra / 2) : 0;
|
|
157
|
+
const padBot = extra - padTop;
|
|
158
|
+
const body = [
|
|
159
|
+
...Array.from({ length: padTop }, () => ''),
|
|
160
|
+
...shownWelcome,
|
|
161
|
+
...Array.from({ length: padBot }, () => ''),
|
|
162
|
+
...visibleScroll,
|
|
163
|
+
...slash.slice(0, slashTake),
|
|
164
|
+
];
|
|
166
165
|
const lines = body.map((line) => (0, theme_1.padVisible)(line, width));
|
|
167
166
|
if (statusRows > 0) {
|
|
168
167
|
lines.push((0, theme_1.padVisible)(theme.dim(state.statusLine ?? ''), width));
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** Display-side text: hide model scratch and wrap by terminal cells. */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.visibleAssistantText = visibleAssistantText;
|
|
5
|
+
exports.wrapCells = wrapCells;
|
|
6
|
+
const theme_1 = require("./theme");
|
|
7
|
+
const THINK_BLOCK = /<think\b[^>]*>[\s\S]*?<\/think>/gi;
|
|
8
|
+
const THINK_OPEN = /<think\b[^>]*>[\s\S]*$/i;
|
|
9
|
+
function visibleAssistantText(raw) {
|
|
10
|
+
let text = String(raw || '');
|
|
11
|
+
text = text.replace(THINK_BLOCK, '');
|
|
12
|
+
text = text.replace(THINK_OPEN, '');
|
|
13
|
+
return text.replace(/^\n+/, '').replace(/\n+$/, '');
|
|
14
|
+
}
|
|
15
|
+
function wrapCells(text, width) {
|
|
16
|
+
const max = Math.max(1, width);
|
|
17
|
+
if (!text)
|
|
18
|
+
return [''];
|
|
19
|
+
const out = [];
|
|
20
|
+
for (const paragraph of text.split('\n')) {
|
|
21
|
+
if (!paragraph) {
|
|
22
|
+
out.push('');
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
let line = '';
|
|
26
|
+
for (const ch of paragraph) {
|
|
27
|
+
if ((0, theme_1.visibleWidth)(line + ch) > max) {
|
|
28
|
+
if (line)
|
|
29
|
+
out.push(line);
|
|
30
|
+
line = ch;
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
line += ch;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (line)
|
|
37
|
+
out.push(line);
|
|
38
|
+
}
|
|
39
|
+
return out.length ? out : [''];
|
|
40
|
+
}
|
package/dist/pager/theme.js
CHANGED
|
@@ -42,8 +42,26 @@ function getTheme(name = 'dark') {
|
|
|
42
42
|
function stripAnsi(s) {
|
|
43
43
|
return s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
44
44
|
}
|
|
45
|
+
function isWide(code) {
|
|
46
|
+
return ((code >= 0x1100 && code <= 0x115f) ||
|
|
47
|
+
(code >= 0x2e80 && code <= 0xa4cf && code !== 0x303f) ||
|
|
48
|
+
(code >= 0xac00 && code <= 0xd7a3) ||
|
|
49
|
+
(code >= 0xf900 && code <= 0xfaff) ||
|
|
50
|
+
(code >= 0xfe10 && code <= 0xfe19) ||
|
|
51
|
+
(code >= 0xfe30 && code <= 0xfe6f) ||
|
|
52
|
+
(code >= 0xff00 && code <= 0xff60) ||
|
|
53
|
+
(code >= 0xffe0 && code <= 0xffe6));
|
|
54
|
+
}
|
|
55
|
+
function charWidth(ch) {
|
|
56
|
+
const code = ch.codePointAt(0) ?? 0;
|
|
57
|
+
return isWide(code) ? 2 : 1;
|
|
58
|
+
}
|
|
59
|
+
/** Terminal cells, not JS string length — CJK is two cells. */
|
|
45
60
|
function visibleWidth(line) {
|
|
46
|
-
|
|
61
|
+
let width = 0;
|
|
62
|
+
for (const ch of stripAnsi(line))
|
|
63
|
+
width += charWidth(ch);
|
|
64
|
+
return width;
|
|
47
65
|
}
|
|
48
66
|
function clipVisible(line, width) {
|
|
49
67
|
if (width <= 0)
|
|
@@ -55,23 +73,29 @@ function clipVisible(line, width) {
|
|
|
55
73
|
const re = /\x1b\[[0-9;]*m/g;
|
|
56
74
|
let last = 0;
|
|
57
75
|
let match = re.exec(line);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
76
|
+
const take = (from, to) => {
|
|
77
|
+
let i = from;
|
|
78
|
+
while (i < to && seen < width) {
|
|
79
|
+
const code = line.charCodeAt(i);
|
|
80
|
+
const wide = code >= 0xd800 && code <= 0xdbff;
|
|
81
|
+
const ch = wide ? line.slice(i, i + 2) : line[i];
|
|
82
|
+
const step = charWidth(ch);
|
|
83
|
+
if (seen + step > width)
|
|
84
|
+
break;
|
|
85
|
+
out += ch;
|
|
86
|
+
seen += step;
|
|
87
|
+
i += ch.length;
|
|
62
88
|
}
|
|
89
|
+
};
|
|
90
|
+
while (match) {
|
|
91
|
+
take(last, match.index);
|
|
63
92
|
if (seen >= width)
|
|
64
93
|
break;
|
|
65
94
|
out += match[0];
|
|
66
95
|
last = match.index + match[0].length;
|
|
67
96
|
match = re.exec(line);
|
|
68
97
|
}
|
|
69
|
-
|
|
70
|
-
if (line[i] === '\x1b')
|
|
71
|
-
break;
|
|
72
|
-
out += line[i];
|
|
73
|
-
seen += 1;
|
|
74
|
-
}
|
|
98
|
+
take(last, line.length);
|
|
75
99
|
return out + RESET;
|
|
76
100
|
}
|
|
77
101
|
function padVisible(line, cols) {
|