xw-devtool-cli 1.0.9 → 1.0.11

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 CHANGED
@@ -19,6 +19,7 @@
19
19
  - **获取时间戳**:快速获取当前毫秒级时间戳。
20
20
  - **开发辅助工具**:
21
21
  - **URL 编解码**:Encode/Decode URL。
22
+ - **Unicode 编解码**:文本与 Unicode 转义序列 (\uXXXX) 互转。
22
23
  - **UUID**:生成 UUID v4。
23
24
  - **中文转拼音**:将汉字转换为不带声调的拼音。
24
25
  - **颜色转换**:Hex <-> RGB 互转。
@@ -61,43 +62,31 @@ xw-devtool
61
62
  =================================
62
63
  xw-devtool-cli Menu
63
64
  =================================
64
- 1. URL Encode/Decode
65
- 2. String Encode/Decode (Base64)
66
- 3. Image <-> Base64
67
- 4. Image Format Convert
68
- 5. Time Format / Timestamp
69
- 6. Mock Text
70
- 7. Get UUID
71
- 8. Chinese to Pinyin
72
- 9. Color Converter (Hex <-> RGB)
73
- a. Variable Format Converter
74
- b. Hash Calculator (MD5/SHA/SM3)
75
- c. QR Code Generator
76
- d. Special Characters (Symbols)
77
- e. Emoji Picker
78
- f. HTML Entity Encode/Decode
79
- g. Placeholder Image Generator
65
+ 1. Image <-> Base64
66
+ 2. Image Format Convert
67
+ 3. Placeholder Image Generator
68
+ 4. QR Code Generator
69
+ 5. URL Encode/Decode
70
+ 6. String Encode/Decode (Base64)
71
+ 7. Unicode Encode/Decode
72
+ 8. HTML Entity Encode/Decode
73
+ 9. Variable Format Converter
74
+ a. Chinese to Pinyin
75
+ b. Time Format / Timestamp
76
+ c. Color Converter (Hex <-> RGB)
77
+ d. Get UUID
78
+ e. Hash Calculator (MD5/SHA/SM3)
79
+ f. Mock Text
80
+ g. Special Characters (Symbols)
81
+ h. Emoji Picker
80
82
  0. Exit
81
83
  =================================
82
84
  ```
83
85
 
84
86
  ## 📖 详细使用指南
85
87
 
86
- ### 1. URL 编解码
88
+ ### 1. 图片与 Base64 互转
87
89
  - 选择 `1` 进入。
88
- - 选择 `Encode` (编码) 或 `Decode` (解码)。
89
- - 输入 URL 字符串,结果自动复制。
90
-
91
- ### 2. 字符串 Base64 转换
92
- - 选择 `2` 进入。
93
- - 支持三种输入源:
94
- - **Clipboard**: 直接读取剪贴板内容(适合处理长文本)。
95
- - **File**: 读取文本文件内容。
96
- - **Manual input**: 手动粘贴或输入文本。
97
- - 输出支持:直接复制、保存为文件、预览后复制。
98
-
99
- ### 3. 图片与 Base64 互转
100
- - 选择 `3` 进入。
101
90
  - **Image -> Base64**:
102
91
  - 选择图片文件(支持对话框选择)。
103
92
  - 输出 Base64 字符串(可保存为 `.txt` 文件防止控制台卡顿)。
@@ -105,80 +94,111 @@ g. Placeholder Image Generator
105
94
  - 输入 Base64 字符串(支持从文件读取或剪贴板读取)。
106
95
  - 自动识别图片格式并保存为文件。
107
96
 
108
- ### 4. 图片格式转换
109
- - 选择 `4` 进入。
97
+ ### 2. 图片格式转换
98
+ - 选择 `2` 进入。
110
99
  - 选择源图片文件。
111
100
  - 选择目标格式 (PNG / JPG / WebP)。
112
101
  - 设置压缩参数(如 JPG 质量 1-100,PNG 压缩等级 0-9)。
113
102
  - 生成的新图片将保存在源文件同级目录。
114
103
 
115
- ### 5. 时间格式化 (Time Format)
104
+ ### 3. 占位图生成 (Placeholder Image)
105
+ - 选择 `3` 进入。
106
+ - **模式 1:本地图片文件 (Local Image File)**
107
+ - 自动生成图片文件到当前目录。
108
+ - 支持自定义:
109
+ - **尺寸**: 宽度和高度 (px)。
110
+ - **颜色**: 背景色和文字颜色(支持 Hex 或颜色名)。
111
+ - **内容**: 自定义图片中间的文字(默认显示尺寸)。
112
+ - **格式**: 输出 PNG, JPG, WebP, GIF。
113
+ - 文件名默认包含时间戳,避免覆盖。
114
+ - **模式 2:远程图片 URL (Remote Image URL)**
115
+ - 自动生成云端占位图链接并复制到剪贴板。
116
+ - 支持多个主流服务商:
117
+ - **Picsum Photos**: 随机风景图,支持灰度、模糊效果。
118
+ - **DummyImage**: 简单、经典。
119
+ - **Via Placeholder**: 标准占位图。
120
+ - **Placehold.jp**: 适合日文环境。
121
+ - **DevTool Tech**: 高度可定制。
122
+ - **FPO Img**: "For Placement Only" 风格。
123
+ - 支持自定义尺寸、颜色、文字等参数(视具体服务商而定)。
124
+
125
+ ### 4. 二维码生成
126
+ - 选择 `4` 进入。
127
+ - 输入文本或 URL(支持直接回车读取剪贴板)。
128
+ - 终端直接显示二维码预览。
129
+ - 可选保存为 PNG 图片,默认文件名包含时间戳。
130
+
131
+ ### 5. URL 编解码
116
132
  - 选择 `5` 进入。
117
- - 支持时间戳转日期字符串,或日期字符串转时间戳。
118
- - 直接回车可获取当前时间戳。
133
+ - 选择 `Encode` (编码) 或 `Decode` (解码)。
134
+ - 输入 URL 字符串,结果自动复制。
119
135
 
120
- ### 6. Mock 数据生成
136
+ ### 6. 字符串 Base64 转换
121
137
  - 选择 `6` 进入。
122
- - 提供多种数据类型:英文段落、中文字符、身份证号、邮箱、手机号等。
123
- - 支持指定生成数量。
138
+ - 支持三种输入源:
139
+ - **Clipboard**: 直接读取剪贴板内容(适合处理长文本)。
140
+ - **File**: 读取文本文件内容。
141
+ - **Manual input**: 手动粘贴或输入文本。
142
+ - 输出支持:直接复制、保存为文件、预览后复制。
124
143
 
125
- ### 7. UUID 生成
144
+ ### 7. Unicode 编码/解码
126
145
  - 选择 `7` 进入。
127
- - 自动生成一个 UUID v4 并复制到剪贴板。
146
+ - **Encode**: 将文本转换为 Unicode 转义序列 (如 `\u4F60\u597D`)。
147
+ - **Decode**: 将 Unicode 转义序列还原为文本。
148
+ - 结果自动复制到剪贴板。
128
149
 
129
- ### 8. 中文转拼音
150
+ ### 8. HTML 实体编码/解码
130
151
  - 选择 `8` 进入。
131
- - 输入中文字符串(支持直接回车读取剪贴板)。
132
- - 输出对应的拼音(无声调)。
152
+ - 支持两种编码模式:
153
+ - **Standard**: 仅编码特殊字符(如 `<` `>` `&` 及非 ASCII 字符)。
154
+ - **Everything**: 编码所有字符(包括 ASCII 字母数字)。
155
+ - 支持直接回车读取剪贴板内容。
156
+ - 结果自动复制到剪贴板。
133
157
 
134
- ### 9. 颜色转换 (Hex <-> RGB)
158
+ ### 9. 变量格式转换
135
159
  - 选择 `9` 进入。
136
- - 输入 Hex 颜色值 (如 #333) 或 RGB 值 (如 rgb(51,51,51))。
137
- - 自动转换并显示对应格式。
138
-
139
- ### 10. 变量格式转换
140
- - 选择 `a` 进入。
141
160
  - 输入变量名(支持直接回车读取剪贴板)。
142
161
  - 自动展示 CamelCase, PascalCase, SnakeCase, KebabCase, ConstantCase 五种格式。
143
162
  - 选择一种格式自动复制。
144
163
 
145
- ### 11. 哈希计算 (Encryption)
164
+ ### 10. 中文转拼音
165
+ - 选择 `a` 进入。
166
+ - 输入中文字符串(支持直接回车读取剪贴板)。
167
+ - 输出对应的拼音(无声调)。
168
+
169
+ ### 11. 时间格式化 (Time Format)
146
170
  - 选择 `b` 进入。
147
- - 输入文本(支持直接回车读取剪贴板)。
148
- - 同时计算并显示 MD5, SHA1, SHA256, SHA512, SM3 哈希值。
171
+ - 支持时间戳转日期字符串,或日期字符串转时间戳。
172
+ - 直接回车可获取当前时间戳。
149
173
 
150
- ### 12. 二维码生成
174
+ ### 12. 颜色转换 (Hex <-> RGB)
151
175
  - 选择 `c` 进入。
152
- - 输入文本或 URL(支持直接回车读取剪贴板)。
153
- - 终端直接显示二维码预览。
154
- - 可选保存为 PNG 图片,默认文件名包含时间戳。
176
+ - 输入 Hex 颜色值 (如 #333) 或 RGB 值 (如 rgb(51,51,51))。
177
+ - 自动转换并显示对应格式。
155
178
 
156
- ### 13. 特殊符号大全
179
+ ### 13. Get UUID
157
180
  - 选择 `d` 进入。
158
- - 网格化展示常用特殊符号。
159
- - 输入符号对应的编号即可一键复制。
181
+ - 自动生成一个 UUID v4 并复制到剪贴板。
160
182
 
161
- ### 14. Emoji 输入
183
+ ### 14. 哈希计算 (Encryption)
162
184
  - 选择 `e` 进入。
163
- - 分类展示常用 Emoji (表情、手势、动物、食物等)。
164
- - 输入编号选择并复制 Emoji 到剪贴板。
185
+ - 输入文本(支持直接回车读取剪贴板)。
186
+ - 同时计算并显示 MD5, SHA1, SHA256, SHA512, SM3 哈希值。
165
187
 
166
- ### 15. HTML 实体编码/解码
188
+ ### 15. Mock 数据生成
167
189
  - 选择 `f` 进入。
168
- - 支持两种编码模式:
169
- - **Standard**: 仅编码特殊字符(如 `<` `>` `&` 及非 ASCII 字符)。
170
- - **Everything**: 编码所有字符(包括 ASCII 字母数字)。
171
- - 支持直接回车读取剪贴板内容。
172
- - 结果自动复制到剪贴板。
190
+ - 提供多种数据类型:英文段落、中文字符、身份证号、邮箱、手机号等。
191
+ - 支持指定生成数量。
173
192
 
174
- ### 16. 占位图生成 (Placeholder Image)
193
+ ### 16. 特殊符号大全
175
194
  - 选择 `g` 进入。
176
- - 支持自定义:
177
- - **尺寸**: 宽度和高度 (px)。
178
- - **颜色**: 背景色和文字颜色(支持 Hex 或颜色名)。
179
- - **内容**: 自定义图片中间的文字(默认显示尺寸)。
180
- - **格式**: 输出 PNG, JPG, WebP, GIF。
181
- - 文件名默认包含时间戳,避免覆盖。
195
+ - 网格化展示常用特殊符号。
196
+ - 输入符号对应的编号即可一键复制。
197
+
198
+ ### 17. Emoji 输入
199
+ - 选择 `h` 进入。
200
+ - 分类展示常用 Emoji (表情、手势、动物、食物等)。
201
+ - 输入编号选择并复制 Emoji 到剪贴板。
182
202
 
183
203
  ## 📄 License
184
204
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xw-devtool-cli",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "type": "module",
5
5
  "description": "基于node的开发者助手cli",
6
6
  "main": "index.js",
@@ -1,30 +1,37 @@
1
1
  import inquirer from 'inquirer';
2
2
  import sharp from 'sharp';
3
3
  import path from 'path';
4
+ import { copy } from '../utils/clipboard.js';
5
+ import { selectFromMenu } from '../utils/menu.js';
4
6
 
5
7
  export async function placeholderImgHandler() {
6
- console.log('\n--- Generate Placeholder Image ---\n');
8
+ const mode = await selectFromMenu('Generate Placeholder Image', [
9
+ { name: 'Local Image File', value: 'local' },
10
+ { name: 'Remote Image URL', value: 'remote' }
11
+ ]);
12
+
13
+ if (mode === 'local') {
14
+ await generateLocalImage();
15
+ } else {
16
+ await generateRemoteUrl();
17
+ }
18
+ }
7
19
 
20
+ async function generateLocalImage() {
8
21
  const answers = await inquirer.prompt([
9
22
  {
10
23
  type: 'input',
11
24
  name: 'width',
12
25
  message: 'Width (px):',
13
26
  default: '300',
14
- validate: (input) => {
15
- const val = parseInt(input);
16
- return !isNaN(val) && val > 0 ? true : 'Please enter a valid positive number';
17
- }
27
+ validate: validateNumber
18
28
  },
19
29
  {
20
30
  type: 'input',
21
31
  name: 'height',
22
32
  message: 'Height (px):',
23
33
  default: '200',
24
- validate: (input) => {
25
- const val = parseInt(input);
26
- return !isNaN(val) && val > 0 ? true : 'Please enter a valid positive number';
27
- }
34
+ validate: validateNumber
28
35
  },
29
36
  {
30
37
  type: 'input',
@@ -61,9 +68,8 @@ export async function placeholderImgHandler() {
61
68
  const width = parseInt(answers.width);
62
69
  const height = parseInt(answers.height);
63
70
  const text = answers.text || `${width}x${height}`;
64
- const fontSize = Math.floor(Math.min(width, height) / 5); // Heuristic for font size
71
+ const fontSize = Math.floor(Math.min(width, height) / 5);
65
72
 
66
- // Create SVG buffer
67
73
  const svgImage = `
68
74
  <svg width="${width}" height="${height}" version="1.1" xmlns="http://www.w3.org/2000/svg">
69
75
  <rect width="100%" height="100%" fill="${answers.bgColor}" />
@@ -72,7 +78,6 @@ export async function placeholderImgHandler() {
72
78
  `;
73
79
 
74
80
  const buffer = Buffer.from(svgImage);
75
-
76
81
  const outputFilename = `${answers.filename}.${answers.format}`;
77
82
  const outputPath = path.resolve(process.cwd(), outputFilename);
78
83
 
@@ -86,3 +91,98 @@ export async function placeholderImgHandler() {
86
91
  console.error(`\nFailed to generate image: ${error.message}`);
87
92
  }
88
93
  }
94
+
95
+ async function generateRemoteUrl() {
96
+ const provider = await selectFromMenu('Select Provider', [
97
+ { name: 'Picsum Photos (Random photo)', value: 'picsum' },
98
+ { name: 'DummyImage (Simple)', value: 'dummyimage' },
99
+ { name: 'Via Placeholder (Standard)', value: 'placeholder' },
100
+ { name: 'Placehold.jp (JP Style)', value: 'placeholdjp' },
101
+ { name: 'DevTool Tech (Customizable)', value: 'devtool' },
102
+ { name: 'FPO Img (For Placement Only)', value: 'fpoimg' }
103
+ ]);
104
+
105
+ // Picsum is special (random photos)
106
+ if (provider === 'picsum') {
107
+ const answers = await inquirer.prompt([
108
+ { type: 'input', name: 'width', message: 'Width:', default: '300', validate: validateNumber },
109
+ { type: 'input', name: 'height', message: 'Height:', default: '300', validate: validateNumber },
110
+ { type: 'confirm', name: 'random', message: 'Add random parameter?', default: true },
111
+ { type: 'confirm', name: 'grayscale', message: 'Grayscale?', default: false },
112
+ { type: 'input', name: 'blur', message: 'Blur level (1-10, leave empty for none):', validate: (val) => !val || (parseInt(val) >= 1 && parseInt(val) <= 10) ? true : '1-10' }
113
+ ]);
114
+
115
+ let url = `https://picsum.photos/${answers.width}/${answers.height}`;
116
+ const params = [];
117
+ if (answers.grayscale) params.push('grayscale');
118
+ if (answers.blur) params.push(`blur=${answers.blur}`);
119
+ if (answers.random) params.push(`random=${Math.floor(Math.random() * 1000)}`);
120
+
121
+ if (params.length > 0) {
122
+ url += '?' + params.join('&');
123
+ }
124
+
125
+ await finishUrl(url);
126
+ return;
127
+ }
128
+
129
+ // Common prompts for others
130
+ const answers = await inquirer.prompt([
131
+ { type: 'input', name: 'width', message: 'Width:', default: '300', validate: validateNumber },
132
+ { type: 'input', name: 'height', message: 'Height:', default: '300', validate: validateNumber },
133
+ { type: 'input', name: 'bgColor', message: 'Background Color (Hex without #):', default: 'cccccc' },
134
+ { type: 'input', name: 'textColor', message: 'Text Color (Hex without #):', default: '969696' },
135
+ { type: 'input', name: 'text', message: 'Text Content (leave empty for size):' },
136
+ { type: 'list', name: 'format', message: 'Format:', choices: ['png', 'jpg', 'gif'], default: 'png' }
137
+ ]);
138
+
139
+ let url = '';
140
+ const { width, height, bgColor, textColor, format } = answers;
141
+ const text = answers.text || `${width}x${height}`;
142
+ const cleanBg = bgColor.replace('#', '');
143
+ const cleanText = textColor.replace('#', '');
144
+ const encodedText = encodeURIComponent(text);
145
+
146
+ switch (provider) {
147
+ case 'dummyimage':
148
+ // https://dummyimage.com/300x300/999999/ff4400.png?text=EXAMPLE
149
+ url = `https://dummyimage.com/${width}x${height}/${cleanBg}/${cleanText}.${format}`;
150
+ if (text) url += `?text=${encodedText}`;
151
+ break;
152
+
153
+ case 'placeholder':
154
+ // https://via.placeholder.com/640x300/999999/ff4400.png?text=example
155
+ url = `https://via.placeholder.com/${width}x${height}/${cleanBg}/${cleanText}.${format}`;
156
+ if (text) url += `?text=${encodedText}`;
157
+ break;
158
+
159
+ case 'placeholdjp':
160
+ // https://placehold.jp/999999/ff4400/300x300.png?text=EXAMPLE
161
+ url = `https://placehold.jp/${cleanBg}/${cleanText}/${width}x${height}.${format}`;
162
+ if (text) url += `?text=${encodedText}`;
163
+ break;
164
+
165
+ case 'devtool':
166
+ // https://devtool.tech/api/placeholder/300/300?text=示例&color=#ffffff&bgColor=#333333
167
+ // Note: devtool.tech needs # for colors
168
+ url = `https://devtool.tech/api/placeholder/${width}/${height}?text=${encodedText}&color=%23${cleanText}&bgColor=%23${cleanBg}`;
169
+ break;
170
+
171
+ case 'fpoimg':
172
+ // https://fpoimg.com/300x300?text=example&bg_color=999999&text_color=ff4400
173
+ url = `https://fpoimg.com/${width}x${height}?text=${encodedText}&bg_color=${cleanBg}&text_color=${cleanText}`;
174
+ break;
175
+ }
176
+
177
+ await finishUrl(url);
178
+ }
179
+
180
+ async function finishUrl(url) {
181
+ console.log(`\nGenerated URL:\n${url}\n`);
182
+ await copy(url);
183
+ }
184
+
185
+ function validateNumber(input) {
186
+ const val = parseInt(input);
187
+ return !isNaN(val) && val > 0 ? true : 'Please enter a valid positive number';
188
+ }
@@ -0,0 +1,52 @@
1
+ import inquirer from 'inquirer';
2
+ import { copy } from '../utils/clipboard.js';
3
+ import { selectFromMenu } from '../utils/menu.js';
4
+
5
+ export async function unicodeHandler() {
6
+ const mode = await selectFromMenu('Unicode Encode/Decode', [
7
+ { name: 'Encode (Text -> \\uXXXX)', value: 'encode' },
8
+ { name: 'Decode (\\uXXXX -> Text)', value: 'decode' }
9
+ ]);
10
+
11
+ const { input } = await inquirer.prompt([
12
+ {
13
+ type: 'input',
14
+ name: 'input',
15
+ message: `Enter string to ${mode}:`
16
+ }
17
+ ]);
18
+
19
+ let result;
20
+ try {
21
+ if (mode === 'encode') {
22
+ const encodeType = await selectFromMenu('Select Encoding Mode', [
23
+ { name: 'Encode Non-ASCII Only (Recommended)', value: 'non-ascii' },
24
+ { name: 'Encode All Characters', value: 'all' }
25
+ ]);
26
+ result = encodeUnicode(input, encodeType === 'non-ascii');
27
+ } else {
28
+ result = decodeUnicode(input);
29
+ }
30
+ console.log(`\nResult:\n${result}\n`);
31
+ await copy(result);
32
+ } catch (e) {
33
+ console.error('Error processing Unicode:', e.message);
34
+ }
35
+ }
36
+
37
+ function encodeUnicode(str, onlyNonAscii) {
38
+ return str.split('').map(char => {
39
+ const code = char.charCodeAt(0);
40
+ if (onlyNonAscii && code <= 127) {
41
+ return char;
42
+ }
43
+ const hex = code.toString(16).toUpperCase();
44
+ return '\\u' + '0000'.substring(0, 4 - hex.length) + hex;
45
+ }).join('');
46
+ }
47
+
48
+ function decodeUnicode(str) {
49
+ return str.replace(/\\u[\dA-F]{4}/gi, (match) => {
50
+ return String.fromCharCode(parseInt(match.replace(/\\u/g, ''), 16));
51
+ });
52
+ }
package/src/index.js CHANGED
@@ -2,6 +2,7 @@ import inquirer from 'inquirer';
2
2
  import { program } from 'commander';
3
3
  import { urlHandler } from './commands/url.js';
4
4
  import { base64Handler } from './commands/base64.js';
5
+ import { unicodeHandler } from './commands/unicode.js';
5
6
  import { imgBase64Handler } from './commands/imgBase64.js';
6
7
  import { imgConvertHandler } from './commands/imgConvert.js';
7
8
  import { timeFormatHandler } from './commands/timeFormat.js';
@@ -33,22 +34,30 @@ process.on('unhandledRejection', (err) => {
33
34
  });
34
35
 
35
36
  const features = [
36
- { name: 'URL Encode/Decode', value: 'url' },
37
- { name: 'String Encode/Decode (Base64)', value: 'base64' },
37
+ // Image Tools
38
38
  { name: 'Image <-> Base64', value: 'imgBase64' },
39
39
  { name: 'Image Format Convert', value: 'imgConvert' },
40
- { name: 'Time Format / Timestamp', value: 'timeFormat' },
41
- { name: 'Mock Text', value: 'mock' },
42
- { name: 'Get UUID', value: 'uuid' },
40
+ { name: 'Placeholder Image Generator', value: 'placeholderImg' },
41
+ { name: 'QR Code Generator', value: 'qrcode' },
42
+
43
+ // Encode/Decode & Formatting
44
+ { name: 'URL Encode/Decode', value: 'url' },
45
+ { name: 'String Encode/Decode (Base64)', value: 'base64' },
46
+ { name: 'Unicode Encode/Decode', value: 'unicode' },
47
+ { name: 'HTML Entity Encode/Decode', value: 'htmlEntities' },
48
+ { name: 'Variable Format Converter', value: 'variableFormat' },
43
49
  { name: 'Chinese to Pinyin', value: 'pinyin' },
50
+
51
+ // Utils (Time, Color, UUID, Hash)
52
+ { name: 'Time Format / Timestamp', value: 'timeFormat' },
44
53
  { name: 'Color Converter (Hex <-> RGB)', value: 'color' },
45
- { name: 'Variable Format Converter', value: 'variableFormat' },
54
+ { name: 'Get UUID', value: 'uuid' },
46
55
  { name: 'Hash Calculator (MD5/SHA/SM3)', value: 'hashing' },
47
- { name: 'QR Code Generator', value: 'qrcode' },
56
+
57
+ // Content/Mocking
58
+ { name: 'Mock Text', value: 'mock' },
48
59
  { name: 'Special Characters (Symbols)', value: 'specialChars' },
49
- { name: 'Emoji Picker', value: 'emoji' },
50
- { name: 'HTML Entity Encode/Decode', value: 'htmlEntities' },
51
- { name: 'Placeholder Image Generator', value: 'placeholderImg' }
60
+ { name: 'Emoji Picker', value: 'emoji' }
52
61
  ];
53
62
 
54
63
  async function main() {
@@ -133,6 +142,9 @@ async function handleAction(action) {
133
142
  case 'base64':
134
143
  await base64Handler();
135
144
  break;
145
+ case 'unicode':
146
+ await unicodeHandler();
147
+ break;
136
148
  case 'imgBase64':
137
149
  await imgBase64Handler();
138
150
  break;