dsh-desktop-windows 0.1.9 → 0.2.1

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,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-desktop-windows",
3
3
  "productName": "DeepSeek Harness Desktop",
4
- "version": "0.1.9",
4
+ "version": "0.2.1",
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
@@ -411,11 +411,13 @@ function openShotWindow() {
411
411
  return
412
412
  }
413
413
  shotWindow = new BrowserWindow({
414
- fullscreen: true,
415
414
  frame: false,
416
415
  resizable: false,
416
+ movable: false,
417
+ fullscreen: true,
417
418
  alwaysOnTop: true,
418
419
  skipTaskbar: true,
420
+ show: false, // 截图加载完成后再显示,避免黑屏闪烁
419
421
  webPreferences: {
420
422
  contextIsolation: true,
421
423
  nodeIntegration: false,
@@ -425,10 +427,6 @@ function openShotWindow() {
425
427
  })
426
428
  shotWindow.setAlwaysOnTop(true, 'screen-saver')
427
429
  shotWindow.loadFile(path.join(__dirname, 'screenshot.html'))
428
- shotWindow.once('ready-to-show', () => {
429
- shotWindow.show()
430
- shotWindow.focus()
431
- })
432
430
  shotWindow.webContents.once('did-finish-load', () => {
433
431
  if (pendingShot && !shotWindow.isDestroyed()) {
434
432
  shotWindow.webContents.send('screenshot:data', pendingShot.toDataURL())
@@ -439,11 +437,20 @@ function openShotWindow() {
439
437
  })
440
438
  }
441
439
 
442
- /** 选区确认:裁剪剪贴板 → 主窗口粘贴。 */
440
+ // 截图在选区窗口内加载完成再显示窗口(避免黑屏"小窗口")
441
+ function onShotLoaded() {
442
+ if (shotWindow && !shotWindow.isDestroyed()) {
443
+ shotWindow.show()
444
+ shotWindow.focus()
445
+ }
446
+ }
447
+
448
+ /** 选区确认:裁剪 → 剪贴板 → 恢复主窗口并粘贴。 */
443
449
  function onShotDone(rect) {
444
450
  const image = pendingShot
445
451
  pendingShot = null
446
452
  if (shotWindow && !shotWindow.isDestroyed()) shotWindow.destroy()
453
+ showMainWindow() // 恢复显示聊天窗口
447
454
  if (!image || !rect) return
448
455
  const sf = screen.getPrimaryDisplay().scaleFactor || 1
449
456
  const r = {
@@ -455,23 +462,34 @@ function onShotDone(rect) {
455
462
  if (r.width <= 0 || r.height <= 0) return
456
463
  const cropped = image.crop(r)
457
464
  clipboard.writeImage(cropped)
465
+ // 等主窗口就绪后再聚焦输入框并粘贴(失败则提示手动 Ctrl+V)
458
466
  if (mainWindow && !mainWindow.isDestroyed()) {
459
- mainWindow.show()
460
- mainWindow.focus()
461
- mainWindow.webContents
462
- .executeJavaScript(
463
- `(() => { const el = document.querySelector('textarea, [contenteditable="true"]'); if (el) { el.focus(); return true } return false })()`
464
- )
465
- .then((focused) => {
466
- if (focused) mainWindow.webContents.paste()
467
- })
468
- .catch(() => {})
467
+ setTimeout(async () => {
468
+ let focused = false
469
+ try {
470
+ focused = await mainWindow.webContents.executeJavaScript(
471
+ `(() => { const el = document.querySelector('textarea, [contenteditable="true"]'); if (el) { el.focus(); return true } return false })()`
472
+ )
473
+ } catch {
474
+ focused = false
475
+ }
476
+ if (focused) {
477
+ mainWindow.webContents.paste()
478
+ } else {
479
+ dialog.showMessageBox(mainWindow, {
480
+ type: 'info',
481
+ message: '截图已复制到剪贴板,请在聊天输入框按 Ctrl+V 粘贴',
482
+ buttons: ['好'],
483
+ })
484
+ }
485
+ }, 400)
469
486
  }
470
487
  }
471
488
 
472
489
  function onShotCancel() {
473
490
  pendingShot = null
474
491
  if (shotWindow && !shotWindow.isDestroyed()) shotWindow.destroy()
492
+ showMainWindow() // 恢复显示聊天窗口
475
493
  }
476
494
 
477
495
  /* ------------------------------------------------------------------ *
@@ -627,9 +645,12 @@ if (!gotLock) {
627
645
  ipcMain.on('dsh-desktop:reload', () => {
628
646
  if (mainWindow) mainWindow.webContents.reload()
629
647
  })
630
- // 选区截图:截全屏打开选区窗口用户框选 → 裁剪 → 剪贴板 → 粘贴聊天框
648
+ // 选区截图:隐藏主窗口截全屏选区窗口 → 裁剪 → 剪贴板 → 粘贴
631
649
  ipcMain.handle('dsh-desktop:capture', async () => {
632
650
  try {
651
+ // 先隐藏主窗口,避免截到聊天框本身
652
+ if (mainWindow && !mainWindow.isDestroyed()) mainWindow.hide()
653
+ await new Promise((r) => setTimeout(r, 250))
633
654
  const display = screen.getPrimaryDisplay()
634
655
  const sf = display.scaleFactor || 1
635
656
  const sources = await desktopCapturer.getSources({
@@ -639,12 +660,19 @@ if (!gotLock) {
639
660
  height: Math.round(display.size.height * sf),
640
661
  },
641
662
  })
642
- if (!sources.length) return { ok: false, error: '未找到屏幕源' }
663
+ if (!sources.length) {
664
+ showMainWindow()
665
+ return { ok: false, error: '未找到屏幕源' }
666
+ }
643
667
  pendingShot = sources[0].thumbnail
644
- if (pendingShot.isEmpty()) return { ok: false, error: '截图为空' }
668
+ if (pendingShot.isEmpty()) {
669
+ showMainWindow()
670
+ return { ok: false, error: '截图为空' }
671
+ }
645
672
  openShotWindow()
646
673
  return { ok: true }
647
674
  } catch (e) {
675
+ showMainWindow()
648
676
  return { ok: false, error: String((e && e.message) || e) }
649
677
  }
650
678
  })
@@ -657,6 +685,7 @@ if (!gotLock) {
657
685
  // 选区截图结果
658
686
  ipcMain.on('screenshot:done', (_e, rect) => onShotDone(rect))
659
687
  ipcMain.on('screenshot:cancel', () => onShotCancel())
688
+ ipcMain.on('screenshot:loaded', () => onShotLoaded())
660
689
  if (external) log('reusing external dsh service on port ' + port)
661
690
  })
662
691
 
@@ -12,4 +12,6 @@ contextBridge.exposeInMainWorld('__screenshot', {
12
12
  done: (rect) => ipcRenderer.send('screenshot:done', rect),
13
13
  // 取消
14
14
  cancel: () => ipcRenderer.send('screenshot:cancel'),
15
+ // 截图已加载完成(可显示窗口了)
16
+ loaded: () => ipcRenderer.send('screenshot:loaded'),
15
17
  })
@@ -58,8 +58,11 @@
58
58
 
59
59
  // 接收主进程传来的截图 dataURL
60
60
  window.__screenshot.onData((dataUrl) => {
61
+ bg.onload = () => {
62
+ window.__screenshot.loaded()
63
+ setTimeout(() => (hint.style.opacity = '0.9'), 100)
64
+ }
61
65
  bg.src = dataUrl
62
- setTimeout(() => (hint.style.opacity = '0.9'), 100)
63
66
  })
64
67
 
65
68
  const updateMask = () => {