xw-devtool-cli 1.0.36 → 1.0.38
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 +88 -54
- package/package.json +4 -3
- package/src/commands/colorPick.js +69 -101
- package/src/index.js +25 -3
package/README.md
CHANGED
|
@@ -15,13 +15,14 @@
|
|
|
15
15
|
- **图片分割**:支持网格等分(自定义行/列数)或自定义分割线(像素/百分比),自动生成分割后的图片文件。
|
|
16
16
|
- **图片主色识别**:提取图片主色调(Hex/RGB/HSL/HSV),默认复制 Hex 到剪贴板,支持保存详细信息到文件。
|
|
17
17
|
- **颜色吸取**:从图片中吸取单像素颜色或区域平均颜色,支持 px/% 坐标输入,结果显示颜色条并自动复制 Hex/Hex8。
|
|
18
|
-
-
|
|
19
|
-
>
|
|
20
|
-
> 1.
|
|
21
|
-
> 2.
|
|
22
|
-
> 3.
|
|
18
|
+
- **屏幕取色**:实时显示鼠标所在位置的颜色值(Hex/RGB)。
|
|
19
|
+
> **操作说明**:
|
|
20
|
+
> 1. 进入模式后,屏幕右下角会实时显示当前颜色预览和数值。
|
|
21
|
+
> 2. 移动鼠标到目标位置,按 **Enter 键** 复制当前颜色 Hex 到剪贴板(会有成功提示)。
|
|
22
|
+
> 3. 按 **ESC 键** 退出取色模式。
|
|
23
|
+
> 4. 支持多显示器。
|
|
23
24
|
- **占位图生成**:快速生成指定尺寸、颜色、文字的占位图片 (Placeholder Image)。
|
|
24
|
-
-
|
|
25
|
+
- **全屏十字辅助线**:显示跟随鼠标的全屏红色十字线,用于屏幕对齐和定位。按 **ESC 键** 退出。
|
|
25
26
|
- **屏幕测距**:点击屏幕上任意两点,测量其像素距离。
|
|
26
27
|
- **屏幕文字标注**:在屏幕上点击并输入文字,进行临时标注。右键撤销,Delete 清空。
|
|
27
28
|
- **图片压缩**:支持文件或文件夹;文件夹可批量压缩其下所有图片(可选包含子文件夹)。支持 PNG/JPG/WebP,质量 1-100;输出文件名带时间戳并复制输出目录到剪贴板。
|
|
@@ -100,29 +101,38 @@ xw-devtool --en # 英文启动 (Start in English)
|
|
|
100
101
|
=================================
|
|
101
102
|
xw-devtool-cli 菜单
|
|
102
103
|
=================================
|
|
103
|
-
1. 图片
|
|
104
|
+
1. 图片 <-> Base64
|
|
104
105
|
2. 图片格式转换
|
|
105
|
-
3.
|
|
106
|
+
3. 图片分割工具
|
|
106
107
|
4. 图片主色识别
|
|
107
|
-
5.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
108
|
+
5. 颜色吸取
|
|
109
|
+
6. 全屏十字辅助线
|
|
110
|
+
7. 屏幕测距 (像素)
|
|
111
|
+
8. 屏幕文字标注
|
|
112
|
+
9. 图片压缩
|
|
113
|
+
a. 占位图生成器
|
|
114
|
+
b. 二维码生成器
|
|
115
|
+
c. URL 编码/解码
|
|
116
|
+
d. 进制转换工具
|
|
117
|
+
e. 字符串 编码/解码 (Base64)
|
|
118
|
+
f. Unicode 编码/解码
|
|
119
|
+
g. HTML 实体 编码/解码
|
|
120
|
+
h. 变量命名格式转换
|
|
121
|
+
i. JSON 格式化 (压缩/美化)
|
|
122
|
+
j. 汉字转拼音
|
|
123
|
+
k. 时间格式化 / 时间戳
|
|
124
|
+
l. 时间计算 (差值/偏移)
|
|
125
|
+
m. 颜色转换 (Hex <-> RGB)
|
|
126
|
+
n. 颜色预览
|
|
127
|
+
o. 生成 UUID
|
|
128
|
+
p. 哈希计算 (MD5/SHA/SM3)
|
|
129
|
+
q. Mock 文本生成
|
|
130
|
+
r. 特殊字符 (符号)
|
|
131
|
+
s. Emoji 选择器
|
|
132
|
+
t. Markdown 片段
|
|
133
|
+
u. VS Code 代码片段生成
|
|
134
|
+
v. Git 助手 (分支/提交模板)
|
|
135
|
+
w. 设置 / 语言 (Settings)
|
|
126
136
|
0. 退出
|
|
127
137
|
=================================
|
|
128
138
|
```
|
|
@@ -146,6 +156,20 @@ s. 设置 (语言)
|
|
|
146
156
|
- 生成的新图片将保存在源文件同级目录。
|
|
147
157
|
- 生成 ICO 时可自定义尺寸,留空默认生成 `256` 尺寸;输出文件名包含时间戳。
|
|
148
158
|
|
|
159
|
+
### 图片压缩
|
|
160
|
+
- 选择 `imgCompress` (菜单对应数字/字母) 进入。
|
|
161
|
+
- **支持输入**:
|
|
162
|
+
- **文件**:单张图片压缩。
|
|
163
|
+
- **文件夹**:批量压缩文件夹内所有图片(可选是否递归子文件夹)。
|
|
164
|
+
- **参数设置**:
|
|
165
|
+
- **质量**:推荐 80,也可自定义 1-100。
|
|
166
|
+
- **文件夹结构**:批量处理时可选保留原目录结构。
|
|
167
|
+
- **文件名后缀**:可选是否添加 `_compressed_时间戳` 后缀。如果不添加,将保持原文件名(建议输出到不同目录以防覆盖)。
|
|
168
|
+
- **输出**:
|
|
169
|
+
- 默认输出到源目录下的 `compressed_YYYYMMDD_HHmmss` 文件夹。
|
|
170
|
+
- 自动复制输出目录路径。
|
|
171
|
+
- 完成后可选直接打开输出目录。
|
|
172
|
+
|
|
149
173
|
### 3. 占位图生成 (Placeholder Image)
|
|
150
174
|
- 选择 `3` 进入。
|
|
151
175
|
- **模式 1:本地图片文件 (Local Image File)**
|
|
@@ -317,12 +341,15 @@ Key features include: Base64 encoding/decoding, image format conversion, image <
|
|
|
317
341
|
- **Image ↔ Base64**: Convert images to Base64 strings and vice versa.
|
|
318
342
|
- **Image Dominant Color**: Extract dominant color (Hex/RGB/HSL/HSV), copy Hex to clipboard by default, or save details to file.
|
|
319
343
|
- **Image Color Picker**: Pick color from image (single pixel or average), supports px/% coordinates, shows color bar and auto-copies Hex/Hex8.
|
|
320
|
-
- **Screen Color Picker**:
|
|
321
|
-
> **
|
|
322
|
-
> 1.
|
|
323
|
-
> 2.
|
|
324
|
-
> 3.
|
|
344
|
+
- **Screen Color Picker**: Real-time color picking from screen (Hex/RGB).
|
|
345
|
+
> **Usage**:
|
|
346
|
+
> 1. Real-time color preview and values are shown in the bottom-right corner.
|
|
347
|
+
> 2. Move mouse to target and press **Enter** to copy Hex color to clipboard.
|
|
348
|
+
> 3. Press **ESC** to exit.
|
|
349
|
+
> 4. Supports multiple monitors.
|
|
325
350
|
- **Placeholder Image**: Quickly generate placeholder images with custom size, color, and text.
|
|
351
|
+
- **Image Compression**: Support file or folder input; batch compress images in folder (recursive optional). Supports PNG/JPG/WebP, Quality 1-100. Output filename suffix optional.
|
|
352
|
+
- **Full Screen Crosshair**: Show red crosshair following mouse. Press **ESC** to exit.
|
|
326
353
|
- **Mock Data**:
|
|
327
354
|
- Generate: Lorem Ipsum, Chinese characters, ID cards, Emails, URLs, Order IDs, Phone numbers.
|
|
328
355
|
- Supports batch generation.
|
|
@@ -385,28 +412,22 @@ The interactive menu will appear:
|
|
|
385
412
|
=================================
|
|
386
413
|
xw-devtool-cli Menu
|
|
387
414
|
=================================
|
|
388
|
-
1. Image <-> Base64
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
h. Mock Text
|
|
405
|
-
i. Special Characters (Symbols)
|
|
406
|
-
j. Emoji Picker
|
|
407
|
-
k. Markdown Snippets
|
|
408
|
-
l. VS Code Snippet Generator
|
|
409
|
-
s. Settings (Language)
|
|
415
|
+
1. Image <-> Base64 2. Image Format Convert
|
|
416
|
+
3. Image Splitter 4. Image Dominant Color
|
|
417
|
+
5. Color Picker 6. Screen Crosshair
|
|
418
|
+
7. Pixel Distance Tool 8. Screen Mark Tool
|
|
419
|
+
9. Image Compression a. Placeholder Image Generator
|
|
420
|
+
b. QR Code Generator c. URL Encode/Decode
|
|
421
|
+
d. Number Base Converter e. String Encode/Decode (Base64)
|
|
422
|
+
f. Unicode Encode/Decode g. HTML Entity Encode/Decode
|
|
423
|
+
h. Variable Format Converter i. JSON Format (Minify/Prettify)
|
|
424
|
+
j. Chinese to Pinyin k. Time Format / Timestamp
|
|
425
|
+
l. Time Calculation (Diff/Offset) m. Color Converter (Hex <-> RGB)
|
|
426
|
+
n. Color Preview o. Get UUID
|
|
427
|
+
p. Hash Calculator (MD5/SHA/SM3) q. Mock Text
|
|
428
|
+
r. Special Characters (Symbols) s. Emoji Picker
|
|
429
|
+
t. Markdown Snippets u. VS Code Snippet Generator
|
|
430
|
+
v. Git Helper (Branch/Commit Template) w. Settings / Language
|
|
410
431
|
0. Exit
|
|
411
432
|
=================================
|
|
412
433
|
```
|
|
@@ -429,6 +450,19 @@ s. Settings (Language)
|
|
|
429
450
|
- Set quality/compression.
|
|
430
451
|
- Saved in the same directory.
|
|
431
452
|
|
|
453
|
+
#### Image Compression
|
|
454
|
+
- Select `imgCompress` (menu key).
|
|
455
|
+
- **Input**: File or Folder (batch processing).
|
|
456
|
+
- **Settings**:
|
|
457
|
+
- Quality (1-100).
|
|
458
|
+
- Recursive (for folders).
|
|
459
|
+
- Preserve directory structure.
|
|
460
|
+
- Suffix option: Add `_compressed_timestamp` or keep original filename.
|
|
461
|
+
- **Output**:
|
|
462
|
+
- Saved to `compressed_YYYYMMDD_HHmmss` folder.
|
|
463
|
+
- Auto-copy output path.
|
|
464
|
+
- Option to open output folder.
|
|
465
|
+
|
|
432
466
|
#### 3. Placeholder Image Generator
|
|
433
467
|
- Select `3`.
|
|
434
468
|
- **Mode 1: Local Image File**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xw-devtool-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.38",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "基于node的开发者助手cli",
|
|
6
6
|
"main": "index.js",
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"url": "https://gitee.com/github-9819409/xw-devtool-cli/issues"
|
|
75
75
|
},
|
|
76
76
|
"dependencies": {
|
|
77
|
+
"cli-table3": "^0.6.5",
|
|
77
78
|
"clipboardy": "^5.0.2",
|
|
78
79
|
"commander": "^14.0.2",
|
|
79
80
|
"dayjs": "^1.11.19",
|
|
@@ -85,7 +86,7 @@
|
|
|
85
86
|
"qrcode": "^1.5.4",
|
|
86
87
|
"sharp": "^0.33.5",
|
|
87
88
|
"tinycolor2": "^1.6.0",
|
|
88
|
-
"
|
|
89
|
-
"
|
|
89
|
+
"to-ico": "^1.1.5",
|
|
90
|
+
"uuid": "^13.0.0"
|
|
90
91
|
}
|
|
91
92
|
}
|
|
@@ -145,45 +145,35 @@ export async function colorPickHandler() {
|
|
|
145
145
|
console.log(i18next.t('colorPick.notSupported'));
|
|
146
146
|
return;
|
|
147
147
|
}
|
|
148
|
-
|
|
149
|
-
let sampler = null;
|
|
150
|
-
const readOneLine = (timeoutMs = 800) => new Promise((resolve) => {
|
|
151
|
-
let settled = false;
|
|
152
|
-
const onData = (buf) => {
|
|
153
|
-
if (settled) return;
|
|
154
|
-
settled = true;
|
|
155
|
-
resolve(buf.toString('utf8').trim());
|
|
156
|
-
};
|
|
157
|
-
sampler.stdout.once('data', onData);
|
|
158
|
-
if (timeoutMs > 0) {
|
|
159
|
-
setTimeout(() => {
|
|
160
|
-
if (settled) return;
|
|
161
|
-
settled = true;
|
|
162
|
-
try { sampler.stdout.removeListener('data', onData); } catch {}
|
|
163
|
-
resolve(null);
|
|
164
|
-
}, timeoutMs);
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
try {
|
|
168
|
-
const overlayScript = `
|
|
148
|
+
const overlayScript = `
|
|
169
149
|
Add-Type -AssemblyName System.Windows.Forms; Add-Type -AssemblyName System.Drawing;
|
|
170
150
|
Add-Type -TypeDefinition @'
|
|
171
151
|
using System;
|
|
172
152
|
using System.Drawing;
|
|
173
|
-
using System.Windows.Forms;
|
|
174
153
|
using System.Runtime.InteropServices;
|
|
154
|
+
using System.Windows.Forms;
|
|
175
155
|
public class OverlayForm : Form {
|
|
176
156
|
const int WS_EX_TRANSPARENT=0x20;
|
|
177
157
|
const int WS_EX_LAYERED=0x80000;
|
|
178
158
|
const int GWL_EXSTYLE=-20;
|
|
179
159
|
[DllImport("user32.dll")] static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
|
180
160
|
[DllImport("user32.dll")] static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
|
|
161
|
+
[DllImport("user32.dll")] static extern IntPtr GetDC(IntPtr hWnd);
|
|
162
|
+
[DllImport("user32.dll")] static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
|
|
163
|
+
[DllImport("gdi32.dll")] static extern int GetPixel(IntPtr hdc, int x, int y);
|
|
181
164
|
Timer t;
|
|
165
|
+
DateTime copyUntil = DateTime.MinValue;
|
|
166
|
+
string lastHex = "#000000";
|
|
167
|
+
string lastRgb = "RGB(0,0,0)";
|
|
182
168
|
public OverlayForm(){
|
|
183
169
|
this.FormBorderStyle=FormBorderStyle.None;
|
|
184
170
|
this.TopMost=true;
|
|
185
171
|
this.ShowInTaskbar=false;
|
|
186
|
-
this.
|
|
172
|
+
this.KeyPreview = true;
|
|
173
|
+
Rectangle totalBounds = Rectangle.Empty;
|
|
174
|
+
foreach(Screen s in Screen.AllScreens){ totalBounds = Rectangle.Union(totalBounds, s.Bounds); }
|
|
175
|
+
this.StartPosition = FormStartPosition.Manual;
|
|
176
|
+
this.Bounds = totalBounds;
|
|
187
177
|
this.BackColor=Color.Fuchsia;
|
|
188
178
|
this.TransparencyKey=this.BackColor;
|
|
189
179
|
this.DoubleBuffered=true;
|
|
@@ -192,20 +182,55 @@ public class OverlayForm : Form {
|
|
|
192
182
|
base.OnShown(e);
|
|
193
183
|
int ex=GetWindowLong(this.Handle,GWL_EXSTYLE);
|
|
194
184
|
SetWindowLong(this.Handle,GWL_EXSTYLE,ex|WS_EX_LAYERED|WS_EX_TRANSPARENT);
|
|
185
|
+
this.Focus();
|
|
186
|
+
this.Activate();
|
|
195
187
|
t=new Timer();
|
|
196
188
|
t.Interval=33;
|
|
197
189
|
t.Tick+=(s,ev)=>{ this.Invalidate(); };
|
|
198
190
|
t.Start();
|
|
199
191
|
}
|
|
192
|
+
protected override void OnKeyDown(KeyEventArgs e){
|
|
193
|
+
if(e.KeyCode==Keys.Escape){ Application.Exit(); }
|
|
194
|
+
if(e.KeyCode==Keys.Enter){ Clipboard.SetText(lastHex); copyUntil = DateTime.Now.AddSeconds(2); }
|
|
195
|
+
}
|
|
200
196
|
protected override void OnPaint(PaintEventArgs e){
|
|
201
|
-
var p=Cursor.Position;
|
|
202
197
|
var g=e.Graphics;
|
|
198
|
+
var p=this.PointToClient(Cursor.Position);
|
|
203
199
|
int gap=16;
|
|
204
200
|
using(var pen=new Pen(Color.Red,1)){
|
|
205
201
|
g.DrawLine(pen,0,p.Y,Math.Max(0,p.X-gap),p.Y);
|
|
206
|
-
g.DrawLine(pen,Math.Min(this.Width,p.X+gap),p.Y,this.Width,p.Y);
|
|
202
|
+
g.DrawLine(pen,Math.Min(this.ClientSize.Width,p.X+gap),p.Y,this.ClientSize.Width,p.Y);
|
|
207
203
|
g.DrawLine(pen,p.X,0,p.X,Math.Max(0,p.Y-gap));
|
|
208
|
-
g.DrawLine(pen,p.X,Math.Min(this.Height,p.Y+gap),p.X,this.Height);
|
|
204
|
+
g.DrawLine(pen,p.X,Math.Min(this.ClientSize.Height,p.Y+gap),p.X,this.ClientSize.Height);
|
|
205
|
+
}
|
|
206
|
+
IntPtr hdc = GetDC(IntPtr.Zero);
|
|
207
|
+
int color = GetPixel(hdc, Cursor.Position.X, Cursor.Position.Y);
|
|
208
|
+
ReleaseDC(IntPtr.Zero, hdc);
|
|
209
|
+
if(color!=-1){
|
|
210
|
+
int r = color & 0xFF;
|
|
211
|
+
int gC = (color >> 8) & 0xFF;
|
|
212
|
+
int b = (color >> 16) & 0xFF;
|
|
213
|
+
lastHex = String.Format("#{0:X2}{1:X2}{2:X2}", r, gC, b);
|
|
214
|
+
lastRgb = String.Format("RGB({0},{1},{2})", r, gC, b);
|
|
215
|
+
SizeF szHex = g.MeasureString(lastHex, new Font("Segoe UI", 12, FontStyle.Bold));
|
|
216
|
+
SizeF szRgb = g.MeasureString(lastRgb, new Font("Segoe UI", 10, FontStyle.Regular));
|
|
217
|
+
float pad = 12;
|
|
218
|
+
float boxW = Math.Max(szHex.Width, szRgb.Width) + pad*2 + 40;
|
|
219
|
+
float boxH = szHex.Height + szRgb.Height + pad*3;
|
|
220
|
+
float x = this.ClientSize.Width - boxW - 16;
|
|
221
|
+
float y = this.ClientSize.Height - boxH - 16;
|
|
222
|
+
using(var bg = new SolidBrush(Color.FromArgb(180,0,0,0))){ g.FillRectangle(bg, x, y, boxW, boxH); }
|
|
223
|
+
using(var preview = new SolidBrush(Color.FromArgb(r,gC,b))){ g.FillRectangle(preview, x+pad, y+pad, 36, boxH - pad*2); }
|
|
224
|
+
g.DrawString(lastHex, new Font("Segoe UI", 12, FontStyle.Bold), Brushes.White, x+pad+40, y+pad);
|
|
225
|
+
g.DrawString(lastRgb, new Font("Segoe UI", 10), Brushes.White, x+pad+40, y+pad+szHex.Height+6);
|
|
226
|
+
if(DateTime.Now < copyUntil){
|
|
227
|
+
string copied = "Copied: " + lastHex;
|
|
228
|
+
SizeF szC = g.MeasureString(copied, new Font("Segoe UI", 10));
|
|
229
|
+
float cx = x + pad + 40;
|
|
230
|
+
float cy = y + boxH - szC.Height - pad;
|
|
231
|
+
using(var bg2 = new SolidBrush(Color.FromArgb(160,0,128,0))){ g.FillRectangle(bg2, cx-6, cy-4, szC.Width+12, szC.Height+8); }
|
|
232
|
+
g.DrawString(copied, new Font("Segoe UI", 10), Brushes.White, cx, cy);
|
|
233
|
+
}
|
|
209
234
|
}
|
|
210
235
|
}
|
|
211
236
|
}
|
|
@@ -215,75 +240,17 @@ public static class Entry {
|
|
|
215
240
|
'@ -ReferencedAssemblies System.Windows.Forms,System.Drawing;
|
|
216
241
|
[Entry]::Main()
|
|
217
242
|
`;
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
using System;
|
|
222
|
-
using System.Runtime.InteropServices;
|
|
223
|
-
public static class ColorSampler {
|
|
224
|
-
[StructLayout(LayoutKind.Sequential)]
|
|
225
|
-
public struct POINT { public int X; public int Y; }
|
|
226
|
-
[DllImport("user32.dll")] static extern bool GetCursorPos(out POINT lpPoint);
|
|
227
|
-
[DllImport("user32.dll")] static extern IntPtr GetDC(IntPtr hWnd);
|
|
228
|
-
[DllImport("user32.dll")] static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
|
|
229
|
-
[DllImport("gdi32.dll")] static extern int GetPixel(IntPtr hdc, int x, int y);
|
|
230
|
-
public static string Sample() {
|
|
231
|
-
POINT p;
|
|
232
|
-
if(!GetCursorPos(out p)) return "";
|
|
233
|
-
IntPtr hdc = GetDC(IntPtr.Zero);
|
|
234
|
-
if (hdc == IntPtr.Zero) return "";
|
|
235
|
-
int color = GetPixel(hdc, p.X, p.Y);
|
|
236
|
-
ReleaseDC(IntPtr.Zero, hdc);
|
|
237
|
-
if (color == -1) return "";
|
|
238
|
-
int r = color & 0xFF;
|
|
239
|
-
int g = (color >> 8) & 0xFF;
|
|
240
|
-
int b = (color >> 16) & 0xFF;
|
|
241
|
-
return $"{r},{g},{b},255";
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
'@
|
|
245
|
-
function Sample {
|
|
246
|
-
$s=[ColorSampler]::Sample();
|
|
247
|
-
if([string]::IsNullOrEmpty($s)){ [Console]::Out.WriteLine(""); } else { [Console]::Out.WriteLine($s) }
|
|
248
|
-
}
|
|
249
|
-
while($true){
|
|
250
|
-
$line = [Console]::In.ReadLine();
|
|
251
|
-
if($line -eq $null){ break }
|
|
252
|
-
if($line -eq 'SAMPLE'){ Sample }
|
|
253
|
-
elseif($line -eq 'EXIT'){ break }
|
|
254
|
-
}
|
|
255
|
-
`;
|
|
256
|
-
sampler = spawn('powershell', ['-NoProfile', '-Command', samplerScript], { windowsHide: true, stdio: ['pipe', 'pipe', 'ignore'] });
|
|
257
|
-
try { sampler.stdout.setEncoding('utf8'); } catch {}
|
|
258
|
-
} catch {}
|
|
259
|
-
console.log(i18next.t('colorPick.moveMousePrompt'));
|
|
260
|
-
let last = null;
|
|
261
|
-
const sampleOnce = async () => {
|
|
243
|
+
const overlay = spawn('powershell', ['-NoProfile', '-Command', overlayScript], { windowsHide: true });
|
|
244
|
+
const sampleCmd = `powershell -NoProfile -Command "Add-Type -AssemblyName System.Windows.Forms; Add-Type -AssemblyName System.Drawing; $p=[System.Windows.Forms.Cursor]::Position; $bmp=New-Object System.Drawing.Bitmap 1,1; $g=[System.Drawing.Graphics]::FromImage($bmp); $g.CopyFromScreen($p.X,$p.Y,0,0,[System.Drawing.Size]::new(1,1)); $c=$bmp.GetPixel(0,0); Write-Output (\\"$($c.R),$($c.G),$($c.B),255\\"); $g.Dispose(); $bmp.Dispose();"`;
|
|
245
|
+
const sampleNow = () => {
|
|
262
246
|
try {
|
|
263
|
-
|
|
264
|
-
if (sampler && sampler.stdin && !sampler.killed) {
|
|
265
|
-
sampler.stdin.write('SAMPLE\n');
|
|
266
|
-
out = await readOneLine(800);
|
|
267
|
-
}
|
|
268
|
-
if (!out) {
|
|
269
|
-
const cmd = `powershell -NoProfile -Command "Add-Type -AssemblyName System.Windows.Forms; Add-Type -AssemblyName System.Drawing; $p=[System.Windows.Forms.Cursor]::Position; $bmp=New-Object System.Drawing.Bitmap 1,1; $g=[System.Drawing.Graphics]::FromImage($bmp); $g.CopyFromScreen($p.X,$p.Y,0,0,[System.Drawing.Size]::new(1,1)); $c=$bmp.GetPixel(0,0); Write-Output (\\"$($c.R),$($c.G),$($c.B),255\\"); $g.Dispose(); $bmp.Dispose();"`;
|
|
270
|
-
out = execSync(cmd, { stdio: ['pipe', 'pipe', 'ignore'] }).toString('utf8').trim();
|
|
271
|
-
}
|
|
247
|
+
const out = execSync(sampleCmd, { stdio: ['pipe', 'pipe', 'ignore'] }).toString('utf8').trim();
|
|
272
248
|
const parts = out.split(',').map(n => parseInt(n, 10));
|
|
273
249
|
const r = parts[0], g = parts[1], b = parts[2];
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
const hex = last.a < 1 ? tc.toHex8String().toUpperCase() : tc.toHexString().toUpperCase();
|
|
277
|
-
const rgb = tc.toRgbString();
|
|
278
|
-
const hsl = tc.toHslString();
|
|
279
|
-
console.log('\n=== ' + i18next.t('colorPick.result') + ' ===');
|
|
280
|
-
console.log(`Hex: ${hex}`);
|
|
281
|
-
console.log(`RGB: ${rgb}`);
|
|
282
|
-
console.log(`HSL: ${hsl}`);
|
|
283
|
-
console.log('==========================\n');
|
|
284
|
-
const label = `${i18next.t('colorPreview.preview')} ${hex} ${rgb} ${hsl}`;
|
|
285
|
-
printColorBar({ r: last.r, g: last.g, b: last.b }, 50, 2, label, last.a);
|
|
250
|
+
const tc = tinycolor({ r, g, b, a: 1 });
|
|
251
|
+
const hex = tc.toHexString().toUpperCase();
|
|
286
252
|
copy(hex);
|
|
253
|
+
console.log(i18next.t('colorPreview.copied', { value: hex }));
|
|
287
254
|
} catch {}
|
|
288
255
|
};
|
|
289
256
|
await new Promise((resolve) => {
|
|
@@ -292,24 +259,25 @@ while($true){
|
|
|
292
259
|
stdin.resume();
|
|
293
260
|
const onData = (data) => {
|
|
294
261
|
const s = data.toString('utf8');
|
|
295
|
-
if (s === '
|
|
296
|
-
sampleOnce();
|
|
297
|
-
} else if (s === '\r' || s === '\n') {
|
|
262
|
+
if (s === '\x1b') {
|
|
298
263
|
stdin.removeListener('data', onData);
|
|
299
264
|
try { stdin.setRawMode(false); } catch {}
|
|
300
265
|
stdin.pause();
|
|
266
|
+
try { overlay.kill(); } catch {}
|
|
301
267
|
resolve();
|
|
268
|
+
} else if (s === '\r' || s === '\n') {
|
|
269
|
+
sampleNow();
|
|
302
270
|
}
|
|
303
271
|
};
|
|
304
272
|
stdin.on('data', onData);
|
|
273
|
+
overlay.on('close', () => {
|
|
274
|
+
stdin.removeListener('data', onData);
|
|
275
|
+
try { stdin.setRawMode(false); } catch {}
|
|
276
|
+
stdin.pause();
|
|
277
|
+
resolve();
|
|
278
|
+
});
|
|
305
279
|
});
|
|
306
|
-
|
|
307
|
-
try { if (overlay) { overlay.kill(); } } catch {}
|
|
308
|
-
if (!last) {
|
|
309
|
-
console.log(i18next.t('colorPick.pickFail'));
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
picked = { r: last.r, g: last.g, b: last.b, a: last.a, region: null };
|
|
280
|
+
return;
|
|
313
281
|
} else {
|
|
314
282
|
const inputPath = await pickInputFile();
|
|
315
283
|
if (inputPath === '__BACK__') {
|
package/src/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import inquirer from 'inquirer';
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
+
import Table from 'cli-table3';
|
|
3
4
|
import i18next from './i18n.js';
|
|
4
5
|
import { saveConfig } from './utils/config.js';
|
|
5
6
|
|
|
@@ -134,10 +135,31 @@ async function showMenu() {
|
|
|
134
135
|
console.log('\n=================================');
|
|
135
136
|
console.log(` ${i18next.t('menu.title')}`);
|
|
136
137
|
console.log('=================================');
|
|
137
|
-
|
|
138
|
-
|
|
138
|
+
|
|
139
|
+
// Prepare items for table
|
|
140
|
+
const items = features.map((feature, index) => `${getFeatureKey(index)}. ${feature.name}`);
|
|
141
|
+
items.push(`0. ${i18next.t('menu.exit')}`);
|
|
142
|
+
|
|
143
|
+
// Configure table
|
|
144
|
+
const table = new Table({
|
|
145
|
+
chars: {
|
|
146
|
+
'top': '', 'top-mid': '', 'top-left': '', 'top-right': '',
|
|
147
|
+
'bottom': '', 'bottom-mid': '', 'bottom-left': '', 'bottom-right': '',
|
|
148
|
+
'left': '', 'left-mid': '', 'mid': '', 'mid-mid': '',
|
|
149
|
+
'right': '', 'right-mid': '', 'middle': ' '
|
|
150
|
+
},
|
|
151
|
+
style: { 'padding-left': 0, 'padding-right': 0 },
|
|
152
|
+
colWidths: [40, 40]
|
|
139
153
|
});
|
|
140
|
-
|
|
154
|
+
|
|
155
|
+
// Add items to table in pairs
|
|
156
|
+
const columns = 2;
|
|
157
|
+
for (let i = 0; i < items.length; i += columns) {
|
|
158
|
+
const row = items.slice(i, i + columns);
|
|
159
|
+
table.push(row);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.log(table.toString());
|
|
141
163
|
console.log('=================================\n');
|
|
142
164
|
|
|
143
165
|
const { choice } = await inquirer.prompt([
|