@nbtca/prompt 1.5.4 → 1.5.6
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 +1 -1
- package/dist/app/chrome.js +25 -3
- package/dist/app/views/docs-render.js +2 -2
- package/dist/app/views/docs.js +32 -0
- package/dist/core/text.js +49 -2
- package/package.json +1 -1
package/dist/app/app.js
CHANGED
|
@@ -76,7 +76,7 @@ export async function runApp() {
|
|
|
76
76
|
const active = nativeViews[view];
|
|
77
77
|
const tabs = getAppTabs();
|
|
78
78
|
const chrome = resolveChromeLayout(rows);
|
|
79
|
-
const header = renderHeader(tabs, view, cols, chrome.headerLines);
|
|
79
|
+
const header = renderHeader(tabs, view, cols, chrome.headerLines, active?.contextPath?.());
|
|
80
80
|
const body = active?.render(ctx) ?? [];
|
|
81
81
|
const bodyScroll = active?.capturesInput?.() ? Number.MAX_SAFE_INTEGER : scroll;
|
|
82
82
|
const height = computeBodyRows(rows, chrome.headerLines, chrome.footerLines);
|
package/dist/app/chrome.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type, space, glyph, brandMark } from '../core/theme.js';
|
|
2
2
|
import { pickIcon } from '../core/icons.js';
|
|
3
3
|
import { t } from '../i18n/index.js';
|
|
4
|
-
import { visualWidth } from '../core/text.js';
|
|
4
|
+
import { clipAnsiToVisualWidth, visualWidth } from '../core/text.js';
|
|
5
5
|
export const HEADER_LINES = 3;
|
|
6
6
|
export const FOOTER_LINES = 2;
|
|
7
7
|
export function resolveChromeLayout(rows) {
|
|
@@ -58,11 +58,33 @@ function renderTabs(views, active, cols) {
|
|
|
58
58
|
return activeNumber;
|
|
59
59
|
return type.active(String(activeIndex + 1));
|
|
60
60
|
}
|
|
61
|
-
export function
|
|
61
|
+
export function renderContextPath(segments, cols) {
|
|
62
|
+
const chevron = pickIcon('›', '>');
|
|
63
|
+
const ellipsis = pickIcon('…', '...');
|
|
64
|
+
const width = Number.isFinite(cols) ? Math.max(1, Math.floor(cols)) : Number.POSITIVE_INFINITY;
|
|
65
|
+
for (let start = 0; start < segments.length; start += 1) {
|
|
66
|
+
const shown = segments.slice(start);
|
|
67
|
+
const last = shown.length - 1;
|
|
68
|
+
const plain = [...(start > 0 ? [ellipsis] : []), ...shown].join(` ${chevron} `);
|
|
69
|
+
if (visualWidth(space.indent + plain) > width)
|
|
70
|
+
continue;
|
|
71
|
+
const styled = shown
|
|
72
|
+
.map((segment, index) => (index === last ? type.label(segment) : type.hint(segment)))
|
|
73
|
+
.join(type.hint(` ${chevron} `));
|
|
74
|
+
return space.indent + (start > 0 ? type.hint(`${ellipsis} ${chevron} `) : '') + styled;
|
|
75
|
+
}
|
|
76
|
+
const leaf = segments[segments.length - 1] ?? '';
|
|
77
|
+
const indent = visualWidth(space.indent) < width ? space.indent : '';
|
|
78
|
+
const room = Math.max(1, width - visualWidth(indent));
|
|
79
|
+
return indent + type.label(clipAnsiToVisualWidth(leaf, room));
|
|
80
|
+
}
|
|
81
|
+
export function renderHeader(views, active, cols, lineCount = HEADER_LINES, contextPath) {
|
|
62
82
|
if (lineCount === 0)
|
|
63
83
|
return [];
|
|
64
84
|
const mark = brandMark('nbtca');
|
|
65
|
-
const brand =
|
|
85
|
+
const brand = contextPath && contextPath.length > 0
|
|
86
|
+
? renderContextPath(contextPath, cols)
|
|
87
|
+
: `${visualWidth(space.indent + mark) <= cols ? space.indent : ''}${mark}`;
|
|
66
88
|
const tabs = renderTabs(views, active, cols);
|
|
67
89
|
const rule = renderRule(cols);
|
|
68
90
|
if (lineCount === 1)
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
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
|
-
import { visualWidth,
|
|
4
|
+
import { visualWidth, wrapAnsiHanging, wrapAnsiWithIndent } from '../../core/text.js';
|
|
5
5
|
import { loadingLines } from '../../core/components/spinner.js';
|
|
6
6
|
function hintLines(label, cols) {
|
|
7
7
|
return wrapAnsiWithIndent(type.hint(label), cols, space.indent);
|
|
8
8
|
}
|
|
9
9
|
function renderReader(lines, cols) {
|
|
10
10
|
const contentWidth = Math.max(1, Math.min(80, cols - visualWidth(space.indent)));
|
|
11
|
-
return lines.flatMap((line) =>
|
|
11
|
+
return lines.flatMap((line) => wrapAnsiHanging(line, contentWidth).map((part) => `${space.indent}${part}`));
|
|
12
12
|
}
|
|
13
13
|
function listFieldForState(state) {
|
|
14
14
|
switch (state.mode) {
|
package/dist/app/views/docs.js
CHANGED
|
@@ -29,6 +29,9 @@ const DOC_HINT_WIDTH = 44;
|
|
|
29
29
|
function isLifecycleActive(ctx, generation) {
|
|
30
30
|
return generation === lifecycleGeneration && ctx.signal?.aborted !== true;
|
|
31
31
|
}
|
|
32
|
+
function dedupeAdjacent(segments) {
|
|
33
|
+
return segments.filter((segment, index) => segment !== segments[index - 1]);
|
|
34
|
+
}
|
|
32
35
|
function backLabel() {
|
|
33
36
|
return t().common.back;
|
|
34
37
|
}
|
|
@@ -419,6 +422,35 @@ export const docsView = {
|
|
|
419
422
|
capturesInput() {
|
|
420
423
|
return state.mode === 'search';
|
|
421
424
|
},
|
|
425
|
+
contextPath() {
|
|
426
|
+
const trans = t();
|
|
427
|
+
const section = sections.find((candidate) => candidate.key === currentSectionKey);
|
|
428
|
+
const branch = currentArchivedGroupKey !== null
|
|
429
|
+
? [trans.docs.categoryArchived, currentArchivedGroupKey]
|
|
430
|
+
: section
|
|
431
|
+
? [section.label]
|
|
432
|
+
: [];
|
|
433
|
+
switch (state.mode) {
|
|
434
|
+
case 'reader':
|
|
435
|
+
case 'readerLoading':
|
|
436
|
+
return dedupeAdjacent([
|
|
437
|
+
trans.menu.docs,
|
|
438
|
+
...branch,
|
|
439
|
+
...(state.readerTitle ? [state.readerTitle] : []),
|
|
440
|
+
]);
|
|
441
|
+
case 'files':
|
|
442
|
+
case 'archivedFiles':
|
|
443
|
+
case 'archivedGroups':
|
|
444
|
+
return [trans.menu.docs, ...branch];
|
|
445
|
+
case 'sections':
|
|
446
|
+
case 'search':
|
|
447
|
+
case 'searchLoading':
|
|
448
|
+
case 'searchResults':
|
|
449
|
+
case 'loading':
|
|
450
|
+
case 'error':
|
|
451
|
+
return undefined;
|
|
452
|
+
}
|
|
453
|
+
},
|
|
422
454
|
scrollsBody() {
|
|
423
455
|
return state.mode === 'reader' && state.readerLinksField === undefined;
|
|
424
456
|
},
|
package/dist/core/text.js
CHANGED
|
@@ -212,8 +212,37 @@ function renderWrappedSegment(tokens, start, end, activeSgr, activeOsc8) {
|
|
|
212
212
|
const osc8AtEnd = advanceOsc8(activeOsc8, tokens, start, end);
|
|
213
213
|
return osc8AtEnd ? `${withReset}${OSC8_CLOSE}` : withReset;
|
|
214
214
|
}
|
|
215
|
-
|
|
215
|
+
const NO_LINE_START = /^[。.、,;:?!)]}〉》」』】〕・ー~…‥%‰°℃,.;:?!)\]}>]$/u;
|
|
216
|
+
const NO_LINE_END = /^[([{〈《「『【〔([{<]$/u;
|
|
217
|
+
function visibleAt(tokens, index) {
|
|
218
|
+
const token = tokens[index];
|
|
219
|
+
return token && !token.sgr && token.width > 0 ? token : undefined;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Pulls a break earlier so a line never begins with closing punctuation or ends
|
|
223
|
+
* with an opening bracket. Moving the break back keeps every line inside the
|
|
224
|
+
* width, which letting the character hang past the edge would not.
|
|
225
|
+
*/
|
|
226
|
+
function applyKinsoku(tokens, start, end) {
|
|
227
|
+
let candidate = end;
|
|
228
|
+
while (candidate > start + 1) {
|
|
229
|
+
const opensNext = visibleAt(tokens, candidate);
|
|
230
|
+
const closesLine = visibleAt(tokens, candidate - 1);
|
|
231
|
+
if (opensNext && NO_LINE_START.test(opensNext.raw)) {
|
|
232
|
+
candidate -= 1;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
if (closesLine && NO_LINE_END.test(closesLine.raw)) {
|
|
236
|
+
candidate -= 1;
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
return candidate;
|
|
240
|
+
}
|
|
241
|
+
return end;
|
|
242
|
+
}
|
|
243
|
+
export function wrapAnsiToVisualWidth(str, maxWidth, continuationWidth = maxWidth) {
|
|
216
244
|
const widthLimit = Math.max(1, Math.floor(maxWidth));
|
|
245
|
+
const nextLimit = Math.max(1, Math.floor(continuationWidth));
|
|
217
246
|
if (!Number.isFinite(maxWidth) || visualWidth(str) <= widthLimit)
|
|
218
247
|
return [str];
|
|
219
248
|
const tokens = tokenizeForWrapping(str);
|
|
@@ -234,7 +263,8 @@ export function wrapAnsiToVisualWidth(str, maxWidth) {
|
|
|
234
263
|
index += 1;
|
|
235
264
|
continue;
|
|
236
265
|
}
|
|
237
|
-
|
|
266
|
+
const limit = lines.length === 0 ? widthLimit : nextLimit;
|
|
267
|
+
if (width + token.width > limit && hasVisible)
|
|
238
268
|
break;
|
|
239
269
|
width += token.width;
|
|
240
270
|
hasVisible = true;
|
|
@@ -260,6 +290,11 @@ export function wrapAnsiToVisualWidth(str, maxWidth) {
|
|
|
260
290
|
end = Math.max(index, start + 1);
|
|
261
291
|
next = end;
|
|
262
292
|
}
|
|
293
|
+
if (end === next) {
|
|
294
|
+
const adjusted = applyKinsoku(tokens, start, end);
|
|
295
|
+
end = adjusted;
|
|
296
|
+
next = adjusted;
|
|
297
|
+
}
|
|
263
298
|
lines.push(renderWrappedSegment(tokens, start, end, activeSgr, activeOsc8));
|
|
264
299
|
activeSgr = advanceSgr(activeSgr, tokens, start, next);
|
|
265
300
|
activeOsc8 = advanceOsc8(activeOsc8, tokens, start, next);
|
|
@@ -267,6 +302,18 @@ export function wrapAnsiToVisualWidth(str, maxWidth) {
|
|
|
267
302
|
}
|
|
268
303
|
return lines.length > 0 ? lines : [''];
|
|
269
304
|
}
|
|
305
|
+
const LEADING_MARKER = /^(\s*)((?:[-*+•]|\d{1,3}[.)])\s+)?/u;
|
|
306
|
+
export function wrapAnsiHanging(str, maxWidth) {
|
|
307
|
+
const width = Number.isFinite(maxWidth)
|
|
308
|
+
? Math.max(1, Math.floor(maxWidth))
|
|
309
|
+
: Number.POSITIVE_INFINITY;
|
|
310
|
+
const match = LEADING_MARKER.exec(stripAnsi(str));
|
|
311
|
+
const hang = visualWidth((match?.[1] ?? '') + (match?.[2] ?? ''));
|
|
312
|
+
if (hang === 0 || hang >= width)
|
|
313
|
+
return wrapAnsiToVisualWidth(str, width);
|
|
314
|
+
const continuation = ' '.repeat(hang);
|
|
315
|
+
return wrapAnsiToVisualWidth(str, width, width - hang).map((line, index) => index === 0 ? line : continuation + line);
|
|
316
|
+
}
|
|
270
317
|
export function wrapAnsiWithIndent(str, maxWidth, preferredIndent = '') {
|
|
271
318
|
const width = Number.isFinite(maxWidth)
|
|
272
319
|
? Math.max(1, Math.floor(maxWidth))
|