@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 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
- let width = 0;
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}`, ` ${latin}`];
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
- const STILL_PHASE = 0.85;
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(`${CLEAR_HOME}${(0, render_1.renderPager)(state, cols, rows, theme)}`);
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 = () => {
@@ -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, height, _theme) {
109
+ function welcomeLines(guest, width, bodyBudget, phase) {
110
110
  const copy = (0, logo_1.sisuWelcomeCopy)(guest, true).map((line) => center(line, width));
111
- const ringW = height >= 16 && width >= 48 ? (0, mobius_1.mobiusFrameWidth)(Math.min(width, 72)) : Math.min(width, 44);
112
- const ringH = Math.min((0, mobius_1.mobiusFrameHeight)(ringW), Math.max(8, height - 7));
113
- const painted = (0, logo_1.sisuMobiusArt)(ringW, 0.85, true).split('\n').slice(0, ringH);
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) ? welcomeLines(guest, width, height, theme) : [];
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 body = [];
153
- body.push(...welcome.slice(0, welcomeTake));
154
- while (body.length + visibleScroll.length < welcomeTake + scrollBudget) {
155
- body.push('');
156
- }
157
- body.push(...visibleScroll);
158
- body.push(...slash.slice(0, slashTake));
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));
@@ -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
- return stripAnsi(line).length;
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
- while (match) {
59
- for (let i = last; i < match.index && seen < width; i += 1) {
60
- out += line[i];
61
- seen += 1;
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
- for (let i = last; i < line.length && seen < width; i += 1) {
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 (deps.pager || shouldUsePager(deps)) {
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: () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stevezhou/sisu",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "SiSu CLI — one login, cloud quota, local workspace",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://www.sisu.chat",