@ash-qw/dsh-theme-prts 0.1.91

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.
Files changed (50) hide show
  1. package/LICENSE +24 -0
  2. package/README.en.md +103 -0
  3. package/README.md +107 -0
  4. package/THIRD_PARTY_NOTICES.md +48 -0
  5. package/cordis.patch.yml +4 -0
  6. package/lib/client.js +7466 -0
  7. package/lib/index.js +3 -0
  8. package/package.json +64 -0
  9. package/src/assets/faction-emblems/lungmen.png +0 -0
  10. package/src/assets/faction-emblems/penguin-logistics.png +0 -0
  11. package/src/assets/faction-emblems/reunion.png +0 -0
  12. package/src/assets/faction-emblems/rhine-lab.png +0 -0
  13. package/src/assets/faction-emblems/rhodes-island.png +0 -0
  14. package/src/assets/prts-original-avatar.png +0 -0
  15. package/src/assets/rhodes-dsh.svg +6 -0
  16. package/src/assets/topographic-map.svg +72 -0
  17. package/src/assets/topographic-tech.svg +19 -0
  18. package/src/client/assistant-glass-adapter.js +231 -0
  19. package/src/client/composer-glass-adapter.js +210 -0
  20. package/src/client/conversation-control-adapter.js +109 -0
  21. package/src/client/conversation-scale-adapter.js +2243 -0
  22. package/src/client/facility-geometry.js +60 -0
  23. package/src/client/facility-vector.js +230 -0
  24. package/src/client/floating-glass-adapter.js +177 -0
  25. package/src/client/host-geometry-adapter.js +123 -0
  26. package/src/client/index.js +359 -0
  27. package/src/client/layout-stability-diagnostics.js +162 -0
  28. package/src/client/operations-shell-v2.js +248 -0
  29. package/src/client/particle-field-adapter.js +1840 -0
  30. package/src/client/preferences.js +239 -0
  31. package/src/client/rc7-adapter.js +83 -0
  32. package/src/client/resize-shield-adapter.js +64 -0
  33. package/src/client/settings-page.js +62 -0
  34. package/src/client/sidebar-control-adapter.js +317 -0
  35. package/src/client/startup-sequence.js +366 -0
  36. package/src/client/status-projection.js +79 -0
  37. package/src/client/terrain-motion-adapter.js +188 -0
  38. package/src/client/theme-controller-v2.js +134 -0
  39. package/src/client/theme-controller.js +119 -0
  40. package/src/client/theme-settings-workbench.js +583 -0
  41. package/src/client/to-bottom-adapter.js +85 -0
  42. package/src/client/ui-store.js +23 -0
  43. package/src/index.js +4 -0
  44. package/src/styles/conversation-scale.css +471 -0
  45. package/src/styles/facility-vector.css +292 -0
  46. package/src/styles/prts.css +1223 -0
  47. package/src/styles/refinement.css +429 -0
  48. package/src/styles/stabilization.css +161 -0
  49. package/src/styles/startup-sequence.css +496 -0
  50. package/src/styles/theme-settings-workbench.css +803 -0
@@ -0,0 +1,359 @@
1
+ import { DEFAULT_PREFERENCES, isSafeMode, loadPreferences, persistPreferences, resetPreferenceGroup, updatePreferenceValue } from './preferences.js'
2
+ import { createHostGeometryAdapter } from './host-geometry-adapter.js'
3
+ import { createComposerGlassAdapter } from './composer-glass-adapter.js'
4
+ import { createAssistantGlassAdapter } from './assistant-glass-adapter.js'
5
+ import { createRc7Adapter } from './rc7-adapter.js'
6
+ import { createHarnessStatusSource, EMPTY_STATUS, projectHarnessStatus } from './status-projection.js'
7
+ import { createPrtsUiStore } from './ui-store.js'
8
+ import { createSettingsPage } from './settings-page.js'
9
+ import { createPrtsStartupSequence, findNativeHarnessLoader } from './startup-sequence.js'
10
+ import { createOperationsShell } from './operations-shell-v2.js'
11
+ import { createThemeController } from './theme-controller-v2.js'
12
+ import { createParticleFieldAdapter } from './particle-field-adapter.js'
13
+ import { createToBottomAdapter } from './to-bottom-adapter.js'
14
+ import { createConversationControlAdapter } from './conversation-control-adapter.js'
15
+ import { createConversationScaleAdapter } from './conversation-scale-adapter.js'
16
+ import { createLayoutStabilityDiagnostics } from './layout-stability-diagnostics.js'
17
+ import { createSidebarControlAdapter } from './sidebar-control-adapter.js'
18
+ import { createFloatingGlassAdapter } from './floating-glass-adapter.js'
19
+
20
+ export const inject = ['slots', 'theme']
21
+
22
+ function contextService(ctx, name) {
23
+ try {
24
+ const service = ctx?.[name]
25
+ if (service !== undefined && service !== null) return service
26
+ } catch {}
27
+ try {
28
+ return ctx?.get?.(name)
29
+ } catch {
30
+ return undefined
31
+ }
32
+ }
33
+
34
+ export function applyPrtsPlugin(ctx, environment) {
35
+ const {
36
+ document,
37
+ window,
38
+ React,
39
+ defineStore,
40
+ cssText = '',
41
+ assets = {},
42
+ version = 'development',
43
+ startupTimings,
44
+ } = environment
45
+ const safeMode = isSafeMode(window?.location?.search ?? '')
46
+ const storage = window?.localStorage
47
+ const themeService = contextService(ctx, 'theme')
48
+ const theme = createThemeController({ document, window, cssText, service: themeService })
49
+ const startup = createPrtsStartupSequence({ document, window, prtsEmblem: assets.prtsEmblem, rhodesEmblem: assets.emblem, timings: startupTimings })
50
+ const composerGlass = createComposerGlassAdapter({ document, window })
51
+ const assistantGlass = createAssistantGlassAdapter({ document, window })
52
+ const particleField = createParticleFieldAdapter({
53
+ document,
54
+ window,
55
+ emblems: assets.emblems,
56
+ emblemMasks: assets.emblemMasks,
57
+ heroEmblemMask: assets.heroEmblemMask,
58
+ onStateChange(next) {
59
+ status = { ...status, particle: next }
60
+ sync()
61
+ },
62
+ })
63
+ const sessions = contextService(ctx, 'sessions')
64
+ const toBottom = createToBottomAdapter({ document, window })
65
+ const conversationControls = createConversationControlAdapter({ document, window })
66
+ const conversationScale = createConversationScaleAdapter({ document, window, sessions })
67
+ const layoutDiagnostics = createLayoutStabilityDiagnostics({ document, window })
68
+ const sidebarControls = createSidebarControlAdapter({ document, window })
69
+ const floatingGlass = createFloatingGlassAdapter({ document, window })
70
+ const adapter = createRc7Adapter({ document })
71
+ const hostGeometry = createHostGeometryAdapter({ document, window })
72
+ const connection = contextService(ctx, 'connection')
73
+ let operations
74
+ let mountObserver
75
+ let mountFrame
76
+ let startupReadyObserver
77
+ let startupSettleFrame
78
+ let preferences = loadPreferences(storage)
79
+ let settingsPersistence = { phase: 'idle', revision: 0 }
80
+ let status = connection ? projectHarnessStatus({ connection }) : EMPTY_STATUS
81
+ const bindings = new Set()
82
+ let revision = 0
83
+ const stopThemeSync = typeof ctx.on === 'function'
84
+ ? ctx.on('theme/change', value => value == null ? theme.refresh() : theme.sync(value))
85
+ : undefined
86
+ let mountWarningIssued = false
87
+
88
+ const sync = () => {
89
+ revision += 1
90
+ for (const actions of bindings) actions.sync({ ...preferences }, { ...status }, revision)
91
+ }
92
+
93
+ const cancelMountFrame = () => {
94
+ if (mountFrame === undefined) return
95
+ if (window?.cancelAnimationFrame) window.cancelAnimationFrame(mountFrame)
96
+ else window?.clearTimeout?.(mountFrame)
97
+ mountFrame = undefined
98
+ }
99
+
100
+ const stopMountWatch = () => {
101
+ mountObserver?.disconnect()
102
+ mountObserver = undefined
103
+ cancelMountFrame()
104
+ }
105
+
106
+ const stopStartupReadyWatch = () => {
107
+ startupReadyObserver?.disconnect()
108
+ startupReadyObserver = undefined
109
+ if (startupSettleFrame === undefined) return
110
+ if (window?.cancelAnimationFrame) window.cancelAnimationFrame(startupSettleFrame)
111
+ else window?.clearTimeout?.(startupSettleFrame)
112
+ startupSettleFrame = undefined
113
+ }
114
+
115
+ const disposeOperations = () => {
116
+ operations?.dispose()
117
+ operations = undefined
118
+ }
119
+
120
+ const scheduleMountCheck = () => {
121
+ if (mountFrame !== undefined) return
122
+ const run = () => {
123
+ mountFrame = undefined
124
+ if (!preferences.enabled || safeMode || operations) return
125
+ applyVisualState()
126
+ }
127
+ mountFrame = window?.requestAnimationFrame?.(run)
128
+ if (mountFrame === undefined) mountFrame = window?.setTimeout?.(run, 16)
129
+ }
130
+
131
+ const watchMount = () => {
132
+ if (mountObserver || !window?.MutationObserver || !document?.body) return
133
+ mountObserver = new window.MutationObserver(scheduleMountCheck)
134
+ mountObserver.observe(document.body, { childList: true, subtree: true })
135
+ }
136
+
137
+ const disposeVisualState = () => {
138
+ startup.stop({ restoreFocus: false })
139
+ stopStartupReadyWatch()
140
+ stopMountWatch()
141
+ mountWarningIssued = false
142
+ disposeOperations()
143
+ composerGlass.dispose()
144
+ assistantGlass.dispose()
145
+ particleField.update({ ...preferences, enabled: false })
146
+ toBottom.dispose()
147
+ conversationControls.dispose()
148
+ conversationScale.dispose()
149
+ layoutDiagnostics.dispose()
150
+ sidebarControls.dispose()
151
+ floatingGlass.dispose()
152
+ theme.apply({ ...preferences, enabled: false })
153
+ }
154
+
155
+ const applyVisualState = ({ themeAlreadyApplied = false } = {}) => {
156
+ if (safeMode || !preferences.enabled) {
157
+ disposeVisualState()
158
+ return
159
+ }
160
+ if (!themeAlreadyApplied) theme.apply(preferences)
161
+ operations ||= createOperationsShell({
162
+ document, window, assets, adapter, hostGeometry,
163
+ onSchemeToggle(next) {
164
+ theme.setTheme(next)
165
+ },
166
+ onThemeDisable() {
167
+ persistAndApply(updatePreferenceValue(preferences, 'enabled', false))
168
+ },
169
+ onPreferenceChange(key, value) {
170
+ persistAndApply(updatePreferenceValue(preferences, key, value))
171
+ },
172
+ onVisualReset() {
173
+ persistAndApply({
174
+ ...DEFAULT_PREFERENCES,
175
+ enabled: preferences.enabled,
176
+ bootAnimation: preferences.bootAnimation,
177
+ })
178
+ },
179
+ onRetrySave() {
180
+ retryPersistence()
181
+ },
182
+ getConversationScalePreview(value) {
183
+ return conversationScale.getCalibrationState(value)
184
+ },
185
+ })
186
+ if (!operations.update(preferences, status, settingsPersistence)) {
187
+ if (!mountWarningIssued) {
188
+ window?.console?.warn?.(
189
+ '[dsh-theme-prts] operations shell mount deferred; base theme remains active',
190
+ )
191
+ mountWarningIssued = true
192
+ }
193
+ disposeOperations()
194
+ watchMount()
195
+ } else {
196
+ mountWarningIssued = false
197
+ stopMountWatch()
198
+ }
199
+ particleField.update(preferences)
200
+ composerGlass.start()
201
+ assistantGlass.start()
202
+ toBottom.start()
203
+ conversationControls.start()
204
+ conversationScale.update(preferences)
205
+ conversationScale.start()
206
+ layoutDiagnostics.start()
207
+ sidebarControls.start()
208
+ floatingGlass.start()
209
+ }
210
+
211
+ const persistAndApply = next => {
212
+ const wasEnabled = Boolean(preferences.enabled)
213
+ const result = persistPreferences(storage, next)
214
+ preferences = result.preferences
215
+ settingsPersistence = {
216
+ phase: result.persisted ? 'saved' : 'error',
217
+ revision: settingsPersistence.revision + 1,
218
+ }
219
+ applyVisualState()
220
+ sync()
221
+ if (!safeMode && !wasEnabled && preferences.enabled && preferences.bootAnimation) startup.play()
222
+ }
223
+
224
+
225
+ const retryPersistence = () => {
226
+ const result = persistPreferences(storage, preferences)
227
+ preferences = result.preferences
228
+ settingsPersistence = {
229
+ phase: result.persisted ? 'saved' : 'error',
230
+ revision: settingsPersistence.revision + 1,
231
+ }
232
+ operations?.update(preferences, status, settingsPersistence)
233
+ sync()
234
+ }
235
+ const statusSource = createHarnessStatusSource({
236
+ connection,
237
+ onChange(next) {
238
+ status = next
239
+ operations?.update(preferences, status, settingsPersistence)
240
+ sync()
241
+ },
242
+ })
243
+
244
+
245
+ const nativeLoader = !safeMode && preferences.enabled && preferences.bootAnimation
246
+ ? findNativeHarnessLoader(document)
247
+ : null
248
+ if (nativeLoader?.isConnected) {
249
+ theme.apply(preferences)
250
+ let visualStatePending = true
251
+ let visualStateSettled = false
252
+ let settleFrames = 0
253
+ const requestSettleFrame = callback => window?.requestAnimationFrame?.(callback)
254
+ ?? window?.setTimeout?.(callback, 16)
255
+ const ensureVisualState = () => {
256
+ if (nativeLoader.isConnected) return false
257
+ if (visualStatePending) {
258
+ visualStatePending = false
259
+ applyVisualState({ themeAlreadyApplied: true })
260
+ const settle = () => {
261
+ startupSettleFrame = undefined
262
+ const particleCanvas = document.querySelector('[data-prts-particle-layer]')
263
+ const particleReady = !particleCanvas || particleCanvas.hasAttribute('data-prts-particle-ready')
264
+ settleFrames = particleReady ? settleFrames + 1 : 0
265
+ if (settleFrames >= 2) {
266
+ visualStateSettled = true
267
+ startupReadyObserver?.disconnect()
268
+ startupReadyObserver = undefined
269
+ return
270
+ }
271
+ startupSettleFrame = requestSettleFrame(settle)
272
+ }
273
+ startupSettleFrame = requestSettleFrame(settle)
274
+ }
275
+ return visualStateSettled
276
+ }
277
+ if (window?.MutationObserver && document?.body) {
278
+ startupReadyObserver = new window.MutationObserver(ensureVisualState)
279
+ startupReadyObserver.observe(document.body, { childList: true, subtree: true })
280
+ }
281
+ const takeoverStarted = startup.play({
282
+ waitForReady: true,
283
+ readyWhen: ensureVisualState,
284
+ })
285
+ if (!takeoverStarted) {
286
+ stopStartupReadyWatch()
287
+ applyVisualState({ themeAlreadyApplied: true })
288
+ }
289
+ } else {
290
+ applyVisualState()
291
+ }
292
+ ctx.effect(() => () => {
293
+ statusSource.dispose()
294
+ startup.dispose({ restoreFocus: false })
295
+ stopStartupReadyWatch()
296
+ stopMountWatch()
297
+ disposeOperations()
298
+ composerGlass.dispose()
299
+ assistantGlass.dispose()
300
+ particleField.dispose()
301
+ toBottom.dispose()
302
+ conversationControls.dispose()
303
+ conversationScale.dispose()
304
+ layoutDiagnostics.dispose()
305
+ sidebarControls.dispose()
306
+ floatingGlass.dispose()
307
+ if (typeof stopThemeSync === 'function') stopThemeSync()
308
+ theme.dispose()
309
+ bindings.clear()
310
+ }, 'dsh-theme-prts: visual cleanup')
311
+
312
+ const initialStoreState = { preferences, status, revision }
313
+ const settingsStore = createPrtsUiStore(defineStore, initialStoreState)
314
+ const PrtsSettingsPage = createSettingsPage(React, {
315
+ emblem: assets.emblem,
316
+ version,
317
+ safeMode,
318
+ })
319
+ const injectActions = actions => {
320
+ bindings.add(actions)
321
+ sync()
322
+ return {
323
+ updatePreference(key, value) { persistAndApply(updatePreferenceValue(preferences, key, value)) },
324
+ resetPreferenceGroup(group) {
325
+ persistAndApply(resetPreferenceGroup(preferences, group))
326
+ },
327
+ resetPreferences() {
328
+ persistAndApply({
329
+ ...DEFAULT_PREFERENCES,
330
+ enabled: preferences.enabled,
331
+ bootAnimation: preferences.bootAnimation,
332
+ })
333
+ },
334
+ }
335
+ }
336
+
337
+ ctx.slots.inject('settings.plugins.tab', () => ctx.slots.register({
338
+ name: 'settings.plugins.tab',
339
+ id: 'prts-appearance',
340
+ order: 20,
341
+ label: () => 'P.R.T.S.',
342
+ store: settingsStore,
343
+ inject: injectActions,
344
+ }, PrtsSettingsPage))
345
+ }
346
+
347
+ export function apply(ctx) {
348
+ const React = require('react')
349
+ const { defineStore } = require('@deepseek-ai/dsh-client-runtime/client')
350
+ applyPrtsPlugin(ctx, {
351
+ document,
352
+ window,
353
+ React,
354
+ defineStore,
355
+ cssText: PRTS_CSS,
356
+ assets: PRTS_ASSETS,
357
+ version: PRTS_VERSION,
358
+ })
359
+ }
@@ -0,0 +1,162 @@
1
+ const USER_TURN_SELECTOR = [
2
+ '[data-chat-flow-kind="user"]',
3
+ '[data-chat-flow-kind="user-step"]',
4
+ '[data-message-role="user"]',
5
+ ].join(',')
6
+
7
+ const RECORD_LIMIT = 240
8
+
9
+ function nodeLabel(node) {
10
+ if (!node?.tagName) return ''
11
+ const role = node.getAttribute?.('data-chat-flow-kind')
12
+ || node.getAttribute?.('data-message-role')
13
+ || node.getAttribute?.('data-prts-conversation-control')
14
+ || node.getAttribute?.('data-prts-to-bottom')
15
+ return `${node.tagName.toLowerCase()}${role ? `[${role}]` : ''}`
16
+ }
17
+
18
+ export function createLayoutStabilityDiagnostics({ document, window }) {
19
+ let frame
20
+ let performanceObserver
21
+ let mutationObserver
22
+ let started = false
23
+ let lastSample
24
+ let lastUserInput = 0
25
+ const records = []
26
+
27
+ function enabled() {
28
+ if (document?.documentElement?.dataset?.prtsDiagnostics === 'layout') return true
29
+ try {
30
+ return new URLSearchParams(window?.location?.search ?? '').get('prtsDiagnostics') === 'layout'
31
+ } catch {
32
+ return false
33
+ }
34
+ }
35
+
36
+ function push(type, detail = {}) {
37
+ records.push({
38
+ type,
39
+ time: Number((window?.performance?.now?.() ?? Date.now()).toFixed?.(2) ?? Date.now()),
40
+ sinceUserInput: Math.max(0, Date.now() - lastUserInput),
41
+ ...detail,
42
+ })
43
+ if (records.length > RECORD_LIMIT) records.splice(0, records.length - RECORD_LIMIT)
44
+ }
45
+
46
+ function visibleAnchor(scroller) {
47
+ const scrollerBox = scroller?.getBoundingClientRect?.()
48
+ if (!scrollerBox) return {}
49
+ for (const turn of scroller.querySelectorAll?.(USER_TURN_SELECTOR) ?? []) {
50
+ const box = turn.getBoundingClientRect?.()
51
+ if (box && box.bottom > scrollerBox.top && box.top < scrollerBox.bottom) {
52
+ return { node: nodeLabel(turn), top: Number(box.top.toFixed(2)) }
53
+ }
54
+ }
55
+ return {}
56
+ }
57
+
58
+ function sample() {
59
+ frame = undefined
60
+ if (!started) return
61
+ const scroller = document?.querySelector?.('[data-prts-region="operation"] [data-conversation-scroll]')
62
+ if (scroller) {
63
+ const anchor = visibleAnchor(scroller)
64
+ const next = {
65
+ scrollTop: Number(Number(scroller.scrollTop).toFixed(2)),
66
+ scrollHeight: Number(scroller.scrollHeight),
67
+ clientHeight: Number(scroller.clientHeight),
68
+ anchor: anchor.node,
69
+ anchorTop: anchor.top,
70
+ }
71
+ if (lastSample) {
72
+ const scrollDelta = next.scrollTop - lastSample.scrollTop
73
+ const heightDelta = next.scrollHeight - lastSample.scrollHeight
74
+ const anchorDelta = next.anchor === lastSample.anchor && Number.isFinite(next.anchorTop) && Number.isFinite(lastSample.anchorTop)
75
+ ? Number((next.anchorTop - lastSample.anchorTop).toFixed(2))
76
+ : undefined
77
+ if (scrollDelta || heightDelta || anchorDelta) {
78
+ push('geometry', { scrollDelta, heightDelta, anchorDelta, anchor: next.anchor })
79
+ }
80
+ }
81
+ lastSample = next
82
+ } else {
83
+ lastSample = undefined
84
+ }
85
+ frame = window?.requestAnimationFrame?.(sample)
86
+ }
87
+
88
+ function onUserInput(event) {
89
+ lastUserInput = Date.now()
90
+ push('input', { event: event.type, key: event.key })
91
+ }
92
+
93
+ function startPerformanceObserver() {
94
+ const PerformanceObserver = window?.PerformanceObserver
95
+ if (typeof PerformanceObserver !== 'function') return
96
+ try {
97
+ performanceObserver = new PerformanceObserver(list => {
98
+ for (const entry of list.getEntries()) {
99
+ if (entry.entryType === 'layout-shift') {
100
+ push('layout-shift', {
101
+ value: entry.value,
102
+ recentInput: entry.hadRecentInput,
103
+ sources: (entry.sources ?? []).map(source => nodeLabel(source.node)).filter(Boolean),
104
+ })
105
+ } else if (entry.entryType === 'longtask') {
106
+ push('long-task', { duration: Number(entry.duration.toFixed(2)) })
107
+ }
108
+ }
109
+ })
110
+ performanceObserver.observe({ type: 'layout-shift', buffered: true })
111
+ performanceObserver.observe({ type: 'longtask', buffered: true })
112
+ } catch {
113
+ performanceObserver?.disconnect?.()
114
+ performanceObserver = undefined
115
+ }
116
+ }
117
+
118
+ return {
119
+ start() {
120
+ if (started || !enabled()) return false
121
+ started = true
122
+ window.__PRTS_LAYOUT_DIAGNOSTICS__ = records
123
+ for (const type of ['wheel', 'touchstart', 'pointerdown', 'keydown']) {
124
+ document?.addEventListener?.(type, onUserInput, { capture: true, passive: true })
125
+ }
126
+ if (window?.MutationObserver && document?.body) {
127
+ mutationObserver = new window.MutationObserver(mutations => {
128
+ for (const mutation of mutations) {
129
+ if (mutation.type === 'attributes') {
130
+ push('adapter-style', {
131
+ attribute: mutation.attributeName,
132
+ node: nodeLabel(mutation.target),
133
+ })
134
+ }
135
+ }
136
+ })
137
+ mutationObserver.observe(document.body, {
138
+ subtree: true,
139
+ attributes: true,
140
+ attributeFilter: ['data-prts-conversation-control', 'data-prts-to-bottom'],
141
+ })
142
+ }
143
+ startPerformanceObserver()
144
+ frame = window?.requestAnimationFrame?.(sample)
145
+ return true
146
+ },
147
+ dispose() {
148
+ started = false
149
+ if (frame !== undefined) window?.cancelAnimationFrame?.(frame)
150
+ frame = undefined
151
+ performanceObserver?.disconnect?.()
152
+ mutationObserver?.disconnect?.()
153
+ performanceObserver = undefined
154
+ mutationObserver = undefined
155
+ for (const type of ['wheel', 'touchstart', 'pointerdown', 'keydown']) {
156
+ document?.removeEventListener?.(type, onUserInput, { capture: true })
157
+ }
158
+ if (window?.__PRTS_LAYOUT_DIAGNOSTICS__ === records) delete window.__PRTS_LAYOUT_DIAGNOSTICS__
159
+ lastSample = undefined
160
+ },
161
+ }
162
+ }