@nbtca/prompt 1.4.2 → 1.5.1
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/README.md +27 -58
- package/SECURITY.md +16 -45
- package/dist/app/app.js +167 -64
- package/dist/app/chrome.js +67 -50
- package/dist/app/fields/list-field.js +12 -25
- package/dist/app/fields/text-field.js +3 -8
- package/dist/app/frame.js +16 -23
- package/dist/app/keys.js +110 -2
- package/dist/app/views/docs-render.js +34 -26
- package/dist/app/views/docs.js +280 -68
- package/dist/app/views/events-render.js +21 -27
- package/dist/app/views/events.js +57 -33
- package/dist/app/views/home.js +67 -47
- package/dist/app/views/schedule-grid-cursor.js +9 -18
- package/dist/app/views/schedule-render.js +51 -74
- package/dist/app/views/schedule.js +246 -101
- package/dist/app/views/settings-render.js +8 -19
- package/dist/app/views/settings.js +92 -17
- package/dist/auth/cookie-transport.js +31 -32
- package/dist/auth/errors.js +3 -1
- package/dist/auth/nbt-auth.js +42 -25
- package/dist/auth/session-store.js +17 -9
- package/dist/cli.js +570 -0
- package/dist/config/data.js +9 -11
- package/dist/config/preferences.js +21 -7
- package/dist/core/calendar-day.js +37 -0
- package/dist/core/canvas.js +1 -0
- package/dist/core/capabilities.js +6 -3
- package/dist/core/components/confirm.js +9 -8
- package/dist/core/components/menu.js +64 -38
- package/dist/core/components/messages.js +12 -4
- package/dist/core/components/painter.js +3 -1
- package/dist/core/components/spinner.js +34 -7
- package/dist/core/components/text-input.js +24 -18
- package/dist/core/icons.js +2 -2
- package/dist/core/logo.js +23 -5
- package/dist/core/motion.js +25 -19
- package/dist/core/text.js +186 -69
- package/dist/core/theme.js +0 -28
- package/dist/core/transitions.js +2 -2
- package/dist/core/ui.js +15 -13
- package/dist/core/vim-keys.js +156 -19
- package/dist/features/about.js +23 -0
- package/dist/features/calendar-heatmap.js +16 -40
- package/dist/features/calendar-query.js +1 -2
- package/dist/features/calendar-store.js +27 -0
- package/dist/features/calendar.js +66 -190
- package/dist/features/docs-client.js +225 -0
- package/dist/features/docs.js +615 -298
- package/dist/features/links.js +44 -29
- package/dist/features/schedule-render.js +65 -101
- package/dist/features/schedule-store.js +51 -9
- package/dist/features/schedule-view.js +46 -213
- package/dist/features/status.js +117 -60
- package/dist/features/student-timetable.js +74 -97
- package/dist/features/theme.js +9 -5
- package/dist/features/timetable-sanitize.js +40 -0
- package/dist/features/update.js +12 -29
- package/dist/i18n/index.js +83 -19
- package/dist/i18n/locales/en.json +8 -3
- package/dist/i18n/locales/zh.json +8 -3
- package/dist/index.js +6 -474
- package/dist/logo/ca-dotmatrix.txt +16 -18
- package/dist/main.js +7 -48
- package/package.json +28 -18
- package/bin/nbtca-welcome.js +0 -2
- package/dist/core/components/screen.js +0 -18
- package/dist/core/menu.js +0 -68
- package/dist/features/schedule-query.js +0 -47
- package/dist/features/settings.js +0 -127
- package/dist/logo/ca-logo.png +0 -0
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"loading": "正在获取活动日历...",
|
|
47
47
|
"noEvents": "近期暂无活动安排",
|
|
48
48
|
"error": "无法获取活动日历",
|
|
49
|
+
"stale": "离线,显示上次获取的数据",
|
|
49
50
|
"errorHint": "请稍后重试或访问 https://nbtca.space",
|
|
50
51
|
"eventsFound": "个活动",
|
|
51
52
|
"dateTime": "日期时间",
|
|
@@ -113,7 +114,6 @@
|
|
|
113
114
|
"opening": "正在打开浏览器...",
|
|
114
115
|
"browserOpened": "已在浏览器中打开知识库",
|
|
115
116
|
"browserError": "无法打开浏览器",
|
|
116
|
-
"browserErrorHint": "请手动访问: https://docs.nbtca.space",
|
|
117
117
|
"retry": "是否重试?",
|
|
118
118
|
"tocTitle": "目录",
|
|
119
119
|
"tableHint": "图表与表格在浏览器中效果更佳",
|
|
@@ -124,7 +124,7 @@
|
|
|
124
124
|
"githubTokenHint": "提示: 设置 GITHUB_TOKEN 环境变量可获得更高的速率限制。",
|
|
125
125
|
"fetchDirFailed": "无法获取目录内容: {error}",
|
|
126
126
|
"fetchFileFailed": "无法获取文件内容: {error}",
|
|
127
|
-
"searchPrompt": "
|
|
127
|
+
"searchPrompt": "搜索文档:",
|
|
128
128
|
"searchPlaceholder": "输入关键词...",
|
|
129
129
|
"searching": "正在搜索文档...",
|
|
130
130
|
"searchResults": "个结果",
|
|
@@ -142,12 +142,14 @@
|
|
|
142
142
|
"repair": "维修服务",
|
|
143
143
|
"opening": "正在打开...",
|
|
144
144
|
"opened": "已在浏览器中打开",
|
|
145
|
-
"error": "无法打开浏览器"
|
|
145
|
+
"error": "无法打开浏览器",
|
|
146
|
+
"openManually": "请手动打开: {url}"
|
|
146
147
|
},
|
|
147
148
|
"status": {
|
|
148
149
|
"checking": "正在检查服务状态...",
|
|
149
150
|
"summaryOk": "所有服务运行正常",
|
|
150
151
|
"summaryFail": "部分服务不可用",
|
|
152
|
+
"summaryIntranet": "公共服务运行正常;内网可用性取决于当前网络",
|
|
151
153
|
"service": "服务",
|
|
152
154
|
"health": "状态",
|
|
153
155
|
"code": "代码",
|
|
@@ -361,6 +363,9 @@
|
|
|
361
363
|
"invalidFlagHint": "运行 `nbtca --help` 查看命令用法。",
|
|
362
364
|
"invalidLang": "无效语言。请使用 `zh` 或 `en`。",
|
|
363
365
|
"invalidNext": "无效的 --next 值。请使用 --next=<数字>(>= 1)。",
|
|
366
|
+
"eventsHeatmapConflict": "--heatmap 不能与 --today、--week、--month、--next 或 --search 同时使用。",
|
|
367
|
+
"eventsRangeConflict": "活动时间范围只能选择一个: --today、--week 或 --month。",
|
|
368
|
+
"unexpectedArguments": "{command} 不接受多余参数。",
|
|
364
369
|
"requiresTty": "交互模式需要 TTY 终端。",
|
|
365
370
|
"requiresTtyHint": "使用 `nbtca --help` 查看命令模式。"
|
|
366
371
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,475 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { renderHeatmap } from './features/calendar-heatmap.js';
|
|
6
|
-
import { checkServices, countServiceHealth, hasServiceFailures, renderServiceStatusTable, serializeServiceStatus } from './features/status.js';
|
|
7
|
-
import { pickIcon } from './core/icons.js';
|
|
8
|
-
import { applyColorModePreference } from './config/preferences.js';
|
|
9
|
-
import { openDocsInBrowser } from './features/docs.js';
|
|
10
|
-
import { runThemeCommand } from './features/theme.js';
|
|
11
|
-
import { saveLanguagePreference, t, fmt } from './i18n/index.js';
|
|
12
|
-
import { clearScreen, handleGracefulExit } from './core/ui.js';
|
|
13
|
-
import { APP_INFO, URLS } from './config/data.js';
|
|
14
|
-
import { runUpdateCheck } from './features/update.js';
|
|
15
|
-
import { runStudentTimetableCommand } from './features/student-timetable.js';
|
|
16
|
-
const ACTION_ALIASES = {
|
|
17
|
-
events: 'events',
|
|
18
|
-
event: 'events',
|
|
19
|
-
repair: 'repair',
|
|
20
|
-
docs: 'docs',
|
|
21
|
-
doc: 'docs',
|
|
22
|
-
website: 'website',
|
|
23
|
-
web: 'website',
|
|
24
|
-
github: 'github',
|
|
25
|
-
gh: 'github',
|
|
26
|
-
roadmap: 'roadmap',
|
|
27
|
-
board: 'roadmap',
|
|
28
|
-
about: 'about',
|
|
29
|
-
status: 'status',
|
|
30
|
-
};
|
|
31
|
-
const URL_ACTIONS = {
|
|
32
|
-
repair: URLS.repair,
|
|
33
|
-
website: URLS.homepage,
|
|
34
|
-
github: URLS.github,
|
|
35
|
-
roadmap: URLS.roadmap
|
|
36
|
-
};
|
|
37
|
-
const KNOWN_FLAGS = new Set([
|
|
38
|
-
'--help', '--version', '--open', '--json', '--plain', '--no-logo', '--watch', '--today', '--heatmap',
|
|
39
|
-
'--week', '--month', '--one-shot', '--no-save',
|
|
40
|
-
]);
|
|
41
|
-
const KNOWN_FLAG_PREFIXES = ['--interval=', '--timeout=', '--retries=', '--next=', '--search=', '--term=', '--output=', '--week-one='];
|
|
42
|
-
const STATUS_WATCH_INTERVAL_MIN = 3;
|
|
43
|
-
const STATUS_WATCH_INTERVAL_MAX = 300;
|
|
44
|
-
const STATUS_TIMEOUT_MIN = 1000;
|
|
45
|
-
const STATUS_TIMEOUT_MAX = 20000;
|
|
46
|
-
const STATUS_RETRIES_MIN = 0;
|
|
47
|
-
const STATUS_RETRIES_MAX = 5;
|
|
48
|
-
function parseArgs(argv) {
|
|
49
|
-
const flags = new Set();
|
|
50
|
-
const positionals = [];
|
|
51
|
-
for (const token of argv) {
|
|
52
|
-
if (token.startsWith('--')) {
|
|
53
|
-
flags.add(token);
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
positionals.push(token);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return {
|
|
60
|
-
command: positionals[0]?.toLowerCase(),
|
|
61
|
-
args: positionals.slice(1),
|
|
62
|
-
flags
|
|
63
|
-
};
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
if (process.argv.slice(2).includes('--plain')) {
|
|
3
|
+
delete process.env['FORCE_COLOR'];
|
|
4
|
+
process.env['NO_COLOR'] = '1';
|
|
64
5
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
function getAllowedFlagsFor(command) {
|
|
69
|
-
const allowed = new Set(['--help', '--plain']);
|
|
70
|
-
if (!command) {
|
|
71
|
-
allowed.add('--no-logo');
|
|
72
|
-
allowed.add('--version');
|
|
73
|
-
return allowed;
|
|
74
|
-
}
|
|
75
|
-
if (command === 'lang' || command === 'language')
|
|
76
|
-
return allowed;
|
|
77
|
-
if (command === 'theme')
|
|
78
|
-
return allowed;
|
|
79
|
-
if (command === 'schedule' || command === 'timetable') {
|
|
80
|
-
allowed.add('--one-shot');
|
|
81
|
-
allowed.add('--no-save');
|
|
82
|
-
return allowed;
|
|
83
|
-
}
|
|
84
|
-
const action = ACTION_ALIASES[command];
|
|
85
|
-
if (!action)
|
|
86
|
-
return allowed;
|
|
87
|
-
switch (action) {
|
|
88
|
-
case 'events':
|
|
89
|
-
allowed.add('--json');
|
|
90
|
-
allowed.add('--today');
|
|
91
|
-
allowed.add('--heatmap');
|
|
92
|
-
allowed.add('--week');
|
|
93
|
-
allowed.add('--month');
|
|
94
|
-
return allowed;
|
|
95
|
-
case 'status':
|
|
96
|
-
allowed.add('--json');
|
|
97
|
-
allowed.add('--watch');
|
|
98
|
-
return allowed;
|
|
99
|
-
case 'repair':
|
|
100
|
-
case 'website':
|
|
101
|
-
case 'github':
|
|
102
|
-
case 'roadmap':
|
|
103
|
-
case 'docs':
|
|
104
|
-
allowed.add('--open');
|
|
105
|
-
return allowed;
|
|
106
|
-
case 'about':
|
|
107
|
-
default:
|
|
108
|
-
return allowed;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
function getAllowedFlagPrefixesFor(command) {
|
|
112
|
-
if (!command)
|
|
113
|
-
return [];
|
|
114
|
-
if (command === 'schedule' || command === 'timetable') {
|
|
115
|
-
return ['--term=', '--output=', '--week-one='];
|
|
116
|
-
}
|
|
117
|
-
const action = ACTION_ALIASES[command];
|
|
118
|
-
if (action === 'events')
|
|
119
|
-
return ['--next=', '--search='];
|
|
120
|
-
if (action === 'status')
|
|
121
|
-
return ['--interval=', '--timeout=', '--retries='];
|
|
122
|
-
return [];
|
|
123
|
-
}
|
|
124
|
-
function validateFlags(command, flags) {
|
|
125
|
-
const unknown = Array.from(flags).filter((flag) => {
|
|
126
|
-
if (KNOWN_FLAGS.has(flag))
|
|
127
|
-
return false;
|
|
128
|
-
return !KNOWN_FLAG_PREFIXES.some((prefix) => flag.startsWith(prefix));
|
|
129
|
-
});
|
|
130
|
-
if (unknown.length > 0) {
|
|
131
|
-
const trans0 = t();
|
|
132
|
-
console.error(chalk.red(fmt(trans0.cli.unknownFlag, { flag: unknown[0] })));
|
|
133
|
-
console.error(chalk.dim(trans0.cli.unknownFlagHint));
|
|
134
|
-
process.exit(1);
|
|
135
|
-
}
|
|
136
|
-
const allowed = getAllowedFlagsFor(command);
|
|
137
|
-
const allowedPrefixes = getAllowedFlagPrefixesFor(command);
|
|
138
|
-
const disallowed = Array.from(flags).filter((flag) => {
|
|
139
|
-
if (allowed.has(flag))
|
|
140
|
-
return false;
|
|
141
|
-
return !allowedPrefixes.some((prefix) => flag.startsWith(prefix));
|
|
142
|
-
});
|
|
143
|
-
if (disallowed.length > 0) {
|
|
144
|
-
const trans1 = t();
|
|
145
|
-
console.error(chalk.red(fmt(trans1.cli.invalidFlag, { flag: disallowed[0] })));
|
|
146
|
-
console.error(chalk.dim(trans1.cli.invalidFlagHint));
|
|
147
|
-
process.exit(1);
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
function printHelp() {
|
|
151
|
-
const trans = t();
|
|
152
|
-
const c = trans.cli;
|
|
153
|
-
console.log(chalk.bold('NBTCA Prompt'));
|
|
154
|
-
console.log();
|
|
155
|
-
console.log(c.usage);
|
|
156
|
-
console.log(` nbtca ${c.interactive}`);
|
|
157
|
-
console.log(` nbtca <command> [flags] ${c.runCommand}`);
|
|
158
|
-
console.log();
|
|
159
|
-
console.log(c.commands);
|
|
160
|
-
console.log(` events ${trans.menu.eventsDesc}`);
|
|
161
|
-
console.log(` docs ${trans.menu.docsDesc}`);
|
|
162
|
-
console.log(` status ${trans.menu.statusDesc}`);
|
|
163
|
-
console.log(' schedule <login|logout|status|terms|export>');
|
|
164
|
-
console.log(` ${c.cmdSchedule}`);
|
|
165
|
-
console.log(` website ${c.cmdWebsite}`);
|
|
166
|
-
console.log(` github ${c.cmdGithub}`);
|
|
167
|
-
console.log(` roadmap ${c.cmdRoadmap}`);
|
|
168
|
-
console.log(` repair ${c.cmdRepair}`);
|
|
169
|
-
console.log(` theme ${c.cmdTheme}`);
|
|
170
|
-
console.log(` lang <zh|en> ${c.cmdLang}`);
|
|
171
|
-
console.log(` update ${c.cmdUpdate}`);
|
|
172
|
-
console.log();
|
|
173
|
-
console.log(c.flags);
|
|
174
|
-
console.log(` --version ${c.flagVersion}`);
|
|
175
|
-
console.log(` --help ${c.flagHelp}`);
|
|
176
|
-
console.log(` --open ${c.flagOpen}`);
|
|
177
|
-
console.log(` --json ${c.flagJson}`);
|
|
178
|
-
console.log(` --heatmap ${c.flagHeatmap}`);
|
|
179
|
-
console.log(` --today ${c.flagToday}`);
|
|
180
|
-
console.log(` --week ${c.flagWeek}`);
|
|
181
|
-
console.log(` --month ${c.flagMonth}`);
|
|
182
|
-
console.log(` --search=<q> ${c.flagSearch}`);
|
|
183
|
-
console.log(` --next=<n> ${c.flagNext}`);
|
|
184
|
-
console.log(` --watch ${c.flagWatch}`);
|
|
185
|
-
console.log(` --interval=<s> ${c.flagInterval}`);
|
|
186
|
-
console.log(` --timeout=<ms> ${c.flagTimeout}`);
|
|
187
|
-
console.log(` --retries=<n> ${c.flagRetries}`);
|
|
188
|
-
console.log(` --plain ${c.flagPlain}`);
|
|
189
|
-
console.log(` --no-logo ${c.flagNoLogo}`);
|
|
190
|
-
console.log(` --one-shot ${c.flagOneShot}`);
|
|
191
|
-
console.log(` --no-save ${c.flagNoSave}`);
|
|
192
|
-
console.log(` --term=<year:code> ${c.flagTerm}`);
|
|
193
|
-
console.log(` --output=<path> ${c.flagOutput}`);
|
|
194
|
-
console.log(` --week-one=<date> ${c.flagWeekOne}`);
|
|
195
|
-
}
|
|
196
|
-
async function runEventsCommand(flags) {
|
|
197
|
-
if (flags.has('--heatmap')) {
|
|
198
|
-
const buckets = await fetchHeatmapBuckets();
|
|
199
|
-
if (flags.has('--json')) {
|
|
200
|
-
process.stdout.write(JSON.stringify(buckets, null, 2) + '\n');
|
|
201
|
-
}
|
|
202
|
-
else {
|
|
203
|
-
const useColor = !flags.has('--plain') && !!process.stdout.isTTY;
|
|
204
|
-
console.log(renderHeatmap(buckets, new Date(), { color: useColor }));
|
|
205
|
-
}
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
const { weekRange, monthRange } = await import('./features/calendar-query.js');
|
|
209
|
-
const { fetchInRange } = await import('./features/calendar.js');
|
|
210
|
-
const now0 = new Date();
|
|
211
|
-
let events;
|
|
212
|
-
if (flags.has('--week')) {
|
|
213
|
-
const r = weekRange(now0);
|
|
214
|
-
events = await fetchInRange(r.start, r.end);
|
|
215
|
-
}
|
|
216
|
-
else if (flags.has('--month')) {
|
|
217
|
-
const r = monthRange(now0);
|
|
218
|
-
events = await fetchInRange(r.start, r.end);
|
|
219
|
-
}
|
|
220
|
-
else {
|
|
221
|
-
events = await fetchEvents();
|
|
222
|
-
}
|
|
223
|
-
const searchFlag = Array.from(flags).find(f => f.startsWith('--search='));
|
|
224
|
-
if (searchFlag) {
|
|
225
|
-
const q = searchFlag.slice('--search='.length).toLowerCase();
|
|
226
|
-
events = events.filter(e => `${e.title} ${e.location}`.toLowerCase().includes(q));
|
|
227
|
-
}
|
|
228
|
-
if (flags.has('--today')) {
|
|
229
|
-
const now = new Date();
|
|
230
|
-
events = events.filter(e => {
|
|
231
|
-
const d = e.startDate;
|
|
232
|
-
return d.getFullYear() === now.getFullYear() &&
|
|
233
|
-
d.getMonth() === now.getMonth() &&
|
|
234
|
-
d.getDate() === now.getDate();
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
const nextFlag = Array.from(flags).find(f => f.startsWith('--next='));
|
|
238
|
-
if (nextFlag) {
|
|
239
|
-
const n = Number.parseInt(nextFlag.split('=')[1] || '', 10);
|
|
240
|
-
if (!Number.isInteger(n) || n < 1) {
|
|
241
|
-
console.error(chalk.red(t().cli.invalidNext));
|
|
242
|
-
process.exit(1);
|
|
243
|
-
}
|
|
244
|
-
events = events.slice(0, n);
|
|
245
|
-
}
|
|
246
|
-
if (flags.has('--json')) {
|
|
247
|
-
process.stdout.write(JSON.stringify(serializeEvents(events), null, 2) + '\n');
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
const useColor = !flags.has('--plain') && !!process.stdout.isTTY;
|
|
251
|
-
console.log(renderEventsTable(events, { color: useColor }));
|
|
252
|
-
}
|
|
253
|
-
async function runStatusCommand(flags) {
|
|
254
|
-
const trans = t();
|
|
255
|
-
const watch = flags.has('--watch');
|
|
256
|
-
const intervalFlag = Array.from(flags).find((flag) => flag.startsWith('--interval='));
|
|
257
|
-
const timeoutFlag = Array.from(flags).find((flag) => flag.startsWith('--timeout='));
|
|
258
|
-
const retriesFlag = Array.from(flags).find((flag) => flag.startsWith('--retries='));
|
|
259
|
-
const intervalSeconds = intervalFlag ? Number.parseInt(intervalFlag.split('=')[1] || '', 10) : 10;
|
|
260
|
-
const timeoutMs = timeoutFlag ? Number.parseInt(timeoutFlag.split('=')[1] || '', 10) : 6000;
|
|
261
|
-
const retries = retriesFlag ? Number.parseInt(retriesFlag.split('=')[1] || '', 10) : 1;
|
|
262
|
-
if (!watch && intervalFlag) {
|
|
263
|
-
console.error(chalk.red(trans.status.intervalNeedsWatch));
|
|
264
|
-
process.exit(1);
|
|
265
|
-
}
|
|
266
|
-
if (!Number.isInteger(timeoutMs) || timeoutMs < STATUS_TIMEOUT_MIN || timeoutMs > STATUS_TIMEOUT_MAX) {
|
|
267
|
-
console.error(chalk.red(fmt(trans.status.invalidTimeout, { min: STATUS_TIMEOUT_MIN, max: STATUS_TIMEOUT_MAX })));
|
|
268
|
-
process.exit(1);
|
|
269
|
-
}
|
|
270
|
-
if (!Number.isInteger(retries) || retries < STATUS_RETRIES_MIN || retries > STATUS_RETRIES_MAX) {
|
|
271
|
-
console.error(chalk.red(fmt(trans.status.invalidRetries, { min: STATUS_RETRIES_MIN, max: STATUS_RETRIES_MAX })));
|
|
272
|
-
process.exit(1);
|
|
273
|
-
}
|
|
274
|
-
if (watch && flags.has('--json')) {
|
|
275
|
-
console.error(chalk.red(trans.status.watchJsonConflict));
|
|
276
|
-
process.exit(1);
|
|
277
|
-
}
|
|
278
|
-
if (watch && (!Number.isInteger(intervalSeconds) || intervalSeconds < STATUS_WATCH_INTERVAL_MIN || intervalSeconds > STATUS_WATCH_INTERVAL_MAX)) {
|
|
279
|
-
console.error(chalk.red(fmt(trans.status.invalidInterval, { min: STATUS_WATCH_INTERVAL_MIN, max: STATUS_WATCH_INTERVAL_MAX })));
|
|
280
|
-
process.exit(1);
|
|
281
|
-
}
|
|
282
|
-
if (watch && !hasInteractiveTerminal()) {
|
|
283
|
-
console.error(chalk.red(trans.status.watchRequiresTty));
|
|
284
|
-
process.exit(1);
|
|
285
|
-
}
|
|
286
|
-
if (watch) {
|
|
287
|
-
let stopped = false;
|
|
288
|
-
const onSigint = () => { stopped = true; };
|
|
289
|
-
process.once('SIGINT', onSigint);
|
|
290
|
-
console.log(chalk.dim(`${fmt(trans.status.watchStarted, { seconds: intervalSeconds })} | ${trans.status.watchHint}`));
|
|
291
|
-
try {
|
|
292
|
-
while (!stopped) {
|
|
293
|
-
const services = await checkServices({ timeoutMs, retries });
|
|
294
|
-
const hasFailures = hasServiceFailures(services);
|
|
295
|
-
const health = countServiceHealth(services);
|
|
296
|
-
clearScreen();
|
|
297
|
-
console.log(chalk.bold(`${trans.status.watchUpdated}: ${new Date().toLocaleString()}`));
|
|
298
|
-
console.log(chalk.dim(`${trans.status.up}: ${health.up} | ${trans.status.down}: ${health.down} | ${trans.status.watchHint}`));
|
|
299
|
-
console.log();
|
|
300
|
-
const useColor = !flags.has('--plain') && !!process.stdout.isTTY;
|
|
301
|
-
console.log(renderServiceStatusTable(services, { color: useColor }));
|
|
302
|
-
if (hasFailures) {
|
|
303
|
-
console.log(chalk.yellow(trans.status.summaryFail));
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
306
|
-
console.log(chalk.green(trans.status.summaryOk));
|
|
307
|
-
}
|
|
308
|
-
await new Promise((resolve) => {
|
|
309
|
-
const stopWait = () => {
|
|
310
|
-
clearTimeout(timer);
|
|
311
|
-
process.removeListener('SIGINT', stopWait);
|
|
312
|
-
resolve();
|
|
313
|
-
};
|
|
314
|
-
const timer = setTimeout(stopWait, intervalSeconds * 1000);
|
|
315
|
-
process.once('SIGINT', stopWait);
|
|
316
|
-
if (stopped)
|
|
317
|
-
stopWait();
|
|
318
|
-
});
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
finally {
|
|
322
|
-
process.removeListener('SIGINT', onSigint);
|
|
323
|
-
}
|
|
324
|
-
console.log();
|
|
325
|
-
console.log(chalk.dim(t().common.goodbye));
|
|
326
|
-
return true;
|
|
327
|
-
}
|
|
328
|
-
const services = await checkServices({ timeoutMs, retries });
|
|
329
|
-
const hasFailures = hasServiceFailures(services);
|
|
330
|
-
if (flags.has('--json')) {
|
|
331
|
-
process.stdout.write(JSON.stringify(serializeServiceStatus(services), null, 2) + '\n');
|
|
332
|
-
}
|
|
333
|
-
else {
|
|
334
|
-
const useColor = !flags.has('--plain') && !!process.stdout.isTTY;
|
|
335
|
-
console.log(renderServiceStatusTable(services, { color: useColor }));
|
|
336
|
-
if (hasFailures) {
|
|
337
|
-
console.error(chalk.yellow(t().status.summaryFail));
|
|
338
|
-
}
|
|
339
|
-
else {
|
|
340
|
-
console.log(chalk.green(t().status.summaryOk));
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
return !hasFailures;
|
|
344
|
-
}
|
|
345
|
-
function maybeDisableColor(flags) {
|
|
346
|
-
applyColorModePreference(flags.has('--plain'));
|
|
347
|
-
}
|
|
348
|
-
async function runCommandMode(argv) {
|
|
349
|
-
const { command, args, flags } = parseArgs(argv);
|
|
350
|
-
maybeDisableColor(flags);
|
|
351
|
-
if (flags.has('--version') || command === '--version' || command === '-v' || command === 'version') {
|
|
352
|
-
console.log(APP_INFO.version);
|
|
353
|
-
return;
|
|
354
|
-
}
|
|
355
|
-
if (flags.has('--help') || command === '--help' || command === '-h' || command === 'help') {
|
|
356
|
-
printHelp();
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
359
|
-
validateFlags(command, flags);
|
|
360
|
-
if (!command) {
|
|
361
|
-
if (!hasInteractiveTerminal()) {
|
|
362
|
-
const cliTrans = t().cli;
|
|
363
|
-
console.error(chalk.red(cliTrans.requiresTty));
|
|
364
|
-
console.error(chalk.dim(cliTrans.requiresTtyHint));
|
|
365
|
-
process.exit(1);
|
|
366
|
-
}
|
|
367
|
-
await main({ skipLogo: flags.has('--no-logo') });
|
|
368
|
-
return;
|
|
369
|
-
}
|
|
370
|
-
if (command === 'lang' || command === 'language') {
|
|
371
|
-
const language = (args[0] || '').toLowerCase();
|
|
372
|
-
if (language !== 'zh' && language !== 'en') {
|
|
373
|
-
console.error(chalk.red(t().cli.invalidLang));
|
|
374
|
-
process.exit(1);
|
|
375
|
-
}
|
|
376
|
-
const persisted = saveLanguagePreference(language);
|
|
377
|
-
if (persisted) {
|
|
378
|
-
console.log(chalk.green(`${pickIcon('✓', 'OK')}: ${t().language.changed}`));
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
console.log(chalk.yellow(`${pickIcon('⚠', 'WARN')}: ${t().language.changedSessionOnly}`));
|
|
382
|
-
}
|
|
383
|
-
return;
|
|
384
|
-
}
|
|
385
|
-
if (command === 'theme') {
|
|
386
|
-
const result = runThemeCommand(args);
|
|
387
|
-
if (!result.ok) {
|
|
388
|
-
console.error(chalk.red(result.message));
|
|
389
|
-
process.exit(1);
|
|
390
|
-
}
|
|
391
|
-
if (result.message) {
|
|
392
|
-
console.log(chalk.green(`${pickIcon('✓', 'OK')}: ${result.message}`));
|
|
393
|
-
}
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
if (command === 'update') {
|
|
397
|
-
await runUpdateCheck();
|
|
398
|
-
return;
|
|
399
|
-
}
|
|
400
|
-
if (command === 'schedule' || command === 'timetable') {
|
|
401
|
-
if (args.length > 1) {
|
|
402
|
-
console.error(chalk.red(t().timetable.invalidArguments));
|
|
403
|
-
process.exitCode = 1;
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
const exitCode = await runStudentTimetableCommand(args[0], { flags });
|
|
407
|
-
if (exitCode !== 0)
|
|
408
|
-
process.exitCode = exitCode;
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
const action = ACTION_ALIASES[command];
|
|
412
|
-
if (!action) {
|
|
413
|
-
const cliT = t().cli;
|
|
414
|
-
console.error(chalk.red(fmt(cliT.unknownCommand, { command })));
|
|
415
|
-
console.error(chalk.dim(cliT.unknownCommandHint));
|
|
416
|
-
process.exit(1);
|
|
417
|
-
}
|
|
418
|
-
if (action === 'events') {
|
|
419
|
-
await runEventsCommand(flags);
|
|
420
|
-
return;
|
|
421
|
-
}
|
|
422
|
-
if (action === 'status') {
|
|
423
|
-
const ok = await runStatusCommand(flags);
|
|
424
|
-
if (!ok && !flags.has('--json'))
|
|
425
|
-
process.exit(1);
|
|
426
|
-
return;
|
|
427
|
-
}
|
|
428
|
-
if (action === 'docs') {
|
|
429
|
-
if (!hasInteractiveTerminal()) {
|
|
430
|
-
if (flags.has('--open')) {
|
|
431
|
-
await openDocsInBrowser();
|
|
432
|
-
}
|
|
433
|
-
else {
|
|
434
|
-
process.stdout.write(URLS.docs + '\n');
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
else {
|
|
438
|
-
const { showDocsMenu } = await import('./features/docs.js');
|
|
439
|
-
await showDocsMenu();
|
|
440
|
-
}
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
if (action === 'about') {
|
|
444
|
-
const { note } = await import('./core/components/note.js');
|
|
445
|
-
const { padEndV } = await import('./core/text.js');
|
|
446
|
-
const pad = 12;
|
|
447
|
-
const row = (label, value) => `${chalk.dim(padEndV(label, pad))}${value}`;
|
|
448
|
-
const link = (label, url) => row(label, chalk.cyan(url));
|
|
449
|
-
const trans = t();
|
|
450
|
-
const content = [
|
|
451
|
-
row(trans.about.project, APP_INFO.name),
|
|
452
|
-
row(trans.about.version, `v${APP_INFO.version}`),
|
|
453
|
-
row(trans.about.description, trans.about.descriptionText),
|
|
454
|
-
'',
|
|
455
|
-
link(trans.about.github, APP_INFO.repository),
|
|
456
|
-
link(trans.about.website, URLS.homepage),
|
|
457
|
-
link(trans.about.email, URLS.email),
|
|
458
|
-
'',
|
|
459
|
-
row(trans.about.license, `MIT | ${trans.about.author}: m1ngsama`),
|
|
460
|
-
].join('\n');
|
|
461
|
-
note(content, trans.about.title);
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
const mappedUrl = URL_ACTIONS[action];
|
|
465
|
-
if (mappedUrl) {
|
|
466
|
-
if (flags.has('--open')) {
|
|
467
|
-
await open(mappedUrl);
|
|
468
|
-
}
|
|
469
|
-
else {
|
|
470
|
-
process.stdout.write(mappedUrl + '\n');
|
|
471
|
-
}
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
runCommandMode(process.argv.slice(2)).catch(handleGracefulExit);
|
|
6
|
+
await import('./cli.js');
|
|
7
|
+
export {};
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
⠀⠀⠀⠀⠀⠀⠀⠙⠳⢤⣀⠂⠱⠄⢄⠈⣍⢍⣭⢩⢁⣤⠢⠞⠀⣉⡤⠞⠋⠀⠀⠀⠀⠀⠀⠀
|
|
18
|
-
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠒⠶⠤⣤⣤⣥⣬⣤⣤⠤⠶⠒⠋⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
|
|
1
|
+
⢀⣠⡤⠶⠚⢚⣋⢋⣙⠛⡛⠓⠦⢤⣄⡀
|
|
2
|
+
⢀⣤⠞⡋⠁⠘⢤⣑⣩⡥⡭⠭⢥⣙⣑⠁⠴⣊⡙⠳⣤⡀
|
|
3
|
+
⢀⡴⢋⣡⡓⣙⡴⠚⠉⠁ ⠈⠉⠓⠮⣙⢓⣄⡙⢦⡀
|
|
4
|
+
⣠⠞⣰⡄⣡⠚⠁ ⠈⠓⣍⢪⢌⠻⣄
|
|
5
|
+
⢰⠋⢬⢊⣼⠥⠴⠒⠒⠢⠤⡀ ⢀⣀⠤⠤⠒⠢⠤⣄⡈⢳⡱⣡⠙⡆
|
|
6
|
+
⢠⡏⣸⣧⣾⣅ ⢀⣀⣀⣀⡀⠈⠱⡄ ⣰⣮⣄ ⢀⢀⣀⡀ ⠉⢢⣳⡨⠃⢹⡄
|
|
7
|
+
⣼⢰⡤⢿⣿⣿⠞⠉⠁ ⠈⠉⠲⣀⡹⠐⣿⣿⡿⠚⠉⠁⠈⠈⠑⠦⣀⡏⣇⠳⠌⣧
|
|
8
|
+
⡟⢘⡅⡟⠈ ⠁ ⠈ ⢻⠰⡖⢻
|
|
9
|
+
⣧⠨⣁⣧ ⣼⠸⢛⣼
|
|
10
|
+
⢻⠘⢶⢻⡀⢇ ⢸⠁⢀⡏⠈⠉⡟
|
|
11
|
+
⠘⣇⠵⢇⢧⠘⣄ ⢠⠇⢀⡼⢩⡕⣹⠃
|
|
12
|
+
⠸⣆⠹⢌⢣⡈⢢⡀ ⢀⡰⠋⢀⡼⣑⠦⣠⠇
|
|
13
|
+
⠙⢦⡓⢴⡙⢤⡉⠲⣄⡀ ⢀⣀⠴⠋⢀⡤⢫⡌⠋⡴⠋
|
|
14
|
+
⠈⠳⣌⡙⢁⡍⡲⢤⣉⡙⠓⠒⠲⠖⠒⠋⢉⣁⡤⢖⡩⣠⠙⣡⠞⠁
|
|
15
|
+
⠈⠛⢦⣌⡀⠲⢜⠭⡝⢓⣒⣚⢚⣍⡭⡰⠆⠙⣨⡴⠛⠁
|
|
16
|
+
⠈⠙⠓⠲⢥⣥⣅⣉⣩⣤⣤⡬⠖⠚⠋⠁
|
package/dist/main.js
CHANGED
|
@@ -1,52 +1,11 @@
|
|
|
1
1
|
import { runStartup } from './core/logo.js';
|
|
2
|
-
import { clearScreen
|
|
3
|
-
import { showMainMenu } from './core/menu.js';
|
|
4
|
-
import { c } from './core/theme.js';
|
|
2
|
+
import { clearScreen } from './core/ui.js';
|
|
5
3
|
import { enableVimKeys } from './core/vim-keys.js';
|
|
6
|
-
import { checkForUpdate } from './features/update.js';
|
|
7
|
-
import { showEventsPreview } from './features/calendar.js';
|
|
8
4
|
export async function main(options = {}) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
await runStartup();
|
|
16
|
-
}
|
|
17
|
-
// Fire update check in background; events fetch provides natural wait time.
|
|
18
|
-
// Its own AbortController is tied to whichever path finishes first, so a
|
|
19
|
-
// still-in-flight request never keeps the process alive after we're done
|
|
20
|
-
// with (or have stopped caring about) its result.
|
|
21
|
-
const updateAbort = new AbortController();
|
|
22
|
-
const updatePromise = checkForUpdate(updateAbort.signal);
|
|
23
|
-
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
24
|
-
// Launch app shell for interactive terminals
|
|
25
|
-
const { runApp } = await import('./app/app.js');
|
|
26
|
-
await runApp();
|
|
27
|
-
updateAbort.abort();
|
|
28
|
-
}
|
|
29
|
-
else {
|
|
30
|
-
// Classic path for non-TTY (CI, pipes, redirects)
|
|
31
|
-
await showEventsPreview();
|
|
32
|
-
try {
|
|
33
|
-
const line = (await import('./features/schedule-view.js')).peekNextClassLine();
|
|
34
|
-
if (line)
|
|
35
|
-
console.log(line);
|
|
36
|
-
}
|
|
37
|
-
catch { /* best effort */ }
|
|
38
|
-
// Update check is very likely done by now; give it a short window if not
|
|
39
|
-
const updateMsg = await Promise.race([
|
|
40
|
-
updatePromise,
|
|
41
|
-
new Promise(r => setTimeout(r, 100, null)),
|
|
42
|
-
]);
|
|
43
|
-
updateAbort.abort();
|
|
44
|
-
if (updateMsg)
|
|
45
|
-
console.log(c.warn(updateMsg));
|
|
46
|
-
await showMainMenu();
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
catch (err) {
|
|
50
|
-
handleGracefulExit(err);
|
|
51
|
-
}
|
|
5
|
+
enableVimKeys();
|
|
6
|
+
clearScreen();
|
|
7
|
+
if (!options.skipLogo)
|
|
8
|
+
await runStartup();
|
|
9
|
+
const { runApp } = await import('./app/app.js');
|
|
10
|
+
await runApp();
|
|
52
11
|
}
|