dsh-desktop-windows 0.2.1 → 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.
- package/package.json +1 -1
- package/src/main.js +13 -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.
|
|
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",
|
package/src/main.js
CHANGED
|
@@ -410,11 +410,17 @@ function openShotWindow() {
|
|
|
410
410
|
shotWindow.focus()
|
|
411
411
|
return
|
|
412
412
|
}
|
|
413
|
+
// 用显示器精确尺寸铺满屏幕(不用 fullscreen,Windows 上更可靠)
|
|
414
|
+
const display = screen.getPrimaryDisplay()
|
|
415
|
+
const bounds = display.bounds
|
|
413
416
|
shotWindow = new BrowserWindow({
|
|
417
|
+
x: bounds.x,
|
|
418
|
+
y: bounds.y,
|
|
419
|
+
width: bounds.width,
|
|
420
|
+
height: bounds.height,
|
|
414
421
|
frame: false,
|
|
415
422
|
resizable: false,
|
|
416
423
|
movable: false,
|
|
417
|
-
fullscreen: true,
|
|
418
424
|
alwaysOnTop: true,
|
|
419
425
|
skipTaskbar: true,
|
|
420
426
|
show: false, // 截图加载完成后再显示,避免黑屏闪烁
|
|
@@ -426,6 +432,7 @@ function openShotWindow() {
|
|
|
426
432
|
},
|
|
427
433
|
})
|
|
428
434
|
shotWindow.setAlwaysOnTop(true, 'screen-saver')
|
|
435
|
+
shotWindow.setBounds(bounds) // 再次确保铺满
|
|
429
436
|
shotWindow.loadFile(path.join(__dirname, 'screenshot.html'))
|
|
430
437
|
shotWindow.webContents.once('did-finish-load', () => {
|
|
431
438
|
if (pendingShot && !shotWindow.isDestroyed()) {
|
|
@@ -461,8 +468,12 @@ function onShotDone(rect) {
|
|
|
461
468
|
}
|
|
462
469
|
if (r.width <= 0 || r.height <= 0) return
|
|
463
470
|
const cropped = image.crop(r)
|
|
471
|
+
// 保存一份临时文件(验证/诊断用)
|
|
472
|
+
try {
|
|
473
|
+
fs.writeFileSync(path.join(os.tmpdir(), 'dsh-screenshot.png'), cropped.toPNG())
|
|
474
|
+
} catch { /* 非致命 */ }
|
|
464
475
|
clipboard.writeImage(cropped)
|
|
465
|
-
//
|
|
476
|
+
// 等主窗口就绪后尝试自动粘贴;若聚焦失败则提示手动 Ctrl+V
|
|
466
477
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
467
478
|
setTimeout(async () => {
|
|
468
479
|
let focused = false
|
package/src/screenshot.html
CHANGED
|
@@ -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
|
-
|
|
12
|
-
#sel {
|
|
13
|
-
|
|
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"
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
bg.onload = () => {
|
|
97
|
+
window.__screenshot.onData(function (dataUrl) {
|
|
98
|
+
bg.onload = function () {
|
|
62
99
|
window.__screenshot.loaded()
|
|
63
|
-
setTimeout(()
|
|
100
|
+
setTimeout(function () { hint.style.opacity = '0.9' }, 100)
|
|
64
101
|
}
|
|
65
102
|
bg.src = dataUrl
|
|
66
103
|
})
|
|
67
104
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
77
|
-
if (
|
|
78
|
-
|
|
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
|
-
|
|
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('
|
|
100
|
-
if (!
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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,
|
|
106
|
-
t.style.top = (rect.y + rect.h +
|
|
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
|
-
|
|
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 = ()
|
|
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
|
})
|