xw-devtool-cli 1.0.2 → 1.0.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xw-devtool-cli",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "type": "module",
5
5
  "description": "基于node的开发者助手cli",
6
6
  "main": "index.js",
@@ -52,6 +52,7 @@
52
52
  "lorem-ipsum": "^2.0.8",
53
53
  "pinyin": "^4.0.0",
54
54
  "sharp": "^0.33.5",
55
+ "tinycolor2": "^1.6.0",
55
56
  "uuid": "^13.0.0"
56
57
  }
57
58
  }
@@ -0,0 +1,56 @@
1
+ import inquirer from 'inquirer';
2
+ import tinycolor from 'tinycolor2';
3
+ import { copy } from '../utils/clipboard.js';
4
+ import { selectFromMenu } from '../utils/menu.js';
5
+
6
+ export async function colorHandler() {
7
+ const { input } = await inquirer.prompt([
8
+ {
9
+ type: 'input',
10
+ name: 'input',
11
+ message: 'Enter color (Hex, RGB, HSL, or Name):',
12
+ validate: (input) => {
13
+ const color = tinycolor(input);
14
+ return color.isValid() || 'Invalid color format';
15
+ }
16
+ }
17
+ ]);
18
+
19
+ const color = tinycolor(input);
20
+
21
+ const results = [];
22
+ results.push(`Hex: ${color.toHexString().toUpperCase()}`);
23
+ results.push(`RGB: ${color.toRgbString()}`);
24
+ results.push(`HSL: ${color.toHslString()}`);
25
+ results.push(`HSV: ${color.toHsvString()}`);
26
+
27
+ // CMYK conversion (manual, as tinycolor doesn't support it directly)
28
+ const rgb = color.toRgb();
29
+ const r = rgb.r / 255;
30
+ const g = rgb.g / 255;
31
+ const b = rgb.b / 255;
32
+ let k = 1 - Math.max(r, g, b);
33
+ let c = (1 - r - k) / (1 - k) || 0;
34
+ let m = (1 - g - k) / (1 - k) || 0;
35
+ let y = (1 - b - k) / (1 - k) || 0;
36
+
37
+ // Round to 2 decimal places
38
+ const toPercent = (n) => Math.round(n * 100);
39
+ const cmyk = `cmyk(${toPercent(c)}%, ${toPercent(m)}%, ${toPercent(y)}%, ${toPercent(k)}%)`;
40
+ results.push(`CMYK: ${cmyk}`);
41
+
42
+ console.log('\n=== Conversion Results ===');
43
+ results.forEach(res => console.log(res));
44
+ console.log('==========================\n');
45
+
46
+ // Allow user to copy one of the formats
47
+ const copyChoice = await selectFromMenu('Select format to copy', [
48
+ { name: 'Hex', value: color.toHexString().toUpperCase() },
49
+ { name: 'RGB', value: color.toRgbString() },
50
+ { name: 'HSL', value: color.toHslString() },
51
+ { name: 'HSV', value: color.toHsvString() },
52
+ { name: 'CMYK', value: cmyk }
53
+ ]);
54
+
55
+ await copy(copyChoice);
56
+ }
@@ -1,6 +1,10 @@
1
1
  import inquirer from 'inquirer';
2
2
  import dayjs from 'dayjs';
3
+ import 'dayjs/locale/zh-cn.js';
3
4
  import { copy } from '../utils/clipboard.js';
5
+ import { selectFromMenu } from '../utils/menu.js';
6
+
7
+ dayjs.locale('zh-cn');
4
8
 
5
9
  export async function timeFormatHandler() {
6
10
  const { input } = await inquirer.prompt([
@@ -35,7 +39,26 @@ export async function timeFormatHandler() {
35
39
  return;
36
40
  }
37
41
 
38
- const formatted = date.format('YYYY-MM-DD HH:mm:ss');
42
+ const formatPattern = await selectFromMenu('Select Output Format', [
43
+ { name: 'YYYY-MM-DD HH:mm:ss', value: 'YYYY-MM-DD HH:mm:ss' },
44
+ { name: 'YYYY-MM-DD HH:mm', value: 'YYYY-MM-DD HH:mm' },
45
+ { name: 'YYYY-MM-DD', value: 'YYYY-MM-DD' },
46
+ { name: 'HH:mm:ss', value: 'HH:mm:ss' },
47
+ { name: 'YYYY-MM-DD HH:mm:ss dddd', value: 'YYYY-MM-DD HH:mm:ss dddd' },
48
+ { name: 'dddd', value: 'dddd' },
49
+ { name: 'Timestamp (ms)', value: 'timestamp-ms' },
50
+ { name: 'Timestamp (s)', value: 'timestamp-s' }
51
+ ]);
52
+
53
+ let formatted;
54
+ if (formatPattern === 'timestamp-ms') {
55
+ formatted = String(date.valueOf());
56
+ } else if (formatPattern === 'timestamp-s') {
57
+ formatted = String(date.unix());
58
+ } else {
59
+ formatted = date.format(formatPattern);
60
+ }
61
+
39
62
  console.log(`\nFormatted: ${formatted}\n`);
40
63
  await copy(formatted);
41
64
  }
@@ -0,0 +1,94 @@
1
+ import inquirer from 'inquirer';
2
+ import { copy, read } from '../utils/clipboard.js';
3
+ import { selectFromMenu } from '../utils/menu.js';
4
+
5
+ export async function variableFormatHandler() {
6
+ const { input } = await inquirer.prompt([
7
+ {
8
+ type: 'input',
9
+ name: 'input',
10
+ message: 'Enter variable name to convert (Press Enter to paste from clipboard):',
11
+ }
12
+ ]);
13
+
14
+ let textToConvert = input;
15
+
16
+ if (!textToConvert || textToConvert.trim().length === 0) {
17
+ textToConvert = await read();
18
+ if (!textToConvert || textToConvert.trim().length === 0) {
19
+ console.log('Clipboard is empty or could not be read.');
20
+ return;
21
+ }
22
+ console.log(`\nUsing clipboard content: "${textToConvert}"`);
23
+ }
24
+
25
+ const words = splitIntoWords(textToConvert);
26
+
27
+ if (words.length === 0) {
28
+ console.log('Could not parse any words from input.');
29
+ return;
30
+ }
31
+
32
+ const results = {
33
+ 'camelCase': toCamelCase(words),
34
+ 'PascalCase': toPascalCase(words),
35
+ 'snake_case': toSnakeCase(words),
36
+ 'kebab-case': toKebabCase(words),
37
+ 'CONSTANT_CASE': toConstantCase(words)
38
+ };
39
+
40
+ console.log('\n=== Conversion Results ===');
41
+ Object.entries(results).forEach(([key, value]) => {
42
+ console.log(`${key.padEnd(15)}: ${value}`);
43
+ });
44
+ console.log('==========================\n');
45
+
46
+ const copyChoice = await selectFromMenu('Select format to copy',
47
+ Object.entries(results).map(([key, value]) => ({
48
+ name: `${key} (${value})`,
49
+ value: value
50
+ }))
51
+ );
52
+
53
+ await copy(copyChoice);
54
+ }
55
+
56
+ function splitIntoWords(str) {
57
+ // 1. Handle CamelCase/camelCase by inserting space before uppercase letters that follow lowercase letters
58
+ let temp = str.replace(/([a-z])([A-Z])/g, '$1 $2');
59
+
60
+ // 2. Handle consecutive uppercase followed by lowercase (e.g. JSONParser -> JSON Parser)
61
+ temp = temp.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2');
62
+
63
+ // 3. Replace non-alphanumeric characters with spaces
64
+ temp = temp.replace(/[^a-zA-Z0-9]+/g, ' ');
65
+
66
+ // 4. Split by whitespace and filter empty strings
67
+ return temp
68
+ .trim()
69
+ .split(/\s+/)
70
+ .filter(w => w.length > 0);
71
+ }
72
+
73
+ function toCamelCase(words) {
74
+ return words.map((w, i) => {
75
+ if (i === 0) return w.toLowerCase();
76
+ return w.charAt(0).toUpperCase() + w.slice(1).toLowerCase();
77
+ }).join('');
78
+ }
79
+
80
+ function toPascalCase(words) {
81
+ return words.map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join('');
82
+ }
83
+
84
+ function toSnakeCase(words) {
85
+ return words.map(w => w.toLowerCase()).join('_');
86
+ }
87
+
88
+ function toKebabCase(words) {
89
+ return words.map(w => w.toLowerCase()).join('-');
90
+ }
91
+
92
+ function toConstantCase(words) {
93
+ return words.map(w => w.toUpperCase()).join('_');
94
+ }
package/src/index.js CHANGED
@@ -5,10 +5,11 @@ import { base64Handler } from './commands/base64.js';
5
5
  import { imgBase64Handler } from './commands/imgBase64.js';
6
6
  import { imgConvertHandler } from './commands/imgConvert.js';
7
7
  import { timeFormatHandler } from './commands/timeFormat.js';
8
- import { timestampHandler } from './commands/timestamp.js';
9
8
  import { mockHandler } from './commands/mock.js';
10
9
  import { uuidHandler } from './commands/uuid.js';
11
10
  import { pinyinHandler } from './commands/pinyin.js';
11
+ import { colorHandler } from './commands/color.js';
12
+ import { variableFormatHandler } from './commands/variableFormat.js';
12
13
 
13
14
  process.on('SIGINT', () => {
14
15
  console.log('\nBye!');
@@ -30,11 +31,12 @@ const features = [
30
31
  { name: 'String Encode/Decode (Base64)', value: 'base64' },
31
32
  { name: 'Image <-> Base64', value: 'imgBase64' },
32
33
  { name: 'Image Format Convert', value: 'imgConvert' },
33
- { name: 'Time Format', value: 'timeFormat' },
34
- { name: 'Get Current Timestamp', value: 'timestamp' },
34
+ { name: 'Time Format / Timestamp', value: 'timeFormat' },
35
35
  { name: 'Mock Text', value: 'mock' },
36
36
  { name: 'Get UUID', value: 'uuid' },
37
- { name: 'Chinese to Pinyin', value: 'pinyin' }
37
+ { name: 'Chinese to Pinyin', value: 'pinyin' },
38
+ { name: 'Color Converter (Hex <-> RGB)', value: 'color' },
39
+ { name: 'Variable Format Converter', value: 'variableFormat' }
38
40
  ];
39
41
 
40
42
  async function main() {
@@ -48,12 +50,29 @@ async function main() {
48
50
  program.parse(process.argv);
49
51
  }
50
52
 
53
+ function getFeatureKey(index) {
54
+ if (index < 9) {
55
+ return String(index + 1);
56
+ }
57
+ return String.fromCharCode('a'.charCodeAt(0) + (index - 9));
58
+ }
59
+
60
+ function getFeatureIndex(key) {
61
+ if (/^[1-9]$/.test(key)) {
62
+ return parseInt(key) - 1;
63
+ }
64
+ if (/^[a-z]$/.test(key.toLowerCase())) {
65
+ return key.toLowerCase().charCodeAt(0) - 'a'.charCodeAt(0) + 9;
66
+ }
67
+ return -1;
68
+ }
69
+
51
70
  async function showMenu() {
52
71
  console.log('\n=================================');
53
72
  console.log(' xw-devtool-cli Menu');
54
73
  console.log('=================================');
55
74
  features.forEach((feature, index) => {
56
- console.log(`${index + 1}. ${feature.name}`);
75
+ console.log(`${getFeatureKey(index)}. ${feature.name}`);
57
76
  });
58
77
  console.log('0. Exit');
59
78
  console.log('=================================\n');
@@ -62,25 +81,26 @@ async function showMenu() {
62
81
  {
63
82
  type: 'input',
64
83
  name: 'choice',
65
- message: 'Please enter the feature number (0-8):',
84
+ message: `Please enter the feature key (0-9, a-z):`,
66
85
  validate: (input) => {
67
- const num = parseInt(input);
68
- if (isNaN(num) || num < 0 || num > features.length) {
69
- return `Please enter a number between 0 and ${features.length}`;
86
+ if (input === '0') return true;
87
+
88
+ const index = getFeatureIndex(input);
89
+ if (index >= 0 && index < features.length) {
90
+ return true;
70
91
  }
71
- return true;
92
+ return 'Invalid selection. Please enter a valid menu key.';
72
93
  }
73
94
  }
74
95
  ]);
75
96
 
76
- const index = parseInt(choice);
77
-
78
- if (index === 0) {
97
+ if (choice === '0') {
79
98
  console.log('Bye!');
80
99
  process.exit(0);
81
100
  }
82
101
 
83
- const selectedFeature = features[index - 1];
102
+ const index = getFeatureIndex(choice);
103
+ const selectedFeature = features[index];
84
104
 
85
105
  try {
86
106
  await handleAction(selectedFeature.value);
@@ -110,9 +130,6 @@ async function handleAction(action) {
110
130
  case 'timeFormat':
111
131
  await timeFormatHandler();
112
132
  break;
113
- case 'timestamp':
114
- await timestampHandler();
115
- break;
116
133
  case 'mock':
117
134
  await mockHandler();
118
135
  break;
@@ -122,6 +139,12 @@ async function handleAction(action) {
122
139
  case 'pinyin':
123
140
  await pinyinHandler();
124
141
  break;
142
+ case 'color':
143
+ await colorHandler();
144
+ break;
145
+ case 'variableFormat':
146
+ await variableFormatHandler();
147
+ break;
125
148
  default:
126
149
  console.log('Feature not implemented yet.');
127
150
  }
@@ -8,3 +8,12 @@ export async function copy(text) {
8
8
  console.error('Failed to copy to clipboard (might not be supported in this environment):', e.message);
9
9
  }
10
10
  }
11
+
12
+ export async function read() {
13
+ try {
14
+ return await clipboardy.read();
15
+ } catch (e) {
16
+ console.error('Failed to read from clipboard:', e.message);
17
+ return '';
18
+ }
19
+ }
@@ -1,7 +0,0 @@
1
- import { copy } from '../utils/clipboard.js';
2
-
3
- export async function timestampHandler() {
4
- const now = Date.now();
5
- console.log(`\nCurrent Timestamp: ${now}\n`);
6
- await copy(String(now));
7
- }