Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nbtca/prompt",
"version": "1.0.5",
"version": "1.0.6",
"type": "module",
"main": "dist/index.js",
"bin": {
Expand Down
2 changes: 1 addition & 1 deletion src/config/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const URLS = {

export const APP_INFO = {
name: 'Prompt',
version: '1.0.2',
version: '1.0.6',
description: '浙大宁波理工学院计算机协会',
fullDescription: 'NBTCA Prompt - 极简命令行工具',
author: 'm1ngsama <[email protected]>',
Expand Down
168 changes: 110 additions & 58 deletions src/core/menu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,65 +11,76 @@ import { showDocsMenu } from '../features/docs.js';
import { openHomepage, openGithub } from '../features/website.js';
import { printDivider, printNewLine } from './ui.js';
import { APP_INFO, URLS } from '../config/data.js';
import { t, getCurrentLanguage, setLanguage, clearTranslationCache, type Language } from '../i18n/index.js';

/**
* Main menu options
* Get main menu options
*/
const MAIN_MENU = [
{
name: '[*] Recent Events ' + chalk.gray('View upcoming events in next 30 days'),
value: 'events',
short: 'Recent Events'
},
{
name: '[*] Repair Service ' + chalk.gray('Computer repair and software installation'),
value: 'repair',
short: 'Repair Service'
},
{
name: '[*] Knowledge Base ' + chalk.gray('Technical docs and tutorials'),
value: 'docs',
short: 'Knowledge Base'
},
{
name: '[*] Official Website ' + chalk.gray('Visit NBTCA homepage'),
value: 'website',
short: 'Official Website'
},
{
name: '[*] GitHub ' + chalk.gray('Open source projects and code'),
value: 'github',
short: 'GitHub'
},
{
name: '[?] About ' + chalk.gray('Project info and help'),
value: 'about',
short: 'About'
},
new inquirer.Separator(' '),
{
name: chalk.dim('[x] Exit'),
value: 'exit',
short: 'Exit'
}
];
function getMainMenuOptions() {
const trans = t();
return [
{
name: '[*] ' + trans.menu.events.padEnd(16) + ' ' + chalk.gray(trans.menu.eventsDesc),
value: 'events',
short: trans.menu.events
},
{
name: '[*] ' + trans.menu.repair.padEnd(16) + ' ' + chalk.gray(trans.menu.repairDesc),
value: 'repair',
short: trans.menu.repair
},
{
name: '[*] ' + trans.menu.docs.padEnd(16) + ' ' + chalk.gray(trans.menu.docsDesc),
value: 'docs',
short: trans.menu.docs
},
{
name: '[*] ' + trans.menu.website.padEnd(16) + ' ' + chalk.gray(trans.menu.websiteDesc),
value: 'website',
short: trans.menu.website
},
{
name: '[*] ' + trans.menu.github.padEnd(16) + ' ' + chalk.gray(trans.menu.githubDesc),
value: 'github',
short: trans.menu.github
},
{
name: '[?] ' + trans.menu.about.padEnd(16) + ' ' + chalk.gray(trans.menu.aboutDesc),
value: 'about',
short: trans.menu.about
},
{
name: '[⚙] ' + trans.menu.language.padEnd(16) + ' ' + chalk.gray(trans.menu.languageDesc),
value: 'language',
short: trans.menu.language
},
new inquirer.Separator(' '),
{
name: chalk.dim('[x] ' + trans.common.exit),
value: 'exit',
short: trans.common.exit
}
];
}

/**
* Display main menu
*/
export async function showMainMenu(): Promise<void> {
while (true) {
try {
const trans = t();

// Show keybinding hints
console.log(chalk.dim(' Navigation: j/k or ↑/↓ | Jump: g/G | Quit: q or Ctrl+C'));
console.log(chalk.dim(' ' + trans.menu.navigationHint));
console.log();

const { action } = await inquirer.prompt<{ action: string }>([
{
type: 'list',
name: 'action',
message: 'Choose an action',
choices: MAIN_MENU,
message: trans.menu.chooseAction,
choices: getMainMenuOptions(),
pageSize: 15,
loop: false
} as any
Expand All @@ -78,7 +89,7 @@ export async function showMainMenu(): Promise<void> {
// Handle user selection
if (action === 'exit') {
console.log();
console.log(chalk.dim('Goodbye!'));
console.log(chalk.dim(trans.common.goodbye));
process.exit(0);
}

Expand All @@ -92,7 +103,7 @@ export async function showMainMenu(): Promise<void> {
// Handle Ctrl+C exit
if (err.message?.includes('User force closed')) {
console.log();
console.log(chalk.dim('Goodbye!'));
console.log(chalk.dim(t().common.goodbye));
process.exit(0);
}
throw err;
Expand Down Expand Up @@ -129,6 +140,10 @@ async function handleAction(action: string): Promise<void> {
showAbout();
break;

case 'language':
await showLanguageMenu();
break;

default:
console.log(chalk.gray('Unknown action'));
}
Expand All @@ -138,24 +153,61 @@ async function handleAction(action: string): Promise<void> {
* Display about information
*/
function showAbout(): void {
const trans = t();
console.log();
console.log(chalk.bold('>> About NBTCA'));
console.log(chalk.bold('>> ' + trans.about.title));
console.log();
console.log(chalk.dim('Project ') + APP_INFO.name);
console.log(chalk.dim('Version ') + `v${APP_INFO.version}`);
console.log(chalk.dim('Description ') + APP_INFO.fullDescription);
console.log(chalk.dim(trans.about.project.padEnd(12)) + APP_INFO.name);
console.log(chalk.dim(trans.about.version.padEnd(12)) + `v${APP_INFO.version}`);
console.log(chalk.dim(trans.about.description.padEnd(12)) + APP_INFO.fullDescription);
console.log();
console.log(chalk.dim('GitHub ') + chalk.cyan(APP_INFO.repository));
console.log(chalk.dim('Website ') + chalk.cyan(URLS.homepage));
console.log(chalk.dim('Email ') + chalk.cyan(URLS.email));
console.log(chalk.dim(trans.about.github.padEnd(12)) + chalk.cyan(APP_INFO.repository));
console.log(chalk.dim(trans.about.website.padEnd(12)) + chalk.cyan(URLS.homepage));
console.log(chalk.dim(trans.about.email.padEnd(12)) + chalk.cyan(URLS.email));
console.log();
console.log(chalk.dim('Features:'));
console.log(' - View recent association events');
console.log(' - Online repair service');
console.log(' - Technical knowledge base access');
console.log(' - Quick access to website and GitHub');
console.log(chalk.dim(trans.about.features));
console.log(' ' + trans.about.feature1);
console.log(' ' + trans.about.feature2);
console.log(' ' + trans.about.feature3);
console.log(' ' + trans.about.feature4);
console.log();
console.log(chalk.dim('License ') + 'MIT License');
console.log(chalk.dim('Author ') + 'm1ngsama');
console.log(chalk.dim(trans.about.license.padEnd(12)) + 'MIT License');
console.log(chalk.dim(trans.about.author.padEnd(12)) + 'm1ngsama');
console.log();
}

/**
* Display language selection menu
*/
async function showLanguageMenu(): Promise<void> {
const trans = t();
const currentLang = getCurrentLanguage();

console.log();
console.log(chalk.bold('>> ' + trans.language.title));
console.log();
console.log(chalk.dim(trans.language.currentLanguage + ': ') + chalk.cyan(trans.language[currentLang]));
console.log();

const { language } = await inquirer.prompt<{ language: Language }>([
{
type: 'list',
name: 'language',
message: trans.language.selectLanguage,
choices: [
{ name: trans.language.zh, value: 'zh' as Language },
{ name: trans.language.en, value: 'en' as Language }
],
default: currentLang
}
]);

if (language !== currentLang) {
setLanguage(language);
clearTranslationCache();
console.log();
console.log(chalk.green('✓ ' + t().language.changed));
console.log();
}
}

38 changes: 23 additions & 15 deletions src/features/calendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import axios from 'axios';
import ICAL from 'ical.js';
import chalk from 'chalk';
import { error, info, printDivider } from '../core/ui.js';
import { t } from '../i18n/index.js';

/**
* 活动接口
Expand Down Expand Up @@ -43,13 +44,17 @@ export async function fetchEvents(): Promise<Event[]> {
const event = new ICAL.Event(vevent);
const startDate = event.startDate.toJSDate();

// 只显示未来30天内的活动
// Only show events within next 30 days
if (startDate >= now && startDate <= thirtyDaysLater) {
const trans = t();
const untitledEvent = trans.calendar.eventName === 'Event Name' ? 'Untitled Event' : '未命名活动';
const tbdLocation = trans.calendar.location === 'Location' ? 'TBD' : '待定';

events.push({
date: formatDate(startDate),
time: formatTime(startDate),
title: event.summary || '未命名活动',
location: event.location || '待定',
title: event.summary || untitledEvent,
location: event.location || tbdLocation,
startDate: startDate
});
}
Expand All @@ -60,38 +65,40 @@ export async function fetchEvents(): Promise<Event[]> {

return events;
} catch (err) {
throw new Error('获取活动日历失败');
throw new Error(t().calendar.error);
}
}

/**
* 以表格形式显示活动
*/
export function displayEvents(events: Event[]): void {
const trans = t();

if (events.length === 0) {
info('近期暂无活动安排');
info(trans.calendar.noEvents);
return;
}

console.log();
console.log(chalk.cyan.bold(' 近期活动') + chalk.gray(` (最近30天)`));
console.log(chalk.cyan.bold(' ' + trans.calendar.title) + chalk.gray(` ${trans.calendar.subtitle}`));
console.log();

// 表头
// Table header
const dateWidth = 14;
const titleWidth = 25;
const locationWidth = 15;

console.log(
' ' +
chalk.bold('日期时间'.padEnd(dateWidth)) +
chalk.bold('活动名称'.padEnd(titleWidth)) +
chalk.bold('地点')
chalk.bold(trans.calendar.dateTime.padEnd(dateWidth)) +
chalk.bold(trans.calendar.eventName.padEnd(titleWidth)) +
chalk.bold(trans.calendar.location)
);

printDivider();

// 活动列表
// Event list
events.forEach(event => {
const dateTime = `${event.date} ${event.time}`.padEnd(dateWidth);
const title = truncate(event.title, titleWidth - 2).padEnd(titleWidth);
Expand Down Expand Up @@ -140,14 +147,15 @@ function truncate(str: string, maxLength: number): string {
* 主函数:获取并显示活动
*/
export async function showCalendar(): Promise<void> {
const trans = t();
try {
info('正在获取活动日历...');
info(trans.calendar.loading);
const events = await fetchEvents();
console.log('\r' + ' '.repeat(50) + '\r'); // 清除加载提示
console.log('\r' + ' '.repeat(50) + '\r'); // Clear loading message
displayEvents(events);
} catch (err) {
error('无法获取活动日历');
console.log(chalk.gray(' 请稍后重试或访问 https://nbtca.space'));
error(trans.calendar.error);
console.log(chalk.gray(' ' + trans.calendar.errorHint));
console.log();
}
}
Loading