@ash-qw/dsh-theme-prts 0.1.93 → 0.1.95
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/CHANGELOG.md +13 -1
- package/README.en.md +21 -50
- package/README.md +22 -51
- package/lib/client.js +172 -2462
- package/package.json +2 -2
- package/src/client/composer-glass-adapter.js +6 -5
- package/src/client/index.js +1 -11
- package/src/client/operations-shell-v2.js +120 -8
- package/src/client/particle-field-adapter.js +45 -3
- package/src/client/preferences.js +1 -35
- package/src/client/resize-shield-adapter.js +5 -0
- package/src/client/theme-settings-workbench.js +3 -234
- package/src/styles/prts.css +42 -72
- package/src/styles/theme-settings-workbench.css +1 -174
- package/src/client/conversation-scale-adapter.js +0 -2320
- package/src/styles/conversation-scale.css +0 -471
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ash-qw/dsh-theme-prts",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.95",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Unofficial Arknights P.R.T.S.-inspired UI theme for DeepSeek Harness Web",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"node": ">=18"
|
|
71
71
|
},
|
|
72
72
|
"devDependencies": {
|
|
73
|
-
"@playwright/test": "1.55.
|
|
73
|
+
"@playwright/test": "1.55.1",
|
|
74
74
|
"jsdom": "24.1.3"
|
|
75
75
|
}
|
|
76
76
|
}
|
|
@@ -9,6 +9,7 @@ const COMPOSER_RELEVANCE_SELECTOR = [
|
|
|
9
9
|
'button[aria-haspopup="listbox"]',
|
|
10
10
|
'[role="menu"]',
|
|
11
11
|
'[role="listbox"]',
|
|
12
|
+
'[data-trigger-menu]',
|
|
12
13
|
'[data-slot="conversation.input.overlay"]',
|
|
13
14
|
].join(', ')
|
|
14
15
|
const PRESET_TRIGGER_SELECTOR = [
|
|
@@ -27,7 +28,7 @@ function controlledPopup(document, trigger) {
|
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
function localPopup(trigger) {
|
|
30
|
-
const selector = '[role="menu"], [role="listbox"], [class*="mufS8W_card"]'
|
|
31
|
+
const selector = '[role="menu"], [role="listbox"], [data-trigger-menu], [class*="mufS8W_card"]'
|
|
31
32
|
const sibling = trigger.nextElementSibling
|
|
32
33
|
if (sibling?.matches(selector)) return sibling
|
|
33
34
|
return trigger.parentElement?.querySelector(`:scope > ${selector}`) ?? null
|
|
@@ -36,7 +37,7 @@ function localPopup(trigger) {
|
|
|
36
37
|
function overlayPopup(document) {
|
|
37
38
|
const overlay = document.querySelector('[data-slot="conversation.input.overlay"]')
|
|
38
39
|
if (!overlay) return null
|
|
39
|
-
return overlay.querySelector('[role="listbox"], [role="menu"], [class*="mufS8W_card"]')
|
|
40
|
+
return overlay.querySelector('[data-trigger-menu], [role="listbox"], [role="menu"], [class*="mufS8W_card"]')
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
function linkedMenu(document, trigger) {
|
|
@@ -48,9 +49,9 @@ function linkedMenu(document, trigger) {
|
|
|
48
49
|
function markPopup(popup, kind) {
|
|
49
50
|
if (!popup) return
|
|
50
51
|
mark(popup, MENU_ATTRIBUTE, kind)
|
|
51
|
-
const card = popup.closest?.('[class*="mufS8W_card"]')
|
|
52
|
+
const card = popup.closest?.('[data-trigger-menu], [class*="mufS8W_card"]')
|
|
52
53
|
mark(card, MENU_ATTRIBUTE, kind)
|
|
53
|
-
for (const nested of popup.querySelectorAll?.('[role="menu"], [role="listbox"], [class*="mufS8W_card"]') ?? []) {
|
|
54
|
+
for (const nested of popup.querySelectorAll?.('[role="menu"], [role="listbox"], [data-trigger-menu], [class*="mufS8W_card"]') ?? []) {
|
|
54
55
|
mark(nested, MENU_ATTRIBUTE, kind)
|
|
55
56
|
}
|
|
56
57
|
}
|
|
@@ -162,7 +163,7 @@ export function createComposerGlassAdapter({ document, window }) {
|
|
|
162
163
|
|
|
163
164
|
const overlay = document?.querySelector?.('[data-slot="conversation.input.overlay"]')
|
|
164
165
|
if (overlay) {
|
|
165
|
-
for (const popup of overlay.querySelectorAll('[role="menu"], [role="listbox"], [class*="mufS8W_card"]')) {
|
|
166
|
+
for (const popup of overlay.querySelectorAll('[data-trigger-menu], [role="menu"], [role="listbox"], [class*="mufS8W_card"]')) {
|
|
166
167
|
markPopup(popup, popup.hasAttribute('aria-busy') ? 'model' : 'action')
|
|
167
168
|
}
|
|
168
169
|
}
|
package/src/client/index.js
CHANGED
|
@@ -12,7 +12,6 @@ import { createThemeController } from './theme-controller-v2.js'
|
|
|
12
12
|
import { createParticleFieldAdapter } from './particle-field-adapter.js'
|
|
13
13
|
import { createToBottomAdapter } from './to-bottom-adapter.js'
|
|
14
14
|
import { createConversationControlAdapter } from './conversation-control-adapter.js'
|
|
15
|
-
import { createConversationScaleAdapter } from './conversation-scale-adapter.js'
|
|
16
15
|
import { createLayoutStabilityDiagnostics } from './layout-stability-diagnostics.js'
|
|
17
16
|
import { createSidebarControlAdapter } from './sidebar-control-adapter.js'
|
|
18
17
|
import { createFloatingGlassAdapter } from './floating-glass-adapter.js'
|
|
@@ -68,10 +67,8 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
68
67
|
particleField.setSchemeTransitionActive(active)
|
|
69
68
|
},
|
|
70
69
|
})
|
|
71
|
-
const sessions = contextService(ctx, 'sessions')
|
|
72
70
|
const toBottom = createToBottomAdapter({ document, window })
|
|
73
71
|
const conversationControls = createConversationControlAdapter({ document, window })
|
|
74
|
-
const conversationScale = createConversationScaleAdapter({ document, window, sessions })
|
|
75
72
|
const layoutDiagnostics = createLayoutStabilityDiagnostics({ document, window })
|
|
76
73
|
const sidebarControls = createSidebarControlAdapter({ document, window })
|
|
77
74
|
const floatingGlass = createFloatingGlassAdapter({ document, window })
|
|
@@ -153,7 +150,6 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
153
150
|
particleField.update({ ...preferences, enabled: false })
|
|
154
151
|
toBottom.dispose()
|
|
155
152
|
conversationControls.dispose()
|
|
156
|
-
conversationScale.dispose()
|
|
157
153
|
layoutDiagnostics.dispose()
|
|
158
154
|
sidebarControls.dispose()
|
|
159
155
|
floatingGlass.dispose()
|
|
@@ -194,9 +190,6 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
194
190
|
onRetrySave() {
|
|
195
191
|
retryPersistence()
|
|
196
192
|
},
|
|
197
|
-
getConversationScalePreview(value) {
|
|
198
|
-
return conversationScale.getCalibrationState(value)
|
|
199
|
-
},
|
|
200
193
|
})
|
|
201
194
|
if (!operations.update(preferences, status, settingsPersistence)) {
|
|
202
195
|
if (!mountWarningIssued) {
|
|
@@ -216,8 +209,6 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
216
209
|
assistantGlass.start()
|
|
217
210
|
toBottom.start()
|
|
218
211
|
conversationControls.start()
|
|
219
|
-
conversationScale.update(preferences)
|
|
220
|
-
conversationScale.start()
|
|
221
212
|
layoutDiagnostics.start()
|
|
222
213
|
sidebarControls.start()
|
|
223
214
|
floatingGlass.start()
|
|
@@ -315,7 +306,6 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
315
306
|
particleField.dispose()
|
|
316
307
|
toBottom.dispose()
|
|
317
308
|
conversationControls.dispose()
|
|
318
|
-
conversationScale.dispose()
|
|
319
309
|
layoutDiagnostics.dispose()
|
|
320
310
|
sidebarControls.dispose()
|
|
321
311
|
floatingGlass.dispose()
|
|
@@ -361,7 +351,7 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
361
351
|
|
|
362
352
|
export function apply(ctx) {
|
|
363
353
|
const React = require('react')
|
|
364
|
-
const { defineStore } = require('@deepseek-ai/dsh-client-
|
|
354
|
+
const { defineStore } = require('@deepseek-ai/dsh-client-store')
|
|
365
355
|
applyPrtsPlugin(ctx, {
|
|
366
356
|
document,
|
|
367
357
|
window,
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { createThemeSettingsOverlay, themeSettingsMarkup } from './theme-settings-workbench.js'
|
|
2
2
|
import { createResizeShieldAdapter } from './resize-shield-adapter.js'
|
|
3
3
|
|
|
4
|
+
const PHONE_RAIL_BREAKPOINT = 640
|
|
5
|
+
const RAIL_HOLD_DELAY_MS = 350
|
|
6
|
+
const RAIL_HOLD_MOVE_TOLERANCE = 8
|
|
7
|
+
const RAIL_SWIPE_THRESHOLD = 32
|
|
8
|
+
const RAIL_SWIPE_AXIS_RATIO = 1.25
|
|
9
|
+
|
|
4
10
|
function setText(root, selector, value) {
|
|
5
11
|
const node = root?.querySelector(selector)
|
|
6
12
|
const next = String(value ?? '')
|
|
@@ -9,7 +15,7 @@ function setText(root, selector, value) {
|
|
|
9
15
|
|
|
10
16
|
function markup(emblem) {
|
|
11
17
|
return `<div data-prts-shell data-plugin="dsh-theme-prts">
|
|
12
|
-
<button type="button" data-prts-rail-launcher aria-label="打开 P.R.T.S. 导航" aria-controls="prts-nav-rail" aria-expanded="false"><span aria-hidden="true">P.R.T.S.</span></button>
|
|
18
|
+
<button type="button" data-prts-rail-launcher aria-label="打开 P.R.T.S. 导航" aria-controls="prts-nav-rail" aria-expanded="false"><span aria-hidden="true">P.R.T.S.</span><i data-prts-rail-gesture-cue aria-hidden="true">⌄<br>⌄</i></button>
|
|
13
19
|
<nav id="prts-nav-rail" data-prts-nav-rail aria-label="P.R.T.S. 导航">
|
|
14
20
|
<button type="button" data-prts-rail-brand aria-label="P.R.T.S. 终端设置" aria-controls="prts-theme-settings" aria-expanded="false">${emblem || ''}<span>P.R.T.S.</span></button>
|
|
15
21
|
<div data-prts-nav-bottom>
|
|
@@ -40,7 +46,6 @@ export function createOperationsShell({
|
|
|
40
46
|
onPreferenceChange = () => {},
|
|
41
47
|
onPreferencePreview = () => {},
|
|
42
48
|
onVisualReset = () => {},
|
|
43
|
-
getConversationScalePreview = () => ({ visible: false, reason: 'unmeasured', mode: 'hidden' }),
|
|
44
49
|
onRetrySave = () => {},
|
|
45
50
|
}) {
|
|
46
51
|
const root = document.documentElement
|
|
@@ -52,6 +57,10 @@ export function createOperationsShell({
|
|
|
52
57
|
let railViewport
|
|
53
58
|
let railDefaultHidden = false
|
|
54
59
|
let railViewportQuery
|
|
60
|
+
let railPointerQuery
|
|
61
|
+
let railHoverQuery
|
|
62
|
+
let railPointer
|
|
63
|
+
let railHoldTimer
|
|
55
64
|
let settingsOverlay
|
|
56
65
|
let schemeToggle
|
|
57
66
|
let schemeIntent
|
|
@@ -72,6 +81,89 @@ export function createOperationsShell({
|
|
|
72
81
|
try { return window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches === true } catch { return false }
|
|
73
82
|
}
|
|
74
83
|
|
|
84
|
+
function phoneRailGesture() {
|
|
85
|
+
if (railMode !== 'overlay' || Number(window.innerWidth) > PHONE_RAIL_BREAKPOINT) return false
|
|
86
|
+
return railPointerQuery?.matches === true
|
|
87
|
+
|| railHoverQuery?.matches === true
|
|
88
|
+
|| Number(window.navigator?.maxTouchPoints) > 0
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function clearRailGesture({ release = true } = {}) {
|
|
92
|
+
if (railHoldTimer !== undefined) window.clearTimeout(railHoldTimer)
|
|
93
|
+
railHoldTimer = undefined
|
|
94
|
+
const pointer = railPointer
|
|
95
|
+
railPointer = undefined
|
|
96
|
+
if (release && pointer) {
|
|
97
|
+
try { railLauncher?.releasePointerCapture?.(pointer.pointerId) } catch {}
|
|
98
|
+
}
|
|
99
|
+
railLauncher?.removeAttribute('data-prts-rail-gesture-active')
|
|
100
|
+
railLauncher?.removeAttribute('data-prts-rail-gesture-ready')
|
|
101
|
+
railLauncher?.style?.removeProperty('--prts-rail-gesture-progress')
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function armRailGesture() {
|
|
105
|
+
railHoldTimer = undefined
|
|
106
|
+
if (!railPointer || !phoneRailGesture()) return
|
|
107
|
+
railPointer.armed = true
|
|
108
|
+
railLauncher?.setAttribute('data-prts-rail-gesture-active', '')
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function onRailPointerDown(event) {
|
|
112
|
+
if (!phoneRailGesture() || railPointer || (event.button ?? 0) !== 0) return
|
|
113
|
+
const pointerId = event.pointerId ?? 1
|
|
114
|
+
railPointer = {
|
|
115
|
+
pointerId,
|
|
116
|
+
startX: Number(event.clientX) || 0,
|
|
117
|
+
startY: Number(event.clientY) || 0,
|
|
118
|
+
armed: false,
|
|
119
|
+
ready: false,
|
|
120
|
+
}
|
|
121
|
+
try { railLauncher?.setPointerCapture?.(pointerId) } catch {}
|
|
122
|
+
railHoldTimer = window.setTimeout(armRailGesture, RAIL_HOLD_DELAY_MS)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function onRailPointerMove(event) {
|
|
126
|
+
const pointer = railPointer
|
|
127
|
+
if (!pointer || (event.pointerId ?? 1) !== pointer.pointerId) return
|
|
128
|
+
const deltaX = (Number(event.clientX) || 0) - pointer.startX
|
|
129
|
+
const deltaY = (Number(event.clientY) || 0) - pointer.startY
|
|
130
|
+
if (!pointer.armed) {
|
|
131
|
+
if (Math.hypot(deltaX, deltaY) > RAIL_HOLD_MOVE_TOLERANCE) clearRailGesture()
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
event.preventDefault?.()
|
|
135
|
+
if (Math.abs(deltaX) > Math.max(RAIL_HOLD_MOVE_TOLERANCE, Math.max(0, deltaY) / RAIL_SWIPE_AXIS_RATIO)) {
|
|
136
|
+
clearRailGesture()
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
const progress = Math.min(1, Math.max(0, deltaY) / RAIL_SWIPE_THRESHOLD)
|
|
140
|
+
pointer.ready = deltaY >= RAIL_SWIPE_THRESHOLD
|
|
141
|
+
&& deltaY >= Math.abs(deltaX) * RAIL_SWIPE_AXIS_RATIO
|
|
142
|
+
railLauncher?.style?.setProperty('--prts-rail-gesture-progress', Math.round(progress * 12) + 'px')
|
|
143
|
+
railLauncher?.toggleAttribute('data-prts-rail-gesture-ready', pointer.ready)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function onRailPointerUp(event) {
|
|
147
|
+
const pointer = railPointer
|
|
148
|
+
if (!pointer || (event.pointerId ?? 1) !== pointer.pointerId) return
|
|
149
|
+
const shouldOpen = pointer.armed && pointer.ready
|
|
150
|
+
clearRailGesture()
|
|
151
|
+
if (shouldOpen) openRail()
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function onRailLostPointerCapture(event) {
|
|
155
|
+
if (!railPointer || (event.pointerId ?? 1) !== railPointer.pointerId) return
|
|
156
|
+
clearRailGesture({ release: false })
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function onRailClick(event) {
|
|
160
|
+
if (phoneRailGesture() && Number(event?.detail) > 0) {
|
|
161
|
+
event.preventDefault?.()
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
toggleRail()
|
|
165
|
+
}
|
|
166
|
+
|
|
75
167
|
function playSchemePress() {
|
|
76
168
|
if (!schemeToggle || reducedMotion()) return
|
|
77
169
|
const revision = ++schemePressRevision
|
|
@@ -129,7 +221,6 @@ export function createOperationsShell({
|
|
|
129
221
|
)
|
|
130
222
|
const active = Boolean(candidate)
|
|
131
223
|
root.dataset.prtsConversationState = active ? 'active' : 'idle'
|
|
132
|
-
settingsOverlay?.refreshScalePreview()
|
|
133
224
|
}
|
|
134
225
|
|
|
135
226
|
function toggleScheme(event) {
|
|
@@ -257,7 +348,8 @@ export function createOperationsShell({
|
|
|
257
348
|
const overlay = railMode === 'overlay'
|
|
258
349
|
const open = overlay && root.hasAttribute('data-prts-rail-open')
|
|
259
350
|
railLauncher.setAttribute('aria-expanded', String(open))
|
|
260
|
-
|
|
351
|
+
const closedLabel = phoneRailGesture() ? '长按并向下滑动打开 P.R.T.S. 导航' : '打开 P.R.T.S. 导航'
|
|
352
|
+
railLauncher.setAttribute('aria-label', open ? '关闭 P.R.T.S. 导航' : closedLabel)
|
|
261
353
|
if (overlay && !open) {
|
|
262
354
|
navRail.setAttribute('aria-hidden', 'true')
|
|
263
355
|
navRail.setAttribute('inert', '')
|
|
@@ -294,6 +386,7 @@ export function createOperationsShell({
|
|
|
294
386
|
if (!viewportChanged && !modeChanged && !forceClose) return railMode
|
|
295
387
|
railViewport = nextViewport
|
|
296
388
|
railMode = nextMode
|
|
389
|
+
clearRailGesture()
|
|
297
390
|
root.dataset.prtsRailMode = nextMode
|
|
298
391
|
root.removeAttribute('data-prts-rail-open')
|
|
299
392
|
syncRailState()
|
|
@@ -320,13 +413,18 @@ export function createOperationsShell({
|
|
|
320
413
|
}
|
|
321
414
|
|
|
322
415
|
function onResponsiveChange() {
|
|
416
|
+
clearRailGesture()
|
|
323
417
|
setRailViewport(window.innerWidth)
|
|
324
|
-
|
|
418
|
+
syncRailState()
|
|
325
419
|
}
|
|
326
420
|
|
|
327
421
|
function bindResponsiveQueries() {
|
|
328
422
|
railViewportQuery = window.matchMedia?.('(min-width: 1180px)')
|
|
423
|
+
railPointerQuery = window.matchMedia?.('(pointer: coarse)')
|
|
424
|
+
railHoverQuery = window.matchMedia?.('(hover: none)')
|
|
329
425
|
railViewportQuery?.addEventListener?.('change', onResponsiveChange)
|
|
426
|
+
railPointerQuery?.addEventListener?.('change', onResponsiveChange)
|
|
427
|
+
railHoverQuery?.addEventListener?.('change', onResponsiveChange)
|
|
330
428
|
}
|
|
331
429
|
function disableTheme() { onThemeDisable() }
|
|
332
430
|
|
|
@@ -356,7 +454,6 @@ export function createOperationsShell({
|
|
|
356
454
|
onPreferencePreview,
|
|
357
455
|
onResetVisual: onVisualReset,
|
|
358
456
|
onRetrySave,
|
|
359
|
-
getConversationScalePreview,
|
|
360
457
|
})
|
|
361
458
|
resizeShield.start([
|
|
362
459
|
operationRegion,
|
|
@@ -367,7 +464,12 @@ export function createOperationsShell({
|
|
|
367
464
|
conversationObserver.observe(operationRegion, { childList: true, subtree: true, attributes: true, attributeFilter: ['hidden', 'data-chat-flow-kind', 'data-message-role'] })
|
|
368
465
|
schemeObserver = new window.MutationObserver(syncSchemeToggle)
|
|
369
466
|
schemeObserver.observe(root, { attributes: true, attributeFilter: ['data-prts-scheme'] })
|
|
370
|
-
railLauncher.addEventListener('click',
|
|
467
|
+
railLauncher.addEventListener('click', onRailClick)
|
|
468
|
+
railLauncher.addEventListener('pointerdown', onRailPointerDown)
|
|
469
|
+
railLauncher.addEventListener('pointermove', onRailPointerMove)
|
|
470
|
+
railLauncher.addEventListener('pointerup', onRailPointerUp)
|
|
471
|
+
railLauncher.addEventListener('pointercancel', clearRailGesture)
|
|
472
|
+
railLauncher.addEventListener('lostpointercapture', onRailLostPointerCapture)
|
|
371
473
|
document.addEventListener('keydown', onRailKeydown)
|
|
372
474
|
document.addEventListener('click', onDocumentClick)
|
|
373
475
|
schemeToggle.addEventListener('click', toggleScheme)
|
|
@@ -384,10 +486,16 @@ export function createOperationsShell({
|
|
|
384
486
|
}
|
|
385
487
|
|
|
386
488
|
function dispose() {
|
|
489
|
+
clearRailGesture()
|
|
387
490
|
cancelSchemePointer()
|
|
388
491
|
clearSchemeClickSuppression()
|
|
389
492
|
clearSchemePressDelays()
|
|
390
|
-
railLauncher?.removeEventListener('click',
|
|
493
|
+
railLauncher?.removeEventListener('click', onRailClick)
|
|
494
|
+
railLauncher?.removeEventListener('pointerdown', onRailPointerDown)
|
|
495
|
+
railLauncher?.removeEventListener('pointermove', onRailPointerMove)
|
|
496
|
+
railLauncher?.removeEventListener('pointerup', onRailPointerUp)
|
|
497
|
+
railLauncher?.removeEventListener('pointercancel', clearRailGesture)
|
|
498
|
+
railLauncher?.removeEventListener('lostpointercapture', onRailLostPointerCapture)
|
|
391
499
|
document.removeEventListener('keydown', onRailKeydown)
|
|
392
500
|
document.removeEventListener('click', onDocumentClick)
|
|
393
501
|
schemeToggle?.removeEventListener('click', toggleScheme)
|
|
@@ -399,7 +507,11 @@ export function createOperationsShell({
|
|
|
399
507
|
window.removeEventListener?.('blur', cancelSchemePointer)
|
|
400
508
|
themeDisable?.removeEventListener('click', disableTheme)
|
|
401
509
|
railViewportQuery?.removeEventListener?.('change', onResponsiveChange)
|
|
510
|
+
railPointerQuery?.removeEventListener?.('change', onResponsiveChange)
|
|
511
|
+
railHoverQuery?.removeEventListener?.('change', onResponsiveChange)
|
|
402
512
|
railViewportQuery = undefined
|
|
513
|
+
railPointerQuery = undefined
|
|
514
|
+
railHoverQuery = undefined
|
|
403
515
|
resizeShield.dispose()
|
|
404
516
|
settingsOverlay?.dispose()
|
|
405
517
|
conversationObserver?.disconnect()
|
|
@@ -557,6 +557,7 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
|
|
|
557
557
|
|
|
558
558
|
|
|
559
559
|
function particleTraversalSpeed(value = preferences) {
|
|
560
|
+
if (phoneStatic()) return 0
|
|
560
561
|
const numeric = Number(value?.particleTraversalSpeed)
|
|
561
562
|
return Number.isFinite(numeric) ? clamp(numeric, 0, 2) : 1
|
|
562
563
|
}
|
|
@@ -1123,6 +1124,19 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
|
|
|
1123
1124
|
const entry = entries.find(candidate => candidate.target === operation) ?? entries[0]
|
|
1124
1125
|
const contentRect = entry?.contentRect
|
|
1125
1126
|
pendingResizeSize = contentRect ? sizeFromRect(contentRect) : readOperationSize()
|
|
1127
|
+
if (phoneStatic()) {
|
|
1128
|
+
const nextSize = pendingResizeSize
|
|
1129
|
+
clearResizeState()
|
|
1130
|
+
const changed = applyFieldSize(nextSize)
|
|
1131
|
+
if (changed && particles.length) snapParticlesToCurrentLayout()
|
|
1132
|
+
resizeCommits += changed ? 1 : 0
|
|
1133
|
+
syncCanvasBackingStore()
|
|
1134
|
+
geometryDirty = false
|
|
1135
|
+
cancelFrame()
|
|
1136
|
+
draw(Number(window.performance?.now?.()) || 0)
|
|
1137
|
+
cancelFrame()
|
|
1138
|
+
return
|
|
1139
|
+
}
|
|
1126
1140
|
if (resizeFrame !== undefined) return
|
|
1127
1141
|
resizeFrame = window.requestAnimationFrame?.(() => {
|
|
1128
1142
|
resizeFrame = undefined
|
|
@@ -1532,7 +1546,7 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
|
|
|
1532
1546
|
if (heroActive) leaveHero()
|
|
1533
1547
|
}
|
|
1534
1548
|
function onScroll() {
|
|
1535
|
-
if (!scroller) return
|
|
1549
|
+
if (!scroller || phoneStatic()) return
|
|
1536
1550
|
if (heroActive) {
|
|
1537
1551
|
scheduleFrame()
|
|
1538
1552
|
return
|
|
@@ -1581,6 +1595,7 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
|
|
|
1581
1595
|
}
|
|
1582
1596
|
|
|
1583
1597
|
function registerContentMutations(mutations) {
|
|
1598
|
+
if (phoneStatic()) return
|
|
1584
1599
|
let weight = mutations.length
|
|
1585
1600
|
for (const mutation of mutations) {
|
|
1586
1601
|
for (const node of [...mutation.addedNodes, ...mutation.removedNodes]) {
|
|
@@ -1693,8 +1708,35 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
|
|
|
1693
1708
|
cancelFrame()
|
|
1694
1709
|
}
|
|
1695
1710
|
|
|
1711
|
+
function rebindScroller(nextScroller) {
|
|
1712
|
+
scroller?.removeAttribute('data-prts-hero-active')
|
|
1713
|
+
scroller?.removeEventListener('scroll', onScroll)
|
|
1714
|
+
cancelHydrationResume()
|
|
1715
|
+
hydrationPaused = false
|
|
1716
|
+
mutationBurstScore = 0
|
|
1717
|
+
if (mutationBurstTimer !== undefined) window.clearTimeout?.(mutationBurstTimer)
|
|
1718
|
+
mutationBurstTimer = undefined
|
|
1719
|
+
heroMarker = undefined
|
|
1720
|
+
heroActive = false
|
|
1721
|
+
scroller = nextScroller
|
|
1722
|
+
scroller.addEventListener('scroll', onScroll, { passive: true })
|
|
1723
|
+
syncHeroState()
|
|
1724
|
+
if (!heroActive) {
|
|
1725
|
+
resetTraversal()
|
|
1726
|
+
if (!phoneStatic()) restoreTraversalFromScroll()
|
|
1727
|
+
commitTransitionTargets(segment, true)
|
|
1728
|
+
}
|
|
1729
|
+
scheduleFrame()
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1696
1732
|
function mount(nextOperation, nextScroller) {
|
|
1697
|
-
if (operation === nextOperation &&
|
|
1733
|
+
if (operation === nextOperation && ambient?.isConnected && canvas?.isConnected) {
|
|
1734
|
+
if (scroller === nextScroller) return
|
|
1735
|
+
if (nextScroller) {
|
|
1736
|
+
rebindScroller(nextScroller)
|
|
1737
|
+
return
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1698
1740
|
unmount()
|
|
1699
1741
|
operation = nextOperation
|
|
1700
1742
|
scroller = nextScroller
|
|
@@ -1705,7 +1747,7 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
|
|
|
1705
1747
|
glow = created.glow
|
|
1706
1748
|
canvas = document.createElement('canvas')
|
|
1707
1749
|
canvas.setAttribute('data-prts-particle-layer', '')
|
|
1708
|
-
canvas.setAttribute('aria-label', '随对话滚动轮换阵营标识的粒子画布')
|
|
1750
|
+
canvas.setAttribute('aria-label', phoneStatic() ? '静态阵营标识粒子画布' : '随对话滚动轮换阵营标识的粒子画布')
|
|
1709
1751
|
canvas.style.visibility = 'hidden'
|
|
1710
1752
|
if (typeof window.CanvasRenderingContext2D === 'function') context = canvas.getContext('2d')
|
|
1711
1753
|
targetCache.clear()
|
|
@@ -1,15 +1,5 @@
|
|
|
1
1
|
export const PRTS_STORAGE_KEY = 'dsh.ui.prts.v1'
|
|
2
2
|
|
|
3
|
-
export const CONVERSATION_SCALE_DISTANCE_MIN = 16
|
|
4
|
-
export const CONVERSATION_SCALE_DISTANCE_MAX = 240
|
|
5
|
-
export const CONVERSATION_SCALE_DISTANCE_STEP = 8
|
|
6
|
-
export const CONVERSATION_SCALE_DISTANCE_DEFAULT = 96
|
|
7
|
-
|
|
8
|
-
export const CONVERSATION_SCALE_FOCUS_CONTRAST_MIN = 0
|
|
9
|
-
export const CONVERSATION_SCALE_FOCUS_CONTRAST_MAX = 100
|
|
10
|
-
export const CONVERSATION_SCALE_FOCUS_CONTRAST_STEP = 10
|
|
11
|
-
export const CONVERSATION_SCALE_FOCUS_CONTRAST_DEFAULT = 70
|
|
12
|
-
|
|
13
3
|
export const PARTICLE_TRAVERSAL_SPEED_MIN = 0
|
|
14
4
|
export const PARTICLE_TRAVERSAL_SPEED_MAX = 2
|
|
15
5
|
export const PARTICLE_TRAVERSAL_SPEED_STEP = 0.25
|
|
@@ -28,8 +18,6 @@ export const DEFAULT_PREFERENCES = Object.freeze({
|
|
|
28
18
|
particleTraversalSpeed: PARTICLE_TRAVERSAL_SPEED_DEFAULT,
|
|
29
19
|
particlePattern: 'orthogonal',
|
|
30
20
|
railDefaultHidden: false,
|
|
31
|
-
conversationScaleMaxDistance: CONVERSATION_SCALE_DISTANCE_DEFAULT,
|
|
32
|
-
conversationScaleFocusContrast: CONVERSATION_SCALE_FOCUS_CONTRAST_DEFAULT,
|
|
33
21
|
})
|
|
34
22
|
|
|
35
23
|
export const VISUAL_PRESETS = Object.freeze({
|
|
@@ -70,7 +58,7 @@ export const PREFERENCE_GROUPS = Object.freeze({
|
|
|
70
58
|
particles: Object.freeze(['conversationParticleDensity', 'heroParticleDensity', 'particleTraversalSpeed']),
|
|
71
59
|
material: Object.freeze(['glass']),
|
|
72
60
|
accessibility: Object.freeze(['motion', 'bootAnimation']),
|
|
73
|
-
navigation: Object.freeze(['railDefaultHidden'
|
|
61
|
+
navigation: Object.freeze(['railDefaultHidden']),
|
|
74
62
|
})
|
|
75
63
|
|
|
76
64
|
const TEXTURES = new Set(['off', 'restrained', 'full'])
|
|
@@ -107,20 +95,6 @@ function enumOr(value, allowed, fallback) {
|
|
|
107
95
|
return allowed.has(value) ? value : fallback
|
|
108
96
|
}
|
|
109
97
|
|
|
110
|
-
function scaleDistanceOr(value, fallback) {
|
|
111
|
-
const numeric = Number(value)
|
|
112
|
-
if (!Number.isFinite(numeric)) return fallback
|
|
113
|
-
const clamped = Math.min(CONVERSATION_SCALE_DISTANCE_MAX, Math.max(CONVERSATION_SCALE_DISTANCE_MIN, numeric))
|
|
114
|
-
return Math.round(clamped / CONVERSATION_SCALE_DISTANCE_STEP) * CONVERSATION_SCALE_DISTANCE_STEP
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function scaleFocusContrastOr(value, fallback) {
|
|
118
|
-
const numeric = Number(value)
|
|
119
|
-
if (!Number.isFinite(numeric)) return fallback
|
|
120
|
-
const clamped = Math.min(CONVERSATION_SCALE_FOCUS_CONTRAST_MAX, Math.max(CONVERSATION_SCALE_FOCUS_CONTRAST_MIN, numeric))
|
|
121
|
-
return Math.round(clamped / CONVERSATION_SCALE_FOCUS_CONTRAST_STEP) * CONVERSATION_SCALE_FOCUS_CONTRAST_STEP
|
|
122
|
-
}
|
|
123
|
-
|
|
124
98
|
function particleTraversalSpeedOr(value, fallback) {
|
|
125
99
|
const numeric = Number(value)
|
|
126
100
|
if (!Number.isFinite(numeric)) return fallback
|
|
@@ -170,14 +144,6 @@ export function normalizePreferences(value) {
|
|
|
170
144
|
),
|
|
171
145
|
particlePattern: 'orthogonal',
|
|
172
146
|
railDefaultHidden: booleanOr(input.railDefaultHidden, DEFAULT_PREFERENCES.railDefaultHidden),
|
|
173
|
-
conversationScaleMaxDistance: scaleDistanceOr(
|
|
174
|
-
input.conversationScaleMaxDistance,
|
|
175
|
-
DEFAULT_PREFERENCES.conversationScaleMaxDistance,
|
|
176
|
-
),
|
|
177
|
-
conversationScaleFocusContrast: scaleFocusContrastOr(
|
|
178
|
-
input.conversationScaleFocusContrast,
|
|
179
|
-
DEFAULT_PREFERENCES.conversationScaleFocusContrast,
|
|
180
|
-
),
|
|
181
147
|
}
|
|
182
148
|
normalized.preset = matchedVisualPreset(normalized)
|
|
183
149
|
return normalized
|
|
@@ -29,6 +29,11 @@ export function createResizeShieldAdapter({
|
|
|
29
29
|
|
|
30
30
|
function onResize() {
|
|
31
31
|
if (!started) return
|
|
32
|
+
if (Number(window?.innerWidth) <= 640) {
|
|
33
|
+
clearTimer()
|
|
34
|
+
setActive(false)
|
|
35
|
+
return
|
|
36
|
+
}
|
|
32
37
|
setActive(true)
|
|
33
38
|
clearTimer()
|
|
34
39
|
settleTimer = window?.setTimeout?.(settle, settleDelay)
|