@nbtca/prompt 1.0.23 → 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/data.js +5 -1
- package/dist/config/paths.js +24 -0
- package/dist/config/preferences.js +4 -15
- package/dist/core/icons.js +18 -5
- package/dist/core/logo.js +1 -2
- package/dist/core/menu.js +7 -122
- package/dist/core/text.js +23 -14
- package/dist/core/ui.js +16 -1
- package/dist/core/vim-keys.js +11 -6
- package/dist/features/calendar.js +66 -16
- package/dist/features/docs.js +270 -131
- package/dist/features/links.js +37 -0
- package/dist/features/settings.js +120 -0
- package/dist/features/status.js +28 -32
- package/dist/features/theme.js +9 -101
- package/dist/features/update.js +74 -0
- package/dist/i18n/index.js +20 -21
- package/dist/i18n/locales/en.json +43 -54
- package/dist/i18n/locales/zh.json +43 -54
- package/dist/index.js +115 -84
- package/dist/main.js +13 -14
- package/package.json +13 -11
- package/dist/config/data.d.ts +0 -23
- package/dist/config/data.d.ts.map +0 -1
- package/dist/config/data.js.map +0 -1
- package/dist/config/preferences.d.ts +0 -14
- package/dist/config/preferences.d.ts.map +0 -1
- package/dist/config/preferences.js.map +0 -1
- package/dist/config/theme.d.ts +0 -23
- package/dist/config/theme.d.ts.map +0 -1
- package/dist/config/theme.js +0 -25
- package/dist/config/theme.js.map +0 -1
- package/dist/core/icons.d.ts +0 -3
- package/dist/core/icons.d.ts.map +0 -1
- package/dist/core/icons.js.map +0 -1
- package/dist/core/logo.d.ts +0 -9
- package/dist/core/logo.d.ts.map +0 -1
- package/dist/core/logo.js.map +0 -1
- package/dist/core/menu.d.ts +0 -14
- package/dist/core/menu.d.ts.map +0 -1
- package/dist/core/menu.js.map +0 -1
- package/dist/core/text.d.ts +0 -7
- package/dist/core/text.d.ts.map +0 -1
- package/dist/core/text.js.map +0 -1
- package/dist/core/ui.d.ts +0 -38
- package/dist/core/ui.d.ts.map +0 -1
- package/dist/core/ui.js.map +0 -1
- package/dist/core/vim-keys.d.ts +0 -8
- package/dist/core/vim-keys.d.ts.map +0 -1
- package/dist/core/vim-keys.js.map +0 -1
- package/dist/features/calendar.d.ts +0 -29
- package/dist/features/calendar.d.ts.map +0 -1
- package/dist/features/calendar.js.map +0 -1
- package/dist/features/docs.d.ts +0 -8
- package/dist/features/docs.d.ts.map +0 -1
- package/dist/features/docs.js.map +0 -1
- package/dist/features/repair.d.ts +0 -10
- package/dist/features/repair.d.ts.map +0 -1
- package/dist/features/repair.js +0 -29
- package/dist/features/repair.js.map +0 -1
- package/dist/features/status.d.ts +0 -31
- package/dist/features/status.d.ts.map +0 -1
- package/dist/features/status.js.map +0 -1
- package/dist/features/theme.d.ts +0 -8
- package/dist/features/theme.d.ts.map +0 -1
- package/dist/features/theme.js.map +0 -1
- package/dist/features/website.d.ts +0 -30
- package/dist/features/website.d.ts.map +0 -1
- package/dist/features/website.js +0 -48
- package/dist/features/website.js.map +0 -1
- package/dist/i18n/index.d.ts +0 -209
- package/dist/i18n/index.d.ts.map +0 -1
- package/dist/i18n/index.js.map +0 -1
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/main.d.ts +0 -12
- package/dist/main.d.ts.map +0 -1
- package/dist/main.js.map +0 -1
- package/dist/types.d.ts +0 -48
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -6
- package/dist/types.js.map +0 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified settings — language, theme, about
|
|
3
|
+
*/
|
|
4
|
+
import { select, isCancel, note } from '@clack/prompts';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { applyColorModePreference, loadPreferences, resetPreferences, setColorMode, setIconMode, } from '../config/preferences.js';
|
|
7
|
+
import { pickIcon } from '../core/icons.js';
|
|
8
|
+
import { resetIconCache } from '../core/icons.js';
|
|
9
|
+
import { padEndV } from '../core/text.js';
|
|
10
|
+
import { success, warning } from '../core/ui.js';
|
|
11
|
+
import { APP_INFO, URLS } from '../config/data.js';
|
|
12
|
+
import { t, getCurrentLanguage, setLanguage, clearTranslationCache } from '../i18n/index.js';
|
|
13
|
+
function notifyResult(saved, successMsg, warningMsg) {
|
|
14
|
+
if (saved) {
|
|
15
|
+
success(successMsg);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
warning(warningMsg);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function showAbout() {
|
|
22
|
+
const trans = t();
|
|
23
|
+
const pad = 12;
|
|
24
|
+
const row = (label, value) => `${chalk.dim(padEndV(label, pad))}${value}`;
|
|
25
|
+
const link = (label, url) => row(label, chalk.cyan(url));
|
|
26
|
+
const content = [
|
|
27
|
+
row(trans.about.project, APP_INFO.name),
|
|
28
|
+
row(trans.about.version, `v${APP_INFO.version}`),
|
|
29
|
+
row(trans.about.description, APP_INFO.fullDescription),
|
|
30
|
+
'',
|
|
31
|
+
link(trans.about.github, APP_INFO.repository),
|
|
32
|
+
link(trans.about.website, URLS.homepage),
|
|
33
|
+
link(trans.about.email, URLS.email),
|
|
34
|
+
'',
|
|
35
|
+
row(trans.about.license, `MIT ${pickIcon('·', '|')} ${trans.about.author}: m1ngsama`),
|
|
36
|
+
].join('\n');
|
|
37
|
+
note(content, trans.about.title);
|
|
38
|
+
}
|
|
39
|
+
export async function showSettingsMenu() {
|
|
40
|
+
while (true) {
|
|
41
|
+
const trans = t();
|
|
42
|
+
const prefs = loadPreferences();
|
|
43
|
+
const currentLang = getCurrentLanguage();
|
|
44
|
+
const action = await select({
|
|
45
|
+
message: trans.theme.chooseAction,
|
|
46
|
+
options: [
|
|
47
|
+
{ value: 'language', label: trans.language.selectLanguage.replace(':', ''), hint: currentLang === 'zh' ? trans.language.zh : trans.language.en },
|
|
48
|
+
{ value: 'icon', label: trans.theme.iconMode, hint: prefs.iconMode },
|
|
49
|
+
{ value: 'color', label: trans.theme.colorMode, hint: prefs.colorMode },
|
|
50
|
+
{ value: 'reset', label: trans.theme.reset },
|
|
51
|
+
{ value: 'about', label: trans.about.title },
|
|
52
|
+
{ value: 'back', label: chalk.dim(trans.common.back) },
|
|
53
|
+
],
|
|
54
|
+
});
|
|
55
|
+
if (isCancel(action) || action === 'back')
|
|
56
|
+
return;
|
|
57
|
+
if (action === 'about') {
|
|
58
|
+
showAbout();
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (action === 'language') {
|
|
62
|
+
const language = await select({
|
|
63
|
+
message: trans.language.selectLanguage,
|
|
64
|
+
options: [
|
|
65
|
+
{ value: 'zh', label: trans.language.zh, hint: currentLang === 'zh' ? trans.common.current : undefined },
|
|
66
|
+
{ value: 'en', label: trans.language.en, hint: currentLang === 'en' ? trans.common.current : undefined },
|
|
67
|
+
],
|
|
68
|
+
initialValue: currentLang,
|
|
69
|
+
});
|
|
70
|
+
if (isCancel(language))
|
|
71
|
+
continue;
|
|
72
|
+
if (language !== currentLang) {
|
|
73
|
+
const saved = setLanguage(language);
|
|
74
|
+
clearTranslationCache();
|
|
75
|
+
notifyResult(saved, t().language.changed, t().language.changedSessionOnly);
|
|
76
|
+
}
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (action === 'icon') {
|
|
80
|
+
const mode = await select({
|
|
81
|
+
message: trans.theme.chooseIconMode,
|
|
82
|
+
options: [
|
|
83
|
+
{ value: 'auto', label: trans.theme.modeAuto, hint: prefs.iconMode === 'auto' ? trans.common.current : undefined },
|
|
84
|
+
{ value: 'ascii', label: trans.theme.modeAscii, hint: prefs.iconMode === 'ascii' ? trans.common.current : undefined },
|
|
85
|
+
{ value: 'unicode', label: trans.theme.modeUnicode, hint: prefs.iconMode === 'unicode' ? trans.common.current : undefined },
|
|
86
|
+
],
|
|
87
|
+
initialValue: prefs.iconMode,
|
|
88
|
+
});
|
|
89
|
+
if (isCancel(mode))
|
|
90
|
+
continue;
|
|
91
|
+
const saved = setIconMode(mode);
|
|
92
|
+
resetIconCache();
|
|
93
|
+
notifyResult(saved, trans.theme.updated, trans.theme.updatedSessionOnly);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (action === 'color') {
|
|
97
|
+
const mode = await select({
|
|
98
|
+
message: trans.theme.chooseColorMode,
|
|
99
|
+
options: [
|
|
100
|
+
{ value: 'auto', label: trans.theme.modeAuto, hint: prefs.colorMode === 'auto' ? trans.common.current : undefined },
|
|
101
|
+
{ value: 'on', label: trans.theme.modeOn, hint: prefs.colorMode === 'on' ? trans.common.current : undefined },
|
|
102
|
+
{ value: 'off', label: trans.theme.modeOff, hint: prefs.colorMode === 'off' ? trans.common.current : undefined },
|
|
103
|
+
],
|
|
104
|
+
initialValue: prefs.colorMode,
|
|
105
|
+
});
|
|
106
|
+
if (isCancel(mode))
|
|
107
|
+
continue;
|
|
108
|
+
const saved = setColorMode(mode);
|
|
109
|
+
applyColorModePreference(false);
|
|
110
|
+
notifyResult(saved, trans.theme.updated, trans.theme.updatedSessionOnly);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (action === 'reset') {
|
|
114
|
+
const saved = resetPreferences();
|
|
115
|
+
resetIconCache();
|
|
116
|
+
applyColorModePreference(false);
|
|
117
|
+
notifyResult(saved, trans.theme.reset, trans.theme.resetSessionOnly);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
package/dist/features/status.js
CHANGED
|
@@ -1,33 +1,39 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
1
|
import chalk from 'chalk';
|
|
3
|
-
import { URLS } from '../config/data.js';
|
|
2
|
+
import { APP_INFO, URLS } from '../config/data.js';
|
|
4
3
|
import { pickIcon } from '../core/icons.js';
|
|
5
4
|
import { padEndV } from '../core/text.js';
|
|
6
|
-
import { createSpinner
|
|
5
|
+
import { createSpinner } from '../core/ui.js';
|
|
7
6
|
import { t } from '../i18n/index.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
function getServiceTargets() {
|
|
8
|
+
const trans = t();
|
|
9
|
+
return [
|
|
10
|
+
{ name: trans.status.serviceWebsite, url: URLS.homepage },
|
|
11
|
+
{ name: trans.status.serviceDocs, url: URLS.docs },
|
|
12
|
+
{ name: trans.status.serviceCalendar, url: URLS.calendar },
|
|
13
|
+
{ name: trans.status.serviceGithub, url: URLS.github },
|
|
14
|
+
{ name: trans.status.serviceRoadmap, url: URLS.roadmap },
|
|
15
|
+
];
|
|
16
|
+
}
|
|
15
17
|
async function checkService(name, url, timeoutMs) {
|
|
16
18
|
const start = Date.now();
|
|
17
19
|
try {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
+
headers: { 'User-Agent': `NBTCA-CLI/${APP_INFO.version}` },
|
|
23
26
|
});
|
|
27
|
+
clearTimeout(timeout);
|
|
24
28
|
const latencyMs = Date.now() - start;
|
|
25
29
|
const ok = response.status >= 200 && response.status < 400;
|
|
26
30
|
return { name, url, ok, statusCode: response.status, latencyMs };
|
|
27
31
|
}
|
|
28
32
|
catch (err) {
|
|
29
33
|
const latencyMs = Date.now() - start;
|
|
30
|
-
const error = err instanceof Error
|
|
34
|
+
const error = err instanceof Error
|
|
35
|
+
? (err.name === 'AbortError' ? 'Request timed out' : err.message)
|
|
36
|
+
: String(err);
|
|
31
37
|
return { name, url, ok: false, latencyMs, error };
|
|
32
38
|
}
|
|
33
39
|
}
|
|
@@ -49,7 +55,7 @@ async function checkServiceWithRetry(name, url, timeoutMs, retries) {
|
|
|
49
55
|
export async function checkServices(options = {}) {
|
|
50
56
|
const timeoutMs = options.timeoutMs ?? 6000;
|
|
51
57
|
const retries = options.retries ?? 1;
|
|
52
|
-
return Promise.all(
|
|
58
|
+
return Promise.all(getServiceTargets().map((service) => checkServiceWithRetry(service.name, service.url, timeoutMs, retries)));
|
|
53
59
|
}
|
|
54
60
|
export function serializeServiceStatus(items) {
|
|
55
61
|
return items.map((item) => ({
|
|
@@ -87,7 +93,6 @@ export function renderServiceStatusTable(items, options) {
|
|
|
87
93
|
const trans = t();
|
|
88
94
|
const nameWidth = 10;
|
|
89
95
|
const statusWidth = 9;
|
|
90
|
-
const codeWidth = 7;
|
|
91
96
|
const latencyWidth = 10;
|
|
92
97
|
const h = pickIcon('─', '-');
|
|
93
98
|
const v = pickIcon('│', '|');
|
|
@@ -100,19 +105,17 @@ export function renderServiceStatusTable(items, options) {
|
|
|
100
105
|
const bottomLeft = pickIcon('└', '+');
|
|
101
106
|
const bottomMid = pickIcon('┴', '+');
|
|
102
107
|
const bottomRight = pickIcon('┘', '+');
|
|
103
|
-
const top = `${topLeft}${h.repeat(nameWidth + 2)}${topMid}${h.repeat(statusWidth + 2)}${topMid}${h.repeat(
|
|
104
|
-
const divider = `${midLeft}${h.repeat(nameWidth + 2)}${midMid}${h.repeat(statusWidth + 2)}${midMid}${h.repeat(
|
|
105
|
-
const bottom = `${bottomLeft}${h.repeat(nameWidth + 2)}${bottomMid}${h.repeat(statusWidth + 2)}${bottomMid}${h.repeat(
|
|
106
|
-
const header = `${v} ${padEndV(trans.status.service, nameWidth)} ${v} ${padEndV(trans.status.health, statusWidth)} ${v} ${padEndV(trans.status.
|
|
108
|
+
const top = `${topLeft}${h.repeat(nameWidth + 2)}${topMid}${h.repeat(statusWidth + 2)}${topMid}${h.repeat(latencyWidth + 2)}${topRight}`;
|
|
109
|
+
const divider = `${midLeft}${h.repeat(nameWidth + 2)}${midMid}${h.repeat(statusWidth + 2)}${midMid}${h.repeat(latencyWidth + 2)}${midRight}`;
|
|
110
|
+
const bottom = `${bottomLeft}${h.repeat(nameWidth + 2)}${bottomMid}${h.repeat(statusWidth + 2)}${bottomMid}${h.repeat(latencyWidth + 2)}${bottomRight}`;
|
|
111
|
+
const header = `${v} ${padEndV(trans.status.service, nameWidth)} ${v} ${padEndV(trans.status.health, statusWidth)} ${v} ${padEndV(trans.status.latency, latencyWidth)} ${v}`;
|
|
107
112
|
const lines = [dim(top), header, dim(divider)];
|
|
108
113
|
for (const item of items) {
|
|
109
114
|
const statusLabel = item.ok
|
|
110
115
|
? green(`${pickIcon('●', 'OK')} ${trans.status.up}`)
|
|
111
116
|
: red(`${pickIcon('●', '!!')} ${trans.status.down}`);
|
|
112
|
-
const code = item.statusCode ? String(item.statusCode) : '-';
|
|
113
117
|
const latency = item.latencyMs != null ? `${item.latencyMs}ms` : '-';
|
|
114
|
-
|
|
115
|
-
lines.push(`${v} ${padEndV(cyan(item.name), nameWidth)} ${v} ${padEndV(statusLabel, statusWidth)} ${v} ${padEndV(code, codeWidth)} ${v} ${padEndV(latency, latencyWidth)} ${v} ${padEndV(url, 34)} ${v}`);
|
|
118
|
+
lines.push(`${v} ${padEndV(cyan(item.name), nameWidth)} ${v} ${padEndV(statusLabel, statusWidth)} ${v} ${padEndV(latency, latencyWidth)} ${v}`);
|
|
116
119
|
}
|
|
117
120
|
lines.push(dim(bottom));
|
|
118
121
|
return lines.join('\n');
|
|
@@ -129,12 +132,5 @@ export async function showServiceStatus() {
|
|
|
129
132
|
spinner.stop(trans.status.summaryOk);
|
|
130
133
|
}
|
|
131
134
|
console.log(renderServiceStatusTable(items, { color: !!process.stdout.isTTY }));
|
|
132
|
-
if (hasFailures) {
|
|
133
|
-
warning(trans.status.summaryFail);
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
success(trans.status.summaryOk);
|
|
137
|
-
}
|
|
138
135
|
return items;
|
|
139
136
|
}
|
|
140
|
-
//# sourceMappingURL=status.js.map
|
package/dist/features/theme.js
CHANGED
|
@@ -1,117 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Theme CLI command handler (non-interactive)
|
|
3
|
+
*/
|
|
3
4
|
import { applyColorModePreference, loadPreferences, resetPreferences, setColorMode, setIconMode, } from '../config/preferences.js';
|
|
4
|
-
import {
|
|
5
|
-
import { success, warning } from '../core/ui.js';
|
|
5
|
+
import { resetIconCache } from '../core/icons.js';
|
|
6
6
|
import { t } from '../i18n/index.js';
|
|
7
7
|
const ICON_MODES = ['auto', 'ascii', 'unicode'];
|
|
8
8
|
const COLOR_MODES = ['auto', 'on', 'off'];
|
|
9
|
-
|
|
9
|
+
function formatThemeSummary() {
|
|
10
10
|
const trans = t();
|
|
11
11
|
const prefs = loadPreferences();
|
|
12
|
-
|
|
13
|
-
`${chalk.dim(trans.theme.iconMode + ':')} ${prefs.iconMode}`,
|
|
14
|
-
`${chalk.dim(trans.theme.colorMode + ':')} ${prefs.colorMode}`,
|
|
15
|
-
'',
|
|
16
|
-
chalk.dim('NBTCA_ICON_MODE / NBTCA_COLOR_MODE can override saved preferences'),
|
|
17
|
-
].join('\n');
|
|
18
|
-
note(content, trans.theme.current);
|
|
19
|
-
}
|
|
20
|
-
function notifyThemeChange(saved, successMessage, warningMessage) {
|
|
21
|
-
if (saved) {
|
|
22
|
-
success(successMessage);
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
warning(warningMessage);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
export async function showThemeMenu() {
|
|
29
|
-
while (true) {
|
|
30
|
-
const trans = t();
|
|
31
|
-
const prefs = loadPreferences();
|
|
32
|
-
const action = await select({
|
|
33
|
-
message: trans.theme.chooseAction,
|
|
34
|
-
options: [
|
|
35
|
-
{
|
|
36
|
-
value: 'summary',
|
|
37
|
-
label: `${pickIcon('ℹ️', '[i]')} ${trans.theme.current}`,
|
|
38
|
-
hint: `${trans.theme.iconMode}: ${prefs.iconMode} | ${trans.theme.colorMode}: ${prefs.colorMode}`,
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
value: 'icon',
|
|
42
|
-
label: `${pickIcon('🎨', '[*]')} ${trans.theme.iconMode}`,
|
|
43
|
-
hint: prefs.iconMode,
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
value: 'color',
|
|
47
|
-
label: `${pickIcon('🖌️', '[*]')} ${trans.theme.colorMode}`,
|
|
48
|
-
hint: prefs.colorMode,
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
value: 'reset',
|
|
52
|
-
label: `${pickIcon('♻️', '[r]')} ${trans.theme.reset}`,
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
value: 'back',
|
|
56
|
-
label: `${pickIcon('←', '[^]')} ${trans.theme.backToMenu}`,
|
|
57
|
-
},
|
|
58
|
-
],
|
|
59
|
-
});
|
|
60
|
-
if (isCancel(action) || action === 'back')
|
|
61
|
-
return;
|
|
62
|
-
if (action === 'summary') {
|
|
63
|
-
printThemeSummary();
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
if (action === 'icon') {
|
|
67
|
-
const mode = await select({
|
|
68
|
-
message: trans.theme.chooseIconMode,
|
|
69
|
-
options: [
|
|
70
|
-
{ value: 'auto', label: trans.theme.modeAuto, hint: prefs.iconMode === 'auto' ? `${pickIcon('✓', '*')} current` : undefined },
|
|
71
|
-
{ value: 'ascii', label: trans.theme.modeAscii, hint: prefs.iconMode === 'ascii' ? `${pickIcon('✓', '*')} current` : undefined },
|
|
72
|
-
{ value: 'unicode', label: trans.theme.modeUnicode, hint: prefs.iconMode === 'unicode' ? `${pickIcon('✓', '*')} current` : undefined },
|
|
73
|
-
],
|
|
74
|
-
initialValue: prefs.iconMode,
|
|
75
|
-
});
|
|
76
|
-
if (isCancel(mode))
|
|
77
|
-
continue;
|
|
78
|
-
const saved = setIconMode(mode);
|
|
79
|
-
notifyThemeChange(saved, trans.theme.updated, trans.theme.updatedSessionOnly);
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
if (action === 'color') {
|
|
83
|
-
const mode = await select({
|
|
84
|
-
message: trans.theme.chooseColorMode,
|
|
85
|
-
options: [
|
|
86
|
-
{ value: 'auto', label: trans.theme.modeAuto, hint: prefs.colorMode === 'auto' ? `${pickIcon('✓', '*')} current` : undefined },
|
|
87
|
-
{ value: 'on', label: trans.theme.modeOn, hint: prefs.colorMode === 'on' ? `${pickIcon('✓', '*')} current` : undefined },
|
|
88
|
-
{ value: 'off', label: trans.theme.modeOff, hint: prefs.colorMode === 'off' ? `${pickIcon('✓', '*')} current` : undefined },
|
|
89
|
-
],
|
|
90
|
-
initialValue: prefs.colorMode,
|
|
91
|
-
});
|
|
92
|
-
if (isCancel(mode))
|
|
93
|
-
continue;
|
|
94
|
-
const saved = setColorMode(mode);
|
|
95
|
-
applyColorModePreference(false);
|
|
96
|
-
notifyThemeChange(saved, trans.theme.updated, trans.theme.updatedSessionOnly);
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
if (action === 'reset') {
|
|
100
|
-
const saved = resetPreferences();
|
|
101
|
-
applyColorModePreference(false);
|
|
102
|
-
notifyThemeChange(saved, trans.theme.reset, trans.theme.resetSessionOnly);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
12
|
+
return `${trans.theme.iconMode}: ${prefs.iconMode}, ${trans.theme.colorMode}: ${prefs.colorMode}`;
|
|
105
13
|
}
|
|
106
14
|
export function runThemeCommand(args) {
|
|
107
15
|
const trans = t();
|
|
108
16
|
const [scope, value] = args;
|
|
109
17
|
if (!scope) {
|
|
110
|
-
|
|
111
|
-
return { ok: true, message: '' };
|
|
18
|
+
return { ok: true, message: formatThemeSummary() };
|
|
112
19
|
}
|
|
113
20
|
if (scope === 'reset') {
|
|
114
21
|
const saved = resetPreferences();
|
|
22
|
+
resetIconCache();
|
|
115
23
|
applyColorModePreference(false);
|
|
116
24
|
const message = saved ? trans.theme.reset : trans.theme.resetSessionOnly;
|
|
117
25
|
return { ok: true, message };
|
|
@@ -122,6 +30,7 @@ export function runThemeCommand(args) {
|
|
|
122
30
|
return { ok: false, message: `${trans.theme.invalidValue} auto, ascii, unicode` };
|
|
123
31
|
}
|
|
124
32
|
const saved = setIconMode(mode);
|
|
33
|
+
resetIconCache();
|
|
125
34
|
return { ok: true, message: saved ? trans.theme.updated : trans.theme.updatedSessionOnly };
|
|
126
35
|
}
|
|
127
36
|
if (scope === 'color') {
|
|
@@ -135,4 +44,3 @@ export function runThemeCommand(args) {
|
|
|
135
44
|
}
|
|
136
45
|
return { ok: false, message: trans.theme.usage };
|
|
137
46
|
}
|
|
138
|
-
//# sourceMappingURL=theme.js.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version update checker
|
|
3
|
+
* Non-blocking check against npm registry for newer versions.
|
|
4
|
+
*/
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { APP_INFO } from '../config/data.js';
|
|
7
|
+
import { t, fmt } from '../i18n/index.js';
|
|
8
|
+
const NPM_REGISTRY_URL = `https://registry.npmjs.org/@nbtca/prompt/latest`;
|
|
9
|
+
/**
|
|
10
|
+
* Fetch latest version from npm registry.
|
|
11
|
+
* Returns null on any failure (network, timeout, parse).
|
|
12
|
+
*/
|
|
13
|
+
async function fetchLatestVersion() {
|
|
14
|
+
try {
|
|
15
|
+
const controller = new AbortController();
|
|
16
|
+
const timeout = setTimeout(() => controller.abort(), 3000);
|
|
17
|
+
const res = await fetch(NPM_REGISTRY_URL, {
|
|
18
|
+
signal: controller.signal,
|
|
19
|
+
headers: { 'Accept': 'application/json' },
|
|
20
|
+
});
|
|
21
|
+
clearTimeout(timeout);
|
|
22
|
+
if (!res.ok)
|
|
23
|
+
return null;
|
|
24
|
+
const data = (await res.json());
|
|
25
|
+
return data.version ?? null;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Compare semver strings. Returns true if remote > local.
|
|
33
|
+
*/
|
|
34
|
+
function isNewer(local, remote) {
|
|
35
|
+
const parse = (v) => v.split('.').map(Number);
|
|
36
|
+
const l = parse(local);
|
|
37
|
+
const r = parse(remote);
|
|
38
|
+
for (let i = 0; i < 3; i++) {
|
|
39
|
+
if ((r[i] ?? 0) > (l[i] ?? 0))
|
|
40
|
+
return true;
|
|
41
|
+
if ((r[i] ?? 0) < (l[i] ?? 0))
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Non-blocking update check for TUI startup.
|
|
48
|
+
* Resolves to a notification string or null.
|
|
49
|
+
*/
|
|
50
|
+
export async function checkForUpdate() {
|
|
51
|
+
const latest = await fetchLatestVersion();
|
|
52
|
+
if (!latest || !isNewer(APP_INFO.version, latest))
|
|
53
|
+
return null;
|
|
54
|
+
const trans = t();
|
|
55
|
+
return `${fmt(trans.update.available, { latest, current: APP_INFO.version })} ${chalk.dim(trans.update.command)}`;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Explicit update check command (nbtca update).
|
|
59
|
+
*/
|
|
60
|
+
export async function runUpdateCheck() {
|
|
61
|
+
const trans = t();
|
|
62
|
+
const latest = await fetchLatestVersion();
|
|
63
|
+
if (!latest) {
|
|
64
|
+
console.log(chalk.yellow(trans.update.checkFailed));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (isNewer(APP_INFO.version, latest)) {
|
|
68
|
+
console.log(chalk.yellow(`${fmt(trans.update.available, { latest, current: APP_INFO.version })}`));
|
|
69
|
+
console.log(chalk.dim(trans.update.command));
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.log(chalk.green(`${fmt(trans.update.upToDate, { version: APP_INFO.version })}`));
|
|
73
|
+
}
|
|
74
|
+
}
|
package/dist/i18n/index.js
CHANGED
|
@@ -6,6 +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, getWritableConfigDir } from '../config/paths.js';
|
|
9
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
11
|
const __dirname = dirname(__filename);
|
|
11
12
|
/**
|
|
@@ -13,17 +14,16 @@ const __dirname = dirname(__filename);
|
|
|
13
14
|
*/
|
|
14
15
|
let currentLanguage = 'zh'; // Default to Chinese
|
|
15
16
|
/**
|
|
16
|
-
* Get configuration
|
|
17
|
+
* Get language configuration file path (read, with legacy fallback)
|
|
17
18
|
*/
|
|
18
|
-
function
|
|
19
|
-
|
|
20
|
-
return path.join(homeDir, '.nbtca');
|
|
19
|
+
function getLanguageConfigPath() {
|
|
20
|
+
return path.join(getConfigDir(), 'language.json');
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
23
|
-
* Get language configuration file path
|
|
23
|
+
* Get writable language configuration file path (XDG, creates dir)
|
|
24
24
|
*/
|
|
25
|
-
function
|
|
26
|
-
return path.join(
|
|
25
|
+
function getWritableLanguageConfigPath() {
|
|
26
|
+
return path.join(getWritableConfigDir(), 'language.json');
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
29
|
* Load language preference from config file
|
|
@@ -31,15 +31,13 @@ function getLanguageConfigPath() {
|
|
|
31
31
|
export function loadLanguagePreference() {
|
|
32
32
|
try {
|
|
33
33
|
const configPath = getLanguageConfigPath();
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
currentLanguage = config.language;
|
|
38
|
-
}
|
|
34
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
35
|
+
if (config.language === 'zh' || config.language === 'en') {
|
|
36
|
+
currentLanguage = config.language;
|
|
39
37
|
}
|
|
40
38
|
}
|
|
41
|
-
catch
|
|
42
|
-
// If loading fails, use default (Chinese)
|
|
39
|
+
catch {
|
|
40
|
+
// If loading fails (file missing or invalid), use default (Chinese)
|
|
43
41
|
}
|
|
44
42
|
return currentLanguage;
|
|
45
43
|
}
|
|
@@ -48,16 +46,12 @@ export function loadLanguagePreference() {
|
|
|
48
46
|
*/
|
|
49
47
|
export function saveLanguagePreference(language) {
|
|
50
48
|
try {
|
|
51
|
-
const
|
|
52
|
-
if (!fs.existsSync(configDir)) {
|
|
53
|
-
fs.mkdirSync(configDir, { recursive: true });
|
|
54
|
-
}
|
|
55
|
-
const configPath = getLanguageConfigPath();
|
|
49
|
+
const configPath = getWritableLanguageConfigPath();
|
|
56
50
|
fs.writeFileSync(configPath, JSON.stringify({ language }, null, 2));
|
|
57
51
|
currentLanguage = language;
|
|
58
52
|
return true;
|
|
59
53
|
}
|
|
60
|
-
catch
|
|
54
|
+
catch {
|
|
61
55
|
return false;
|
|
62
56
|
}
|
|
63
57
|
}
|
|
@@ -103,6 +97,12 @@ export function t() {
|
|
|
103
97
|
}
|
|
104
98
|
return translationsCache.get(currentLanguage);
|
|
105
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
|
+
}
|
|
106
106
|
/**
|
|
107
107
|
* Clear translation cache (useful when switching languages)
|
|
108
108
|
*/
|
|
@@ -111,4 +111,3 @@ export function clearTranslationCache() {
|
|
|
111
111
|
}
|
|
112
112
|
// Initialize language preference on module load
|
|
113
113
|
loadLanguagePreference();
|
|
114
|
-
//# sourceMappingURL=index.js.map
|