@nbtca/prompt 1.0.22 → 1.0.24

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.
Files changed (85) hide show
  1. package/dist/config/data.js +5 -1
  2. package/dist/config/paths.js +5 -0
  3. package/dist/config/preferences.js +1 -8
  4. package/dist/core/icons.js +18 -5
  5. package/dist/core/logo.js +1 -2
  6. package/dist/core/menu.js +7 -122
  7. package/dist/core/text.js +9 -3
  8. package/dist/core/ui.js +0 -1
  9. package/dist/core/vim-keys.js +6 -5
  10. package/dist/features/calendar.js +50 -9
  11. package/dist/features/docs.js +128 -49
  12. package/dist/features/links.js +42 -0
  13. package/dist/features/settings.js +120 -0
  14. package/dist/features/status.js +19 -26
  15. package/dist/features/theme.js +9 -101
  16. package/dist/features/update.js +74 -0
  17. package/dist/i18n/index.js +7 -16
  18. package/dist/i18n/locales/en.json +43 -54
  19. package/dist/i18n/locales/zh.json +43 -54
  20. package/dist/index.js +107 -67
  21. package/dist/logo/ascii-logo.txt +12 -6
  22. package/dist/logo/logo.txt +12 -2
  23. package/dist/main.js +14 -5
  24. package/package.json +4 -1
  25. package/dist/config/data.d.ts +0 -23
  26. package/dist/config/data.d.ts.map +0 -1
  27. package/dist/config/data.js.map +0 -1
  28. package/dist/config/preferences.d.ts +0 -14
  29. package/dist/config/preferences.d.ts.map +0 -1
  30. package/dist/config/preferences.js.map +0 -1
  31. package/dist/config/theme.d.ts +0 -23
  32. package/dist/config/theme.d.ts.map +0 -1
  33. package/dist/config/theme.js +0 -25
  34. package/dist/config/theme.js.map +0 -1
  35. package/dist/core/icons.d.ts +0 -3
  36. package/dist/core/icons.d.ts.map +0 -1
  37. package/dist/core/icons.js.map +0 -1
  38. package/dist/core/logo.d.ts +0 -9
  39. package/dist/core/logo.d.ts.map +0 -1
  40. package/dist/core/logo.js.map +0 -1
  41. package/dist/core/menu.d.ts +0 -14
  42. package/dist/core/menu.d.ts.map +0 -1
  43. package/dist/core/menu.js.map +0 -1
  44. package/dist/core/text.d.ts +0 -7
  45. package/dist/core/text.d.ts.map +0 -1
  46. package/dist/core/text.js.map +0 -1
  47. package/dist/core/ui.d.ts +0 -38
  48. package/dist/core/ui.d.ts.map +0 -1
  49. package/dist/core/ui.js.map +0 -1
  50. package/dist/core/vim-keys.d.ts +0 -8
  51. package/dist/core/vim-keys.d.ts.map +0 -1
  52. package/dist/core/vim-keys.js.map +0 -1
  53. package/dist/features/calendar.d.ts +0 -29
  54. package/dist/features/calendar.d.ts.map +0 -1
  55. package/dist/features/calendar.js.map +0 -1
  56. package/dist/features/docs.d.ts +0 -8
  57. package/dist/features/docs.d.ts.map +0 -1
  58. package/dist/features/docs.js.map +0 -1
  59. package/dist/features/repair.d.ts +0 -10
  60. package/dist/features/repair.d.ts.map +0 -1
  61. package/dist/features/repair.js +0 -29
  62. package/dist/features/repair.js.map +0 -1
  63. package/dist/features/status.d.ts +0 -31
  64. package/dist/features/status.d.ts.map +0 -1
  65. package/dist/features/status.js.map +0 -1
  66. package/dist/features/theme.d.ts +0 -8
  67. package/dist/features/theme.d.ts.map +0 -1
  68. package/dist/features/theme.js.map +0 -1
  69. package/dist/features/website.d.ts +0 -30
  70. package/dist/features/website.d.ts.map +0 -1
  71. package/dist/features/website.js +0 -48
  72. package/dist/features/website.js.map +0 -1
  73. package/dist/i18n/index.d.ts +0 -209
  74. package/dist/i18n/index.d.ts.map +0 -1
  75. package/dist/i18n/index.js.map +0 -1
  76. package/dist/index.d.ts +0 -5
  77. package/dist/index.d.ts.map +0 -1
  78. package/dist/index.js.map +0 -1
  79. package/dist/main.d.ts +0 -12
  80. package/dist/main.d.ts.map +0 -1
  81. package/dist/main.js.map +0 -1
  82. package/dist/types.d.ts +0 -48
  83. package/dist/types.d.ts.map +0 -1
  84. package/dist/types.js +0 -6
  85. package/dist/types.js.map +0 -1
@@ -26,6 +26,11 @@ export const URLS = {
26
26
  calendar: 'https://ical.nbtca.space',
27
27
  email: 'contact@nbtca.space',
28
28
  };
29
+ export const GITHUB_REPO = {
30
+ owner: 'nbtca',
31
+ repo: 'documents',
32
+ branch: 'main',
33
+ };
29
34
  export const APP_INFO = {
30
35
  name: 'Prompt',
31
36
  version: readPackageVersion(),
@@ -35,4 +40,3 @@ export const APP_INFO = {
35
40
  license: 'MIT',
36
41
  repository: 'https://github.com/nbtca/prompt'
37
42
  };
38
- //# sourceMappingURL=data.js.map
@@ -0,0 +1,5 @@
1
+ import path from 'path';
2
+ export function getConfigDir() {
3
+ const homeDir = process.env['HOME'] || process.env['USERPROFILE'] || '';
4
+ return path.join(homeDir, '.nbtca');
5
+ }
@@ -1,13 +1,10 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import { getConfigDir } from './paths.js';
3
4
  const DEFAULT_PREFERENCES = {
4
5
  iconMode: 'auto',
5
6
  colorMode: 'auto',
6
7
  };
7
- function getConfigDir() {
8
- const homeDir = process.env['HOME'] || process.env['USERPROFILE'] || '';
9
- return path.join(homeDir, '.nbtca');
10
- }
11
8
  function getPreferencesPath() {
12
9
  return path.join(getConfigDir(), 'preferences.json');
13
10
  }
@@ -20,9 +17,6 @@ function ensureConfigDir() {
20
17
  export function loadPreferences() {
21
18
  try {
22
19
  const prefPath = getPreferencesPath();
23
- if (!fs.existsSync(prefPath)) {
24
- return { ...DEFAULT_PREFERENCES };
25
- }
26
20
  const raw = JSON.parse(fs.readFileSync(prefPath, 'utf-8'));
27
21
  const iconMode = raw.iconMode === 'ascii' || raw.iconMode === 'unicode' || raw.iconMode === 'auto'
28
22
  ? raw.iconMode
@@ -87,4 +81,3 @@ export function applyColorModePreference(forcePlain) {
87
81
  delete process.env['NO_COLOR'];
88
82
  }
89
83
  }
90
- //# sourceMappingURL=preferences.js.map
@@ -3,18 +3,31 @@ function localeSupportsUnicode() {
3
3
  const locale = `${process.env['LC_ALL'] || ''} ${process.env['LANG'] || ''}`.toLowerCase();
4
4
  return locale.includes('utf-8') || locale.includes('utf8');
5
5
  }
6
+ let cachedUseUnicode = null;
6
7
  export function useUnicodeIcons() {
8
+ if (cachedUseUnicode !== null)
9
+ return cachedUseUnicode;
7
10
  const configured = resolveIconMode();
8
- if (configured === 'ascii')
11
+ if (configured === 'ascii') {
12
+ cachedUseUnicode = false;
9
13
  return false;
10
- if (configured === 'unicode')
14
+ }
15
+ if (configured === 'unicode') {
16
+ cachedUseUnicode = true;
11
17
  return true;
18
+ }
12
19
  const term = (process.env['TERM'] || '').toLowerCase();
13
- if (!process.stdout.isTTY || term === 'dumb')
20
+ if (!process.stdout.isTTY || term === 'dumb') {
21
+ cachedUseUnicode = false;
14
22
  return false;
15
- return localeSupportsUnicode();
23
+ }
24
+ cachedUseUnicode = localeSupportsUnicode();
25
+ return cachedUseUnicode;
26
+ }
27
+ /** Invalidate the cached icon mode (call after theme changes). */
28
+ export function resetIconCache() {
29
+ cachedUseUnicode = null;
16
30
  }
17
31
  export function pickIcon(unicodeIcon, asciiIcon) {
18
32
  return useUnicodeIcons() ? unicodeIcon : asciiIcon;
19
33
  }
20
- //# sourceMappingURL=icons.js.map
package/dist/core/logo.js CHANGED
@@ -36,7 +36,7 @@ function printDescription() {
36
36
  /**
37
37
  * Attempt to read and display logo file
38
38
  */
39
- export async function printLogo() {
39
+ export function printLogo() {
40
40
  if (!process.stdout.isTTY) {
41
41
  return;
42
42
  }
@@ -68,4 +68,3 @@ export async function printLogo() {
68
68
  printDescription();
69
69
  }
70
70
  }
71
- //# sourceMappingURL=logo.js.map
package/dist/core/menu.js CHANGED
@@ -1,39 +1,24 @@
1
1
  /**
2
2
  * Minimalist menu system
3
- * Modern @clack/prompts select() with emoji icons and hint text
4
3
  */
5
- import { select, isCancel, outro, note } from '@clack/prompts';
4
+ import { select, isCancel, outro } from '@clack/prompts';
6
5
  import chalk from 'chalk';
7
6
  import { showCalendar } from '../features/calendar.js';
8
- import { openRepairService } from '../features/repair.js';
9
7
  import { showDocsMenu } from '../features/docs.js';
10
8
  import { showServiceStatus } from '../features/status.js';
11
- import { showThemeMenu } from '../features/theme.js';
12
- import { openHomepage, openGithub, openRoadmap } from '../features/website.js';
13
- import { printDivider, printNewLine, success, warning } from './ui.js';
14
- import { pickIcon } from './icons.js';
15
- import { padEndV } from './text.js';
16
- import { APP_INFO, URLS } from '../config/data.js';
17
- import { t, getCurrentLanguage, setLanguage, clearTranslationCache } from '../i18n/index.js';
18
- /**
19
- * Get main menu options — 6 items
20
- */
9
+ import { showLinksMenu } from '../features/links.js';
10
+ import { showSettingsMenu } from '../features/settings.js';
11
+ import { t } from '../i18n/index.js';
21
12
  function getMainMenuOptions() {
22
13
  const trans = t();
23
14
  return [
24
15
  { value: 'events', label: trans.menu.events, hint: trans.menu.eventsDesc },
25
- { value: 'repair', label: trans.menu.repair, hint: trans.menu.repairDesc },
26
16
  { value: 'docs', label: trans.menu.docs, hint: trans.menu.docsDesc },
27
17
  { value: 'status', label: trans.menu.status, hint: trans.menu.statusDesc },
28
18
  { value: 'links', label: trans.menu.links, hint: trans.menu.linksDesc },
29
- { value: 'about', label: trans.menu.about, hint: trans.menu.aboutDesc },
30
- { value: 'theme', label: trans.menu.theme, hint: trans.menu.themeDesc },
31
- { value: 'language', label: trans.menu.language, hint: trans.menu.languageDesc },
19
+ { value: 'settings', label: trans.menu.settings, hint: trans.menu.settingsDesc },
32
20
  ];
33
21
  }
34
- /**
35
- * Display main menu — loops until user exits via Ctrl+C
36
- */
37
22
  export async function showMainMenu() {
38
23
  while (true) {
39
24
  const trans = t();
@@ -46,22 +31,13 @@ export async function showMainMenu() {
46
31
  process.exit(0);
47
32
  }
48
33
  await runMenuAction(action);
49
- printNewLine();
50
- printDivider();
51
- printNewLine();
52
34
  }
53
35
  }
54
- /**
55
- * Handle user action
56
- */
57
36
  export async function runMenuAction(action) {
58
37
  switch (action) {
59
38
  case 'events':
60
39
  await showCalendar();
61
40
  break;
62
- case 'repair':
63
- await openRepairService();
64
- break;
65
41
  case 'docs':
66
42
  await showDocsMenu();
67
43
  break;
@@ -71,99 +47,8 @@ export async function runMenuAction(action) {
71
47
  case 'links':
72
48
  await showLinksMenu();
73
49
  break;
74
- case 'website':
75
- await openHomepage();
76
- break;
77
- case 'github':
78
- await openGithub();
50
+ case 'settings':
51
+ await showSettingsMenu();
79
52
  break;
80
- case 'roadmap':
81
- await openRoadmap();
82
- break;
83
- case 'about':
84
- showAbout();
85
- break;
86
- case 'theme':
87
- await showThemeMenu();
88
- break;
89
- case 'language':
90
- await showLanguageMenu();
91
- break;
92
- }
93
- }
94
- /**
95
- * Links submenu — website, GitHub, roadmap
96
- */
97
- async function showLinksMenu() {
98
- const trans = t();
99
- const link = await select({
100
- message: trans.menu.chooseLink,
101
- options: [
102
- { value: 'website', label: trans.menu.website, hint: 'nbtca.space' },
103
- { value: 'github', label: 'GitHub', hint: 'github.com/nbtca' },
104
- { value: 'roadmap', label: trans.menu.roadmap, hint: trans.menu.roadmapDesc },
105
- ],
106
- });
107
- if (isCancel(link))
108
- return;
109
- switch (link) {
110
- case 'website':
111
- await openHomepage();
112
- break;
113
- case 'github':
114
- await openGithub();
115
- break;
116
- case 'roadmap':
117
- await openRoadmap();
118
- break;
119
- }
120
- }
121
- /**
122
- * Display about information using clack note() box
123
- */
124
- function showAbout() {
125
- const trans = t();
126
- const pad = 12;
127
- const row = (label, value) => `${chalk.dim(padEndV(label, pad))}${value}`;
128
- const link = (label, url) => row(label, chalk.cyan(url));
129
- const content = [
130
- row(trans.about.project, APP_INFO.name),
131
- row(trans.about.version, `v${APP_INFO.version}`),
132
- row(trans.about.description, APP_INFO.fullDescription),
133
- '',
134
- link(trans.about.github, APP_INFO.repository),
135
- link(trans.about.website, URLS.homepage),
136
- link(trans.about.email, URLS.email),
137
- '',
138
- row(trans.about.license, `MIT ${pickIcon('·', '|')} Author: m1ngsama`),
139
- ].join('\n');
140
- note(content, trans.about.title);
141
- }
142
- /**
143
- * Display language selection menu
144
- */
145
- async function showLanguageMenu() {
146
- const trans = t();
147
- const currentLang = getCurrentLanguage();
148
- const language = await select({
149
- message: trans.language.selectLanguage,
150
- options: [
151
- { value: 'zh', label: trans.language.zh, hint: currentLang === 'zh' ? `${pickIcon('✓', '*')} current` : undefined },
152
- { value: 'en', label: trans.language.en, hint: currentLang === 'en' ? `${pickIcon('✓', '*')} current` : undefined },
153
- ],
154
- initialValue: currentLang,
155
- });
156
- if (isCancel(language))
157
- return;
158
- if (language !== currentLang) {
159
- const persisted = setLanguage(language);
160
- clearTranslationCache();
161
- if (persisted) {
162
- success(t().language.changed);
163
- }
164
- else {
165
- warning(t().language.changedSessionOnly);
166
- }
167
53
  }
168
54
  }
169
- //# sourceMappingURL=menu.js.map
package/dist/core/text.js CHANGED
@@ -13,10 +13,17 @@ function charWidth(ch) {
13
13
  (cp >= 0xFFE0 && cp <= 0xFFE6) // Fullwidth Signs
14
14
  ) ? 2 : 1;
15
15
  }
16
- /** Total visual width of a string (CJK characters count as 2). */
16
+ /** Strip ANSI escape sequences from a string. */
17
+ // eslint-disable-next-line no-control-regex
18
+ const ANSI_RE = /\x1b\[[0-9;]*m/g;
19
+ export function stripAnsi(str) {
20
+ return str.replace(ANSI_RE, '');
21
+ }
22
+ /** Total visual width of a string (CJK characters count as 2, ANSI codes ignored). */
17
23
  export function visualWidth(str) {
24
+ const plain = stripAnsi(str);
18
25
  let w = 0;
19
- for (const ch of str)
26
+ for (const ch of plain)
20
27
  w += charWidth(ch);
21
28
  return w;
22
29
  }
@@ -40,4 +47,3 @@ export function truncate(str, maxWidth) {
40
47
  }
41
48
  return str.slice(0, i) + '...';
42
49
  }
43
- //# sourceMappingURL=text.js.map
package/dist/core/ui.js CHANGED
@@ -62,4 +62,3 @@ export function createSpinner(msg) {
62
62
  s.start(msg);
63
63
  return s;
64
64
  }
65
- //# sourceMappingURL=ui.js.map
@@ -4,13 +4,15 @@
4
4
  * before readline processes them. This avoids patching the keypress layer which
5
5
  * breaks in Node.js v25+ (emitKeys generator crash).
6
6
  */
7
- // Maps single-byte vim keys to terminal escape sequences
7
+ // Maps single-byte vim keys to terminal escape sequences (ranger-style hjkl)
8
8
  const VIM_TO_SEQ = {
9
+ h: Buffer.from('\u0003'), // back/cancel (ranger: go to parent)
9
10
  j: Buffer.from('\u001b[B'), // down arrow
10
11
  k: Buffer.from('\u001b[A'), // up arrow
11
- g: Buffer.from('\u001b[H'), // home
12
- G: Buffer.from('\u001b[F'), // end
13
- q: Buffer.from('\u0003'), // ctrl+c (cancel)
12
+ l: Buffer.from('\r'), // enter/confirm (ranger: open/enter)
13
+ g: Buffer.from('\u001b[H'), // home (first item)
14
+ G: Buffer.from('\u001b[F'), // end (last item)
15
+ q: Buffer.from('\u0003'), // quit
14
16
  };
15
17
  export function enableVimKeys() {
16
18
  const stdin = process.stdin;
@@ -29,4 +31,3 @@ export function enableVimKeys() {
29
31
  return originalEmit(event, ...args);
30
32
  };
31
33
  }
32
- //# sourceMappingURL=vim-keys.js.map
@@ -5,16 +5,18 @@
5
5
  import axios from 'axios';
6
6
  import ICAL from 'ical.js';
7
7
  import chalk from 'chalk';
8
- import { info, printDivider, createSpinner } from '../core/ui.js';
8
+ import { select, isCancel } from '@clack/prompts';
9
+ import { info, createSpinner } from '../core/ui.js';
9
10
  import { pickIcon } from '../core/icons.js';
10
11
  import { padEndV, truncate } from '../core/text.js';
11
12
  import { t } from '../i18n/index.js';
13
+ import { APP_INFO, URLS } from '../config/data.js';
12
14
  export async function fetchEvents() {
13
15
  try {
14
16
  const response = await axios.get('https://ical.nbtca.space', {
15
17
  timeout: 5000,
16
18
  headers: {
17
- 'User-Agent': 'NBTCA-CLI/2.3.1'
19
+ 'User-Agent': `NBTCA-CLI/${APP_INFO.version}`
18
20
  }
19
21
  });
20
22
  const jcalData = ICAL.parse(response.data);
@@ -28,13 +30,14 @@ export async function fetchEvents() {
28
30
  const startDate = event.startDate.toJSDate();
29
31
  if (startDate >= now && startDate <= thirtyDaysLater) {
30
32
  const trans = t();
31
- const untitledEvent = trans.calendar.eventName === 'Event Name' ? 'Untitled Event' : '未命名活动';
32
- const tbdLocation = trans.calendar.location === 'Location' ? 'TBD' : '待定';
33
+ const untitledEvent = trans.calendar.untitledEvent;
34
+ const tbdLocation = trans.calendar.tbdLocation;
33
35
  events.push({
34
36
  date: formatDate(startDate),
35
37
  time: formatTime(startDate),
36
38
  title: event.summary || untitledEvent,
37
39
  location: event.location || tbdLocation,
40
+ description: event.description || '',
38
41
  startDate
39
42
  });
40
43
  }
@@ -42,8 +45,9 @@ export async function fetchEvents() {
42
45
  events.sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
43
46
  return events;
44
47
  }
45
- catch {
46
- throw new Error(t().calendar.error);
48
+ catch (err) {
49
+ const detail = err instanceof Error ? err.message : String(err);
50
+ throw new Error(`${t().calendar.error}: ${detail}`);
47
51
  }
48
52
  }
49
53
  export function serializeEvents(events) {
@@ -52,6 +56,7 @@ export function serializeEvents(events) {
52
56
  time: event.time,
53
57
  title: event.title,
54
58
  location: event.location,
59
+ description: event.description,
55
60
  startDateISO: event.startDate.toISOString()
56
61
  }));
57
62
  }
@@ -108,14 +113,31 @@ export function renderEventsTable(events, options) {
108
113
  lines.push(dim(bottom));
109
114
  return lines.join('\n');
110
115
  }
111
- export function displayEvents(events) {
116
+ function displayEvents(events) {
112
117
  if (events.length === 0) {
113
118
  info(t().calendar.noEvents);
114
119
  return;
115
120
  }
116
121
  console.log();
117
122
  console.log(renderEventsTable(events, { color: true }));
118
- printDivider();
123
+ console.log(chalk.dim(` ${pickIcon('📅', '[ical]')} ${t().calendar.subscribeHint}: ${URLS.calendar}`));
124
+ console.log();
125
+ }
126
+ async function showEventDetail(event) {
127
+ const trans = t();
128
+ console.log();
129
+ console.log(chalk.bold.cyan(` ${event.title}`));
130
+ console.log(chalk.dim(` ${event.date} ${event.time} ${pickIcon('·', '|')} ${event.location}`));
131
+ if (event.description) {
132
+ console.log();
133
+ const lines = event.description.trim().split('\n');
134
+ for (const line of lines) {
135
+ console.log(chalk.white(` ${line}`));
136
+ }
137
+ }
138
+ else {
139
+ console.log(chalk.dim(` ${trans.calendar.noDescription}`));
140
+ }
119
141
  console.log();
120
142
  }
121
143
  export async function showCalendar() {
@@ -125,6 +147,26 @@ export async function showCalendar() {
125
147
  const events = await fetchEvents();
126
148
  s.stop(`${events.length} ${trans.calendar.eventsFound}`);
127
149
  displayEvents(events);
150
+ if (events.length > 0) {
151
+ const options = [
152
+ ...events.map((e, i) => ({
153
+ value: String(i),
154
+ label: `${e.date} ${e.time} ${e.title}`,
155
+ hint: e.location,
156
+ })),
157
+ { value: '__back__', label: chalk.dim(trans.common.back) },
158
+ ];
159
+ const selected = await select({
160
+ message: trans.calendar.viewDetail,
161
+ options,
162
+ });
163
+ if (!isCancel(selected) && selected !== '__back__') {
164
+ const idx = Number.parseInt(selected, 10);
165
+ const event = events[idx];
166
+ if (event)
167
+ await showEventDetail(event);
168
+ }
169
+ }
128
170
  }
129
171
  catch {
130
172
  s.error(trans.calendar.error);
@@ -132,4 +174,3 @@ export async function showCalendar() {
132
174
  console.log();
133
175
  }
134
176
  }
135
- //# sourceMappingURL=calendar.js.map