xw-devtool-cli 1.0.28 → 1.0.29

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
@@ -21,8 +21,9 @@
21
21
  > 2. 由于涉及跨进程通信,按下空格后可能存在轻微延时,请耐心等待结果。
22
22
  > 3. 取色模式下会显示全屏十字辅助线,按 **回车键** 退出模式。
23
23
  - **占位图生成**:快速生成指定尺寸、颜色、文字的占位图片 (Placeholder Image)。
24
- - **全屏十字辅助线**:显示跟随鼠标的全屏红色十字线,用于屏幕对齐和定位。
24
+ - **全屏十字辅助线**:显示跟随鼠标的全屏红色十字线,用于屏幕对齐和定位。
25
25
  - **屏幕测距**:点击屏幕上任意两点,测量其像素距离。
26
+ - **屏幕文字标注**:在屏幕上点击并输入文字,进行临时标注。右键撤销,Delete 清空。
26
27
  - **Mock 数据生成**:
27
28
  - 支持生成:英文段落 (Lorem Ipsum)、中文字符、中国居民身份证号、电子邮箱、URL、订单号、手机号、座机号。
28
29
  - 支持批量生成。
package/README_EN.md CHANGED
@@ -18,6 +18,7 @@ Key features include: Base64 encoding/decoding, image format conversion, image <
18
18
  - **Screen Picker**: Pick color directly from the screen at the mouse position; move the cursor and press Enter to sample; supports alpha preview.
19
19
  - **Screen Crosshair**: Display a full-screen red crosshair following the mouse for alignment.
20
20
  - **Pixel Distance**: Measure pixel distance between two points on the screen.
21
+ - **Screen Mark**: Click on the screen to add text annotations. Right-click to undo, Delete to clear.
21
22
  - **Mock Data**:
22
23
  - Generate: Lorem Ipsum, Chinese characters, ID cards, Emails, URLs, Order IDs, Phone numbers.
23
24
  - Supports batch generation.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xw-devtool-cli",
3
- "version": "1.0.28",
3
+ "version": "1.0.29",
4
4
  "type": "module",
5
5
  "description": "基于node的开发者助手cli",
6
6
  "main": "index.js",
@@ -49,7 +49,9 @@
49
49
  "md5",
50
50
  "color-converter",
51
51
  "emoji",
52
- "markdown"
52
+ "markdown",
53
+ "screen-mark",
54
+ "pixel-distance"
53
55
  ],
54
56
  "author": "npmxw",
55
57
  "license": "ISC",
@@ -0,0 +1,169 @@
1
+ import { spawn } from 'child_process';
2
+ import i18next from '../i18n.js';
3
+
4
+ export async function screenMarkHandler() {
5
+ if (process.platform !== 'win32') {
6
+ console.log(i18next.t('screenMark.notSupported'));
7
+ return;
8
+ }
9
+
10
+ console.log(i18next.t('screenMark.startPrompt'));
11
+
12
+ const overlayScript = `
13
+ Add-Type -AssemblyName System.Windows.Forms;
14
+ Add-Type -AssemblyName System.Drawing;
15
+
16
+ Add-Type -TypeDefinition @'
17
+ using System;
18
+ using System.Drawing;
19
+ using System.Windows.Forms;
20
+ using System.Collections.Generic;
21
+
22
+ public class MarkItem {
23
+ public Point Location;
24
+ public string Text;
25
+ public MarkItem(Point loc, string text) {
26
+ Location = loc;
27
+ Text = text;
28
+ }
29
+ }
30
+
31
+ public class InputBox : Form {
32
+ private TextBox textBox;
33
+ private Button btnOk;
34
+ private Button btnCancel;
35
+ public string InputText { get { return textBox.Text; } }
36
+
37
+ public InputBox(string title, string prompt, Point location) {
38
+ this.Text = title;
39
+ this.Size = new Size(300, 150);
40
+ this.FormBorderStyle = FormBorderStyle.FixedDialog;
41
+ this.StartPosition = FormStartPosition.Manual;
42
+ this.Location = location;
43
+ this.TopMost = true;
44
+ this.MinimizeBox = false;
45
+ this.MaximizeBox = false;
46
+
47
+ Label lbl = new Label();
48
+ lbl.Text = prompt;
49
+ lbl.Location = new Point(10, 10);
50
+ lbl.AutoSize = true;
51
+ this.Controls.Add(lbl);
52
+
53
+ textBox = new TextBox();
54
+ textBox.Location = new Point(10, 35);
55
+ textBox.Size = new Size(260, 20);
56
+ this.Controls.Add(textBox);
57
+
58
+ btnOk = new Button();
59
+ btnOk.Text = "OK";
60
+ btnOk.DialogResult = DialogResult.OK;
61
+ btnOk.Location = new Point(110, 70);
62
+ this.Controls.Add(btnOk);
63
+
64
+ btnCancel = new Button();
65
+ btnCancel.Text = "Cancel";
66
+ btnCancel.DialogResult = DialogResult.Cancel;
67
+ btnCancel.Location = new Point(190, 70);
68
+ this.Controls.Add(btnCancel);
69
+
70
+ this.AcceptButton = btnOk;
71
+ this.CancelButton = btnCancel;
72
+ }
73
+ }
74
+
75
+ public class MarkForm : Form {
76
+ private List<MarkItem> marks = new List<MarkItem>();
77
+ private Bitmap screenCapture;
78
+
79
+ public MarkForm() {
80
+ this.FormBorderStyle = FormBorderStyle.None;
81
+ this.TopMost = true;
82
+ this.Cursor = Cursors.IBeam;
83
+ this.DoubleBuffered = true;
84
+
85
+ // Calculate total bounds
86
+ Rectangle totalBounds = Rectangle.Empty;
87
+ foreach(Screen s in Screen.AllScreens) {
88
+ totalBounds = Rectangle.Union(totalBounds, s.Bounds);
89
+ }
90
+
91
+ this.StartPosition = FormStartPosition.Manual;
92
+ this.Bounds = totalBounds;
93
+
94
+ // Capture screen
95
+ screenCapture = new Bitmap(totalBounds.Width, totalBounds.Height);
96
+ using (Graphics g = Graphics.FromImage(screenCapture)) {
97
+ g.CopyFromScreen(totalBounds.Location, Point.Empty, totalBounds.Size);
98
+ }
99
+ this.BackgroundImage = screenCapture;
100
+ }
101
+
102
+ protected override void OnMouseDown(MouseEventArgs e) {
103
+ if (e.Button == MouseButtons.Right) {
104
+ // Remove last mark
105
+ if (marks.Count > 0) {
106
+ marks.RemoveAt(marks.Count - 1);
107
+ this.Invalidate();
108
+ }
109
+ return;
110
+ }
111
+
112
+ if (e.Button == MouseButtons.Left) {
113
+ // Show input box
114
+ using (InputBox input = new InputBox("Add Text", "Enter text:", Cursor.Position)) {
115
+ if (input.ShowDialog() == DialogResult.OK && !string.IsNullOrWhiteSpace(input.InputText)) {
116
+ marks.Add(new MarkItem(e.Location, input.InputText));
117
+ this.Invalidate();
118
+ }
119
+ }
120
+ }
121
+ }
122
+
123
+ protected override void OnPaint(PaintEventArgs e) {
124
+ Graphics g = e.Graphics;
125
+ g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
126
+ g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
127
+
128
+ Font font = new Font("Segoe UI", 12, FontStyle.Bold);
129
+ Brush textBrush = Brushes.White;
130
+
131
+ foreach (var mark in marks) {
132
+ SizeF size = g.MeasureString(mark.Text, font);
133
+ // Draw text without background frame
134
+ g.DrawString(mark.Text, font, textBrush, mark.Location.X, mark.Location.Y - size.Height);
135
+
136
+ // Draw anchor point
137
+ g.FillEllipse(Brushes.Red, mark.Location.X - 3, mark.Location.Y - 3, 6, 6);
138
+ }
139
+ }
140
+
141
+ protected override void OnKeyDown(KeyEventArgs e) {
142
+ if (e.KeyCode == Keys.Escape) {
143
+ Application.Exit();
144
+ }
145
+ if (e.KeyCode == Keys.Delete || e.KeyCode == Keys.C) {
146
+ marks.Clear();
147
+ this.Invalidate();
148
+ }
149
+ }
150
+ }
151
+
152
+ public static class Entry {
153
+ public static void Main() {
154
+ Application.EnableVisualStyles();
155
+ Application.SetCompatibleTextRenderingDefault(false);
156
+ Application.Run(new MarkForm());
157
+ }
158
+ }
159
+ '@ -ReferencedAssemblies System.Windows.Forms,System.Drawing
160
+
161
+ [Entry]::Main()
162
+ `;
163
+
164
+ const proc = spawn('powershell', ['-NoProfile', '-Command', overlayScript], { stdio: 'inherit' });
165
+
166
+ proc.on('close', (code) => {
167
+ // console.log('Exited');
168
+ });
169
+ }
package/src/index.js CHANGED
@@ -31,6 +31,7 @@ import { dominantColorHandler } from './commands/dominantColor.js';
31
31
  import { colorPickHandler } from './commands/colorPick.js';
32
32
  import { crosshairHandler } from './commands/crosshair.js';
33
33
  import { pixelDistanceHandler } from './commands/pixelDistance.js';
34
+ import { screenMarkHandler } from './commands/screenMark.js';
34
35
 
35
36
  process.on('SIGINT', () => {
36
37
  console.log(`\n${i18next.t('menu.bye')}`);
@@ -57,6 +58,7 @@ function getFeatures() {
57
58
  { name: i18next.t('menu.features.colorPick'), value: 'colorPick' },
58
59
  { name: i18next.t('menu.features.crosshair'), value: 'crosshair' },
59
60
  { name: i18next.t('menu.features.pixelDistance'), value: 'pixelDistance' },
61
+ { name: i18next.t('menu.features.screenMark'), value: 'screenMark' },
60
62
  { name: i18next.t('menu.features.placeholderImg'), value: 'placeholderImg' },
61
63
  { name: i18next.t('menu.features.qrcode'), value: 'qrcode' },
62
64
 
@@ -204,6 +206,9 @@ async function handleAction(action) {
204
206
  case 'pixelDistance':
205
207
  await pixelDistanceHandler();
206
208
  break;
209
+ case 'screenMark':
210
+ await screenMarkHandler();
211
+ break;
207
212
  case 'timeFormat':
208
213
  await timeFormatHandler();
209
214
  break;
package/src/locales/en.js CHANGED
@@ -6,6 +6,10 @@ export default {
6
6
  notSupported: 'Pixel distance tool not supported on this OS.',
7
7
  startPrompt: 'Pixel distance tool started. Click 2 points to measure. Right-click to reset. ESC to exit.'
8
8
  },
9
+ screenMark: {
10
+ notSupported: 'Screen mark tool not supported on this OS.',
11
+ startPrompt: 'Screen mark tool started. Click to add text. Right-click to undo. ESC to exit.'
12
+ },
9
13
  menu: {
10
14
  title: 'xw-devtool-cli Menu',
11
15
  exit: 'Exit',
@@ -20,6 +24,7 @@ export default {
20
24
  colorPick: 'Color Picker',
21
25
  crosshair: 'Screen Crosshair',
22
26
  pixelDistance: 'Pixel Distance Tool',
27
+ screenMark: 'Screen Mark Tool',
23
28
  placeholderImg: 'Placeholder Image Generator',
24
29
  qrcode: 'QR Code Generator',
25
30
  url: 'URL Encode/Decode',
package/src/locales/zh.js CHANGED
@@ -24,6 +24,7 @@ export default {
24
24
  colorPick: '颜色吸取',
25
25
  crosshair: '全屏十字辅助线',
26
26
  pixelDistance: '屏幕测距 (像素)',
27
+ screenMark: '屏幕文字标注',
27
28
  placeholderImg: '占位图生成器',
28
29
  qrcode: '二维码生成器',
29
30
  url: 'URL 编码/解码',