dsh-plugin-beyond-simulator 2.0.6 → 2.0.8
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/dsh-plugin/lib/play.html
CHANGED
|
@@ -42,6 +42,10 @@
|
|
|
42
42
|
'use strict'
|
|
43
43
|
const API = '/qxqy-simulator/api/play'
|
|
44
44
|
const sessionId = decodeURIComponent(location.hash.slice(1))
|
|
45
|
+
const embedded = window.parent !== window && new URLSearchParams(location.search).get('embedded') === '1'
|
|
46
|
+
let closing = false
|
|
47
|
+
let stopped = false
|
|
48
|
+
let embeddedApi = null
|
|
45
49
|
const $ = (id) => document.getElementById(id)
|
|
46
50
|
const stage = $('stage')
|
|
47
51
|
const cv = $('cv')
|
|
@@ -64,11 +68,16 @@
|
|
|
64
68
|
if (!showLog) requestAnimationFrame(() => fitStage(true))
|
|
65
69
|
}
|
|
66
70
|
|
|
67
|
-
|
|
71
|
+
function api(action, args = {}) {
|
|
72
|
+
if (closing && action !== 'stop') return Promise.reject(new Error('试玩正在关闭'))
|
|
73
|
+
return embeddedApi ? embeddedApi(action, args) : request(action, args)
|
|
74
|
+
}
|
|
75
|
+
async function request(action, args) {
|
|
68
76
|
const response = await fetch(API, {
|
|
69
77
|
method: 'POST',
|
|
70
78
|
headers: { 'content-type': 'application/json' },
|
|
71
79
|
body: JSON.stringify({ sessionId, action, args }),
|
|
80
|
+
signal: AbortSignal.timeout(15_000),
|
|
72
81
|
})
|
|
73
82
|
const envelope = await response.json().catch(() => null)
|
|
74
83
|
if (!response.ok || !envelope || envelope.ok !== true) throw new Error(envelope?.error || '试玩请求失败')
|
|
@@ -225,6 +234,7 @@
|
|
|
225
234
|
}
|
|
226
235
|
|
|
227
236
|
function showError(error) {
|
|
237
|
+
if (closing) return
|
|
228
238
|
$('loading').classList.add('hidden')
|
|
229
239
|
$('fatal').classList.remove('hidden')
|
|
230
240
|
$('fatal').textContent = error?.message || String(error)
|
|
@@ -238,6 +248,7 @@
|
|
|
238
248
|
onSnapshot: renderChrome,
|
|
239
249
|
onStatus: applyStatus,
|
|
240
250
|
onError: showError,
|
|
251
|
+
isActive: () => !closing,
|
|
241
252
|
})
|
|
242
253
|
play.bindInput(stage, { keysOn: window })
|
|
243
254
|
|
|
@@ -300,7 +311,40 @@
|
|
|
300
311
|
revealStage()
|
|
301
312
|
} catch (error) { showError(error) }
|
|
302
313
|
})
|
|
303
|
-
|
|
314
|
+
function quiesce() {
|
|
315
|
+
closing = true
|
|
316
|
+
document.querySelector('.app').inert = true
|
|
317
|
+
play.reset()
|
|
318
|
+
}
|
|
319
|
+
function focusStage() {
|
|
320
|
+
stage.tabIndex = -1
|
|
321
|
+
stage.focus({ preventScroll: true })
|
|
322
|
+
}
|
|
323
|
+
stage.addEventListener('pointerdown', focusStage)
|
|
324
|
+
function requestEmbeddedClose() {
|
|
325
|
+
// Custom desktop schemes can serialize their origin as "null".
|
|
326
|
+
// The parent checks the exact iframe WindowProxy and session id.
|
|
327
|
+
parent.postMessage({ type: 'qxqy-play-close', sessionId }, location.origin === 'null' ? '*' : location.origin)
|
|
328
|
+
}
|
|
329
|
+
addEventListener('keydown', event => {
|
|
330
|
+
if (embedded && event.key === 'Escape') { event.preventDefault(); requestEmbeddedClose() }
|
|
331
|
+
})
|
|
332
|
+
$('close').addEventListener('click', async () => {
|
|
333
|
+
if (embedded) {
|
|
334
|
+
requestEmbeddedClose()
|
|
335
|
+
return
|
|
336
|
+
}
|
|
337
|
+
try {
|
|
338
|
+
quiesce()
|
|
339
|
+
await api('stop')
|
|
340
|
+
stopped = true
|
|
341
|
+
window.close()
|
|
342
|
+
} catch (error) {
|
|
343
|
+
closing = false
|
|
344
|
+
document.querySelector('.app').inert = false
|
|
345
|
+
showError(error)
|
|
346
|
+
}
|
|
347
|
+
})
|
|
304
348
|
$('theme').addEventListener('click', () => { theme = theme === 'dark' ? 'light' : 'dark'; applyTheme() })
|
|
305
349
|
let lastCase = null
|
|
306
350
|
function parseAsserts() {
|
|
@@ -327,12 +371,37 @@
|
|
|
327
371
|
for (const button of document.querySelectorAll('[data-view]')) button.addEventListener('click', () => setView(button.dataset.view))
|
|
328
372
|
addEventListener('resize', () => fitStage(true))
|
|
329
373
|
addEventListener('pagehide', () => {
|
|
374
|
+
closing = true
|
|
330
375
|
play.destroy()
|
|
331
|
-
|
|
376
|
+
// Embedded close/unmount is owned by the parent. A late keepalive stop
|
|
377
|
+
// from the old frame must never terminate the next play session.
|
|
378
|
+
if (!embedded && !stopped) fetch(API, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ sessionId, action: 'stop', args: {} }), keepalive: true }).catch(() => {})
|
|
332
379
|
})
|
|
380
|
+
if (embedded) {
|
|
381
|
+
// Closing the whole application document has no awaitable teardown.
|
|
382
|
+
// This fallback is separate from normal modal close, which awaits stop.
|
|
383
|
+
const parentPagehide = () => {
|
|
384
|
+
quiesce()
|
|
385
|
+
fetch(API, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ sessionId, action: 'stop', args: {} }), keepalive: true }).catch(() => {})
|
|
386
|
+
}
|
|
387
|
+
parent.addEventListener('pagehide', parentPagehide)
|
|
388
|
+
addEventListener('pagehide', () => parent.removeEventListener('pagehide', parentPagehide))
|
|
389
|
+
}
|
|
333
390
|
|
|
334
391
|
if (!sessionId) showError(new Error('缺少会话 ID'))
|
|
335
|
-
else
|
|
392
|
+
else if (embedded) {
|
|
393
|
+
// Wait for the parent's load handler: closing a still-loading iframe
|
|
394
|
+
// must not race an automatic start from this document.
|
|
395
|
+
window.qxqyEmbeddedPlay = {
|
|
396
|
+
start: request => {
|
|
397
|
+
if (closing) return Promise.resolve()
|
|
398
|
+
embeddedApi = request
|
|
399
|
+
return play.start().then(() => { if (closing) play.reset(); else { revealStage(); focusStage() } })
|
|
400
|
+
},
|
|
401
|
+
quiesce,
|
|
402
|
+
focus: focusStage,
|
|
403
|
+
}
|
|
404
|
+
} else play.start().then(() => setTimeout(revealStage, 0)).catch(showError)
|
|
336
405
|
})()
|
|
337
406
|
</script>
|
|
338
407
|
</body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-plugin-beyond-simulator",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.8",
|
|
4
4
|
"description": "千星奇域客户端 UI 编辑与试玩模拟器 for DeepSeek Harness",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "pnpm@10.15.0",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
53
|
-
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6"
|
|
53
|
+
"@deepseek-ai/dsh-tools": "^0.1.0-rc.6 || ^0.2.0-rc.1"
|
|
54
54
|
},
|
|
55
55
|
"license": "GPL-3.0-only"
|
|
56
56
|
}
|