@stevezhou/sisu 0.1.7 → 0.1.9
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 +6 -20
- package/dist/pager/app.js +16 -3
- package/dist/pager/render.js +24 -13
- package/dist/pager/theme.js +35 -11
- package/dist/tui.js +16 -11
- package/package.json +1 -1
package/dist/logo.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sisuTreeLines = exports.sisuTreeArt = exports.sisuMarkLines = exports.sisuMarkArt = void 0;
|
|
3
|
+
exports.SISU_STILL_PHASE = exports.sisuTreeLines = exports.sisuTreeArt = exports.sisuMarkLines = exports.sisuMarkArt = void 0;
|
|
4
4
|
exports.sisuMobiusArt = sisuMobiusArt;
|
|
5
5
|
exports.sisuWordmark = sisuWordmark;
|
|
6
6
|
exports.displayWidth = displayWidth;
|
|
@@ -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}`;
|
|
@@ -80,7 +65,8 @@ function center(text, width) {
|
|
|
80
65
|
function centerBlock(lines, width) {
|
|
81
66
|
return lines.map((line) => center(line, width));
|
|
82
67
|
}
|
|
83
|
-
|
|
68
|
+
exports.SISU_STILL_PHASE = 0.85;
|
|
69
|
+
const STILL_PHASE = exports.SISU_STILL_PHASE;
|
|
84
70
|
/**
|
|
85
71
|
* Möbius splash. `phase` slides the half-twist around the single face.
|
|
86
72
|
* Line count is stable for a given width so the intro can rewrite in place.
|
package/dist/pager/app.js
CHANGED
|
@@ -7,10 +7,10 @@ const store_1 = require("../store");
|
|
|
7
7
|
const input_1 = require("./input");
|
|
8
8
|
const model_1 = require("./model");
|
|
9
9
|
const history_1 = require("./history");
|
|
10
|
+
const logo_1 = require("../logo");
|
|
10
11
|
const render_1 = require("./render");
|
|
11
12
|
const ALT_ENTER = '\x1b[?1049h\x1b[?25l';
|
|
12
13
|
const ALT_LEAVE = '\x1b[?1049l\x1b[?25h';
|
|
13
|
-
const CLEAR_HOME = '\x1b[2J\x1b[H';
|
|
14
14
|
function formatChromeStatus(email, quota, conversationId) {
|
|
15
15
|
const who = (email || '').trim();
|
|
16
16
|
if (!who) {
|
|
@@ -103,10 +103,21 @@ async function runPager(io, transport, options = {}) {
|
|
|
103
103
|
let processing = false;
|
|
104
104
|
const pending = [];
|
|
105
105
|
let unsubscribe = () => undefined;
|
|
106
|
-
const paint = () => {
|
|
106
|
+
const paint = (phase = logo_1.SISU_STILL_PHASE) => {
|
|
107
107
|
if (!running)
|
|
108
108
|
return;
|
|
109
|
-
io.write(
|
|
109
|
+
io.write(`\x1b[H${(0, render_1.renderPager)(state, cols, rows, theme, { phase })}\x1b[J`);
|
|
110
|
+
};
|
|
111
|
+
const playIntro = async () => {
|
|
112
|
+
const frames = Math.max(2, options.introFrames ?? 32);
|
|
113
|
+
const sleep = options.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
114
|
+
for (let i = 0; i < frames; i += 1) {
|
|
115
|
+
if (!running)
|
|
116
|
+
return;
|
|
117
|
+
const u = i / (frames - 1);
|
|
118
|
+
paint(logo_1.SISU_STILL_PHASE + u * Math.PI * 2);
|
|
119
|
+
await sleep(38);
|
|
120
|
+
}
|
|
110
121
|
};
|
|
111
122
|
let removeTerm = () => undefined;
|
|
112
123
|
let settle;
|
|
@@ -123,6 +134,8 @@ async function runPager(io, transport, options = {}) {
|
|
|
123
134
|
try {
|
|
124
135
|
io.enterRaw();
|
|
125
136
|
io.write(ALT_ENTER);
|
|
137
|
+
if (options.intro)
|
|
138
|
+
await playIntro();
|
|
126
139
|
const onTerm = () => finish(0);
|
|
127
140
|
process.on('SIGTERM', onTerm);
|
|
128
141
|
removeTerm = () => {
|
package/dist/pager/render.js
CHANGED
|
@@ -106,13 +106,18 @@ function center(text, width, vis = (0, logo_1.displayWidth)(text)) {
|
|
|
106
106
|
const padLeft = Math.max(0, Math.floor((width - vis) / 2));
|
|
107
107
|
return `${' '.repeat(padLeft)}${text}`;
|
|
108
108
|
}
|
|
109
|
-
function welcomeLines(guest, width,
|
|
109
|
+
function welcomeLines(guest, width, bodyBudget, phase) {
|
|
110
110
|
const copy = (0, logo_1.sisuWelcomeCopy)(guest, true).map((line) => center(line, width));
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
const ringRoom = Math.max(0, bodyBudget - copy.length - 1);
|
|
112
|
+
if (ringRoom < 4 || width < 28)
|
|
113
|
+
return copy;
|
|
114
|
+
const ringW = (0, mobius_1.mobiusFrameWidth)(width >= 48 ? Math.min(width, 72) : Math.min(width, 44));
|
|
115
|
+
const ringH = Math.min((0, mobius_1.mobiusFrameHeight)(ringW), ringRoom);
|
|
116
|
+
const painted = (0, logo_1.sisuMobiusArt)(ringW, phase, true).split('\n').slice(0, ringH);
|
|
114
117
|
const pad = Math.max(0, Math.floor((width - ringW) / 2));
|
|
115
118
|
const ring = painted.map((line) => `${' '.repeat(pad)}${line}`);
|
|
119
|
+
if (bodyBudget >= 16)
|
|
120
|
+
return [...ring, '', ...copy];
|
|
116
121
|
return [...copy, '', ...ring];
|
|
117
122
|
}
|
|
118
123
|
function slashMenuLines(state, theme) {
|
|
@@ -130,7 +135,7 @@ function slashMenuLines(state, theme) {
|
|
|
130
135
|
* Fixed-grid frame: always `rows` lines, each `cols` visible cells.
|
|
131
136
|
* Colors are 24-bit SGR; measure width with stripAnsi / visibleWidth.
|
|
132
137
|
*/
|
|
133
|
-
function renderPager(state, cols, rows, themeName = 'dark') {
|
|
138
|
+
function renderPager(state, cols, rows, themeName = 'dark', view = {}) {
|
|
134
139
|
const theme = (0, theme_1.getTheme)(themeName);
|
|
135
140
|
const height = Math.max(0, rows);
|
|
136
141
|
const width = Math.max(0, cols);
|
|
@@ -143,19 +148,25 @@ function renderPager(state, cols, rows, themeName = 'dark') {
|
|
|
143
148
|
const slash = slashMenuLines(state, theme);
|
|
144
149
|
const slashTake = Math.min(slash.length, bodyBudget);
|
|
145
150
|
const guest = !(state.statusLine || '').trim() || (state.statusLine || '').includes('not signed in');
|
|
146
|
-
const welcome = isIdleWelcome(state)
|
|
151
|
+
const welcome = isIdleWelcome(state)
|
|
152
|
+
? welcomeLines(guest, width, bodyBudget, view.phase ?? logo_1.SISU_STILL_PHASE)
|
|
153
|
+
: [];
|
|
147
154
|
const welcomeTake = Math.min(welcome.length, Math.max(0, bodyBudget - slashTake));
|
|
148
155
|
const scrollBudget = bodyBudget - slashTake - welcomeTake;
|
|
149
156
|
const laid = layoutScrollback(state, width, theme);
|
|
150
157
|
const lastEntry = Math.max(0, state.entries.length - 1);
|
|
151
158
|
const visibleScroll = windowAroundSelected(laid.lines, laid.entryOf, state.selected, scrollBudget, lastEntry);
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
+
const shownWelcome = welcome.slice(0, welcomeTake);
|
|
160
|
+
const extra = Math.max(0, welcomeTake + scrollBudget - shownWelcome.length - visibleScroll.length);
|
|
161
|
+
const padTop = isIdleWelcome(state) && visibleScroll.length === 0 ? Math.floor(extra / 2) : 0;
|
|
162
|
+
const padBot = extra - padTop;
|
|
163
|
+
const body = [
|
|
164
|
+
...Array.from({ length: padTop }, () => ''),
|
|
165
|
+
...shownWelcome,
|
|
166
|
+
...Array.from({ length: padBot }, () => ''),
|
|
167
|
+
...visibleScroll,
|
|
168
|
+
...slash.slice(0, slashTake),
|
|
169
|
+
];
|
|
159
170
|
const lines = body.map((line) => (0, theme_1.padVisible)(line, width));
|
|
160
171
|
if (statusRows > 0) {
|
|
161
172
|
lines.push((0, theme_1.padVisible)(theme.dim(state.statusLine ?? ''), width));
|
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) {
|
package/dist/tui.js
CHANGED
|
@@ -195,17 +195,20 @@ async function runTui(io, deps = {}) {
|
|
|
195
195
|
const columns = deps.columns ?? process.stdout.columns ?? 80;
|
|
196
196
|
const animate = deps.animate ?? shouldAnimateSplash();
|
|
197
197
|
try {
|
|
198
|
-
if (animate) {
|
|
199
|
-
await playTreeIntro(io, {
|
|
200
|
-
columns,
|
|
201
|
-
color: deps.color ?? true,
|
|
202
|
-
sleep: deps.sleep,
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
io.write(`${(0, logo_1.sisuSplash)(columns, true)}\n`);
|
|
207
|
-
}
|
|
208
198
|
const account = auth();
|
|
199
|
+
const usePager = Boolean(deps.pager || shouldUsePager(deps));
|
|
200
|
+
if (!usePager) {
|
|
201
|
+
if (animate) {
|
|
202
|
+
await playTreeIntro(io, {
|
|
203
|
+
columns,
|
|
204
|
+
color: deps.color ?? true,
|
|
205
|
+
sleep: deps.sleep,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
io.write(`${(0, logo_1.sisuSplash)(columns, true)}\n`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
209
212
|
const startWebLogin = async (notify) => {
|
|
210
213
|
return webLogin({
|
|
211
214
|
onStart: (info) => {
|
|
@@ -214,13 +217,15 @@ async function runTui(io, deps = {}) {
|
|
|
214
217
|
},
|
|
215
218
|
}, http);
|
|
216
219
|
};
|
|
217
|
-
if (
|
|
220
|
+
if (usePager) {
|
|
218
221
|
io.close?.();
|
|
219
222
|
const transport = (0, transport_1.createFastApiTransport)(http);
|
|
220
223
|
return await (deps.pager ?? app_1.runPager)((0, stdio_1.stdioPagerIo)(), transport, {
|
|
221
224
|
columns,
|
|
222
225
|
email: account?.email,
|
|
223
226
|
login: startWebLogin,
|
|
227
|
+
intro: animate,
|
|
228
|
+
sleep: deps.sleep,
|
|
224
229
|
quota: async () => (0, commands_1.formatQuota)(await (0, commands_1.fetchBalance)(http)),
|
|
225
230
|
status: () => status(http),
|
|
226
231
|
ls: () => {
|