@ash-qw/dsh-theme-prts 0.1.96 → 0.1.97
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 +5 -1
- package/README.en.md +3 -3
- package/README.md +3 -3
- package/lib/client.js +114 -24
- package/package.json +2 -2
- package/src/client/assistant-glass-adapter.js +54 -4
- package/src/client/composer-glass-adapter.js +1 -1
- package/src/client/index.js +7 -2
- package/src/client/particle-field-adapter.js +1 -1
- package/src/client/preferences.js +7 -3
- package/src/client/rc7-adapter.js +48 -11
- 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 +0 -2
- 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.97",
|
|
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
|
}
|
|
@@ -187,7 +187,7 @@ export function createComposerGlassAdapter({ document, window }) {
|
|
|
187
187
|
const Observer = window?.MutationObserver
|
|
188
188
|
if (!Observer) return
|
|
189
189
|
const observationRoot = document.querySelector('[data-prts-region="operation"]')
|
|
190
|
-
?? document.querySelector('[data-slot="conversation"]')
|
|
190
|
+
?? document.querySelector('[data-slot="main.conversation"], [data-slot="conversation"]')
|
|
191
191
|
if (!observationRoot) return
|
|
192
192
|
observer = new Observer(onMutations)
|
|
193
193
|
observer.observe(observationRoot, {
|
package/src/client/index.js
CHANGED
|
@@ -46,7 +46,11 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
46
46
|
const themeService = contextService(ctx, 'theme')
|
|
47
47
|
const startup = createPrtsStartupSequence({ document, window, prtsEmblem: assets.prtsEmblem, rhodesEmblem: assets.emblem, timings: startupTimings })
|
|
48
48
|
const composerGlass = createComposerGlassAdapter({ document, window })
|
|
49
|
-
const assistantGlass = createAssistantGlassAdapter({
|
|
49
|
+
const assistantGlass = createAssistantGlassAdapter({
|
|
50
|
+
document,
|
|
51
|
+
window,
|
|
52
|
+
avatarImage: assets.prtsEmblem,
|
|
53
|
+
})
|
|
50
54
|
const particleField = createParticleFieldAdapter({
|
|
51
55
|
document,
|
|
52
56
|
window,
|
|
@@ -206,7 +210,8 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
206
210
|
}
|
|
207
211
|
particleField.update(preferences)
|
|
208
212
|
composerGlass.start()
|
|
209
|
-
assistantGlass.start()
|
|
213
|
+
if (preferences.conversationStyle === 'deck-chat') assistantGlass.start()
|
|
214
|
+
else assistantGlass.dispose()
|
|
210
215
|
toBottom.start()
|
|
211
216
|
conversationControls.start()
|
|
212
217
|
layoutDiagnostics.start()
|
|
@@ -1877,7 +1877,7 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
|
|
|
1877
1877
|
mutationObserver.observe(observationRoot, { childList: true, subtree: true })
|
|
1878
1878
|
}
|
|
1879
1879
|
}
|
|
1880
|
-
if (ambient) ambient.hidden = preferences.texture === 'off'
|
|
1880
|
+
if (ambient) ambient.hidden = preferences.texture === 'off' && preferences.conversationStyle !== 'deck-chat'
|
|
1881
1881
|
scheduleFrame()
|
|
1882
1882
|
},
|
|
1883
1883
|
inspect() {
|
|
@@ -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: 'deck-chat',
|
|
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)
|
|
@@ -7,6 +7,7 @@ const ROOT_ATTRIBUTES = [
|
|
|
7
7
|
'data-prts-glass',
|
|
8
8
|
'data-prts-motion',
|
|
9
9
|
'data-prts-particle-pattern',
|
|
10
|
+
'data-prts-conversation-style',
|
|
10
11
|
]
|
|
11
12
|
|
|
12
13
|
const TRANSITION_ATTRIBUTE = 'data-prts-scheme-transition'
|
|
@@ -715,6 +716,7 @@ export function createThemeController({ document, window, cssText, service, onTr
|
|
|
715
716
|
root.dataset.prtsGlass = preferences.glass
|
|
716
717
|
root.dataset.prtsMotion = preferences.motion
|
|
717
718
|
root.dataset.prtsParticlePattern = 'orthogonal'
|
|
719
|
+
root.dataset.prtsConversationStyle = preferences.conversationStyle
|
|
718
720
|
refresh()
|
|
719
721
|
},
|
|
720
722
|
sync,
|
|
@@ -7,6 +7,7 @@ const ROOT_ATTRIBUTES = [
|
|
|
7
7
|
'data-prts-glass',
|
|
8
8
|
'data-prts-motion',
|
|
9
9
|
'data-prts-particle-pattern',
|
|
10
|
+
'data-prts-conversation-style',
|
|
10
11
|
]
|
|
11
12
|
|
|
12
13
|
function themeIdentity(value) {
|
|
@@ -108,6 +109,7 @@ export function createThemeController({ document, window, cssText, service }) {
|
|
|
108
109
|
root.dataset.prtsGlass = preferences.glass
|
|
109
110
|
root.dataset.prtsMotion = preferences.motion
|
|
110
111
|
root.dataset.prtsParticlePattern = 'orthogonal'
|
|
112
|
+
root.dataset.prtsConversationStyle = preferences.conversationStyle
|
|
111
113
|
refresh()
|
|
112
114
|
},
|
|
113
115
|
sync,
|
|
@@ -19,6 +19,7 @@ const COMMON_CONTROLS = [
|
|
|
19
19
|
['texture', '环境底纹', [['off', '关闭'], ['restrained', '克制'], ['full', '完整']]],
|
|
20
20
|
['glass', '玻璃材质', [['off', '关闭'], ['soft', '柔和'], ['standard', '标准'], ['clear', '清晰']]],
|
|
21
21
|
['motion', '动态效果', [['system', '跟随系统'], ['reduced', '减少']]],
|
|
22
|
+
['conversationStyle', '会话主题', [['native', '原有'], ['deck-chat', '通讯链路']]],
|
|
22
23
|
]
|
|
23
24
|
|
|
24
25
|
const PARTICLE_OPTIONS = [
|
|
@@ -34,6 +35,8 @@ function settingButtons(key, label, options) {
|
|
|
34
35
|
? '<small data-prts-setting-note data-prts-effective-motion hidden>系统当前要求减少动态</small>'
|
|
35
36
|
: key === 'railDefaultHidden'
|
|
36
37
|
? '<small data-prts-setting-note>在任意窗口宽度下默认收起 P.R.T.S. 导航,可从左侧按钮临时展开</small>'
|
|
38
|
+
: key === 'conversationStyle'
|
|
39
|
+
? '<small data-prts-setting-note>立即切换会话背景、头像与消息气泡</small>'
|
|
37
40
|
: ''
|
|
38
41
|
const sample = value => key === 'texture' || key === 'glass'
|
|
39
42
|
? `<i data-prts-option-sample="${key}" data-prts-option-sample-value="${value}" aria-hidden="true"></i>`
|