xw-devtool-cli 1.0.34 → 1.0.36
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 +1 -1
- package/src/commands/crosshair.js +47 -39
- package/src/commands/imgCompress.js +16 -4
- package/src/locales/en.js +2 -1
- package/src/locales/zh.js +2 -0
package/package.json
CHANGED
|
@@ -16,40 +16,45 @@ using System;
|
|
|
16
16
|
using System.Drawing;
|
|
17
17
|
using System.Windows.Forms;
|
|
18
18
|
using System.Runtime.InteropServices;
|
|
19
|
-
public class OverlayForm : Form {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
19
|
+
public class OverlayForm : Form {
|
|
20
|
+
const int WS_EX_TRANSPARENT=0x20;
|
|
21
|
+
const int WS_EX_LAYERED=0x80000;
|
|
22
|
+
const int GWL_EXSTYLE=-20;
|
|
23
|
+
[DllImport("user32.dll")] static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
|
24
|
+
[DllImport("user32.dll")] static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
|
|
25
|
+
Timer t;
|
|
26
|
+
public OverlayForm(){
|
|
27
|
+
this.FormBorderStyle=FormBorderStyle.None;
|
|
28
|
+
this.TopMost=true;
|
|
29
|
+
this.ShowInTaskbar=false;
|
|
30
|
+
this.BackColor=Color.Fuchsia;
|
|
31
|
+
this.TransparencyKey=this.BackColor;
|
|
32
|
+
this.DoubleBuffered=true;
|
|
33
|
+
this.Cursor = Cursors.Default;
|
|
34
|
+
Rectangle totalBounds = Rectangle.Empty;
|
|
35
|
+
foreach(Screen s in Screen.AllScreens) {
|
|
36
|
+
totalBounds = Rectangle.Union(totalBounds, s.Bounds);
|
|
37
|
+
}
|
|
38
|
+
this.StartPosition = FormStartPosition.Manual;
|
|
39
|
+
this.Bounds = totalBounds;
|
|
40
|
+
}
|
|
41
|
+
protected override void OnShown(EventArgs e){
|
|
42
|
+
base.OnShown(e);
|
|
43
|
+
int ex=GetWindowLong(this.Handle,GWL_EXSTYLE);
|
|
44
|
+
SetWindowLong(this.Handle,GWL_EXSTYLE,ex|WS_EX_LAYERED|WS_EX_TRANSPARENT);
|
|
45
|
+
t=new Timer();
|
|
46
|
+
t.Interval=16;
|
|
47
|
+
t.Tick+=(s,ev)=>{ this.Invalidate(); };
|
|
48
|
+
t.Start();
|
|
49
|
+
}
|
|
50
|
+
protected override void OnPaint(PaintEventArgs e){
|
|
51
|
+
var p=this.PointToClient(Cursor.Position);
|
|
47
52
|
var g=e.Graphics;
|
|
48
53
|
using(var pen=new Pen(Color.Red,1)){
|
|
49
|
-
|
|
50
|
-
|
|
54
|
+
g.DrawLine(pen,0,p.Y,this.Width,p.Y);
|
|
55
|
+
g.DrawLine(pen,p.X,0,p.X,this.Height);
|
|
56
|
+
}
|
|
51
57
|
}
|
|
52
|
-
}
|
|
53
58
|
}
|
|
54
59
|
public static class Entry {
|
|
55
60
|
public static void Main(){ Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new OverlayForm()); }
|
|
@@ -81,14 +86,17 @@ public static class Entry {
|
|
|
81
86
|
try { stdin.setRawMode(true); } catch {}
|
|
82
87
|
stdin.resume();
|
|
83
88
|
const onData = (data) => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
const s = data.toString('utf8');
|
|
90
|
+
if (s === '\x1b') {
|
|
91
|
+
stdin.removeListener('data', onData);
|
|
92
|
+
try { stdin.setRawMode(false); } catch {}
|
|
93
|
+
stdin.pause();
|
|
94
|
+
process.removeListener('exit', exitHandler);
|
|
95
|
+
process.removeListener('SIGINT', exitHandler);
|
|
96
|
+
cleanup();
|
|
97
|
+
resolve();
|
|
98
|
+
}
|
|
91
99
|
};
|
|
92
|
-
stdin.
|
|
100
|
+
stdin.on('data', onData);
|
|
93
101
|
});
|
|
94
102
|
}
|
|
@@ -124,7 +124,16 @@ async function pickPreserveStructure() {
|
|
|
124
124
|
return choice;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
async function
|
|
127
|
+
async function pickSuffixOption() {
|
|
128
|
+
const choice = await selectFromMenu(i18next.t('imgCompress.suffixPrompt'), [
|
|
129
|
+
{ name: i18next.t('imgCompress.yes'), value: true },
|
|
130
|
+
{ name: i18next.t('imgCompress.no'), value: false }
|
|
131
|
+
], true, i18next.t('common.back'));
|
|
132
|
+
if (choice === '__BACK__') return '__BACK__';
|
|
133
|
+
return choice;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async function compressOne(inputPath, quality, outDir, baseDir, preserveStructure, addSuffix) {
|
|
128
137
|
const ext = path.extname(inputPath).toLowerCase();
|
|
129
138
|
const name = path.basename(inputPath, ext);
|
|
130
139
|
const ts = dayjs().format('YYYYMMDD_HHmmss');
|
|
@@ -134,12 +143,11 @@ async function compressOne(inputPath, quality, outDir, baseDir, preserveStructur
|
|
|
134
143
|
targetDir = path.join(outDir, rel);
|
|
135
144
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
136
145
|
}
|
|
137
|
-
const outPath = path.join(targetDir, `${name}_compressed_${ts}${ext}`);
|
|
146
|
+
const outPath = addSuffix ? path.join(targetDir, `${name}_compressed_${ts}${ext}`) : path.join(targetDir, `${name}${ext}`);
|
|
138
147
|
const image = sharp(inputPath, { failOnError: false });
|
|
139
148
|
if (ext === '.jpg' || ext === '.jpeg') {
|
|
140
149
|
await image.jpeg({ quality, mozjpeg: true }).toFile(outPath);
|
|
141
150
|
} else if (ext === '.png') {
|
|
142
|
-
// Map quality (1-100) to compressionLevel (0-9) roughly
|
|
143
151
|
const level = Math.max(0, Math.min(9, Math.round((100 - quality) / 10)));
|
|
144
152
|
await image.png({ quality, compressionLevel: level }).toFile(outPath);
|
|
145
153
|
} else if (ext === '.webp') {
|
|
@@ -179,15 +187,19 @@ export async function imgCompressHandler() {
|
|
|
179
187
|
const outDir = path.resolve(baseDir, `compressed_${dayjs().format('YYYYMMDD_HHmmss')}`);
|
|
180
188
|
fs.mkdirSync(outDir, { recursive: true });
|
|
181
189
|
let preserveStructure = false;
|
|
190
|
+
let addSuffix = input.type === 'folder';
|
|
182
191
|
if (input.type === 'folder') {
|
|
183
192
|
const preserveChoice = await pickPreserveStructure();
|
|
184
193
|
if (preserveChoice === '__BACK__') return;
|
|
185
194
|
preserveStructure = preserveChoice;
|
|
195
|
+
const suffixChoice = await pickSuffixOption();
|
|
196
|
+
if (suffixChoice === '__BACK__') return;
|
|
197
|
+
addSuffix = suffixChoice;
|
|
186
198
|
}
|
|
187
199
|
const results = [];
|
|
188
200
|
for (const f of files) {
|
|
189
201
|
try {
|
|
190
|
-
const out = await compressOne(f, quality, outDir, baseDir, preserveStructure);
|
|
202
|
+
const out = await compressOne(f, quality, outDir, baseDir, preserveStructure, addSuffix);
|
|
191
203
|
if (out) results.push(out);
|
|
192
204
|
} catch (e) {
|
|
193
205
|
console.error(`${i18next.t('imgCompress.fail')}: ${f} -> ${e.message}`);
|
package/src/locales/en.js
CHANGED
|
@@ -155,6 +155,7 @@ export default {
|
|
|
155
155
|
openFail: 'Failed to open directory',
|
|
156
156
|
opened: 'Opened output directory',
|
|
157
157
|
scanResult: 'Images found',
|
|
158
|
-
preservePrompt: 'Preserve original directory structure in output?'
|
|
158
|
+
preservePrompt: 'Preserve original directory structure in output?',
|
|
159
|
+
suffixPrompt: 'Append filename suffix (compressed + timestamp)?'
|
|
159
160
|
}
|
|
160
161
|
};
|