@nbtca/prompt 1.0.24 → 1.0.25
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/config/paths.js +22 -3
- package/dist/config/preferences.js +4 -8
- package/dist/core/text.js +14 -11
- package/dist/core/ui.js +16 -0
- package/dist/core/vim-keys.js +5 -1
- package/dist/features/calendar.js +18 -9
- package/dist/features/docs.js +165 -105
- package/dist/features/links.js +0 -5
- package/dist/features/status.js +9 -6
- package/dist/features/update.js +4 -4
- package/dist/i18n/index.js +15 -7
- package/dist/index.js +13 -22
- package/dist/main.js +2 -12
- package/package.json +10 -11
package/dist/config/paths.js
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
function getXdgConfigDir() {
|
|
5
|
+
const xdgHome = process.env['XDG_CONFIG_HOME'] || join(homedir(), '.config');
|
|
6
|
+
return join(xdgHome, 'nbtca');
|
|
7
|
+
}
|
|
8
|
+
function getLegacyConfigDir() {
|
|
9
|
+
return join(homedir(), '.nbtca');
|
|
10
|
+
}
|
|
2
11
|
export function getConfigDir() {
|
|
3
|
-
const
|
|
4
|
-
|
|
12
|
+
const xdgDir = getXdgConfigDir();
|
|
13
|
+
if (existsSync(xdgDir))
|
|
14
|
+
return xdgDir;
|
|
15
|
+
const legacyDir = getLegacyConfigDir();
|
|
16
|
+
if (existsSync(legacyDir))
|
|
17
|
+
return legacyDir;
|
|
18
|
+
return xdgDir;
|
|
19
|
+
}
|
|
20
|
+
export function getWritableConfigDir() {
|
|
21
|
+
const dir = getXdgConfigDir();
|
|
22
|
+
mkdirSync(dir, { recursive: true });
|
|
23
|
+
return dir;
|
|
5
24
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { getConfigDir } from './paths.js';
|
|
3
|
+
import { getConfigDir, getWritableConfigDir } from './paths.js';
|
|
4
4
|
const DEFAULT_PREFERENCES = {
|
|
5
5
|
iconMode: 'auto',
|
|
6
6
|
colorMode: 'auto',
|
|
@@ -8,11 +8,8 @@ const DEFAULT_PREFERENCES = {
|
|
|
8
8
|
function getPreferencesPath() {
|
|
9
9
|
return path.join(getConfigDir(), 'preferences.json');
|
|
10
10
|
}
|
|
11
|
-
function
|
|
12
|
-
|
|
13
|
-
if (!fs.existsSync(configDir)) {
|
|
14
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
15
|
-
}
|
|
11
|
+
function getWritablePreferencesPath() {
|
|
12
|
+
return path.join(getWritableConfigDir(), 'preferences.json');
|
|
16
13
|
}
|
|
17
14
|
export function loadPreferences() {
|
|
18
15
|
try {
|
|
@@ -32,8 +29,7 @@ export function loadPreferences() {
|
|
|
32
29
|
}
|
|
33
30
|
function savePreferences(preferences) {
|
|
34
31
|
try {
|
|
35
|
-
|
|
36
|
-
fs.writeFileSync(getPreferencesPath(), JSON.stringify(preferences, null, 2));
|
|
32
|
+
fs.writeFileSync(getWritablePreferencesPath(), JSON.stringify(preferences, null, 2));
|
|
37
33
|
return true;
|
|
38
34
|
}
|
|
39
35
|
catch {
|
package/dist/core/text.js
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
/** Width of a single Unicode character: 2 for CJK/fullwidth, 1 otherwise. */
|
|
2
2
|
function charWidth(ch) {
|
|
3
3
|
const cp = ch.codePointAt(0) ?? 0;
|
|
4
|
-
return ((cp >= 0x1100 && cp <= 0x115F) ||
|
|
5
|
-
(cp >= 0x2E80 && cp <= 0x303F) ||
|
|
6
|
-
(cp >= 0x3040 && cp <= 0x33FF) ||
|
|
7
|
-
(cp >= 0x3400 && cp <= 0x4DBF) ||
|
|
8
|
-
(cp >= 0x4E00 && cp <= 0x9FFF) ||
|
|
9
|
-
(cp >= 0xAC00 && cp <= 0xD7AF) ||
|
|
10
|
-
(cp >= 0xF900 && cp <= 0xFAFF) ||
|
|
11
|
-
(cp >= 0xFE30 && cp <= 0xFE4F) ||
|
|
12
|
-
(cp >= 0xFF00 && cp <= 0xFF60) ||
|
|
13
|
-
(cp >= 0xFFE0 && cp <= 0xFFE6)
|
|
14
|
-
|
|
4
|
+
return ((cp >= 0x1100 && cp <= 0x115F) ||
|
|
5
|
+
(cp >= 0x2E80 && cp <= 0x303F) ||
|
|
6
|
+
(cp >= 0x3040 && cp <= 0x33FF) ||
|
|
7
|
+
(cp >= 0x3400 && cp <= 0x4DBF) ||
|
|
8
|
+
(cp >= 0x4E00 && cp <= 0x9FFF) ||
|
|
9
|
+
(cp >= 0xAC00 && cp <= 0xD7AF) ||
|
|
10
|
+
(cp >= 0xF900 && cp <= 0xFAFF) ||
|
|
11
|
+
(cp >= 0xFE30 && cp <= 0xFE4F) ||
|
|
12
|
+
(cp >= 0xFF00 && cp <= 0xFF60) ||
|
|
13
|
+
(cp >= 0xFFE0 && cp <= 0xFFE6) ||
|
|
14
|
+
(cp >= 0x20000 && cp <= 0x2A6DF) ||
|
|
15
|
+
(cp >= 0x2A700 && cp <= 0x2CEAF) ||
|
|
16
|
+
(cp >= 0x2CEB0 && cp <= 0x2EBEF) ||
|
|
17
|
+
(cp >= 0x30000 && cp <= 0x323AF)) ? 2 : 1;
|
|
15
18
|
}
|
|
16
19
|
/** Strip ANSI escape sequences from a string. */
|
|
17
20
|
// eslint-disable-next-line no-control-regex
|
package/dist/core/ui.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import { log, spinner as clackSpinner } from '@clack/prompts';
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import { pickIcon } from './icons.js';
|
|
8
|
+
import { t } from '../i18n/index.js';
|
|
8
9
|
/**
|
|
9
10
|
* Display success message
|
|
10
11
|
*/
|
|
@@ -62,3 +63,18 @@ export function createSpinner(msg) {
|
|
|
62
63
|
s.start(msg);
|
|
63
64
|
return s;
|
|
64
65
|
}
|
|
66
|
+
export function handleGracefulExit(err) {
|
|
67
|
+
const message = err instanceof Error ? err.message : String(err ?? '');
|
|
68
|
+
if (message.includes('SIGINT') || message.includes('User force closed')) {
|
|
69
|
+
console.log();
|
|
70
|
+
console.log(chalk.dim(t().common.goodbye));
|
|
71
|
+
process.exit(0);
|
|
72
|
+
}
|
|
73
|
+
if (message) {
|
|
74
|
+
console.error(message);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
console.error('Error occurred:', err);
|
|
78
|
+
}
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
package/dist/core/vim-keys.js
CHANGED
|
@@ -14,13 +14,17 @@ const VIM_TO_SEQ = {
|
|
|
14
14
|
G: Buffer.from('\u001b[F'), // end (last item)
|
|
15
15
|
q: Buffer.from('\u0003'), // quit
|
|
16
16
|
};
|
|
17
|
+
let vimActive = true;
|
|
18
|
+
export function setVimKeysActive(active) {
|
|
19
|
+
vimActive = active;
|
|
20
|
+
}
|
|
17
21
|
export function enableVimKeys() {
|
|
18
22
|
const stdin = process.stdin;
|
|
19
23
|
if (!stdin.isTTY)
|
|
20
24
|
return;
|
|
21
25
|
const originalEmit = stdin.emit.bind(stdin);
|
|
22
26
|
stdin.emit = function (event, ...args) {
|
|
23
|
-
if (event === 'data') {
|
|
27
|
+
if (event === 'data' && vimActive) {
|
|
24
28
|
const chunk = args[0];
|
|
25
29
|
if (Buffer.isBuffer(chunk) && chunk.length === 1) {
|
|
26
30
|
const seq = VIM_TO_SEQ[String.fromCharCode(chunk[0])];
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* ICS calendar module
|
|
3
3
|
* Fetches and renders upcoming events with Unicode box table.
|
|
4
4
|
*/
|
|
5
|
-
import axios from 'axios';
|
|
6
5
|
import ICAL from 'ical.js';
|
|
7
6
|
import chalk from 'chalk';
|
|
8
7
|
import { select, isCancel } from '@clack/prompts';
|
|
@@ -13,13 +12,17 @@ import { t } from '../i18n/index.js';
|
|
|
13
12
|
import { APP_INFO, URLS } from '../config/data.js';
|
|
14
13
|
export async function fetchEvents() {
|
|
15
14
|
try {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
15
|
+
const controller = new AbortController();
|
|
16
|
+
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
17
|
+
const response = await fetch('https://ical.nbtca.space', {
|
|
18
|
+
signal: controller.signal,
|
|
19
|
+
headers: { 'User-Agent': `NBTCA-CLI/${APP_INFO.version}` },
|
|
21
20
|
});
|
|
22
|
-
|
|
21
|
+
clearTimeout(timeout);
|
|
22
|
+
if (!response.ok)
|
|
23
|
+
throw new Error(`HTTP ${response.status}`);
|
|
24
|
+
const data = await response.text();
|
|
25
|
+
const jcalData = ICAL.parse(data);
|
|
23
26
|
const comp = new ICAL.Component(jcalData);
|
|
24
27
|
const vevents = comp.getAllSubcomponents('vevent');
|
|
25
28
|
const events = [];
|
|
@@ -46,7 +49,9 @@ export async function fetchEvents() {
|
|
|
46
49
|
return events;
|
|
47
50
|
}
|
|
48
51
|
catch (err) {
|
|
49
|
-
const detail = err instanceof Error
|
|
52
|
+
const detail = err instanceof Error
|
|
53
|
+
? (err.name === 'AbortError' ? 'Request timed out' : err.message)
|
|
54
|
+
: String(err);
|
|
50
55
|
throw new Error(`${t().calendar.error}: ${detail}`);
|
|
51
56
|
}
|
|
52
57
|
}
|
|
@@ -61,8 +66,12 @@ export function serializeEvents(events) {
|
|
|
61
66
|
}));
|
|
62
67
|
}
|
|
63
68
|
function formatDate(date) {
|
|
69
|
+
const now = new Date();
|
|
64
70
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
65
71
|
const day = String(date.getDate()).padStart(2, '0');
|
|
72
|
+
if (date.getFullYear() !== now.getFullYear()) {
|
|
73
|
+
return `${date.getFullYear()}-${month}-${day}`;
|
|
74
|
+
}
|
|
66
75
|
return `${month}-${day}`;
|
|
67
76
|
}
|
|
68
77
|
function formatTime(date) {
|
|
@@ -78,7 +87,7 @@ export function renderEventsTable(events, options) {
|
|
|
78
87
|
const color = options?.color !== false;
|
|
79
88
|
if (events.length === 0)
|
|
80
89
|
return trans.calendar.noEvents;
|
|
81
|
-
const dateWidth =
|
|
90
|
+
const dateWidth = 16;
|
|
82
91
|
const titleWidth = 30;
|
|
83
92
|
const locationWidth = 16;
|
|
84
93
|
const h = pickIcon('─', '-');
|
package/dist/features/docs.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* 知识库终端查看模块
|
|
3
3
|
* 获取并渲染Markdown文档
|
|
4
4
|
*/
|
|
5
|
-
import axios from 'axios';
|
|
6
5
|
import { marked } from 'marked';
|
|
7
6
|
import TerminalRenderer from 'marked-terminal';
|
|
8
7
|
import chalk from 'chalk';
|
|
@@ -12,7 +11,8 @@ import { error, warning, success, createSpinner } from '../core/ui.js';
|
|
|
12
11
|
import { pickIcon } from '../core/icons.js';
|
|
13
12
|
import { spawn, execFileSync } from 'child_process';
|
|
14
13
|
import { APP_INFO, GITHUB_REPO, URLS } from '../config/data.js';
|
|
15
|
-
import { t } from '../i18n/index.js';
|
|
14
|
+
import { t, fmt } from '../i18n/index.js';
|
|
15
|
+
import { setVimKeysActive } from '../core/vim-keys.js';
|
|
16
16
|
function detectTerminalType() {
|
|
17
17
|
const term = (process.env['TERM'] || '').toLowerCase();
|
|
18
18
|
const termProgram = (process.env['TERM_PROGRAM'] || '').toLowerCase();
|
|
@@ -55,8 +55,9 @@ function ensureMarkedConfigured() {
|
|
|
55
55
|
if (_markedConfigured)
|
|
56
56
|
return;
|
|
57
57
|
_markedConfigured = true;
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
marked.use({
|
|
59
|
+
renderer: new TerminalRenderer(getRendererOptions(getTerminalType()))
|
|
60
|
+
});
|
|
60
61
|
}
|
|
61
62
|
// ─── marked-terminal renderer ─────────────────────────────────────────────────
|
|
62
63
|
function getRendererOptions(type) {
|
|
@@ -135,6 +136,18 @@ function getAnyCacheValue(cache, key) {
|
|
|
135
136
|
function setCacheValue(cache, key, value, ttlMs) {
|
|
136
137
|
cache.set(key, { value, expiresAt: Date.now() + ttlMs });
|
|
137
138
|
}
|
|
139
|
+
const DIR_CACHE_MAX = 30;
|
|
140
|
+
const FILE_CACHE_MAX = 50;
|
|
141
|
+
const RENDER_CACHE_MAX = 50;
|
|
142
|
+
function evictStalest(cache, maxSize) {
|
|
143
|
+
if (cache.size <= maxSize)
|
|
144
|
+
return;
|
|
145
|
+
const oldest = [...cache.entries()]
|
|
146
|
+
.sort((a, b) => a[1].expiresAt - b[1].expiresAt)
|
|
147
|
+
.slice(0, cache.size - maxSize);
|
|
148
|
+
for (const [key] of oldest)
|
|
149
|
+
cache.delete(key);
|
|
150
|
+
}
|
|
138
151
|
function contentFingerprint(content) {
|
|
139
152
|
const head = content.slice(0, 80);
|
|
140
153
|
const tail = content.slice(-80);
|
|
@@ -157,12 +170,29 @@ async function fetchGitHubDirectory(path = '', options = {}) {
|
|
|
157
170
|
try {
|
|
158
171
|
const headers = {
|
|
159
172
|
'Accept': 'application/vnd.github.v3+json',
|
|
160
|
-
'User-Agent': `NBTCA-CLI/${APP_INFO.version}
|
|
173
|
+
'User-Agent': `NBTCA-CLI/${APP_INFO.version}`,
|
|
161
174
|
};
|
|
162
175
|
if (GITHUB_TOKEN)
|
|
163
176
|
headers['Authorization'] = `Bearer ${GITHUB_TOKEN}`;
|
|
164
|
-
const
|
|
165
|
-
const
|
|
177
|
+
const controller = new AbortController();
|
|
178
|
+
const timeout = setTimeout(() => controller.abort(), 10000);
|
|
179
|
+
const response = await fetch(url, { signal: controller.signal, headers });
|
|
180
|
+
clearTimeout(timeout);
|
|
181
|
+
if (!response.ok) {
|
|
182
|
+
const trans = t();
|
|
183
|
+
if (response.status === 403) {
|
|
184
|
+
const rateLimitRemaining = response.headers.get('x-ratelimit-remaining');
|
|
185
|
+
const rateLimitReset = response.headers.get('x-ratelimit-reset');
|
|
186
|
+
if (rateLimitRemaining === '0' && rateLimitReset) {
|
|
187
|
+
const resetDate = new Date(Number.parseInt(rateLimitReset, 10) * 1000);
|
|
188
|
+
throw new Error(`${fmt(trans.docs.githubRateLimited, { time: resetDate.toLocaleTimeString() })}\n${trans.docs.githubTokenHint}`);
|
|
189
|
+
}
|
|
190
|
+
throw new Error(`${trans.docs.githubForbidden}\n${trans.docs.githubTokenHint}`);
|
|
191
|
+
}
|
|
192
|
+
throw new Error(`HTTP ${response.status}`);
|
|
193
|
+
}
|
|
194
|
+
const data = (await response.json());
|
|
195
|
+
const items = data
|
|
166
196
|
.filter((item) => !item.name.startsWith('.') &&
|
|
167
197
|
!SKIP_NAMES.has(item.name) &&
|
|
168
198
|
!(item.type === 'file' && !item.name.endsWith('.md')))
|
|
@@ -180,6 +210,7 @@ async function fetchGitHubDirectory(path = '', options = {}) {
|
|
|
180
210
|
return a.name.localeCompare(b.name);
|
|
181
211
|
});
|
|
182
212
|
setCacheValue(dirCache, cacheKey, items, DIR_CACHE_TTL_MS);
|
|
213
|
+
evictStalest(dirCache, DIR_CACHE_MAX);
|
|
183
214
|
return { data: items, fromCache: false, staleFallback: false };
|
|
184
215
|
}
|
|
185
216
|
catch (err) {
|
|
@@ -188,18 +219,10 @@ async function fetchGitHubDirectory(path = '', options = {}) {
|
|
|
188
219
|
return { data: staleCached, fromCache: true, staleFallback: true };
|
|
189
220
|
}
|
|
190
221
|
const trans = t();
|
|
191
|
-
const errorMessage = err instanceof Error
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const rateLimitReset = axiosErr.response.headers?.['x-ratelimit-reset'];
|
|
196
|
-
if (rateLimitRemaining === '0' && rateLimitReset) {
|
|
197
|
-
const resetDate = new Date(Number.parseInt(rateLimitReset, 10) * 1000);
|
|
198
|
-
throw new Error(`${trans.docs.githubRateLimited.replace('{time}', resetDate.toLocaleTimeString())}\n${trans.docs.githubTokenHint}`);
|
|
199
|
-
}
|
|
200
|
-
throw new Error(`${trans.docs.githubForbidden}\n${trans.docs.githubTokenHint}`);
|
|
201
|
-
}
|
|
202
|
-
throw new Error(trans.docs.fetchDirFailed.replace('{error}', errorMessage));
|
|
222
|
+
const errorMessage = err instanceof Error
|
|
223
|
+
? (err.name === 'AbortError' ? 'Request timed out' : err.message)
|
|
224
|
+
: String(err);
|
|
225
|
+
throw new Error(fmt(trans.docs.fetchDirFailed, { error: errorMessage }));
|
|
203
226
|
}
|
|
204
227
|
}
|
|
205
228
|
async function fetchGitHubRawContent(path, options = {}) {
|
|
@@ -211,9 +234,18 @@ async function fetchGitHubRawContent(path, options = {}) {
|
|
|
211
234
|
}
|
|
212
235
|
const url = `https://raw.githubusercontent.com/${GITHUB_REPO.owner}/${GITHUB_REPO.repo}/${GITHUB_REPO.branch}/${path}`;
|
|
213
236
|
try {
|
|
214
|
-
const
|
|
215
|
-
const
|
|
237
|
+
const controller = new AbortController();
|
|
238
|
+
const timeout = setTimeout(() => controller.abort(), 15000);
|
|
239
|
+
const response = await fetch(url, {
|
|
240
|
+
signal: controller.signal,
|
|
241
|
+
headers: { 'User-Agent': `NBTCA-CLI/${APP_INFO.version}` },
|
|
242
|
+
});
|
|
243
|
+
clearTimeout(timeout);
|
|
244
|
+
if (!response.ok)
|
|
245
|
+
throw new Error(`HTTP ${response.status}`);
|
|
246
|
+
const content = await response.text();
|
|
216
247
|
setCacheValue(fileCache, path, content, FILE_CACHE_TTL_MS);
|
|
248
|
+
evictStalest(fileCache, FILE_CACHE_MAX);
|
|
217
249
|
return { data: content, fromCache: false, staleFallback: false };
|
|
218
250
|
}
|
|
219
251
|
catch (err) {
|
|
@@ -222,8 +254,10 @@ async function fetchGitHubRawContent(path, options = {}) {
|
|
|
222
254
|
return { data: staleCached, fromCache: true, staleFallback: true };
|
|
223
255
|
}
|
|
224
256
|
const trans = t();
|
|
225
|
-
const errorMessage = err instanceof Error
|
|
226
|
-
|
|
257
|
+
const errorMessage = err instanceof Error
|
|
258
|
+
? (err.name === 'AbortError' ? 'Request timed out' : err.message)
|
|
259
|
+
: String(err);
|
|
260
|
+
throw new Error(fmt(trans.docs.fetchFileFailed, { error: errorMessage }));
|
|
227
261
|
}
|
|
228
262
|
}
|
|
229
263
|
// ─── Content cleaning ─────────────────────────────────────────────────────────
|
|
@@ -361,22 +395,41 @@ async function displayWithLess(rendered, title, filePath, readTime) {
|
|
|
361
395
|
});
|
|
362
396
|
}
|
|
363
397
|
// ─── Directory browser ────────────────────────────────────────────────────────
|
|
364
|
-
async function browseDirectory(
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
398
|
+
async function browseDirectory(initialPath = '') {
|
|
399
|
+
let currentPath = initialPath;
|
|
400
|
+
while (true) {
|
|
401
|
+
const trans = t();
|
|
402
|
+
let items;
|
|
403
|
+
try {
|
|
404
|
+
const s = createSpinner(currentPath ? `${trans.docs.loadingDir}: ${currentPath}` : trans.docs.loading);
|
|
405
|
+
const result = await fetchGitHubDirectory(currentPath);
|
|
406
|
+
items = result.data;
|
|
407
|
+
s.stop(currentPath || trans.docs.chooseDoc);
|
|
408
|
+
if (result.staleFallback) {
|
|
409
|
+
warning(trans.docs.usingCachedData);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
catch (err) {
|
|
413
|
+
error(trans.docs.loadError);
|
|
414
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
415
|
+
console.log(chalk.gray(` ${trans.docs.errorHint}: ${errMsg}`));
|
|
416
|
+
setVimKeysActive(false);
|
|
417
|
+
const retry = await confirm({ message: trans.docs.retry });
|
|
418
|
+
setVimKeysActive(true);
|
|
419
|
+
if (!isCancel(retry) && retry)
|
|
420
|
+
continue;
|
|
421
|
+
return;
|
|
373
422
|
}
|
|
374
423
|
if (items.length === 0) {
|
|
375
424
|
warning(trans.docs.emptyDir);
|
|
425
|
+
if (currentPath) {
|
|
426
|
+
currentPath = currentPath.split('/').slice(0, -1).join('/');
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
376
429
|
return;
|
|
377
430
|
}
|
|
378
431
|
const options = [
|
|
379
|
-
...(
|
|
432
|
+
...(currentPath ? [{ value: '__back__', label: chalk.dim(trans.docs.upToParent) }] : []),
|
|
380
433
|
...items.map(item => ({
|
|
381
434
|
value: item.path,
|
|
382
435
|
label: item.type === 'dir'
|
|
@@ -387,93 +440,89 @@ async function browseDirectory(dirPath = '') {
|
|
|
387
440
|
{ value: '__exit__', label: chalk.dim(trans.docs.returnToMenu) },
|
|
388
441
|
];
|
|
389
442
|
const selected = await select({
|
|
390
|
-
message:
|
|
443
|
+
message: currentPath ? `${trans.docs.currentDir}: ${currentPath}` : trans.docs.chooseDoc,
|
|
391
444
|
options,
|
|
392
445
|
});
|
|
393
446
|
if (isCancel(selected) || selected === '__exit__')
|
|
394
447
|
return;
|
|
395
448
|
if (selected === '__back__') {
|
|
396
|
-
|
|
397
|
-
|
|
449
|
+
currentPath = currentPath.split('/').slice(0, -1).join('/');
|
|
450
|
+
continue;
|
|
398
451
|
}
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
else if (item?.type === 'file') {
|
|
405
|
-
await viewMarkdownFile(selected);
|
|
406
|
-
await browseDirectory(dirPath);
|
|
407
|
-
}
|
|
452
|
+
const item = items.find(i => i.path === selected);
|
|
453
|
+
if (item?.type === 'dir') {
|
|
454
|
+
currentPath = selected;
|
|
455
|
+
continue;
|
|
408
456
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
error(trans.docs.loadError);
|
|
412
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
413
|
-
console.log(chalk.gray(` ${trans.docs.errorHint}: ${errMsg}`));
|
|
414
|
-
const retry = await confirm({ message: trans.docs.retry });
|
|
415
|
-
if (!isCancel(retry) && retry) {
|
|
416
|
-
await browseDirectory(dirPath);
|
|
457
|
+
if (item?.type === 'file') {
|
|
458
|
+
await viewMarkdownFile(selected);
|
|
417
459
|
}
|
|
418
460
|
}
|
|
419
461
|
}
|
|
420
462
|
// ─── Document viewer ──────────────────────────────────────────────────────────
|
|
421
463
|
async function viewMarkdownFile(filePath) {
|
|
422
464
|
const trans = t();
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
465
|
+
while (true) {
|
|
466
|
+
try {
|
|
467
|
+
ensureMarkedConfigured();
|
|
468
|
+
const s = createSpinner(`${trans.docs.loading.replace('...', '')}: ${filePath}`);
|
|
469
|
+
const rawResult = await fetchGitHubRawContent(filePath);
|
|
470
|
+
if (rawResult.staleFallback) {
|
|
471
|
+
warning(trans.docs.usingCachedData);
|
|
472
|
+
}
|
|
473
|
+
const rawContent = rawResult.data;
|
|
474
|
+
const fingerprint = contentFingerprint(rawContent);
|
|
475
|
+
const cachedRendered = getFreshCacheValue(renderCache, filePath);
|
|
476
|
+
let renderedDoc;
|
|
477
|
+
if (cachedRendered && cachedRendered.fingerprint === fingerprint) {
|
|
478
|
+
renderedDoc = cachedRendered;
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
const cleaned = cleanMarkdownContent(rawContent, getTerminalType());
|
|
482
|
+
const title = extractDocTitle(rawContent, cleaned) || filePath.split('/').pop() || filePath;
|
|
483
|
+
const readTime = estimateReadTime(cleaned);
|
|
484
|
+
const rendered = await marked(cleaned);
|
|
485
|
+
renderedDoc = { fingerprint, cleaned, rendered, title, readTime };
|
|
486
|
+
setCacheValue(renderCache, filePath, renderedDoc, RENDER_CACHE_TTL_MS);
|
|
487
|
+
evictStalest(renderCache, RENDER_CACHE_MAX);
|
|
488
|
+
}
|
|
489
|
+
s.stop(`${chalk.bold(renderedDoc.title)} ${chalk.dim(renderedDoc.readTime)}`);
|
|
490
|
+
if (hasGlow()) {
|
|
491
|
+
await displayWithGlow(renderedDoc.cleaned);
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
await displayWithLess(renderedDoc.rendered, renderedDoc.title, filePath, renderedDoc.readTime);
|
|
495
|
+
}
|
|
496
|
+
console.log();
|
|
497
|
+
success(trans.docs.docCompleted);
|
|
498
|
+
console.log();
|
|
499
|
+
const action = await select({
|
|
500
|
+
message: trans.docs.chooseAction,
|
|
501
|
+
options: [
|
|
502
|
+
{ value: 'back', label: trans.docs.backToList },
|
|
503
|
+
{ value: 'reread', label: trans.docs.reread },
|
|
504
|
+
{ value: 'browser', label: trans.docs.openBrowser },
|
|
505
|
+
],
|
|
506
|
+
});
|
|
507
|
+
if (isCancel(action) || action === 'back')
|
|
508
|
+
return;
|
|
509
|
+
if (action === 'browser') {
|
|
510
|
+
await openDocsInBrowser(filePath);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
// action === 'reread' → continue loop
|
|
451
514
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
});
|
|
463
|
-
if (isCancel(action))
|
|
515
|
+
catch (err) {
|
|
516
|
+
error(trans.docs.loadError);
|
|
517
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
518
|
+
console.log(chalk.gray(` ${trans.docs.errorHint}: ${errMsg}`));
|
|
519
|
+
setVimKeysActive(false);
|
|
520
|
+
const openBrowser = await confirm({ message: trans.docs.openBrowserPrompt });
|
|
521
|
+
setVimKeysActive(true);
|
|
522
|
+
if (!isCancel(openBrowser) && openBrowser) {
|
|
523
|
+
await openDocsInBrowser(filePath);
|
|
524
|
+
}
|
|
464
525
|
return;
|
|
465
|
-
if (action === 'browser')
|
|
466
|
-
await openDocsInBrowser(filePath);
|
|
467
|
-
if (action === 'reread')
|
|
468
|
-
await viewMarkdownFile(filePath);
|
|
469
|
-
}
|
|
470
|
-
catch (err) {
|
|
471
|
-
error(trans.docs.loadError);
|
|
472
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
473
|
-
console.log(chalk.gray(` ${trans.docs.errorHint}: ${errMsg}`));
|
|
474
|
-
const openBrowser = await confirm({ message: trans.docs.openBrowserPrompt });
|
|
475
|
-
if (!isCancel(openBrowser) && openBrowser) {
|
|
476
|
-
await openDocsInBrowser(filePath);
|
|
477
526
|
}
|
|
478
527
|
}
|
|
479
528
|
}
|
|
@@ -497,10 +546,12 @@ export async function openDocsInBrowser(path) {
|
|
|
497
546
|
// ─── Search ────────────────────────────────────────────────────────────────────
|
|
498
547
|
async function searchDocs() {
|
|
499
548
|
const trans = t();
|
|
549
|
+
setVimKeysActive(false);
|
|
500
550
|
const query = await text({
|
|
501
551
|
message: trans.docs.searchPrompt,
|
|
502
552
|
placeholder: trans.docs.searchPlaceholder,
|
|
503
553
|
});
|
|
554
|
+
setVimKeysActive(true);
|
|
504
555
|
if (isCancel(query) || !query.trim())
|
|
505
556
|
return;
|
|
506
557
|
const keyword = query.trim().toLowerCase();
|
|
@@ -523,6 +574,15 @@ async function searchDocs() {
|
|
|
523
574
|
}
|
|
524
575
|
}
|
|
525
576
|
}
|
|
577
|
+
// Also search already-cached file content
|
|
578
|
+
for (const [cachedPath, entry] of fileCache) {
|
|
579
|
+
if (results.some(r => r.path === cachedPath))
|
|
580
|
+
continue;
|
|
581
|
+
if (entry.value.toLowerCase().includes(keyword)) {
|
|
582
|
+
const name = cachedPath.split('/').pop() || cachedPath;
|
|
583
|
+
results.push({ name, path: cachedPath, category: trans.docs.searchResults });
|
|
584
|
+
}
|
|
585
|
+
}
|
|
526
586
|
s.stop(`${results.length} ${trans.docs.searchResults}`);
|
|
527
587
|
}
|
|
528
588
|
catch {
|
package/dist/features/links.js
CHANGED
|
@@ -35,8 +35,3 @@ export async function showLinksMenu() {
|
|
|
35
35
|
return;
|
|
36
36
|
await openUrl(selected);
|
|
37
37
|
}
|
|
38
|
-
/** Direct openers for CLI non-interactive mode */
|
|
39
|
-
export async function openHomepage() { await openUrl(URLS.homepage); }
|
|
40
|
-
export async function openGithub() { await openUrl(URLS.github); }
|
|
41
|
-
export async function openRoadmap() { await openUrl(URLS.roadmap); }
|
|
42
|
-
export async function openRepairService() { await openUrl(URLS.repair); }
|
package/dist/features/status.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
1
|
import chalk from 'chalk';
|
|
3
2
|
import { APP_INFO, URLS } from '../config/data.js';
|
|
4
3
|
import { pickIcon } from '../core/icons.js';
|
|
@@ -18,19 +17,23 @@ function getServiceTargets() {
|
|
|
18
17
|
async function checkService(name, url, timeoutMs) {
|
|
19
18
|
const start = Date.now();
|
|
20
19
|
try {
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
const controller = new AbortController();
|
|
21
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
22
|
+
const response = await fetch(url, {
|
|
23
|
+
signal: controller.signal,
|
|
24
|
+
redirect: 'follow',
|
|
25
25
|
headers: { 'User-Agent': `NBTCA-CLI/${APP_INFO.version}` },
|
|
26
26
|
});
|
|
27
|
+
clearTimeout(timeout);
|
|
27
28
|
const latencyMs = Date.now() - start;
|
|
28
29
|
const ok = response.status >= 200 && response.status < 400;
|
|
29
30
|
return { name, url, ok, statusCode: response.status, latencyMs };
|
|
30
31
|
}
|
|
31
32
|
catch (err) {
|
|
32
33
|
const latencyMs = Date.now() - start;
|
|
33
|
-
const error = err instanceof Error
|
|
34
|
+
const error = err instanceof Error
|
|
35
|
+
? (err.name === 'AbortError' ? 'Request timed out' : err.message)
|
|
36
|
+
: String(err);
|
|
34
37
|
return { name, url, ok: false, latencyMs, error };
|
|
35
38
|
}
|
|
36
39
|
}
|
package/dist/features/update.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import { APP_INFO } from '../config/data.js';
|
|
7
|
-
import { t } from '../i18n/index.js';
|
|
7
|
+
import { t, fmt } from '../i18n/index.js';
|
|
8
8
|
const NPM_REGISTRY_URL = `https://registry.npmjs.org/@nbtca/prompt/latest`;
|
|
9
9
|
/**
|
|
10
10
|
* Fetch latest version from npm registry.
|
|
@@ -52,7 +52,7 @@ export async function checkForUpdate() {
|
|
|
52
52
|
if (!latest || !isNewer(APP_INFO.version, latest))
|
|
53
53
|
return null;
|
|
54
54
|
const trans = t();
|
|
55
|
-
return `${trans.update.available
|
|
55
|
+
return `${fmt(trans.update.available, { latest, current: APP_INFO.version })} ${chalk.dim(trans.update.command)}`;
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
58
|
* Explicit update check command (nbtca update).
|
|
@@ -65,10 +65,10 @@ export async function runUpdateCheck() {
|
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
67
67
|
if (isNewer(APP_INFO.version, latest)) {
|
|
68
|
-
console.log(chalk.yellow(`${trans.update.available
|
|
68
|
+
console.log(chalk.yellow(`${fmt(trans.update.available, { latest, current: APP_INFO.version })}`));
|
|
69
69
|
console.log(chalk.dim(trans.update.command));
|
|
70
70
|
}
|
|
71
71
|
else {
|
|
72
|
-
console.log(chalk.green(`${trans.update.upToDate
|
|
72
|
+
console.log(chalk.green(`${fmt(trans.update.upToDate, { version: APP_INFO.version })}`));
|
|
73
73
|
}
|
|
74
74
|
}
|
package/dist/i18n/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import fs from 'fs';
|
|
|
6
6
|
import path from 'path';
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
8
|
import { dirname } from 'path';
|
|
9
|
-
import { getConfigDir } from '../config/paths.js';
|
|
9
|
+
import { getConfigDir, getWritableConfigDir } from '../config/paths.js';
|
|
10
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
11
|
const __dirname = dirname(__filename);
|
|
12
12
|
/**
|
|
@@ -14,11 +14,17 @@ const __dirname = dirname(__filename);
|
|
|
14
14
|
*/
|
|
15
15
|
let currentLanguage = 'zh'; // Default to Chinese
|
|
16
16
|
/**
|
|
17
|
-
* Get language configuration file path
|
|
17
|
+
* Get language configuration file path (read, with legacy fallback)
|
|
18
18
|
*/
|
|
19
19
|
function getLanguageConfigPath() {
|
|
20
20
|
return path.join(getConfigDir(), 'language.json');
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Get writable language configuration file path (XDG, creates dir)
|
|
24
|
+
*/
|
|
25
|
+
function getWritableLanguageConfigPath() {
|
|
26
|
+
return path.join(getWritableConfigDir(), 'language.json');
|
|
27
|
+
}
|
|
22
28
|
/**
|
|
23
29
|
* Load language preference from config file
|
|
24
30
|
*/
|
|
@@ -40,11 +46,7 @@ export function loadLanguagePreference() {
|
|
|
40
46
|
*/
|
|
41
47
|
export function saveLanguagePreference(language) {
|
|
42
48
|
try {
|
|
43
|
-
const
|
|
44
|
-
if (!fs.existsSync(configDir)) {
|
|
45
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
46
|
-
}
|
|
47
|
-
const configPath = getLanguageConfigPath();
|
|
49
|
+
const configPath = getWritableLanguageConfigPath();
|
|
48
50
|
fs.writeFileSync(configPath, JSON.stringify({ language }, null, 2));
|
|
49
51
|
currentLanguage = language;
|
|
50
52
|
return true;
|
|
@@ -95,6 +97,12 @@ export function t() {
|
|
|
95
97
|
}
|
|
96
98
|
return translationsCache.get(currentLanguage);
|
|
97
99
|
}
|
|
100
|
+
export function fmt(template, vars) {
|
|
101
|
+
return template.replace(/\{(\w+)\}/g, (_, key) => {
|
|
102
|
+
const val = vars[key];
|
|
103
|
+
return val !== undefined ? String(val) : `{${key}}`;
|
|
104
|
+
});
|
|
105
|
+
}
|
|
98
106
|
/**
|
|
99
107
|
* Clear translation cache (useful when switching languages)
|
|
100
108
|
*/
|
package/dist/index.js
CHANGED
|
@@ -10,8 +10,8 @@ import { pickIcon } from './core/icons.js';
|
|
|
10
10
|
import { applyColorModePreference } from './config/preferences.js';
|
|
11
11
|
import { openDocsInBrowser } from './features/docs.js';
|
|
12
12
|
import { runThemeCommand } from './features/theme.js';
|
|
13
|
-
import { setLanguage, t } from './i18n/index.js';
|
|
14
|
-
import { clearScreen } from './core/ui.js';
|
|
13
|
+
import { setLanguage, t, fmt } from './i18n/index.js';
|
|
14
|
+
import { clearScreen, handleGracefulExit } from './core/ui.js';
|
|
15
15
|
import { APP_INFO, URLS } from './config/data.js';
|
|
16
16
|
import { runUpdateCheck } from './features/update.js';
|
|
17
17
|
const ACTION_ALIASES = {
|
|
@@ -169,8 +169,12 @@ async function runEventsCommand(flags) {
|
|
|
169
169
|
let events = await fetchEvents();
|
|
170
170
|
if (flags.has('--today')) {
|
|
171
171
|
const now = new Date();
|
|
172
|
-
|
|
173
|
-
|
|
172
|
+
events = events.filter(e => {
|
|
173
|
+
const d = e.startDate;
|
|
174
|
+
return d.getFullYear() === now.getFullYear() &&
|
|
175
|
+
d.getMonth() === now.getMonth() &&
|
|
176
|
+
d.getDate() === now.getDate();
|
|
177
|
+
});
|
|
174
178
|
}
|
|
175
179
|
const nextFlag = Array.from(flags).find(f => f.startsWith('--next='));
|
|
176
180
|
if (nextFlag) {
|
|
@@ -202,11 +206,11 @@ async function runStatusCommand(flags) {
|
|
|
202
206
|
process.exit(1);
|
|
203
207
|
}
|
|
204
208
|
if (!Number.isInteger(timeoutMs) || timeoutMs < STATUS_TIMEOUT_MIN || timeoutMs > STATUS_TIMEOUT_MAX) {
|
|
205
|
-
console.error(chalk.red(trans.status.invalidTimeout
|
|
209
|
+
console.error(chalk.red(fmt(trans.status.invalidTimeout, { min: STATUS_TIMEOUT_MIN, max: STATUS_TIMEOUT_MAX })));
|
|
206
210
|
process.exit(1);
|
|
207
211
|
}
|
|
208
212
|
if (!Number.isInteger(retries) || retries < STATUS_RETRIES_MIN || retries > STATUS_RETRIES_MAX) {
|
|
209
|
-
console.error(chalk.red(trans.status.invalidRetries
|
|
213
|
+
console.error(chalk.red(fmt(trans.status.invalidRetries, { min: STATUS_RETRIES_MIN, max: STATUS_RETRIES_MAX })));
|
|
210
214
|
process.exit(1);
|
|
211
215
|
}
|
|
212
216
|
if (watch && flags.has('--json')) {
|
|
@@ -214,7 +218,7 @@ async function runStatusCommand(flags) {
|
|
|
214
218
|
process.exit(1);
|
|
215
219
|
}
|
|
216
220
|
if (watch && (!Number.isInteger(intervalSeconds) || intervalSeconds < STATUS_WATCH_INTERVAL_MIN || intervalSeconds > STATUS_WATCH_INTERVAL_MAX)) {
|
|
217
|
-
console.error(chalk.red(trans.status.invalidInterval
|
|
221
|
+
console.error(chalk.red(fmt(trans.status.invalidInterval, { min: STATUS_WATCH_INTERVAL_MIN, max: STATUS_WATCH_INTERVAL_MAX })));
|
|
218
222
|
process.exit(1);
|
|
219
223
|
}
|
|
220
224
|
if (watch && !hasInteractiveTerminal()) {
|
|
@@ -225,7 +229,7 @@ async function runStatusCommand(flags) {
|
|
|
225
229
|
let stopped = false;
|
|
226
230
|
const onSigint = () => { stopped = true; };
|
|
227
231
|
process.once('SIGINT', onSigint);
|
|
228
|
-
console.log(chalk.dim(`${trans.status.watchStarted
|
|
232
|
+
console.log(chalk.dim(`${fmt(trans.status.watchStarted, { seconds: intervalSeconds })} | ${trans.status.watchHint}`));
|
|
229
233
|
try {
|
|
230
234
|
while (!stopped) {
|
|
231
235
|
const services = await checkServices({ timeoutMs, retries });
|
|
@@ -394,17 +398,4 @@ async function runCommandMode(argv) {
|
|
|
394
398
|
return;
|
|
395
399
|
}
|
|
396
400
|
}
|
|
397
|
-
runCommandMode(process.argv.slice(2)).catch(
|
|
398
|
-
if (err?.message?.includes('SIGINT') || err?.message?.includes('User force closed')) {
|
|
399
|
-
console.log();
|
|
400
|
-
console.log(chalk.dim(t().common.goodbye));
|
|
401
|
-
process.exit(0);
|
|
402
|
-
}
|
|
403
|
-
if (err?.message) {
|
|
404
|
-
console.error(err.message);
|
|
405
|
-
}
|
|
406
|
-
else {
|
|
407
|
-
console.error('Error occurred:', err);
|
|
408
|
-
}
|
|
409
|
-
process.exit(1);
|
|
410
|
-
});
|
|
401
|
+
runCommandMode(process.argv.slice(2)).catch(handleGracefulExit);
|
package/dist/main.js
CHANGED
|
@@ -5,11 +5,10 @@
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import { intro } from '@clack/prompts';
|
|
7
7
|
import { printLogo } from './core/logo.js';
|
|
8
|
-
import { clearScreen } from './core/ui.js';
|
|
8
|
+
import { clearScreen, handleGracefulExit } from './core/ui.js';
|
|
9
9
|
import { showMainMenu } from './core/menu.js';
|
|
10
10
|
import { APP_INFO } from './config/data.js';
|
|
11
11
|
import { enableVimKeys } from './core/vim-keys.js';
|
|
12
|
-
import { t } from './i18n/index.js';
|
|
13
12
|
import { checkForUpdate } from './features/update.js';
|
|
14
13
|
/**
|
|
15
14
|
* Main program entry point
|
|
@@ -41,15 +40,6 @@ export async function main(options = {}) {
|
|
|
41
40
|
await showMainMenu();
|
|
42
41
|
}
|
|
43
42
|
catch (err) {
|
|
44
|
-
|
|
45
|
-
if (message.includes('SIGINT') || message.includes('User force closed')) {
|
|
46
|
-
console.log();
|
|
47
|
-
console.log(chalk.dim(t().common.goodbye));
|
|
48
|
-
process.exit(0);
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
console.error('Error occurred:', message || err);
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
43
|
+
handleGracefulExit(err);
|
|
54
44
|
}
|
|
55
45
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nbtca/prompt",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.25",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dev": "tsx src/index.ts",
|
|
22
22
|
"dev:watch": "tsx watch src/index.ts",
|
|
23
23
|
"build": "tsc",
|
|
24
|
-
"postbuild": "
|
|
24
|
+
"postbuild": "node scripts/copy-assets.js",
|
|
25
25
|
"clean": "rm -rf dist",
|
|
26
26
|
"prebuild": "npm run clean",
|
|
27
27
|
"prepublishOnly": "npm run build",
|
|
@@ -38,21 +38,20 @@
|
|
|
38
38
|
"interactive"
|
|
39
39
|
],
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@clack/prompts": "^1.0
|
|
42
|
-
"
|
|
43
|
-
"chalk": "^5.4.1",
|
|
41
|
+
"@clack/prompts": "^1.2.0",
|
|
42
|
+
"chalk": "^5.6.2",
|
|
44
43
|
"gradient-string": "^3.0.0",
|
|
45
|
-
"ical.js": "^2.
|
|
46
|
-
"marked": "^
|
|
44
|
+
"ical.js": "^2.2.1",
|
|
45
|
+
"marked": "^15.0.12",
|
|
47
46
|
"marked-terminal": "^7.0.0",
|
|
48
|
-
"open": "^
|
|
47
|
+
"open": "^11.0.0"
|
|
49
48
|
},
|
|
50
49
|
"devDependencies": {
|
|
51
50
|
"@types/gradient-string": "^1.1.6",
|
|
52
51
|
"@types/marked-terminal": "^3.1.3",
|
|
53
|
-
"@types/node": "^
|
|
54
|
-
"tsx": "^4.
|
|
55
|
-
"typescript": "^5.
|
|
52
|
+
"@types/node": "^22.19.17",
|
|
53
|
+
"tsx": "^4.21.0",
|
|
54
|
+
"typescript": "^5.9.3"
|
|
56
55
|
},
|
|
57
56
|
"engines": {
|
|
58
57
|
"node": ">=20.12.0"
|