dsh-desktop-windows 0.2.4 → 0.2.6
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/screenshot.html +89 -74
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.
|
|
4
|
+
"version": "0.2.6",
|
|
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",
|
package/src/screenshot.html
CHANGED
|
@@ -8,11 +8,17 @@
|
|
|
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
|
-
|
|
11
|
+
/* 遮罩:全屏半透明黑 + clip-path 挖洞(GPU 加速,每帧只改字符串) */
|
|
12
|
+
#mask {
|
|
13
|
+
position: fixed; inset: 0; z-index: 2; pointer-events: none;
|
|
14
|
+
background: rgba(0, 0, 0, 0.45);
|
|
15
|
+
clip-path: polygon(0px 0px, 0px 0px, 0px 0px);
|
|
16
|
+
}
|
|
17
|
+
/* 选区框:border div,transform 定位 + 尺寸更新 */
|
|
12
18
|
#sel {
|
|
13
|
-
position: fixed;
|
|
14
|
-
background: rgba(37, 99, 235, 0.
|
|
15
|
-
|
|
19
|
+
position: fixed; z-index: 3; pointer-events: none;
|
|
20
|
+
border: 1.5px solid #2563eb; background: rgba(37, 99, 235, 0.08);
|
|
21
|
+
transform-origin: 0 0;
|
|
16
22
|
}
|
|
17
23
|
#sel .sz {
|
|
18
24
|
position: absolute; left: 0; top: -22px;
|
|
@@ -23,6 +29,7 @@
|
|
|
23
29
|
position: fixed; width: 9px; height: 9px; background: #2563eb;
|
|
24
30
|
border: 1px solid #fff; border-radius: 2px; display: none;
|
|
25
31
|
pointer-events: auto; z-index: 4;
|
|
32
|
+
transform: translate(-50%, -50%);
|
|
26
33
|
}
|
|
27
34
|
#toolbar {
|
|
28
35
|
position: fixed; display: none; align-items: center; gap: 8px;
|
|
@@ -32,8 +39,8 @@
|
|
|
32
39
|
}
|
|
33
40
|
#toolbar .tip { color: #94a3b8; font: 12px/1 sans-serif; margin-right: 4px; }
|
|
34
41
|
#toolbar button {
|
|
35
|
-
border: none; border-radius: 6px; padding:
|
|
36
|
-
font: 600
|
|
42
|
+
border: none; border-radius: 6px; padding: 7px 16px; cursor: pointer;
|
|
43
|
+
font: 600 13px/1 sans-serif;
|
|
37
44
|
}
|
|
38
45
|
#okBtn { background: #2563eb; color: #fff; }
|
|
39
46
|
#okBtn:hover { background: #1d4ed8; }
|
|
@@ -49,10 +56,7 @@
|
|
|
49
56
|
</head>
|
|
50
57
|
<body>
|
|
51
58
|
<div id="stage"><img id="bg" alt=""></div>
|
|
52
|
-
<div id="
|
|
53
|
-
<div id="maskBottom" class="mask"></div>
|
|
54
|
-
<div id="maskLeft" class="mask"></div>
|
|
55
|
-
<div id="maskRight" class="mask"></div>
|
|
59
|
+
<div id="mask"></div>
|
|
56
60
|
<div id="sel"><div class="sz"></div></div>
|
|
57
61
|
<div id="hN" class="handle" style="cursor:n-resize"></div>
|
|
58
62
|
<div id="hS" class="handle" style="cursor:s-resize"></div>
|
|
@@ -62,26 +66,21 @@
|
|
|
62
66
|
<div id="hNW" class="handle" style="cursor:nw-resize"></div>
|
|
63
67
|
<div id="hSE" class="handle" style="cursor:se-resize"></div>
|
|
64
68
|
<div id="hSW" class="handle" style="cursor:sw-resize"></div>
|
|
65
|
-
<div id="toolbar">
|
|
66
|
-
<span class="tip" id="tip"
|
|
69
|
+
<div id="toolbar" style="cursor:default">
|
|
70
|
+
<span class="tip" id="tip">可拖拽边框微调 · 点确定或双击完成</span>
|
|
67
71
|
<button id="okBtn">✔ 确定</button>
|
|
68
72
|
<button id="cancelBtn">✕ 取消 (Esc)</button>
|
|
69
73
|
</div>
|
|
70
|
-
<div id="hint">拖动鼠标框选要截取的区域 · 画完后可拖拽边框微调 · Esc 取消</div>
|
|
74
|
+
<div id="hint">拖动鼠标框选要截取的区域 · 画完后可拖拽边框微调 · 双击完成 · Esc 取消</div>
|
|
71
75
|
|
|
72
76
|
<script>
|
|
73
77
|
var bg = document.getElementById('bg')
|
|
78
|
+
var mask = document.getElementById('mask')
|
|
74
79
|
var sel = document.getElementById('sel')
|
|
80
|
+
var sz = sel.querySelector('.sz')
|
|
75
81
|
var toolbar = document.getElementById('toolbar')
|
|
76
82
|
var hint = document.getElementById('hint')
|
|
77
83
|
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
84
|
var handles = {
|
|
86
85
|
n: document.getElementById('hN'), s: document.getElementById('hS'),
|
|
87
86
|
e: document.getElementById('hE'), w: document.getElementById('hW'),
|
|
@@ -93,10 +92,12 @@
|
|
|
93
92
|
var rect = null
|
|
94
93
|
var mode = 'draw' // draw | adjust
|
|
95
94
|
var drag = null
|
|
95
|
+
var rafPending = false
|
|
96
96
|
|
|
97
97
|
window.__screenshot.onData(function (dataUrl) {
|
|
98
98
|
bg.onload = function () {
|
|
99
99
|
window.__screenshot.loaded()
|
|
100
|
+
update()
|
|
100
101
|
setTimeout(function () { hint.style.opacity = '0.9' }, 100)
|
|
101
102
|
}
|
|
102
103
|
bg.src = dataUrl
|
|
@@ -105,38 +106,58 @@
|
|
|
105
106
|
function clampRect(r) {
|
|
106
107
|
var x = Math.max(0, Math.min(W - MIN, r.x))
|
|
107
108
|
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)
|
|
109
|
+
var w = Math.max(MIN, Math.min(r.w, W - x))
|
|
110
|
+
var h = Math.max(MIN, Math.min(r.h, H - y))
|
|
111
111
|
return { x: x, y: y, w: w, h: h }
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
// 每帧只更新 2 个元素:mask 的 clip-path(GPU)+ sel 的位置尺寸
|
|
115
|
+
function update() {
|
|
116
|
+
if (!rect) {
|
|
117
|
+
mask.style.clipPath = 'polygon(0px 0px, 0px 0px, 0px 0px, 0px 0px, 0px 0px)'
|
|
118
|
+
sel.style.display = 'none'
|
|
119
|
+
hideHandles()
|
|
120
|
+
return
|
|
121
|
+
}
|
|
116
122
|
var x = rect.x, y = rect.y, w = rect.w, h = rect.h
|
|
123
|
+
// clip-path:外框逆时针 + 选区顺时针 → 挖洞
|
|
124
|
+
mask.style.clipPath =
|
|
125
|
+
'polygon(0px 0px, ' + W + 'px 0px, ' + W + 'px ' + H + 'px, 0px ' + H + 'px, 0px 0px, ' +
|
|
126
|
+
x + 'px ' + y + 'px, ' + (x + w) + 'px ' + y + 'px, ' + (x + w) + 'px ' + (y + h) + 'px, ' + x + 'px ' + (y + h) + 'px, ' + x + 'px ' + y + 'px)'
|
|
117
127
|
sel.style.display = 'block'
|
|
118
|
-
sel.style.
|
|
119
|
-
sel.style.width = w + 'px'
|
|
128
|
+
sel.style.transform = 'translate(' + x + 'px, ' + y + 'px)'
|
|
129
|
+
sel.style.width = w + 'px'
|
|
130
|
+
sel.style.height = h + 'px'
|
|
120
131
|
sz.textContent = Math.round(w) + ' × ' + Math.round(h)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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' })
|
|
132
|
+
updateHandles()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function updateHandles() {
|
|
136
|
+
if (mode !== 'adjust' || !rect) { hideHandles(); return }
|
|
137
|
+
var x = rect.x, y = rect.y, w = rect.w, h = rect.h
|
|
138
|
+
var pos = {
|
|
139
|
+
n: [x + w / 2, y], s: [x + w / 2, y + h],
|
|
140
|
+
e: [x + w, y + h / 2], w: [x, y + h / 2],
|
|
141
|
+
ne: [x + w, y], nw: [x, y], se: [x + w, y + h], sw: [x, y + h],
|
|
139
142
|
}
|
|
143
|
+
for (var k in pos) {
|
|
144
|
+
handles[k].style.display = 'block'
|
|
145
|
+
handles[k].style.left = pos[k][0] + 'px'
|
|
146
|
+
handles[k].style.top = pos[k][1] + 'px'
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function hideHandles() {
|
|
151
|
+
for (var k in handles) handles[k].style.display = 'none'
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function scheduleUpdate() {
|
|
155
|
+
if (rafPending) return
|
|
156
|
+
rafPending = true
|
|
157
|
+
requestAnimationFrame(function () {
|
|
158
|
+
rafPending = false
|
|
159
|
+
update()
|
|
160
|
+
})
|
|
140
161
|
}
|
|
141
162
|
|
|
142
163
|
function inside(e, r) {
|
|
@@ -147,25 +168,21 @@
|
|
|
147
168
|
if (e.button !== 0) return
|
|
148
169
|
if (mode === 'draw') {
|
|
149
170
|
drag = { type: 'draw', x0: e.clientX, y0: e.clientY }
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
var hKey = null
|
|
174
|
+
for (var k in handles) if (e.target === handles[k]) { hKey = k; break }
|
|
175
|
+
if (hKey) {
|
|
176
|
+
drag = { type: 'resize', dir: hKey, x0: e.clientX, y0: e.clientY, orig: { x: rect.x, y: rect.y, w: rect.w, h: rect.h } }
|
|
177
|
+
} else if (inside(e, rect)) {
|
|
178
|
+
drag = { type: 'move', x0: e.clientX, y0: e.clientY, orig: { x: rect.x, y: rect.y } }
|
|
150
179
|
} else {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
}
|
|
180
|
+
mode = 'draw'
|
|
181
|
+
rect = null
|
|
182
|
+
toolbar.style.display = 'none'
|
|
183
|
+
hint.style.opacity = '0.9'
|
|
184
|
+
drag = { type: 'draw', x0: e.clientX, y0: e.clientY }
|
|
185
|
+
scheduleUpdate()
|
|
169
186
|
}
|
|
170
187
|
})
|
|
171
188
|
|
|
@@ -175,10 +192,8 @@
|
|
|
175
192
|
var dy = e.clientY - drag.y0
|
|
176
193
|
if (drag.type === 'draw') {
|
|
177
194
|
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
195
|
} else if (drag.type === 'move') {
|
|
180
196
|
rect = clampRect({ x: drag.orig.x + dx, y: drag.orig.y + dy, w: rect.w, h: rect.h })
|
|
181
|
-
layout()
|
|
182
197
|
} else if (drag.type === 'resize') {
|
|
183
198
|
var o = drag.orig
|
|
184
199
|
var r = { x: o.x, y: o.y, w: o.w, h: o.h }
|
|
@@ -187,8 +202,8 @@
|
|
|
187
202
|
if (drag.dir.indexOf('w') !== -1) { r.x = o.x + dx; r.w = o.w - dx }
|
|
188
203
|
if (drag.dir.indexOf('n') !== -1) { r.y = o.y + dy; r.h = o.h - dy }
|
|
189
204
|
rect = clampRect(r)
|
|
190
|
-
layout()
|
|
191
205
|
}
|
|
206
|
+
scheduleUpdate()
|
|
192
207
|
})
|
|
193
208
|
|
|
194
209
|
document.addEventListener('mouseup', function () {
|
|
@@ -198,23 +213,23 @@
|
|
|
198
213
|
mode = 'adjust'
|
|
199
214
|
var t = document.getElementById('toolbar')
|
|
200
215
|
t.style.display = 'flex'
|
|
201
|
-
t.style.left = Math.min(rect.x + rect.w + 10, W -
|
|
202
|
-
t.style.top = (rect.y + rect.h + 14 > H - 60 ? rect.y -
|
|
203
|
-
tip.textContent = '可拖拽边框微调 · 点确定或双击完成'
|
|
216
|
+
t.style.left = Math.min(rect.x + rect.w + 10, W - 250) + 'px'
|
|
217
|
+
t.style.top = (rect.y + rect.h + 14 > H - 60 ? rect.y - 60 : rect.y + rect.h + 14) + 'px'
|
|
204
218
|
hint.style.opacity = '0'
|
|
205
|
-
|
|
219
|
+
scheduleUpdate()
|
|
206
220
|
}
|
|
207
221
|
})
|
|
208
222
|
|
|
209
|
-
//
|
|
210
|
-
|
|
211
|
-
if (rect && inside(e, rect)) window.__screenshot.done(rect)
|
|
212
|
-
})
|
|
223
|
+
// 关键:点击工具栏(确定/取消)不触发 document 的画框逻辑
|
|
224
|
+
toolbar.addEventListener('mousedown', function (e) { e.stopPropagation() })
|
|
213
225
|
|
|
214
226
|
document.getElementById('okBtn').onclick = function () {
|
|
215
227
|
if (rect) window.__screenshot.done(rect)
|
|
216
228
|
}
|
|
217
229
|
document.getElementById('cancelBtn').onclick = function () { window.__screenshot.cancel() }
|
|
230
|
+
document.addEventListener('dblclick', function (e) {
|
|
231
|
+
if (rect && inside(e, rect)) window.__screenshot.done(rect)
|
|
232
|
+
})
|
|
218
233
|
document.addEventListener('keydown', function (e) {
|
|
219
234
|
if (e.key === 'Escape') window.__screenshot.cancel()
|
|
220
235
|
if (e.key === 'Enter' && rect) window.__screenshot.done(rect)
|