@nbtca/prompt 1.5.0 → 1.5.2
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/app/app.js +142 -13
- package/dist/app/chrome.js +13 -2
- package/dist/app/frame.js +14 -2
- package/dist/app/keys.js +109 -1
- package/dist/app/views/docs-render.js +3 -2
- package/dist/app/views/docs.js +90 -25
- package/dist/app/views/events-render.js +2 -1
- package/dist/app/views/events.js +13 -2
- package/dist/app/views/home.js +46 -24
- package/dist/app/views/schedule-render.js +4 -3
- package/dist/app/views/schedule.js +101 -33
- package/dist/cli.js +570 -0
- package/dist/config/preferences.js +7 -0
- package/dist/core/canvas.js +1 -0
- package/dist/core/components/menu.js +23 -22
- package/dist/core/components/spinner.js +17 -1
- package/dist/core/text.js +7 -3
- package/dist/core/vim-keys.js +149 -6
- package/dist/features/calendar-store.js +27 -0
- package/dist/features/calendar.js +55 -6
- package/dist/features/docs-client.js +225 -0
- package/dist/features/docs.js +224 -68
- package/dist/features/links.js +51 -0
- package/dist/features/status.js +83 -14
- package/dist/features/student-timetable.js +1 -2
- package/dist/features/theme.js +3 -3
- package/dist/features/update.js +3 -2
- package/dist/i18n/locales/en.json +7 -2
- package/dist/i18n/locales/zh.json +7 -2
- package/dist/index.js +5 -498
- package/package.json +2 -1
package/dist/app/app.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ansi, ensureCursorRestored } from '../core/canvas.js';
|
|
2
|
-
import {
|
|
3
|
-
import { routeGlobalKey } from './keys.js';
|
|
2
|
+
import { composeFrameLines, computeBodyRows, diffFrame } from './frame.js';
|
|
3
|
+
import { isPrintableKey, KeyStreamDecoder, routeGlobalKey } from './keys.js';
|
|
4
4
|
import { renderHeader, renderFooter, resolveChromeLayout } from './chrome.js';
|
|
5
5
|
import { homeView } from './views/home.js';
|
|
6
6
|
import { scheduleView } from './views/schedule.js';
|
|
@@ -8,6 +8,7 @@ import { docsView } from './views/docs.js';
|
|
|
8
8
|
import { eventsView } from './views/events.js';
|
|
9
9
|
import { settingsView } from './views/settings.js';
|
|
10
10
|
import { getAppTabs } from './tabs.js';
|
|
11
|
+
import { SPINNER_FRAME_MS } from '../core/components/spinner.js';
|
|
11
12
|
export async function runApp() {
|
|
12
13
|
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
13
14
|
return;
|
|
@@ -15,6 +16,14 @@ export async function runApp() {
|
|
|
15
16
|
let scroll = 0;
|
|
16
17
|
let running = true;
|
|
17
18
|
let suspended = false;
|
|
19
|
+
let entered = false;
|
|
20
|
+
const lifecycle = new AbortController();
|
|
21
|
+
const keyDecoder = new KeyStreamDecoder();
|
|
22
|
+
let keyFlushTimer;
|
|
23
|
+
let clockTimer;
|
|
24
|
+
let busyTimer;
|
|
25
|
+
let painted;
|
|
26
|
+
let lastBody = { length: 0, height: 0 };
|
|
18
27
|
const viewIds = getAppTabs().map((tab) => tab.id);
|
|
19
28
|
const nativeViews = {
|
|
20
29
|
home: homeView,
|
|
@@ -38,6 +47,7 @@ export async function runApp() {
|
|
|
38
47
|
return { rows: process.stdout.rows || 24, cols: process.stdout.columns || 80 };
|
|
39
48
|
}
|
|
40
49
|
const ctx = {
|
|
50
|
+
signal: lifecycle.signal,
|
|
41
51
|
get size() {
|
|
42
52
|
return size();
|
|
43
53
|
},
|
|
@@ -67,14 +77,47 @@ export async function runApp() {
|
|
|
67
77
|
const tabs = getAppTabs();
|
|
68
78
|
const chrome = resolveChromeLayout(rows);
|
|
69
79
|
const header = renderHeader(tabs, view, cols, chrome.headerLines);
|
|
70
|
-
const footer = renderFooter(view, cols, tabs.length, active?.footerHint?.(tabs.length, cols), chrome.footerLines);
|
|
71
80
|
const body = active?.render(ctx) ?? [];
|
|
72
81
|
const bodyScroll = active?.capturesInput?.() ? Number.MAX_SAFE_INTEGER : scroll;
|
|
73
|
-
|
|
82
|
+
const height = computeBodyRows(rows, chrome.headerLines, chrome.footerLines);
|
|
83
|
+
lastBody = { length: body.length, height };
|
|
84
|
+
const footer = renderFooter(view, cols, tabs.length, active?.footerHint?.(tabs.length, cols), chrome.footerLines, active?.scrollsBody?.() === true ? scrollPercent() : undefined);
|
|
85
|
+
const lines = composeFrameLines(header, body, footer, rows, cols, bodyScroll);
|
|
86
|
+
const patch = diffFrame(painted?.cols === cols ? painted.lines : undefined, lines);
|
|
87
|
+
painted = { cols, lines };
|
|
88
|
+
if (patch)
|
|
89
|
+
process.stdout.write(patch);
|
|
90
|
+
scheduleBusyTick(active?.isBusy?.() === true);
|
|
74
91
|
}
|
|
75
|
-
function
|
|
76
|
-
|
|
92
|
+
function maxScroll() {
|
|
93
|
+
return Math.max(0, lastBody.length - lastBody.height);
|
|
94
|
+
}
|
|
95
|
+
function scrollPercent() {
|
|
96
|
+
const max = maxScroll();
|
|
97
|
+
if (max === 0)
|
|
98
|
+
return undefined;
|
|
99
|
+
return `${Math.round((Math.min(scroll, max) / max) * 100)}%`;
|
|
100
|
+
}
|
|
101
|
+
function scrollTo(next) {
|
|
102
|
+
scroll = Math.min(Math.max(0, next), maxScroll());
|
|
103
|
+
render();
|
|
104
|
+
}
|
|
105
|
+
function scheduleBusyTick(busy) {
|
|
106
|
+
if (busy && running && busyTimer === undefined) {
|
|
107
|
+
busyTimer = setTimeout(() => {
|
|
108
|
+
busyTimer = undefined;
|
|
109
|
+
render();
|
|
110
|
+
}, SPINNER_FRAME_MS);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (busy || busyTimer === undefined)
|
|
114
|
+
return;
|
|
115
|
+
clearTimeout(busyTimer);
|
|
116
|
+
busyTimer = undefined;
|
|
117
|
+
}
|
|
118
|
+
function dispatchKey(key) {
|
|
77
119
|
if (key === '\x03') {
|
|
120
|
+
process.exitCode = 130;
|
|
78
121
|
quit();
|
|
79
122
|
return;
|
|
80
123
|
} // Ctrl-C always quits, even mid-capture.
|
|
@@ -112,22 +155,88 @@ export async function runApp() {
|
|
|
112
155
|
return;
|
|
113
156
|
}
|
|
114
157
|
const page = Math.max(1, ctx.bodyRows - 2);
|
|
115
|
-
|
|
116
|
-
|
|
158
|
+
scrollTo(scroll + g.scrollBy * page);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if ((g.scrollLines !== undefined || g.scrollTo !== undefined) && active?.scrollsBody?.()) {
|
|
162
|
+
if (g.scrollTo === 'top')
|
|
163
|
+
scrollTo(0);
|
|
164
|
+
else if (g.scrollTo === 'end')
|
|
165
|
+
scrollTo(maxScroll());
|
|
166
|
+
else
|
|
167
|
+
scrollTo(scroll + (g.scrollLines ?? 0));
|
|
117
168
|
return;
|
|
118
169
|
}
|
|
119
170
|
active?.handleKey?.(key, ctx);
|
|
120
171
|
render();
|
|
121
172
|
}
|
|
173
|
+
function dispatchKeys(keys) {
|
|
174
|
+
for (let index = 0; index < keys.length && running && !suspended; index += 1) {
|
|
175
|
+
let key = keys[index];
|
|
176
|
+
if (key === undefined)
|
|
177
|
+
continue;
|
|
178
|
+
if (nativeViews[view]?.capturesInput?.() && isPrintableKey(key)) {
|
|
179
|
+
while (index + 1 < keys.length) {
|
|
180
|
+
const next = keys[index + 1];
|
|
181
|
+
if (next === undefined || !isPrintableKey(next))
|
|
182
|
+
break;
|
|
183
|
+
key += next;
|
|
184
|
+
index += 1;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
dispatchKey(key);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
function scheduleClock() {
|
|
191
|
+
if (!running)
|
|
192
|
+
return;
|
|
193
|
+
clockTimer = setTimeout(() => {
|
|
194
|
+
clockTimer = undefined;
|
|
195
|
+
render();
|
|
196
|
+
scheduleClock();
|
|
197
|
+
}, 60_000 - (Date.now() % 60_000));
|
|
198
|
+
}
|
|
199
|
+
function clearKeyFlush() {
|
|
200
|
+
if (keyFlushTimer === undefined)
|
|
201
|
+
return;
|
|
202
|
+
clearTimeout(keyFlushTimer);
|
|
203
|
+
keyFlushTimer = undefined;
|
|
204
|
+
}
|
|
205
|
+
function onKey(data) {
|
|
206
|
+
clearKeyFlush();
|
|
207
|
+
dispatchKeys(keyDecoder.write(data));
|
|
208
|
+
if (!running || suspended || !keyDecoder.hasPending)
|
|
209
|
+
return;
|
|
210
|
+
keyFlushTimer = setTimeout(() => {
|
|
211
|
+
keyFlushTimer = undefined;
|
|
212
|
+
dispatchKeys(keyDecoder.flush());
|
|
213
|
+
}, 20);
|
|
214
|
+
}
|
|
122
215
|
function enter() {
|
|
216
|
+
if (entered || lifecycle.signal.aborted)
|
|
217
|
+
return;
|
|
218
|
+
entered = true;
|
|
219
|
+
painted = undefined;
|
|
220
|
+
keyDecoder.reset();
|
|
123
221
|
ensureCursorRestored();
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
process.stdin.
|
|
127
|
-
|
|
128
|
-
|
|
222
|
+
try {
|
|
223
|
+
process.stdout.write(ansi.enterAlt + ansi.hideCursor);
|
|
224
|
+
if (process.stdin.isTTY)
|
|
225
|
+
process.stdin.setRawMode(true);
|
|
226
|
+
process.stdin.resume();
|
|
227
|
+
process.stdin.on('data', onKey);
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
leave();
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
129
233
|
}
|
|
130
234
|
function leave() {
|
|
235
|
+
if (!entered)
|
|
236
|
+
return;
|
|
237
|
+
entered = false;
|
|
238
|
+
clearKeyFlush();
|
|
239
|
+
keyDecoder.reset();
|
|
131
240
|
process.stdin.removeListener('data', onKey);
|
|
132
241
|
if (process.stdin.isTTY)
|
|
133
242
|
process.stdin.setRawMode(false);
|
|
@@ -162,6 +271,15 @@ export async function runApp() {
|
|
|
162
271
|
leave();
|
|
163
272
|
}
|
|
164
273
|
function onSigint() {
|
|
274
|
+
process.exitCode = 130;
|
|
275
|
+
quit();
|
|
276
|
+
}
|
|
277
|
+
function onSigterm() {
|
|
278
|
+
process.exitCode = 143;
|
|
279
|
+
quit();
|
|
280
|
+
}
|
|
281
|
+
function onSighup() {
|
|
282
|
+
process.exitCode = 129;
|
|
165
283
|
quit();
|
|
166
284
|
}
|
|
167
285
|
let resolveRun;
|
|
@@ -172,6 +290,12 @@ export async function runApp() {
|
|
|
172
290
|
if (!running)
|
|
173
291
|
return;
|
|
174
292
|
running = false;
|
|
293
|
+
lifecycle.abort();
|
|
294
|
+
if (clockTimer !== undefined) {
|
|
295
|
+
clearTimeout(clockTimer);
|
|
296
|
+
clockTimer = undefined;
|
|
297
|
+
}
|
|
298
|
+
scheduleBusyTick(false);
|
|
175
299
|
try {
|
|
176
300
|
leave();
|
|
177
301
|
}
|
|
@@ -179,16 +303,21 @@ export async function runApp() {
|
|
|
179
303
|
process.stdout.removeListener('resize', onResize);
|
|
180
304
|
process.removeListener('exit', onExit);
|
|
181
305
|
process.removeListener('SIGINT', onSigint);
|
|
306
|
+
process.removeListener('SIGTERM', onSigterm);
|
|
307
|
+
process.removeListener('SIGHUP', onSighup);
|
|
182
308
|
resolveRun();
|
|
183
309
|
}
|
|
184
310
|
}
|
|
185
311
|
process.on('exit', onExit);
|
|
186
312
|
process.stdout.on('resize', onResize);
|
|
187
313
|
process.once('SIGINT', onSigint);
|
|
314
|
+
process.once('SIGTERM', onSigterm);
|
|
315
|
+
process.once('SIGHUP', onSighup);
|
|
188
316
|
try {
|
|
189
317
|
enter();
|
|
190
318
|
loadView('home');
|
|
191
319
|
render();
|
|
320
|
+
scheduleClock();
|
|
192
321
|
await done;
|
|
193
322
|
}
|
|
194
323
|
finally {
|
package/dist/app/chrome.js
CHANGED
|
@@ -110,12 +110,23 @@ function interactiveFooterHint(tabCount, cols) {
|
|
|
110
110
|
];
|
|
111
111
|
return fitFooterHint(cols, ...candidates);
|
|
112
112
|
}
|
|
113
|
-
export function renderFooter(_active, cols, tabCount, overrideHint, lineCount = FOOTER_LINES) {
|
|
113
|
+
export function renderFooter(_active, cols, tabCount, overrideHint, lineCount = FOOTER_LINES, position) {
|
|
114
114
|
if (lineCount === 0)
|
|
115
115
|
return [];
|
|
116
116
|
const rule = renderRule(cols);
|
|
117
117
|
const hintText = overrideHint ?? interactiveFooterHint(tabCount, cols);
|
|
118
118
|
const indent = visualWidth(space.indent + hintText) <= cols ? space.indent : '';
|
|
119
119
|
const hint = indent + type.hint(hintText);
|
|
120
|
-
return lineCount === 1
|
|
120
|
+
return lineCount === 1
|
|
121
|
+
? [withPosition(hint, hintText, indent, position, cols)]
|
|
122
|
+
: [rule, withPosition(hint, hintText, indent, position, cols)];
|
|
123
|
+
}
|
|
124
|
+
function withPosition(hint, hintText, indent, position, cols) {
|
|
125
|
+
if (position === undefined)
|
|
126
|
+
return hint;
|
|
127
|
+
const margin = visualWidth(space.indent) < cols ? space.indent : '';
|
|
128
|
+
const gap = cols - visualWidth(indent + hintText) - visualWidth(position) - visualWidth(margin);
|
|
129
|
+
if (gap < 2)
|
|
130
|
+
return hint;
|
|
131
|
+
return hint + ' '.repeat(gap) + type.hint(position) + margin;
|
|
121
132
|
}
|
package/dist/app/frame.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ansi } from '../core/canvas.js';
|
|
1
2
|
import { clipAnsiToVisualWidth, visualWidth } from '../core/text.js';
|
|
2
3
|
export function clipToWidth(line, cols) {
|
|
3
4
|
if (visualWidth(line) <= cols)
|
|
@@ -17,12 +18,23 @@ export function fitBody(lines, height, scroll, cols) {
|
|
|
17
18
|
out.push(' '.repeat(cols));
|
|
18
19
|
return out;
|
|
19
20
|
}
|
|
20
|
-
export function
|
|
21
|
+
export function composeFrameLines(header, body, footer, rows, cols, scroll) {
|
|
21
22
|
const h = header.map((l) => fitLine(l, cols));
|
|
22
23
|
const f = footer.map((l) => fitLine(l, cols));
|
|
23
24
|
const bodyH = Math.max(0, rows - h.length - f.length);
|
|
24
25
|
const b = fitBody(body, bodyH, scroll, cols);
|
|
25
|
-
return [...h, ...b, ...f].slice(0, rows)
|
|
26
|
+
return [...h, ...b, ...f].slice(0, rows);
|
|
27
|
+
}
|
|
28
|
+
export function diffFrame(prev, next) {
|
|
29
|
+
if (prev?.length !== next.length)
|
|
30
|
+
return ansi.home + next.join('\n') + ansi.eraseDown;
|
|
31
|
+
let out = '';
|
|
32
|
+
for (let row = 0; row < next.length; row += 1) {
|
|
33
|
+
const line = next[row];
|
|
34
|
+
if (line !== undefined && prev[row] !== line)
|
|
35
|
+
out += ansi.cursorToRow(row + 1) + line;
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
26
38
|
}
|
|
27
39
|
export function computeBodyRows(rows, headerLines, footerLines) {
|
|
28
40
|
return Math.max(0, rows - headerLines - footerLines);
|
package/dist/app/keys.js
CHANGED
|
@@ -1,3 +1,103 @@
|
|
|
1
|
+
const ESC = '\x1b';
|
|
2
|
+
const GRAPHEME_SEGMENTER = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
|
|
3
|
+
function isControl(value) {
|
|
4
|
+
const code = value.charCodeAt(0);
|
|
5
|
+
return code <= 0x1f || (code >= 0x7f && code <= 0x9f);
|
|
6
|
+
}
|
|
7
|
+
export function isPrintableKey(value) {
|
|
8
|
+
if (value.length === 0 || value.startsWith(ESC))
|
|
9
|
+
return false;
|
|
10
|
+
return Array.from(value).every((char) => !isControl(char));
|
|
11
|
+
}
|
|
12
|
+
function escapeSequenceLength(value) {
|
|
13
|
+
if (value.length === 1)
|
|
14
|
+
return null;
|
|
15
|
+
const introducer = value[1];
|
|
16
|
+
if (introducer === ESC || (introducer !== undefined && isControl(introducer)))
|
|
17
|
+
return 1;
|
|
18
|
+
if (introducer === '[' || introducer === 'O') {
|
|
19
|
+
for (let index = 2; index < value.length; index += 1) {
|
|
20
|
+
const code = value.charCodeAt(index);
|
|
21
|
+
if (code >= 0x40 && code <= 0x7e)
|
|
22
|
+
return index + 1;
|
|
23
|
+
if (code < 0x20 || code > 0x3f)
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
if (introducer === ']' || introducer === 'P' || introducer === '^' || introducer === '_') {
|
|
29
|
+
for (let index = 2; index < value.length; index += 1) {
|
|
30
|
+
if (value[index] === '\x07')
|
|
31
|
+
return index + 1;
|
|
32
|
+
if (value[index] === ESC && value[index + 1] === '\\')
|
|
33
|
+
return index + 2;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
const segment = GRAPHEME_SEGMENTER.segment(value.slice(1))[Symbol.iterator]().next();
|
|
38
|
+
return segment.done ? null : 1 + segment.value.segment.length;
|
|
39
|
+
}
|
|
40
|
+
export class KeyStreamDecoder {
|
|
41
|
+
decoder = new TextDecoder();
|
|
42
|
+
pending = '';
|
|
43
|
+
get hasPending() {
|
|
44
|
+
return this.pending.length > 0;
|
|
45
|
+
}
|
|
46
|
+
write(data) {
|
|
47
|
+
this.pending += typeof data === 'string' ? data : this.decoder.decode(data, { stream: true });
|
|
48
|
+
return this.drain(false);
|
|
49
|
+
}
|
|
50
|
+
flush() {
|
|
51
|
+
return this.drain(true);
|
|
52
|
+
}
|
|
53
|
+
reset() {
|
|
54
|
+
this.pending = '';
|
|
55
|
+
this.decoder = new TextDecoder();
|
|
56
|
+
}
|
|
57
|
+
drain(flush) {
|
|
58
|
+
const keys = [];
|
|
59
|
+
let offset = 0;
|
|
60
|
+
while (offset < this.pending.length) {
|
|
61
|
+
const value = this.pending.slice(offset);
|
|
62
|
+
const first = value[0];
|
|
63
|
+
if (first === undefined)
|
|
64
|
+
break;
|
|
65
|
+
if (first === ESC) {
|
|
66
|
+
const length = escapeSequenceLength(value);
|
|
67
|
+
if (length === null) {
|
|
68
|
+
if (flush) {
|
|
69
|
+
keys.push(value);
|
|
70
|
+
offset = this.pending.length;
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
keys.push(value.slice(0, length));
|
|
75
|
+
offset += length;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (isControl(first)) {
|
|
79
|
+
keys.push(first);
|
|
80
|
+
offset += 1;
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
let end = offset;
|
|
84
|
+
while (end < this.pending.length) {
|
|
85
|
+
const char = this.pending[end];
|
|
86
|
+
if (char === undefined || char === ESC || isControl(char))
|
|
87
|
+
break;
|
|
88
|
+
end += char.length;
|
|
89
|
+
}
|
|
90
|
+
const run = this.pending.slice(offset, end);
|
|
91
|
+
const segments = Array.from(GRAPHEME_SEGMENTER.segment(run), ({ segment }) => segment);
|
|
92
|
+
for (const segment of segments) {
|
|
93
|
+
keys.push(segment);
|
|
94
|
+
offset += segment.length;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
this.pending = this.pending.slice(offset);
|
|
98
|
+
return keys;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
1
101
|
function switchResult(target) {
|
|
2
102
|
return target === undefined ? { handled: true } : { switchTo: target, handled: true };
|
|
3
103
|
}
|
|
@@ -17,8 +117,16 @@ export function routeGlobalKey(key, viewIds, current) {
|
|
|
17
117
|
}
|
|
18
118
|
if (key === '\x1b[5~')
|
|
19
119
|
return { scrollBy: -1, handled: true };
|
|
20
|
-
if (key === '\x1b[6~')
|
|
120
|
+
if (key === '\x1b[6~' || key === ' ')
|
|
21
121
|
return { scrollBy: 1, handled: true };
|
|
122
|
+
if (key === '\x1b[A')
|
|
123
|
+
return { scrollLines: -1, handled: true };
|
|
124
|
+
if (key === '\x1b[B')
|
|
125
|
+
return { scrollLines: 1, handled: true };
|
|
126
|
+
if (key === '\x1b[H')
|
|
127
|
+
return { scrollTo: 'top', handled: true };
|
|
128
|
+
if (key === '\x1b[F')
|
|
129
|
+
return { scrollTo: 'end', handled: true };
|
|
22
130
|
if (/^[1-9]$/.test(key)) {
|
|
23
131
|
const idx = Number(key) - 1;
|
|
24
132
|
if (idx < viewIds.length)
|
|
@@ -2,6 +2,7 @@ import { type, space } from '../../core/theme.js';
|
|
|
2
2
|
import { t } from '../../i18n/index.js';
|
|
3
3
|
import { renderListFieldWithContext } from '../fields/list-field.js';
|
|
4
4
|
import { visualWidth, wrapAnsiToVisualWidth, wrapAnsiWithIndent } from '../../core/text.js';
|
|
5
|
+
import { loadingLines } from '../../core/components/spinner.js';
|
|
5
6
|
function hintLines(label, cols) {
|
|
6
7
|
return wrapAnsiWithIndent(type.hint(label), cols, space.indent);
|
|
7
8
|
}
|
|
@@ -36,7 +37,7 @@ export function renderDocs(state, cols = 80, bodyRows = Number.POSITIVE_INFINITY
|
|
|
36
37
|
let lines;
|
|
37
38
|
switch (state.mode) {
|
|
38
39
|
case 'loading':
|
|
39
|
-
lines =
|
|
40
|
+
lines = loadingLines(trans.common.loading, cols);
|
|
40
41
|
break;
|
|
41
42
|
case 'sections':
|
|
42
43
|
lines = state.sectionsField?.render(bodyRows, cols) ?? [];
|
|
@@ -66,7 +67,7 @@ export function renderDocs(state, cols = 80, bodyRows = Number.POSITIVE_INFINITY
|
|
|
66
67
|
: [];
|
|
67
68
|
break;
|
|
68
69
|
case 'readerLoading':
|
|
69
|
-
lines =
|
|
70
|
+
lines = loadingLines(trans.docs.loadingFile, cols);
|
|
70
71
|
break;
|
|
71
72
|
case 'reader':
|
|
72
73
|
lines = state.readerLinksField
|