@ash-qw/dsh-theme-prts 0.1.96 → 0.1.98
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 +12 -1
- package/README.en.md +3 -3
- package/README.md +3 -3
- package/lib/client.js +272 -52
- package/package.json +2 -2
- package/src/client/assistant-glass-adapter.js +54 -4
- package/src/client/composer-glass-adapter.js +27 -10
- package/src/client/index.js +25 -6
- package/src/client/operations-shell-v2.js +4 -1
- package/src/client/particle-field-adapter.js +24 -12
- package/src/client/preferences.js +7 -3
- package/src/client/rc7-adapter.js +48 -11
- package/src/client/startup-sequence.js +102 -9
- package/src/client/theme-controller-v2.js +2 -0
- package/src/client/theme-controller.js +2 -0
- package/src/client/theme-settings-workbench.js +3 -0
- package/src/styles/prts.css +362 -19
- package/src/styles/refinement.css +8 -4
- package/src/styles/stabilization.css +34 -2
- package/src/styles/startup-sequence.css +13 -6
- package/src/styles/theme-settings-workbench.css +7 -0
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.98",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Unofficial Arknights P.R.T.S.-inspired UI theme for DeepSeek Harness Web",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
},
|
|
69
69
|
"engines": {
|
|
70
70
|
"node": ">=18",
|
|
71
|
-
"dsh": ">=0.1.2-rc.1 <0.1.3-0"
|
|
71
|
+
"dsh": ">=0.1.2-rc.1 <0.1.3-0 || >=0.1.5-rc.1 <0.1.6-0"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
74
|
"@playwright/test": "1.55.1",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const ASSISTANT_SURFACE_ATTRIBUTE = 'data-prts-ai-surface'
|
|
2
2
|
export const ASSISTANT_STATE_ATTRIBUTE = 'data-prts-ai-surface-state'
|
|
3
|
+
export const ASSISTANT_AVATAR_PROPERTY = '--prts-assistant-avatar-image'
|
|
3
4
|
|
|
4
5
|
const STEP_SELECTOR = '[data-chat-flow-kind="assistant-step"]'
|
|
5
6
|
const CONTENT_HINT_SELECTOR = [
|
|
@@ -16,6 +17,7 @@ const EXCLUDED_SELECTOR = [
|
|
|
16
17
|
].join(', ')
|
|
17
18
|
const SCAN_CHUNK_LIMIT = 64
|
|
18
19
|
const SCAN_BUDGET_MS = 4
|
|
20
|
+
const INITIAL_LATEST_LIMIT = 12
|
|
19
21
|
|
|
20
22
|
function isElement(node) {
|
|
21
23
|
return node?.nodeType === 1
|
|
@@ -90,13 +92,42 @@ export function resolveAssistantSurface(step) {
|
|
|
90
92
|
return resolution(step).surface
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
|
|
95
|
+
function cssUrl(value) {
|
|
96
|
+
if (!value) return ''
|
|
97
|
+
const escaped = String(value)
|
|
98
|
+
.replaceAll('\\', '\\\\')
|
|
99
|
+
.replaceAll('"', '\\"')
|
|
100
|
+
.replaceAll('\n', '')
|
|
101
|
+
return `url("${escaped}")`
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function createAssistantGlassAdapter({ document, window, avatarImage = '' }) {
|
|
94
105
|
let observer
|
|
95
106
|
let scanFrame
|
|
96
107
|
const ownedSurfaces = new Set()
|
|
97
108
|
const ownedSteps = new Set()
|
|
98
109
|
const stepSurfaces = new Map()
|
|
99
110
|
const pendingSteps = new Set()
|
|
111
|
+
let previousAvatarImage
|
|
112
|
+
let ownsAvatarImage = false
|
|
113
|
+
|
|
114
|
+
function mountAvatarImage() {
|
|
115
|
+
const root = document?.documentElement
|
|
116
|
+
const value = cssUrl(avatarImage)
|
|
117
|
+
if (!root?.style || !value || ownsAvatarImage) return
|
|
118
|
+
previousAvatarImage = root.style.getPropertyValue(ASSISTANT_AVATAR_PROPERTY)
|
|
119
|
+
root.style.setProperty(ASSISTANT_AVATAR_PROPERTY, value)
|
|
120
|
+
ownsAvatarImage = true
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function releaseAvatarImage() {
|
|
124
|
+
if (!ownsAvatarImage) return
|
|
125
|
+
const root = document?.documentElement
|
|
126
|
+
if (previousAvatarImage) root?.style?.setProperty(ASSISTANT_AVATAR_PROPERTY, previousAvatarImage)
|
|
127
|
+
else root?.style?.removeProperty(ASSISTANT_AVATAR_PROPERTY)
|
|
128
|
+
previousAvatarImage = undefined
|
|
129
|
+
ownsAvatarImage = false
|
|
130
|
+
}
|
|
100
131
|
|
|
101
132
|
function collectSteps(node, includeParent = true) {
|
|
102
133
|
if (!isElement(node)) return
|
|
@@ -108,6 +139,23 @@ export function createAssistantGlassAdapter({ document, window }) {
|
|
|
108
139
|
}
|
|
109
140
|
}
|
|
110
141
|
|
|
142
|
+
function collectInitialSteps(node) {
|
|
143
|
+
if (!isElement(node)) return
|
|
144
|
+
const steps = [...(node.querySelectorAll?.(STEP_SELECTOR) ?? [])]
|
|
145
|
+
if (node.matches?.(STEP_SELECTOR)) steps.unshift(node)
|
|
146
|
+
const priorityStart = Math.max(0, steps.length - INITIAL_LATEST_LIMIT)
|
|
147
|
+
|
|
148
|
+
// The host restores conversations in chronological DOM order. Resolve the
|
|
149
|
+
// newest screenful synchronously so the startup cover never reveals plain
|
|
150
|
+
// assistant content while an older history backlog is still being scanned.
|
|
151
|
+
for (let index = steps.length - 1; index >= priorityStart; index -= 1) {
|
|
152
|
+
processStep(steps[index])
|
|
153
|
+
}
|
|
154
|
+
for (let index = priorityStart - 1; index >= 0; index -= 1) {
|
|
155
|
+
pendingSteps.add(steps[index])
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
111
159
|
function stableBodyContains(step, node) {
|
|
112
160
|
const surface = stepSurfaces.get(step)
|
|
113
161
|
return step?.getAttribute?.(ASSISTANT_STATE_ATTRIBUTE) === 'body'
|
|
@@ -201,12 +249,13 @@ export function createAssistantGlassAdapter({ document, window }) {
|
|
|
201
249
|
return {
|
|
202
250
|
start() {
|
|
203
251
|
if (!document) return
|
|
252
|
+
mountAvatarImage()
|
|
204
253
|
const observationRoot = document.querySelector('[data-prts-region="operation"]')
|
|
205
|
-
?? document.querySelector('[data-slot="conversation"]')
|
|
254
|
+
?? document.querySelector('[data-slot="main.conversation"], [data-slot="conversation"]')
|
|
206
255
|
if (!observationRoot) return
|
|
207
|
-
collectSteps(observationRoot)
|
|
208
|
-
scan()
|
|
209
256
|
if (observer) return
|
|
257
|
+
collectInitialSteps(observationRoot)
|
|
258
|
+
scan()
|
|
210
259
|
const Observer = window?.MutationObserver
|
|
211
260
|
if (!Observer) return
|
|
212
261
|
observer = new Observer(onMutations)
|
|
@@ -226,6 +275,7 @@ export function createAssistantGlassAdapter({ document, window }) {
|
|
|
226
275
|
}
|
|
227
276
|
scanFrame = undefined
|
|
228
277
|
removeMarkers()
|
|
278
|
+
releaseAvatarImage()
|
|
229
279
|
},
|
|
230
280
|
}
|
|
231
281
|
}
|
|
@@ -2,8 +2,13 @@ const CONTROL_ATTRIBUTE = 'data-prts-glass-control'
|
|
|
2
2
|
const MENU_ATTRIBUTE = 'data-prts-glass-menu'
|
|
3
3
|
const FALLBACK_COMPOSER_ATTRIBUTE = 'data-prts-composer-fallback'
|
|
4
4
|
const COMPOSER_SIGNAL_ATTRIBUTE = 'data-prts-composer-signal'
|
|
5
|
+
const FALLBACK_COMPOSER_ROOT_SELECTOR = [
|
|
6
|
+
'[data-slot="conversation.composer"]',
|
|
7
|
+
'[data-composer-seat]',
|
|
8
|
+
].join(', ')
|
|
5
9
|
const COMPOSER_RELEVANCE_SELECTOR = [
|
|
6
10
|
'[data-composer-card]',
|
|
11
|
+
FALLBACK_COMPOSER_ROOT_SELECTOR,
|
|
7
12
|
'form textarea',
|
|
8
13
|
'button[aria-haspopup="menu"]',
|
|
9
14
|
'button[aria-haspopup="listbox"]',
|
|
@@ -56,6 +61,18 @@ function markPopup(popup, kind) {
|
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
|
|
64
|
+
function fallbackComposerForms(document) {
|
|
65
|
+
const forms = new Set()
|
|
66
|
+
for (const root of document?.querySelectorAll?.(FALLBACK_COMPOSER_ROOT_SELECTOR) ?? []) {
|
|
67
|
+
if (root.querySelector('[data-composer-card]')) continue
|
|
68
|
+
for (const textarea of root.querySelectorAll('form textarea')) {
|
|
69
|
+
const form = textarea.closest('form')
|
|
70
|
+
if (form && root.contains(form)) forms.add(form)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return forms
|
|
74
|
+
}
|
|
75
|
+
|
|
59
76
|
export function createComposerGlassAdapter({ document, window }) {
|
|
60
77
|
let observer
|
|
61
78
|
let scanFrame
|
|
@@ -104,17 +121,17 @@ export function createComposerGlassAdapter({ document, window }) {
|
|
|
104
121
|
signal.remove?.()
|
|
105
122
|
ownedSignals.delete(signal)
|
|
106
123
|
}
|
|
124
|
+
const fallbackForms = fallbackComposerForms(document)
|
|
107
125
|
for (const form of [...ownedFallbacks]) {
|
|
108
|
-
if (
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
126
|
+
if (form.isConnected && fallbackForms.has(form) && !form.hasAttribute('data-composer-card')) continue
|
|
127
|
+
form.removeAttribute?.(FALLBACK_COMPOSER_ATTRIBUTE)
|
|
128
|
+
ownedFallbacks.delete(form)
|
|
129
|
+
if (form.hasAttribute('data-composer-card')) continue
|
|
130
|
+
const signal = form.querySelector?.(`:scope > [${COMPOSER_SIGNAL_ATTRIBUTE}]`)
|
|
131
|
+
if (!ownedSignals.has(signal)) continue
|
|
132
|
+
signal.remove?.()
|
|
133
|
+
ownedSignals.delete(signal)
|
|
112
134
|
}
|
|
113
|
-
const fallbackForms = new Set(
|
|
114
|
-
[...(document?.querySelectorAll?.('form textarea') ?? [])]
|
|
115
|
-
.map(textarea => textarea.closest('form'))
|
|
116
|
-
.filter(form => form && !form.querySelector('[data-composer-card]')),
|
|
117
|
-
)
|
|
118
135
|
for (const form of fallbackForms) {
|
|
119
136
|
form.setAttribute(FALLBACK_COMPOSER_ATTRIBUTE, '')
|
|
120
137
|
ownedFallbacks.add(form)
|
|
@@ -187,7 +204,7 @@ export function createComposerGlassAdapter({ document, window }) {
|
|
|
187
204
|
const Observer = window?.MutationObserver
|
|
188
205
|
if (!Observer) return
|
|
189
206
|
const observationRoot = document.querySelector('[data-prts-region="operation"]')
|
|
190
|
-
?? document.querySelector('[data-slot="conversation"]')
|
|
207
|
+
?? document.querySelector('[data-slot="main.conversation"], [data-slot="conversation"]')
|
|
191
208
|
if (!observationRoot) return
|
|
192
209
|
observer = new Observer(onMutations)
|
|
193
210
|
observer.observe(observationRoot, {
|
package/src/client/index.js
CHANGED
|
@@ -44,9 +44,12 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
44
44
|
const safeMode = isSafeMode(window?.location?.search ?? '')
|
|
45
45
|
const storage = window?.localStorage
|
|
46
46
|
const themeService = contextService(ctx, 'theme')
|
|
47
|
-
const startup = createPrtsStartupSequence({ document, window, prtsEmblem: assets.prtsEmblem, rhodesEmblem: assets.emblem, timings: startupTimings })
|
|
48
47
|
const composerGlass = createComposerGlassAdapter({ document, window })
|
|
49
|
-
const assistantGlass = createAssistantGlassAdapter({
|
|
48
|
+
const assistantGlass = createAssistantGlassAdapter({
|
|
49
|
+
document,
|
|
50
|
+
window,
|
|
51
|
+
avatarImage: assets.prtsEmblem,
|
|
52
|
+
})
|
|
50
53
|
const particleField = createParticleFieldAdapter({
|
|
51
54
|
document,
|
|
52
55
|
window,
|
|
@@ -58,6 +61,19 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
58
61
|
sync()
|
|
59
62
|
},
|
|
60
63
|
})
|
|
64
|
+
const startup = createPrtsStartupSequence({
|
|
65
|
+
document,
|
|
66
|
+
window,
|
|
67
|
+
prtsEmblem: assets.prtsEmblem,
|
|
68
|
+
rhodesEmblem: assets.emblem,
|
|
69
|
+
timings: startupTimings,
|
|
70
|
+
onActiveChange(active) {
|
|
71
|
+
particleField.setStartupActive(active)
|
|
72
|
+
},
|
|
73
|
+
onReady() {
|
|
74
|
+
particleField.setStartupActive(false)
|
|
75
|
+
},
|
|
76
|
+
})
|
|
61
77
|
const theme = createThemeController({
|
|
62
78
|
document,
|
|
63
79
|
window,
|
|
@@ -140,6 +156,7 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
140
156
|
}
|
|
141
157
|
|
|
142
158
|
const disposeVisualState = () => {
|
|
159
|
+
particleField.update({ ...preferences, enabled: false })
|
|
143
160
|
startup.stop({ restoreFocus: false })
|
|
144
161
|
stopStartupReadyWatch()
|
|
145
162
|
stopMountWatch()
|
|
@@ -147,7 +164,6 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
147
164
|
disposeOperations()
|
|
148
165
|
composerGlass.dispose()
|
|
149
166
|
assistantGlass.dispose()
|
|
150
|
-
particleField.update({ ...preferences, enabled: false })
|
|
151
167
|
toBottom.dispose()
|
|
152
168
|
conversationControls.dispose()
|
|
153
169
|
layoutDiagnostics.dispose()
|
|
@@ -206,7 +222,8 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
206
222
|
}
|
|
207
223
|
particleField.update(preferences)
|
|
208
224
|
composerGlass.start()
|
|
209
|
-
assistantGlass.start()
|
|
225
|
+
if (preferences.conversationStyle === 'deck-chat') assistantGlass.start()
|
|
226
|
+
else assistantGlass.dispose()
|
|
210
227
|
toBottom.start()
|
|
211
228
|
conversationControls.start()
|
|
212
229
|
layoutDiagnostics.start()
|
|
@@ -222,9 +239,11 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
222
239
|
phase: result.persisted ? 'saved' : 'error',
|
|
223
240
|
revision: settingsPersistence.revision + 1,
|
|
224
241
|
}
|
|
242
|
+
const shouldPlayStartup = !safeMode && !wasEnabled && preferences.enabled && preferences.bootAnimation
|
|
243
|
+
if (shouldPlayStartup) particleField.setStartupActive(true)
|
|
225
244
|
applyVisualState()
|
|
226
245
|
sync()
|
|
227
|
-
if (
|
|
246
|
+
if (shouldPlayStartup && !startup.play()) particleField.setStartupActive(false)
|
|
228
247
|
}
|
|
229
248
|
|
|
230
249
|
|
|
@@ -297,13 +316,13 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
297
316
|
}
|
|
298
317
|
ctx.effect(() => () => {
|
|
299
318
|
statusSource.dispose()
|
|
319
|
+
particleField.dispose()
|
|
300
320
|
startup.dispose({ restoreFocus: false })
|
|
301
321
|
stopStartupReadyWatch()
|
|
302
322
|
stopMountWatch()
|
|
303
323
|
disposeOperations()
|
|
304
324
|
composerGlass.dispose()
|
|
305
325
|
assistantGlass.dispose()
|
|
306
|
-
particleField.dispose()
|
|
307
326
|
toBottom.dispose()
|
|
308
327
|
conversationControls.dispose()
|
|
309
328
|
layoutDiagnostics.dispose()
|
|
@@ -27,7 +27,10 @@ function markup(emblem) {
|
|
|
27
27
|
<svg data-prts-scheme-icon="dark" viewBox="0 0 24 24" aria-hidden="true"><path d="M20.2 15.4A8.4 8.4 0 0 1 8.6 3.8 8.5 8.5 0 1 0 20.2 15.4Z"></path></svg>
|
|
28
28
|
<small>明暗</small>
|
|
29
29
|
</button>
|
|
30
|
-
<button type="button" data-prts-theme-disable aria-label="
|
|
30
|
+
<button type="button" data-prts-theme-disable aria-label="退出 P.R.T.S.,返回 Harness 原生界面">
|
|
31
|
+
<svg data-prts-theme-disable-icon viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v8"></path><path d="M7.1 6.8a7 7 0 1 0 9.8 0"></path></svg>
|
|
32
|
+
<small>退出 P.R.T.S.</small>
|
|
33
|
+
</button>
|
|
31
34
|
</div>
|
|
32
35
|
</nav>
|
|
33
36
|
${themeSettingsMarkup()}
|
|
@@ -326,7 +326,7 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
|
|
|
326
326
|
const rhodesIndex = Math.max(0, emblemSequence.findIndex(entry => entry.key === 'rhodes-island'))
|
|
327
327
|
const suppliedHeroMask = normalizeEmblemMasks(heroEmblemMask ? [heroEmblemMask] : [], window).get('rhodes-island-hero')
|
|
328
328
|
const empty = { mounted: false, hero: false, side: -1, transition: null, anchor: { horizontalProgress: 0, verticalProgress: 0 }, phase: 'error', error: '徽记资源异常' }
|
|
329
|
-
if (!document || !window) return { update() {}, setSchemeTransitionActive() {}, inspect: () => empty, dispose() {} }
|
|
329
|
+
if (!document || !window) return { update() {}, setStartupActive() {}, setSchemeTransitionActive() {}, inspect: () => empty, dispose() {} }
|
|
330
330
|
|
|
331
331
|
let preferences = {}
|
|
332
332
|
let operation
|
|
@@ -396,6 +396,7 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
|
|
|
396
396
|
let pauseStartedAt
|
|
397
397
|
let hydrationPaused = false
|
|
398
398
|
let schemeTransitionPaused = false
|
|
399
|
+
let startupPaused = false
|
|
399
400
|
let hydrationTimer
|
|
400
401
|
let hydrationFrame
|
|
401
402
|
let hydrationStableFrames = 0
|
|
@@ -429,7 +430,7 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
|
|
|
429
430
|
: preferences.conversationParticleDensity
|
|
430
431
|
const densityProfile = density => PARTICLE_DENSITY_PROFILES[density] ?? PARTICLE_DENSITY_PROFILES.standard
|
|
431
432
|
const particleRadius = density => densityProfile(density ?? activeDensity()).radius
|
|
432
|
-
const canRenderFrame = () => !disposed && documentVisible && canvasVisible && !hydrationPaused && !schemeTransitionPaused && !resizing
|
|
433
|
+
const canRenderFrame = () => !disposed && documentVisible && canvasVisible && !hydrationPaused && !startupPaused && !schemeTransitionPaused && !resizing
|
|
433
434
|
const layoutWidth = () => width
|
|
434
435
|
const layoutHeight = () => height
|
|
435
436
|
const layoutShiftX = () => 0
|
|
@@ -707,6 +708,7 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
|
|
|
707
708
|
}
|
|
708
709
|
|
|
709
710
|
function buildParticles() {
|
|
711
|
+
if (startupPaused) return false
|
|
710
712
|
const selected = populationSource()
|
|
711
713
|
if (!selected.length) {
|
|
712
714
|
reportState({ phase: 'error', error: '徽记资源异常' })
|
|
@@ -1646,14 +1648,17 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
|
|
|
1646
1648
|
cancelFrame()
|
|
1647
1649
|
return
|
|
1648
1650
|
}
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1651
|
+
const resumedFromPause = pauseStartedAt !== undefined
|
|
1652
|
+
if (resumedFromPause) resumePausedTimelines(now)
|
|
1653
|
+
let particlesBuilt = false
|
|
1654
|
+
if (canvas && !particles.length && maskRecords.length) {
|
|
1655
|
+
particlesBuilt = buildParticles()
|
|
1656
|
+
}
|
|
1657
|
+
if (canvas && particles.length && (resumedFromPause || particlesBuilt)) {
|
|
1658
|
+
syncCanvasBackingStore()
|
|
1659
|
+
cancelFrame()
|
|
1660
|
+
draw(now)
|
|
1661
|
+
return
|
|
1657
1662
|
}
|
|
1658
1663
|
scheduleFrame()
|
|
1659
1664
|
}
|
|
@@ -1819,6 +1824,12 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
|
|
|
1819
1824
|
if (!mediaQuery?.addEventListener) mediaQuery?.addListener?.(mediaListener)
|
|
1820
1825
|
document.addEventListener?.('visibilitychange', onVisibilityChange)
|
|
1821
1826
|
return {
|
|
1827
|
+
setStartupActive(active) {
|
|
1828
|
+
const next = Boolean(active)
|
|
1829
|
+
if (startupPaused === next) return
|
|
1830
|
+
startupPaused = next
|
|
1831
|
+
updateFrameGate()
|
|
1832
|
+
},
|
|
1822
1833
|
setSchemeTransitionActive(active) {
|
|
1823
1834
|
const next = Boolean(active)
|
|
1824
1835
|
if (schemeTransitionPaused === next) return
|
|
@@ -1877,7 +1888,7 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
|
|
|
1877
1888
|
mutationObserver.observe(observationRoot, { childList: true, subtree: true })
|
|
1878
1889
|
}
|
|
1879
1890
|
}
|
|
1880
|
-
if (ambient) ambient.hidden = preferences.texture === 'off'
|
|
1891
|
+
if (ambient) ambient.hidden = preferences.texture === 'off' && preferences.conversationStyle !== 'deck-chat'
|
|
1881
1892
|
scheduleFrame()
|
|
1882
1893
|
},
|
|
1883
1894
|
inspect() {
|
|
@@ -1903,12 +1914,13 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
|
|
|
1903
1914
|
: transition?.toCount !== undefined
|
|
1904
1915
|
? Math.round(transition.fromCount + (transition.toCount - transition.fromCount) * easeInOut(transition.progress))
|
|
1905
1916
|
: particles.length,
|
|
1906
|
-
targetParticles: transition?.toCount ?? targetParticleCount(),
|
|
1917
|
+
targetParticles: startupPaused && !particles.length ? 0 : transition?.toCount ?? targetParticleCount(),
|
|
1907
1918
|
density: activeDensity() || 'standard',
|
|
1908
1919
|
firstFrameReady: canvas?.hasAttribute('data-prts-particle-ready') === true,
|
|
1909
1920
|
hydrationPaused,
|
|
1910
1921
|
sleeping: Boolean(context && particles.length && frame === undefined && canRenderFrame() && !motionReduced() && !phoneStatic()),
|
|
1911
1922
|
schemeTransitionPaused,
|
|
1923
|
+
startupPaused,
|
|
1912
1924
|
pointer: {
|
|
1913
1925
|
active: pointerActive,
|
|
1914
1926
|
clientX: pointerClientX,
|
|
@@ -6,7 +6,7 @@ export const PARTICLE_TRAVERSAL_SPEED_STEP = 0.25
|
|
|
6
6
|
export const PARTICLE_TRAVERSAL_SPEED_DEFAULT = 1
|
|
7
7
|
|
|
8
8
|
export const DEFAULT_PREFERENCES = Object.freeze({
|
|
9
|
-
version:
|
|
9
|
+
version: 9,
|
|
10
10
|
enabled: false,
|
|
11
11
|
preset: 'standard-tactical',
|
|
12
12
|
texture: 'full',
|
|
@@ -18,6 +18,7 @@ export const DEFAULT_PREFERENCES = Object.freeze({
|
|
|
18
18
|
particleTraversalSpeed: PARTICLE_TRAVERSAL_SPEED_DEFAULT,
|
|
19
19
|
particlePattern: 'orthogonal',
|
|
20
20
|
railDefaultHidden: false,
|
|
21
|
+
conversationStyle: 'native',
|
|
21
22
|
})
|
|
22
23
|
|
|
23
24
|
export const VISUAL_PRESETS = Object.freeze({
|
|
@@ -59,6 +60,7 @@ export const PREFERENCE_GROUPS = Object.freeze({
|
|
|
59
60
|
material: Object.freeze(['glass']),
|
|
60
61
|
accessibility: Object.freeze(['motion', 'bootAnimation']),
|
|
61
62
|
navigation: Object.freeze(['railDefaultHidden']),
|
|
63
|
+
conversation: Object.freeze(['conversationStyle']),
|
|
62
64
|
})
|
|
63
65
|
|
|
64
66
|
const TEXTURES = new Set(['off', 'restrained', 'full'])
|
|
@@ -66,6 +68,7 @@ const GLASS_STRENGTHS = new Set(['off', 'soft', 'standard', 'clear'])
|
|
|
66
68
|
const MOTIONS = new Set(['system', 'reduced'])
|
|
67
69
|
const PARTICLE_DENSITIES = new Set(['sparse', 'light', 'standard', 'dense', 'ultra'])
|
|
68
70
|
const PRESETS = new Set([...Object.keys(VISUAL_PRESETS), 'custom'])
|
|
71
|
+
const CONVERSATION_STYLES = new Set(['native', 'deck-chat'])
|
|
69
72
|
const PRESET_LINKED_KEYS = new Set(Object.keys(VISUAL_PRESETS['standard-tactical']))
|
|
70
73
|
|
|
71
74
|
|
|
@@ -129,7 +132,7 @@ export function normalizePreferences(value) {
|
|
|
129
132
|
const particlePreset = VISUAL_PRESETS[input.preset] ?? DEFAULT_PREFERENCES
|
|
130
133
|
const legacyDensities = legacyParticleDensities(input)
|
|
131
134
|
const normalized = {
|
|
132
|
-
version:
|
|
135
|
+
version: 9,
|
|
133
136
|
enabled: booleanOr(input.enabled, DEFAULT_PREFERENCES.enabled),
|
|
134
137
|
preset: enumOr(migratedPreset, PRESETS, DEFAULT_PREFERENCES.preset),
|
|
135
138
|
texture: enumOr(input.texture, TEXTURES, DEFAULT_PREFERENCES.texture),
|
|
@@ -144,6 +147,7 @@ export function normalizePreferences(value) {
|
|
|
144
147
|
),
|
|
145
148
|
particlePattern: 'orthogonal',
|
|
146
149
|
railDefaultHidden: booleanOr(input.railDefaultHidden, DEFAULT_PREFERENCES.railDefaultHidden),
|
|
150
|
+
conversationStyle: enumOr(input.conversationStyle, CONVERSATION_STYLES, DEFAULT_PREFERENCES.conversationStyle),
|
|
147
151
|
}
|
|
148
152
|
normalized.preset = matchedVisualPreset(normalized)
|
|
149
153
|
return normalized
|
|
@@ -190,7 +194,7 @@ export function loadPreferences(storage) {
|
|
|
190
194
|
const raw = storage?.getItem(PRTS_STORAGE_KEY)
|
|
191
195
|
if (raw === null || raw === undefined) return { ...DEFAULT_PREFERENCES }
|
|
192
196
|
const parsed = JSON.parse(raw)
|
|
193
|
-
if (![1, 2, 3, 4, 5, 6, 7, 8].includes(parsed?.version)) return { ...DEFAULT_PREFERENCES }
|
|
197
|
+
if (![1, 2, 3, 4, 5, 6, 7, 8, 9].includes(parsed?.version)) return { ...DEFAULT_PREFERENCES }
|
|
194
198
|
return normalizePreferences(parsed)
|
|
195
199
|
} catch {
|
|
196
200
|
return { ...DEFAULT_PREFERENCES }
|
|
@@ -7,36 +7,73 @@ function closestCommonContainer(root, nodes) {
|
|
|
7
7
|
return root && nodes.every(node => root.contains(node)) ? root : null
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
const LAYOUT_CONTRACTS = [
|
|
11
|
+
{
|
|
12
|
+
version: '0.1.5',
|
|
13
|
+
center: '[data-slot="main"]',
|
|
14
|
+
auxiliary: '[data-slot="rightbar"]',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
version: '0.1.2',
|
|
18
|
+
center: '[data-slot="conversation"]',
|
|
19
|
+
auxiliary: '[data-slot="details"]',
|
|
20
|
+
},
|
|
21
|
+
]
|
|
22
|
+
|
|
10
23
|
export function resolveRc7Regions(document, report = () => {}) {
|
|
11
24
|
const root = document?.querySelector('[data-slot="root"]')
|
|
12
25
|
const sidebarSlot = root?.querySelector('[data-slot="sidebar"]')
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
26
|
+
const contract = LAYOUT_CONTRACTS
|
|
27
|
+
.map(candidate => ({
|
|
28
|
+
...candidate,
|
|
29
|
+
centerSlot: root?.querySelector(candidate.center),
|
|
30
|
+
auxiliarySlot: root?.querySelector(candidate.auxiliary),
|
|
31
|
+
}))
|
|
32
|
+
.find(candidate => candidate.centerSlot && candidate.auxiliarySlot)
|
|
33
|
+
if (!root || !sidebarSlot || !contract) {
|
|
16
34
|
report('required layout slots are unavailable', {
|
|
17
35
|
root: Boolean(root), sidebar: Boolean(sidebarSlot),
|
|
18
|
-
|
|
36
|
+
main: Boolean(root?.querySelector('[data-slot="main"]')),
|
|
37
|
+
rightbar: Boolean(root?.querySelector('[data-slot="rightbar"]')),
|
|
38
|
+
conversation: Boolean(root?.querySelector('[data-slot="conversation"]')),
|
|
39
|
+
details: Boolean(root?.querySelector('[data-slot="details"]')),
|
|
19
40
|
})
|
|
20
41
|
return null
|
|
21
42
|
}
|
|
43
|
+
const { centerSlot, auxiliarySlot } = contract
|
|
22
44
|
const sidebar = sidebarSlot?.parentElement
|
|
23
|
-
const center =
|
|
24
|
-
const
|
|
45
|
+
const center = centerSlot?.parentElement
|
|
46
|
+
const auxiliary = auxiliarySlot?.parentElement
|
|
25
47
|
|
|
26
|
-
if (!sidebar || !center || !
|
|
48
|
+
if (!sidebar || !center || !auxiliary) {
|
|
27
49
|
report('required layout region containers are unavailable', {
|
|
28
|
-
sidebar: Boolean(sidebar),
|
|
50
|
+
sidebar: Boolean(sidebar), center: Boolean(center), auxiliary: Boolean(auxiliary),
|
|
29
51
|
})
|
|
30
52
|
return null
|
|
31
53
|
}
|
|
32
54
|
|
|
33
|
-
const frame = closestCommonContainer(root, [sidebar, center,
|
|
55
|
+
const frame = closestCommonContainer(root, [sidebar, center, auxiliary])
|
|
34
56
|
if (!frame) {
|
|
35
57
|
report('common layout container is unavailable')
|
|
36
58
|
return null
|
|
37
59
|
}
|
|
38
60
|
|
|
39
|
-
return {
|
|
61
|
+
return {
|
|
62
|
+
root,
|
|
63
|
+
frame,
|
|
64
|
+
sidebar,
|
|
65
|
+
center,
|
|
66
|
+
auxiliary,
|
|
67
|
+
sidebarSlot,
|
|
68
|
+
centerSlot,
|
|
69
|
+
auxiliarySlot,
|
|
70
|
+
layoutVersion: contract.version,
|
|
71
|
+
// Legacy aliases keep the internal adapter face compatible with callers
|
|
72
|
+
// built against the 0.1.2 layout vocabulary.
|
|
73
|
+
conversationSlot: centerSlot,
|
|
74
|
+
details: auxiliary,
|
|
75
|
+
detailsSlot: auxiliarySlot,
|
|
76
|
+
}
|
|
40
77
|
}
|
|
41
78
|
|
|
42
79
|
export function createRc7Adapter({ document, warn } = {}) {
|
|
@@ -64,7 +101,7 @@ export function createRc7Adapter({ document, warn } = {}) {
|
|
|
64
101
|
[regions.frame, 'frame'],
|
|
65
102
|
[regions.sidebar, 'sessions'],
|
|
66
103
|
[regions.center, 'operation'],
|
|
67
|
-
[regions.
|
|
104
|
+
[regions.auxiliary, 'auxiliary'],
|
|
68
105
|
]) {
|
|
69
106
|
owned.push([node, node.getAttribute('data-prts-region')])
|
|
70
107
|
node.setAttribute('data-prts-region', value)
|