dsh-desktop-windows 0.2.2 → 0.2.3

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/screenshot.html +150 -56
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-desktop-windows",
3
3
  "productName": "DeepSeek Harness Desktop",
4
- "version": "0.2.2",
4
+ "version": "0.2.3",
5
5
  "description": "DeepSeek Harness 桌面版(Windows)· Electron desktop shell for DeepSeek Harness (dsh web) — 双击即用,无需终端 / double-click to run the harness in a standalone window",
6
6
  "main": "src/main.js",
7
7
  "license": "MIT",
@@ -8,9 +8,22 @@
8
8
  html, body { width: 100%; height: 100%; overflow: hidden; background: #000; cursor: crosshair; }
9
9
  #stage { position: fixed; inset: 0; }
10
10
  #stage img { width: 100%; height: 100%; object-fit: fill; display: block; }
11
- #mask { position: fixed; inset: 0; pointer-events: none; }
12
- #sel { position: fixed; border: 1.5px solid #2563eb; background: rgba(37, 99, 235, 0.12); display: none; pointer-events: none; }
13
- #sel .sz { position: absolute; left: 0; top: -22px; background: #2563eb; color: #fff; font: 11px/18px sans-serif; padding: 0 6px; border-radius: 4px; white-space: nowrap; }
11
+ .mask { position: fixed; background: rgba(0, 0, 0, 0.45); pointer-events: none; }
12
+ #sel {
13
+ position: fixed; border: 1.5px solid #2563eb;
14
+ background: rgba(37, 99, 235, 0.10);
15
+ display: none; pointer-events: none; z-index: 3;
16
+ }
17
+ #sel .sz {
18
+ position: absolute; left: 0; top: -22px;
19
+ background: #2563eb; color: #fff;
20
+ font: 11px/18px sans-serif; padding: 0 6px; border-radius: 4px; white-space: nowrap;
21
+ }
22
+ .handle {
23
+ position: fixed; width: 9px; height: 9px; background: #2563eb;
24
+ border: 1px solid #fff; border-radius: 2px; display: none;
25
+ pointer-events: auto; z-index: 4;
26
+ }
14
27
  #toolbar {
15
28
  position: fixed; display: none; align-items: center; gap: 8px;
16
29
  background: rgba(15, 23, 42, 0.92); border: 1px solid #334155;
@@ -30,92 +43,173 @@
30
43
  position: fixed; left: 50%; top: 20px; transform: translateX(-50%);
31
44
  background: rgba(15, 23, 42, 0.85); color: #e2e8f0;
32
45
  font: 13px/1 sans-serif; padding: 8px 16px; border-radius: 8px;
33
- z-index: 5; pointer-events: none;
46
+ z-index: 5; pointer-events: none; transition: opacity .15s;
34
47
  }
35
48
  </style>
36
49
  </head>
37
50
  <body>
38
51
  <div id="stage"><img id="bg" alt=""></div>
39
- <div id="mask"></div>
52
+ <div id="maskTop" class="mask"></div>
53
+ <div id="maskBottom" class="mask"></div>
54
+ <div id="maskLeft" class="mask"></div>
55
+ <div id="maskRight" class="mask"></div>
40
56
  <div id="sel"><div class="sz"></div></div>
57
+ <div id="hN" class="handle" style="cursor:n-resize"></div>
58
+ <div id="hS" class="handle" style="cursor:s-resize"></div>
59
+ <div id="hE" class="handle" style="cursor:e-resize"></div>
60
+ <div id="hW" class="handle" style="cursor:w-resize"></div>
61
+ <div id="hNE" class="handle" style="cursor:ne-resize"></div>
62
+ <div id="hNW" class="handle" style="cursor:nw-resize"></div>
63
+ <div id="hSE" class="handle" style="cursor:se-resize"></div>
64
+ <div id="hSW" class="handle" style="cursor:sw-resize"></div>
41
65
  <div id="toolbar">
42
- <span class="tip">拖动鼠标框选区域</span>
66
+ <span class="tip" id="tip">拖动边框微调选区</span>
43
67
  <button id="okBtn">✔ 确定</button>
44
68
  <button id="cancelBtn">✕ 取消 (Esc)</button>
45
69
  </div>
46
- <div id="hint">拖动鼠标框选要截取的区域 · Esc 取消</div>
70
+ <div id="hint">拖动鼠标框选要截取的区域 · 画完后可拖拽边框微调 · Esc 取消</div>
47
71
 
48
72
  <script>
49
- const bg = document.getElementById('bg')
50
- const mask = document.getElementById('mask')
51
- const sel = document.getElementById('sel')
52
- const toolbar = document.getElementById('toolbar')
53
- const hint = document.getElementById('hint')
54
- const sz = sel.querySelector('.sz')
55
-
56
- let startX = 0, startY = 0, drawing = false
57
- let rect = null
73
+ var bg = document.getElementById('bg')
74
+ var sel = document.getElementById('sel')
75
+ var toolbar = document.getElementById('toolbar')
76
+ var hint = document.getElementById('hint')
77
+ var tip = document.getElementById('tip')
78
+ var sz = sel.querySelector('.sz')
79
+ var masks = {
80
+ top: document.getElementById('maskTop'),
81
+ bottom: document.getElementById('maskBottom'),
82
+ left: document.getElementById('maskLeft'),
83
+ right: document.getElementById('maskRight'),
84
+ }
85
+ var handles = {
86
+ n: document.getElementById('hN'), s: document.getElementById('hS'),
87
+ e: document.getElementById('hE'), w: document.getElementById('hW'),
88
+ ne: document.getElementById('hNE'), nw: document.getElementById('hNW'),
89
+ se: document.getElementById('hSE'), sw: document.getElementById('hSW'),
90
+ }
91
+ var W = window.innerWidth, H = window.innerHeight
92
+ var MIN = 8
93
+ var rect = null
94
+ var mode = 'draw' // draw | adjust
95
+ var drag = null
58
96
 
59
- // 接收主进程传来的截图 dataURL
60
- window.__screenshot.onData((dataUrl) => {
61
- bg.onload = () => {
97
+ window.__screenshot.onData(function (dataUrl) {
98
+ bg.onload = function () {
62
99
  window.__screenshot.loaded()
63
- setTimeout(() => (hint.style.opacity = '0.9'), 100)
100
+ setTimeout(function () { hint.style.opacity = '0.9' }, 100)
64
101
  }
65
102
  bg.src = dataUrl
66
103
  })
67
104
 
68
- const updateMask = () => {
69
- if (!rect) { mask.style.background = 'rgba(0,0,0,0.45)'; return }
70
- const { x, y, w, h } = rect
71
- mask.style.background =
72
- `linear-gradient(rgba(0,0,0,0.45) ${y}px, transparent ${y}px ${y + h}px, rgba(0,0,0,0.45) ${y + h}px),` +
73
- `linear-gradient(90deg, rgba(0,0,0,0.45) ${x}px, transparent ${x}px ${x + w}px, rgba(0,0,0,0.45) ${x + w}px)`
105
+ function clampRect(r) {
106
+ var x = Math.max(0, Math.min(W - MIN, r.x))
107
+ var y = Math.max(0, Math.min(H - MIN, r.y))
108
+ var w = Math.min(r.w, W - x)
109
+ var h = Math.min(r.h, H - y)
110
+ w = Math.max(MIN, w); h = Math.max(MIN, h)
111
+ return { x: x, y: y, w: w, h: h }
74
112
  }
75
113
 
76
- document.addEventListener('mousedown', (e) => {
77
- if (e.button !== 0) return
78
- startX = e.clientX; startY = e.clientY
79
- drawing = true
80
- rect = null
81
- sel.style.display = 'none'
82
- toolbar.style.display = 'none'
83
- })
84
-
85
- document.addEventListener('mousemove', (e) => {
86
- if (!drawing) return
87
- const x = Math.min(startX, e.clientX)
88
- const y = Math.min(startY, e.clientY)
89
- const w = Math.abs(e.clientX - startX)
90
- const h = Math.abs(e.clientY - startY)
91
- rect = { x, y, w, h }
114
+ function layout() {
115
+ if (!rect) return
116
+ var x = rect.x, y = rect.y, w = rect.w, h = rect.h
92
117
  sel.style.display = 'block'
93
118
  sel.style.left = x + 'px'; sel.style.top = y + 'px'
94
119
  sel.style.width = w + 'px'; sel.style.height = h + 'px'
95
120
  sz.textContent = Math.round(w) + ' × ' + Math.round(h)
96
- updateMask()
121
+ // 遮罩(4 块)
122
+ masks.top.style.cssText = 'left:0;top:0;width:' + W + 'px;height:' + y + 'px;display:block'
123
+ masks.bottom.style.cssText = 'left:0;top:' + (y + h) + 'px;width:' + W + 'px;height:' + (H - y - h) + 'px;display:block'
124
+ masks.left.style.cssText = 'left:0;top:' + y + 'px;width:' + x + 'px;height:' + h + 'px;display:block'
125
+ masks.right.style.cssText = 'left:' + (x + w) + 'px;top:' + y + 'px;width:' + (W - x - w) + 'px;height:' + h + 'px;display:block'
126
+ // 手柄
127
+ if (mode === 'adjust') {
128
+ var half = 4.5
129
+ handles.n.style.cssText = 'left:' + (x + w / 2 - half) + 'px;top:' + (y - half) + 'px;display:block'
130
+ handles.s.style.cssText = 'left:' + (x + w / 2 - half) + 'px;top:' + (y + h - half) + 'px;display:block'
131
+ handles.e.style.cssText = 'left:' + (x + w - half) + 'px;top:' + (y + h / 2 - half) + 'px;display:block'
132
+ handles.w.style.cssText = 'left:' + (x - half) + 'px;top:' + (y + h / 2 - half) + 'px;display:block'
133
+ handles.ne.style.cssText = 'left:' + (x + w - half) + 'px;top:' + (y - half) + 'px;display:block'
134
+ handles.nw.style.cssText = 'left:' + (x - half) + 'px;top:' + (y - half) + 'px;display:block'
135
+ handles.se.style.cssText = 'left:' + (x + w - half) + 'px;top:' + (y + h - half) + 'px;display:block'
136
+ handles.sw.style.cssText = 'left:' + (x - half) + 'px;top:' + (y + h - half) + 'px;display:block'
137
+ } else {
138
+ Object.keys(handles).forEach(function (k) { handles[k].style.display = 'none' })
139
+ }
140
+ }
141
+
142
+ function inside(e, r) {
143
+ return e.clientX >= r.x && e.clientX <= r.x + r.w && e.clientY >= r.y && e.clientY <= r.y + r.h
144
+ }
145
+
146
+ document.addEventListener('mousedown', function (e) {
147
+ if (e.button !== 0) return
148
+ if (mode === 'draw') {
149
+ drag = { type: 'draw', x0: e.clientX, y0: e.clientY }
150
+ } else {
151
+ // 调整模式:手柄 / 拖动移动 / 外部重画
152
+ var hKey = null
153
+ for (var k in handles) if (e.target === handles[k]) { hKey = k; break }
154
+ if (hKey) {
155
+ drag = { type: 'resize', dir: hKey, x0: e.clientX, y0: e.clientY, orig: { x: rect.x, y: rect.y, w: rect.w, h: rect.h } }
156
+ } else if (inside(e, rect)) {
157
+ drag = { type: 'move', x0: e.clientX, y0: e.clientY, orig: { x: rect.x, y: rect.y } }
158
+ } else {
159
+ // 点击选区外 → 重新画
160
+ mode = 'draw'
161
+ rect = null
162
+ sel.style.display = 'none'
163
+ toolbar.style.display = 'none'
164
+ Object.keys(handles).forEach(function (k) { handles[k].style.display = 'none' })
165
+ masks.top.style.display = masks.bottom.style.display = masks.left.style.display = masks.right.style.display = 'none'
166
+ hint.style.opacity = '0.9'
167
+ drag = { type: 'draw', x0: e.clientX, y0: e.clientY }
168
+ }
169
+ }
97
170
  })
98
171
 
99
- document.addEventListener('mouseup', () => {
100
- if (!drawing) return
101
- drawing = false
102
- if (rect && rect.w > 5 && rect.h > 5) {
103
- const t = document.getElementById('toolbar')
172
+ document.addEventListener('mousemove', function (e) {
173
+ if (!drag) return
174
+ var dx = e.clientX - drag.x0
175
+ var dy = e.clientY - drag.y0
176
+ if (drag.type === 'draw') {
177
+ rect = clampRect({ x: Math.min(drag.x0, e.clientX), y: Math.min(drag.y0, e.clientY), w: Math.abs(e.clientX - drag.x0), h: Math.abs(e.clientY - drag.y0) })
178
+ layout()
179
+ } else if (drag.type === 'move') {
180
+ rect = clampRect({ x: drag.orig.x + dx, y: drag.orig.y + dy, w: rect.w, h: rect.h })
181
+ layout()
182
+ } else if (drag.type === 'resize') {
183
+ var o = drag.orig
184
+ var r = { x: o.x, y: o.y, w: o.w, h: o.h }
185
+ if (drag.dir.indexOf('e') !== -1) r.w = o.w + dx
186
+ if (drag.dir.indexOf('s') !== -1) r.h = o.h + dy
187
+ if (drag.dir.indexOf('w') !== -1) { r.x = o.x + dx; r.w = o.w - dx }
188
+ if (drag.dir.indexOf('n') !== -1) { r.y = o.y + dy; r.h = o.h - dy }
189
+ rect = clampRect(r)
190
+ layout()
191
+ }
192
+ })
193
+
194
+ document.addEventListener('mouseup', function () {
195
+ if (!drag) return
196
+ drag = null
197
+ if (mode === 'draw' && rect && rect.w >= MIN && rect.h >= MIN) {
198
+ mode = 'adjust'
199
+ var t = document.getElementById('toolbar')
104
200
  t.style.display = 'flex'
105
- t.style.left = Math.min(rect.x + rect.w + 10, window.innerWidth - 220) + 'px'
106
- t.style.top = (rect.y + rect.h + 12 > window.innerHeight - 60 ? rect.y - 56 : rect.y + rect.h + 12) + 'px'
201
+ t.style.left = Math.min(rect.x + rect.w + 10, W - 230) + 'px'
202
+ t.style.top = (rect.y + rect.h + 14 > H - 60 ? rect.y - 58 : rect.y + rect.h + 14) + 'px'
107
203
  hint.style.opacity = '0'
108
- } else {
109
- rect = null; sel.style.display = 'none'
204
+ layout() // 显示手柄
110
205
  }
111
206
  })
112
207
 
113
- document.getElementById('okBtn').onclick = () => {
208
+ document.getElementById('okBtn').onclick = function () {
114
209
  if (rect) window.__screenshot.done(rect)
115
210
  }
116
- document.getElementById('cancelBtn').onclick = () => window.__screenshot.cancel()
117
-
118
- document.addEventListener('keydown', (e) => {
211
+ document.getElementById('cancelBtn').onclick = function () { window.__screenshot.cancel() }
212
+ document.addEventListener('keydown', function (e) {
119
213
  if (e.key === 'Escape') window.__screenshot.cancel()
120
214
  if (e.key === 'Enter' && rect) window.__screenshot.done(rect)
121
215
  })