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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xw-devtool-cli",
3
- "version": "1.0.34",
3
+ "version": "1.0.36",
4
4
  "type": "module",
5
5
  "description": "基于node的开发者助手cli",
6
6
  "main": "index.js",
@@ -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
- 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.WindowState=FormWindowState.Maximized;
31
- this.BackColor=Color.Fuchsia;
32
- this.TransparencyKey=this.BackColor;
33
- this.DoubleBuffered=true;
34
- this.Cursor = Cursors.Default;
35
- }
36
- protected override void OnShown(EventArgs e){
37
- base.OnShown(e);
38
- int ex=GetWindowLong(this.Handle,GWL_EXSTYLE);
39
- SetWindowLong(this.Handle,GWL_EXSTYLE,ex|WS_EX_LAYERED|WS_EX_TRANSPARENT);
40
- t=new Timer();
41
- t.Interval=16;
42
- t.Tick+=(s,ev)=>{ this.Invalidate(); };
43
- t.Start();
44
- }
45
- protected override void OnPaint(PaintEventArgs e){
46
- var p=Cursor.Position;
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
- g.DrawLine(pen,0,p.Y,this.Width,p.Y);
50
- g.DrawLine(pen,p.X,0,p.X,this.Height);
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
- stdin.removeListener('data', onData);
85
- try { stdin.setRawMode(false); } catch {}
86
- stdin.pause();
87
- process.removeListener('exit', exitHandler);
88
- process.removeListener('SIGINT', exitHandler);
89
- cleanup();
90
- resolve();
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.once('data', onData);
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 compressOne(inputPath, quality, outDir, baseDir, preserveStructure) {
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
  };
package/src/locales/zh.js CHANGED
@@ -156,5 +156,7 @@ export default {
156
156
  opened: '已打开输出目录',
157
157
  scanResult: '扫描到图片数量',
158
158
  preservePrompt: '递归时是否按原始目录结构输出'
159
+ ,
160
+ suffixPrompt: '是否在文件名添加后缀(compressed+时间戳)'
159
161
  }
160
162
  };