@nbtca/prompt 1.5.7 → 1.5.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.
|
@@ -44,7 +44,7 @@ export class ListField {
|
|
|
44
44
|
const maxVisible = this.maxVisible;
|
|
45
45
|
if (!maxVisible || options.length <= maxVisible) {
|
|
46
46
|
return renderMenu({
|
|
47
|
-
title,
|
|
47
|
+
...(title === undefined ? {} : { title }),
|
|
48
48
|
options,
|
|
49
49
|
selectedIndex: this.index,
|
|
50
50
|
...(footer === undefined ? {} : { footer }),
|
|
@@ -52,7 +52,7 @@ export class ListField {
|
|
|
52
52
|
}
|
|
53
53
|
const visible = options.slice(this.scrollTop, this.scrollTop + maxVisible);
|
|
54
54
|
const lines = renderMenu({
|
|
55
|
-
title,
|
|
55
|
+
...(title === undefined ? {} : { title }),
|
|
56
56
|
options: visible,
|
|
57
57
|
selectedIndex: this.index - this.scrollTop,
|
|
58
58
|
}, cols).split('\n');
|
|
@@ -80,12 +80,16 @@ export class ListField {
|
|
|
80
80
|
const labelWidth = options.reduce((width, option) => Math.max(width, visualWidth(option.label)), 0);
|
|
81
81
|
const optionGroups = options.map((option, index) => renderMenuOption(option, index === this.index, labelWidth, cols));
|
|
82
82
|
const selectedLines = optionGroups[this.index] ?? [];
|
|
83
|
-
const titleValue =
|
|
84
|
-
?
|
|
85
|
-
:
|
|
83
|
+
const titleValue = !title
|
|
84
|
+
? ''
|
|
85
|
+
: options.length > 1
|
|
86
|
+
? `${type.heading(title)}${type.hint(` ${this.index + 1}/${options.length}`)}`
|
|
87
|
+
: type.heading(title);
|
|
86
88
|
const titleLines = title ? renderIndentedOutput(titleValue, cols) : [];
|
|
87
89
|
let header = [];
|
|
88
|
-
if (titleLines.length
|
|
90
|
+
if (titleLines.length === 0)
|
|
91
|
+
header = [];
|
|
92
|
+
else if (titleLines.length + 1 + selectedLines.length <= maxRows)
|
|
89
93
|
header = [...titleLines, ''];
|
|
90
94
|
else if (titleLines.length + selectedLines.length <= maxRows)
|
|
91
95
|
header = titleLines;
|
package/dist/app/views/docs.js
CHANGED
|
@@ -7,7 +7,7 @@ import { pickIcon } from '../../core/icons.js';
|
|
|
7
7
|
import { glyph } from '../../core/theme.js';
|
|
8
8
|
import { fmt, getCurrentLanguage, t } from '../../i18n/index.js';
|
|
9
9
|
import { sanitizeTerminalLine, truncate } from '../../core/text.js';
|
|
10
|
-
import { localizeDocSections, fetchSections, fetchDocMetadata, fetchSectionMetadata, searchDocuments, getArchivedGroups, displayDocTitle, loadDocForReader, openDocsInBrowser, docsUrlFromPath, clearDocsCache, } from '../../features/docs.js';
|
|
10
|
+
import { localizeDocSections, fetchSections, peekSections, fetchDocMetadata, fetchSectionMetadata, searchDocuments, getArchivedGroups, displayDocTitle, loadDocForReader, openDocsInBrowser, docsUrlFromPath, clearDocsCache, } from '../../features/docs.js';
|
|
11
11
|
let state = { mode: 'loading' };
|
|
12
12
|
let sections = [];
|
|
13
13
|
let archivedGroups = new Map();
|
|
@@ -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 sectionsSignature(value) {
|
|
33
|
+
return value.map((section) => `${section.key}:${String(section.files.length)}`).join('|');
|
|
34
|
+
}
|
|
32
35
|
function dedupeAdjacent(segments) {
|
|
33
36
|
return segments.filter((segment, index) => segment !== segments[index - 1]);
|
|
34
37
|
}
|
|
@@ -379,16 +382,27 @@ export const docsView = {
|
|
|
379
382
|
return;
|
|
380
383
|
}
|
|
381
384
|
const requestId = ++sectionsRequestId;
|
|
382
|
-
|
|
385
|
+
const cached = peekSections();
|
|
386
|
+
if (cached) {
|
|
387
|
+
sections = localizeDocSections(cached, t());
|
|
388
|
+
loadedLanguage = getCurrentLanguage();
|
|
389
|
+
goToSections();
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
state = { mode: 'loading' };
|
|
393
|
+
}
|
|
383
394
|
ctx.rerender();
|
|
384
395
|
try {
|
|
385
396
|
const nextSections = await fetchSections(ctx.signal);
|
|
386
397
|
if (!isLifecycleActive(ctx, generation) || requestId !== sectionsRequestId)
|
|
387
398
|
return;
|
|
388
|
-
|
|
399
|
+
const localized = localizeDocSections(nextSections, t());
|
|
400
|
+
const changed = sectionsSignature(localized) !== sectionsSignature(sections);
|
|
401
|
+
sections = localized;
|
|
389
402
|
loaded = true;
|
|
390
403
|
loadedLanguage = getCurrentLanguage();
|
|
391
|
-
|
|
404
|
+
if (!cached || changed || state.mode !== 'sections')
|
|
405
|
+
goToSections();
|
|
392
406
|
}
|
|
393
407
|
catch {
|
|
394
408
|
if (!isLifecycleActive(ctx, generation) || requestId !== sectionsRequestId)
|
|
@@ -92,10 +92,8 @@ function goToLoginId(errorMessage) {
|
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
94
|
function buildPublicField() {
|
|
95
|
-
const trans = t();
|
|
96
95
|
return new ListField({
|
|
97
|
-
|
|
98
|
-
options: [{ value: 'login', label: trans.timetable.publicLoginAction }],
|
|
96
|
+
options: [{ value: 'login', label: t().timetable.publicLoginAction }],
|
|
99
97
|
});
|
|
100
98
|
}
|
|
101
99
|
async function goToPublic(ctx, generation = lifecycleGeneration) {
|
|
@@ -87,8 +87,7 @@ export function renderMenuOption(option, selected, labelWidth = visualWidth(opti
|
|
|
87
87
|
}
|
|
88
88
|
export function renderMenu(state, cols = Number.POSITIVE_INFINITY) {
|
|
89
89
|
const labelWidth = state.options.reduce((width, option) => Math.max(width, visualWidth(option.label)), 0);
|
|
90
|
-
const lines = renderIndentedText(state.title, cols, type.heading);
|
|
91
|
-
lines.push('');
|
|
90
|
+
const lines = state.title ? [...renderIndentedText(state.title, cols, type.heading), ''] : [];
|
|
92
91
|
state.options.forEach((option, index) => {
|
|
93
92
|
lines.push(...renderMenuOption(option, index === state.selectedIndex, labelWidth, cols));
|
|
94
93
|
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { getStateDir, getWritableStateDir } from '../config/paths.js';
|
|
4
|
+
const INDEX_FILE = 'docs-index.json';
|
|
5
|
+
const MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
6
|
+
function isDocItem(value) {
|
|
7
|
+
const item = value;
|
|
8
|
+
return (typeof item?.name === 'string' &&
|
|
9
|
+
typeof item.path === 'string' &&
|
|
10
|
+
(item.type === 'file' || item.type === 'dir'));
|
|
11
|
+
}
|
|
12
|
+
export function saveDocsIndex(docs, dir) {
|
|
13
|
+
try {
|
|
14
|
+
fs.writeFileSync(path.join(dir ?? getWritableStateDir(), INDEX_FILE), JSON.stringify(docs), {
|
|
15
|
+
encoding: 'utf8',
|
|
16
|
+
mode: 0o600,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
/* best effort */
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function loadDocsIndex(dir, maxAgeMs = MAX_AGE_MS) {
|
|
24
|
+
try {
|
|
25
|
+
const file = path.join(dir ?? getStateDir(), INDEX_FILE);
|
|
26
|
+
if (Date.now() - fs.statSync(file).mtimeMs > maxAgeMs)
|
|
27
|
+
return null;
|
|
28
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
29
|
+
return Array.isArray(parsed) && parsed.every(isDocItem) ? parsed : null;
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
package/dist/features/docs.js
CHANGED
|
@@ -11,6 +11,7 @@ import { spawn, execFileSync } from 'child_process';
|
|
|
11
11
|
import { URLS } from '../config/data.js';
|
|
12
12
|
import { t, fmt, getCurrentLanguage } from '../i18n/index.js';
|
|
13
13
|
import { enterScreen, breadcrumb } from '../core/transitions.js';
|
|
14
|
+
import { loadDocsIndex, saveDocsIndex } from './docs-store.js';
|
|
14
15
|
import { sanitizeTerminalLine, sanitizeTerminalText, stripAnsi, truncate } from '../core/text.js';
|
|
15
16
|
import { clearDocsClients, runDocsClientOperation } from './docs-client.js';
|
|
16
17
|
import { launchBrowserUrl } from './links.js';
|
|
@@ -742,7 +743,20 @@ export async function fetchAllDocs(signal) {
|
|
|
742
743
|
return runDocsClientOperation(signal, (client) => client.listAll());
|
|
743
744
|
}
|
|
744
745
|
export async function fetchSections(signal) {
|
|
745
|
-
|
|
746
|
+
const docs = await fetchAllDocs(signal);
|
|
747
|
+
saveDocsIndex(docs);
|
|
748
|
+
return buildSections(docs);
|
|
749
|
+
}
|
|
750
|
+
export function peekSections() {
|
|
751
|
+
const docs = loadDocsIndex();
|
|
752
|
+
if (!docs)
|
|
753
|
+
return null;
|
|
754
|
+
try {
|
|
755
|
+
return buildSections(docs);
|
|
756
|
+
}
|
|
757
|
+
catch {
|
|
758
|
+
return null;
|
|
759
|
+
}
|
|
746
760
|
}
|
|
747
761
|
async function loadSections() {
|
|
748
762
|
const trans = t();
|