xw-devtool-cli 1.0.44 → 1.0.45

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_EN.md CHANGED
@@ -30,7 +30,7 @@ Key features include: Base64 encoding/decoding, image format conversion, image <
30
30
  - **Timestamp**: Quickly get current millisecond timestamp.
31
31
  - **Calculation**: Calculate date differences or offsets (Add/Subtract).
32
32
  - **Console Clock**: Show real-time current time in terminal, press `Enter` or `Q` to go back.
33
- - **Countdown Timer**: Enter countdown minutes to start timer; once it reaches zero, it keeps counting with a negative sign (such as `-00:00:05`).
33
+ - **Countdown Timer**: Select seconds/minutes/hours first, then enter the value to start; once it reaches zero, it keeps counting with a negative sign (such as `-00:00:05`).
34
34
  - **Stopwatch**: Start from `00:00:00` and count up every second in terminal.
35
35
  - **Dev Tools**:
36
36
  - **URL Encode/Decode**
@@ -234,7 +234,7 @@ s. Settings (Language)
234
234
 
235
235
  ### Countdown Timer
236
236
  - Select `Countdown Timer` in the menu.
237
- - Enter minutes to start the countdown.
237
+ - Select unit first (Seconds / Minutes / Hours), then enter the value.
238
238
  - After reaching zero, the timer continues with a negative sign (for example `-00:00:01`).
239
239
  - Press `Enter` or `Q` to go back.
240
240
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xw-devtool-cli",
3
- "version": "1.0.44",
3
+ "version": "1.0.45",
4
4
  "type": "module",
5
5
  "description": "基于node的开发者助手cli",
6
6
  "main": "index.js",
@@ -62,17 +62,24 @@ function fitText(text, width) {
62
62
 
63
63
  function renderClockBlock() {
64
64
  const { timeText, dateText } = getNowDisplay(getLocale());
65
- const innerWidth = 34;
65
+ const contentLines = [
66
+ i18next.t('clock.title'),
67
+ '',
68
+ `${i18next.t('clock.now')}${timeText}`,
69
+ dateText,
70
+ i18next.t('clock.exitTip')
71
+ ];
72
+ const innerWidth = Math.max(34, ...contentLines.map((text) => getDisplayWidth(text)));
66
73
  const top = `┌${'─'.repeat(innerWidth + 2)}┐`;
67
74
  const bottom = `└${'─'.repeat(innerWidth + 2)}┘`;
68
75
  const line = (text) => `│ ${fitText(text, innerWidth)} │`;
69
76
  return [
70
77
  top,
71
- line(i18next.t('clock.title')),
78
+ line(contentLines[0]),
72
79
  line(''),
73
- line(`${i18next.t('clock.now')}${timeText}`),
74
- line(dateText),
75
- line(i18next.t('clock.exitTip')),
80
+ line(contentLines[2]),
81
+ line(contentLines[3]),
82
+ line(contentLines[4]),
76
83
  bottom
77
84
  ];
78
85
  }
@@ -1,6 +1,7 @@
1
1
  import inquirer from 'inquirer';
2
2
  import readline from 'readline';
3
3
  import i18next from '../i18n.js';
4
+ import { selectFromMenu } from '../utils/menu.js';
4
5
 
5
6
  function getCharDisplayWidth(char) {
6
7
  if (/[\u0000-\u001f\u007f]/.test(char)) {
@@ -50,7 +51,7 @@ function formatSeconds(seconds) {
50
51
  return `${sign}${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`;
51
52
  }
52
53
 
53
- function parseMinutes(input) {
54
+ function parseAmount(input) {
54
55
  const value = Number(input);
55
56
  if (!Number.isFinite(value) || value < 0) {
56
57
  return null;
@@ -60,43 +61,78 @@ function parseMinutes(input) {
60
61
 
61
62
  function renderCountdownBlock(totalSeconds, elapsedSeconds) {
62
63
  const remainSeconds = totalSeconds - elapsedSeconds;
63
- const innerWidth = 34;
64
+ const contentLines = [
65
+ i18next.t('countdown.title'),
66
+ '',
67
+ `${i18next.t('countdown.remaining')}${formatSeconds(remainSeconds)}`,
68
+ `${i18next.t('countdown.total')}${formatSeconds(totalSeconds)}`,
69
+ `${i18next.t('countdown.status')}${remainSeconds < 0 ? i18next.t('countdown.statusOvertime') : i18next.t('countdown.statusCounting')}`,
70
+ i18next.t('countdown.exitTip')
71
+ ];
72
+ const innerWidth = Math.max(34, ...contentLines.map((text) => getDisplayWidth(text)));
64
73
  const top = `┌${'─'.repeat(innerWidth + 2)}┐`;
65
74
  const bottom = `└${'─'.repeat(innerWidth + 2)}┘`;
66
75
  const line = (text) => `│ ${fitText(text, innerWidth)} │`;
67
- const status = remainSeconds < 0 ? i18next.t('countdown.statusOvertime') : i18next.t('countdown.statusCounting');
68
76
 
69
77
  return [
70
78
  top,
71
- line(i18next.t('countdown.title')),
79
+ line(contentLines[0]),
72
80
  line(''),
73
- line(`${i18next.t('countdown.remaining')}${formatSeconds(remainSeconds)}`),
74
- line(`${i18next.t('countdown.total')}${formatSeconds(totalSeconds)}`),
75
- line(`${i18next.t('countdown.status')}${status}`),
76
- line(i18next.t('countdown.exitTip')),
81
+ line(contentLines[2]),
82
+ line(contentLines[3]),
83
+ line(contentLines[4]),
84
+ line(contentLines[5]),
77
85
  bottom
78
86
  ];
79
87
  }
80
88
 
81
89
  export async function countdownHandler() {
82
- const { minutesInput } = await inquirer.prompt([
90
+ const unitOptions = [
91
+ { name: i18next.t('countdown.unitSecond'), value: 'second' },
92
+ { name: i18next.t('countdown.unitMinute'), value: 'minute' },
93
+ { name: i18next.t('countdown.unitHour'), value: 'hour' }
94
+ ];
95
+
96
+ const unit = await selectFromMenu(
97
+ i18next.t('countdown.selectUnit'),
98
+ unitOptions,
99
+ true,
100
+ i18next.t('common.back')
101
+ );
102
+
103
+ if (unit === '__BACK__') {
104
+ return;
105
+ }
106
+
107
+ const unitNameMap = {
108
+ second: i18next.t('countdown.unitSecond'),
109
+ minute: i18next.t('countdown.unitMinute'),
110
+ hour: i18next.t('countdown.unitHour')
111
+ };
112
+
113
+ const { amountInput } = await inquirer.prompt([
83
114
  {
84
115
  type: 'input',
85
- name: 'minutesInput',
86
- message: i18next.t('countdown.inputPrompt'),
116
+ name: 'amountInput',
117
+ message: i18next.t('countdown.inputPrompt', { unit: unitNameMap[unit] }),
87
118
  default: '1',
88
119
  validate: (input) => {
89
- const value = parseMinutes(input);
120
+ const value = parseAmount(input);
90
121
  if (value === null) {
91
- return i18next.t('countdown.invalidMinutes');
122
+ return i18next.t('countdown.invalidAmount');
92
123
  }
93
124
  return true;
94
125
  }
95
126
  }
96
127
  ]);
97
128
 
98
- const minutes = parseMinutes(minutesInput);
99
- const totalSeconds = Math.round(minutes * 60);
129
+ const amount = parseAmount(amountInput);
130
+ const multiplierMap = {
131
+ second: 1,
132
+ minute: 60,
133
+ hour: 3600
134
+ };
135
+ const totalSeconds = Math.round(amount * multiplierMap[unit]);
100
136
 
101
137
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
102
138
  console.log(i18next.t('countdown.nonTtyTip', { value: formatSeconds(totalSeconds) }));
@@ -47,18 +47,25 @@ function formatSeconds(seconds) {
47
47
  }
48
48
 
49
49
  function renderStopwatchBlock(elapsedSeconds) {
50
- const innerWidth = 34;
50
+ const contentLines = [
51
+ i18next.t('stopwatch.title'),
52
+ '',
53
+ `${i18next.t('stopwatch.elapsed')}${formatSeconds(elapsedSeconds)}`,
54
+ `${i18next.t('stopwatch.status')}${i18next.t('stopwatch.statusRunning')}`,
55
+ i18next.t('stopwatch.exitTip')
56
+ ];
57
+ const innerWidth = Math.max(34, ...contentLines.map((text) => getDisplayWidth(text)));
51
58
  const top = `┌${'─'.repeat(innerWidth + 2)}┐`;
52
59
  const bottom = `└${'─'.repeat(innerWidth + 2)}┘`;
53
60
  const line = (text) => `│ ${fitText(text, innerWidth)} │`;
54
61
 
55
62
  return [
56
63
  top,
57
- line(i18next.t('stopwatch.title')),
64
+ line(contentLines[0]),
58
65
  line(''),
59
- line(`${i18next.t('stopwatch.elapsed')}${formatSeconds(elapsedSeconds)}`),
60
- line(`${i18next.t('stopwatch.status')}${i18next.t('stopwatch.statusRunning')}`),
61
- line(i18next.t('stopwatch.exitTip')),
66
+ line(contentLines[2]),
67
+ line(contentLines[3]),
68
+ line(contentLines[4]),
62
69
  bottom
63
70
  ];
64
71
  }
package/src/locales/en.js CHANGED
@@ -142,8 +142,12 @@ export default {
142
142
  },
143
143
  countdown: {
144
144
  title: 'Countdown Timer',
145
- inputPrompt: 'Enter countdown minutes:',
146
- invalidMinutes: 'Please enter a number greater than or equal to 0',
145
+ selectUnit: 'Select countdown unit',
146
+ unitSecond: 'Seconds',
147
+ unitMinute: 'Minutes',
148
+ unitHour: 'Hours',
149
+ inputPrompt: 'Enter countdown {{unit}} value:',
150
+ invalidAmount: 'Please enter a number greater than or equal to 0',
147
151
  remaining: 'Remaining: ',
148
152
  total: 'Total: ',
149
153
  status: 'Status: ',
package/src/locales/zh.js CHANGED
@@ -142,8 +142,12 @@ export default {
142
142
  },
143
143
  countdown: {
144
144
  title: '倒计时工具',
145
- inputPrompt: '请输入倒计时分钟数:',
146
- invalidMinutes: '请输入大于等于 0 的数字分钟数',
145
+ selectUnit: '请选择倒计时单位',
146
+ unitSecond: '',
147
+ unitMinute: '分钟',
148
+ unitHour: '小时',
149
+ inputPrompt: '请输入倒计时{{unit}}数值:',
150
+ invalidAmount: '请输入大于等于 0 的数字',
147
151
  remaining: '剩余时间: ',
148
152
  total: '倒计总时: ',
149
153
  status: '当前状态: ',