@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,79 @@
1
+ export const EMPTY_STATUS = Object.freeze({
2
+ connection: 'unknown',
3
+ connectionLabel: '未知',
4
+ })
5
+
6
+ const CONNECTION_LABELS = Object.freeze({
7
+ connected: '已连接',
8
+ connecting: '连接中',
9
+ disconnected: '已断开',
10
+ unknown: '未知',
11
+ })
12
+
13
+ function snapshotOf(source) {
14
+ try {
15
+ return source?.getSnapshot?.()
16
+ } catch {
17
+ return undefined
18
+ }
19
+ }
20
+
21
+ function subscribeTo(source, listener) {
22
+ try {
23
+ return typeof source?.subscribe === 'function' ? source.subscribe(listener) : () => {}
24
+ } catch {
25
+ return () => {}
26
+ }
27
+ }
28
+
29
+ function connectionState(connection) {
30
+ if (!connection) return 'unknown'
31
+ const stateSource = connection.state ?? connection.status
32
+ const state = snapshotOf(stateSource)
33
+ if (state === 'connected') return 'connected'
34
+ if (state === 'connecting' || state === 'reconnecting') return 'connecting'
35
+ if (state === 'disconnected') return 'disconnected'
36
+ if (connection.hostDescription?.getSnapshot) {
37
+ return snapshotOf(connection.hostDescription) ? 'connected' : 'connecting'
38
+ }
39
+ return 'unknown'
40
+ }
41
+
42
+ export function projectHarnessStatus({ connection } = {}) {
43
+ const connectionValue = connectionState(connection)
44
+ return {
45
+ connection: connectionValue,
46
+ connectionLabel: CONNECTION_LABELS[connectionValue],
47
+ }
48
+ }
49
+
50
+ function equalStatus(previous, next) {
51
+ return previous?.connection === next.connection
52
+ && previous?.connectionLabel === next.connectionLabel
53
+ }
54
+
55
+ export function createHarnessStatusSource({ connection, onChange }) {
56
+ let disposed = false
57
+ let lastStatus
58
+ const stops = []
59
+
60
+ function publish() {
61
+ if (disposed) return
62
+ const nextStatus = projectHarnessStatus({ connection })
63
+ if (equalStatus(lastStatus, nextStatus)) return
64
+ lastStatus = nextStatus
65
+ onChange(nextStatus)
66
+ }
67
+
68
+ stops.push(subscribeTo(connection?.hostDescription, publish))
69
+ stops.push(subscribeTo(connection?.state ?? connection?.status, publish))
70
+ publish()
71
+
72
+ return {
73
+ refresh: publish,
74
+ dispose() {
75
+ disposed = true
76
+ for (const stop of stops.splice(0).reverse()) stop()
77
+ },
78
+ }
79
+ }
@@ -0,0 +1,188 @@
1
+ const PARALLAX_RATIO = 0.2
2
+ const TILE_HEIGHT = 4800
3
+ const DESKTOP_MAX_LAG = 42
4
+ const TABLET_MAX_LAG = 20
5
+ const PHONE_BREAKPOINT = 640
6
+ const TABLET_BREAKPOINT = 1024
7
+ const SETTLE_TIME_MS = 260
8
+
9
+ function clamp(value, min, max) {
10
+ return Math.min(max, Math.max(min, value))
11
+ }
12
+
13
+ export function createTerrainMotionAdapter({ document, window }) {
14
+ if (!document || !window) {
15
+ return { update() {}, dispose() {}, inspect: () => ({ current: 0, target: 0 }) }
16
+ }
17
+
18
+ let preferences = {}
19
+ let operation
20
+ let scroller
21
+ let layer
22
+ let observer
23
+ let frame
24
+ let mediaQuery
25
+ let mediaListener
26
+ let lastFrameTime = 0
27
+ let lastScroll = 0
28
+ let viewportMode
29
+ let current = 0
30
+ let target = 0
31
+
32
+ const motionReduced = () => preferences.motion === 'reduced' || Boolean(mediaQuery?.matches)
33
+ const phoneStatic = () => window.innerWidth <= PHONE_BREAKPOINT
34
+ const maxLag = () => window.innerWidth <= TABLET_BREAKPOINT ? TABLET_MAX_LAG : DESKTOP_MAX_LAG
35
+ const targetFor = scrollTop => phoneStatic() ? 0 : scrollTop * PARALLAX_RATIO
36
+
37
+ function paint() {
38
+ if (!layer) return
39
+ const wrapped = ((current % TILE_HEIGHT) + TILE_HEIGHT) % TILE_HEIGHT
40
+ layer.style.transform = `translate3d(0, -${wrapped.toFixed(3)}px, 0)`
41
+ }
42
+
43
+ function cancelFrame() {
44
+ if (frame !== undefined) window.cancelAnimationFrame?.(frame)
45
+ frame = undefined
46
+ lastFrameTime = 0
47
+ }
48
+
49
+ function animate(timestamp) {
50
+ frame = undefined
51
+ const elapsed = lastFrameTime ? Math.min(48, Math.max(1, timestamp - lastFrameTime)) : 16
52
+ lastFrameTime = timestamp
53
+ const response = SETTLE_TIME_MS / 3
54
+ const alpha = 1 - Math.exp(-elapsed / response)
55
+ current += (target - current) * alpha
56
+ if (Math.abs(target - current) < 0.08) current = target
57
+ paint()
58
+ if (current !== target) frame = window.requestAnimationFrame(animate)
59
+ else lastFrameTime = 0
60
+ }
61
+
62
+ function snap() {
63
+ cancelFrame()
64
+ current = target
65
+ paint()
66
+ }
67
+
68
+ function schedule() {
69
+ if (current === target || frame !== undefined) return
70
+ frame = window.requestAnimationFrame(animate)
71
+ }
72
+
73
+ function syncFromScroll(forceSnap = false) {
74
+ if (!scroller) return
75
+ const nextScroll = Number(scroller.scrollTop) || 0
76
+ const delta = Math.abs(nextScroll - lastScroll)
77
+ lastScroll = nextScroll
78
+ target = targetFor(nextScroll)
79
+ const jumpThreshold = Math.max(720, (Number(scroller.clientHeight) || window.innerHeight || 800) * 0.9)
80
+ if (forceSnap || phoneStatic() || motionReduced() || delta >= jumpThreshold) {
81
+ snap()
82
+ return
83
+ }
84
+ const lag = maxLag()
85
+ current = clamp(current, target - lag, target + lag)
86
+ paint()
87
+ schedule()
88
+ }
89
+
90
+ function onScroll() {
91
+ syncFromScroll(false)
92
+ }
93
+
94
+ function unbindScroller() {
95
+ scroller?.removeEventListener('scroll', onScroll)
96
+ scroller = undefined
97
+ layer?.remove()
98
+ layer = undefined
99
+ cancelFrame()
100
+ }
101
+
102
+ function bindScroller(next) {
103
+ if (next === scroller && layer?.isConnected) return
104
+ unbindScroller()
105
+ scroller = next
106
+ if (!scroller || !operation) return
107
+ layer = document.createElement('div')
108
+ layer.setAttribute('data-prts-terrain-layer', '')
109
+ layer.setAttribute('aria-hidden', 'true')
110
+ operation.insertBefore(layer, operation.firstChild)
111
+ lastScroll = Number(scroller.scrollTop) || 0
112
+ target = targetFor(lastScroll)
113
+ current = target
114
+ paint()
115
+ scroller.addEventListener('scroll', onScroll, { passive: true })
116
+ }
117
+
118
+ function findScroller() {
119
+ const nextOperation = document.querySelector('[data-prts-region="operation"]')
120
+ if (nextOperation !== operation) {
121
+ unbindScroller()
122
+ observer?.disconnect()
123
+ operation = nextOperation
124
+ if (operation && window.MutationObserver) {
125
+ observer = new window.MutationObserver(findScroller)
126
+ observer.observe(operation, { childList: true, subtree: true })
127
+ }
128
+ }
129
+ const nextScroller = operation?.querySelector('[data-conversation-scroll]')
130
+ bindScroller(nextScroller)
131
+ }
132
+
133
+ function onViewportChange() {
134
+ const nextMode = window.innerWidth <= PHONE_BREAKPOINT
135
+ ? 'phone'
136
+ : window.innerWidth <= TABLET_BREAKPOINT ? 'tablet' : 'desktop'
137
+ if (nextMode === viewportMode) return
138
+ viewportMode = nextMode
139
+ syncFromScroll(true)
140
+ }
141
+
142
+ function attachMotionPreference() {
143
+ mediaQuery = window.matchMedia?.('(prefers-reduced-motion: reduce)')
144
+ mediaListener = onViewportChange
145
+ mediaQuery?.addEventListener?.('change', mediaListener)
146
+ if (!mediaQuery?.addEventListener) mediaQuery?.addListener?.(mediaListener)
147
+ }
148
+
149
+ function detachMotionPreference() {
150
+ if (mediaQuery && mediaListener) {
151
+ mediaQuery.removeEventListener?.('change', mediaListener)
152
+ mediaQuery.removeListener?.(mediaListener)
153
+ }
154
+ mediaQuery = undefined
155
+ mediaListener = undefined
156
+ }
157
+
158
+ attachMotionPreference()
159
+ viewportMode = window.innerWidth <= PHONE_BREAKPOINT ? 'phone' : window.innerWidth <= TABLET_BREAKPOINT ? 'tablet' : 'desktop'
160
+ window.addEventListener?.('resize', onViewportChange, { passive: true })
161
+
162
+ return {
163
+ update(nextPreferences = {}) {
164
+ preferences = nextPreferences
165
+ if (!preferences.enabled) {
166
+ unbindScroller()
167
+ observer?.disconnect()
168
+ observer = undefined
169
+ operation = undefined
170
+ return
171
+ }
172
+ findScroller()
173
+ if (layer) layer.hidden = preferences.texture === 'off'
174
+ syncFromScroll(true)
175
+ },
176
+ inspect() {
177
+ return { current, target, lastScroll, mounted: Boolean(layer?.isConnected) }
178
+ },
179
+ dispose() {
180
+ unbindScroller()
181
+ observer?.disconnect()
182
+ observer = undefined
183
+ operation = undefined
184
+ detachMotionPreference()
185
+ window.removeEventListener?.('resize', onViewportChange)
186
+ },
187
+ }
188
+ }
@@ -0,0 +1,134 @@
1
+ import { normalizePreferences } from './preferences.js'
2
+
3
+ const ROOT_ATTRIBUTES = [
4
+ 'data-dsh-prts',
5
+ 'data-prts-scheme',
6
+ 'data-prts-texture',
7
+ 'data-prts-glass',
8
+ 'data-prts-motion',
9
+ 'data-prts-particle-pattern',
10
+ ]
11
+
12
+ function themeIdentity(value) {
13
+ if (typeof value === 'string') return value
14
+ if (!value || typeof value !== 'object') return ''
15
+ const active = value.active
16
+ if (active && typeof active === 'object') {
17
+ const resolved = active.colorScheme || active.scheme || active.appearance || active.mode || active.id || active.name
18
+ if (resolved) return resolved
19
+ }
20
+ return value.colorScheme || value.scheme || value.appearance || value.mode || value.id || value.name || value.preference || ''
21
+ }
22
+
23
+ function resolveScheme(value) {
24
+ const identity = String(themeIdentity(value)).toLowerCase()
25
+ return identity.includes('dark') || identity.includes('night') ? 'dark' : 'light'
26
+ }
27
+
28
+ export function createThemeController({ document, window, cssText, service }) {
29
+ if (!document || !window) return { apply() {}, sync() {}, refresh() {}, setTheme() {}, toggle() {}, dispose() {} }
30
+
31
+ const root = document.documentElement
32
+ let style
33
+ let disposed = false
34
+ let refreshRevision = 0
35
+ let setRevision = 0
36
+
37
+ function clearThemeState() {
38
+ for (const attribute of ROOT_ATTRIBUTES) root.removeAttribute(attribute)
39
+ }
40
+
41
+ function removeOwnedState() {
42
+ disposed = true
43
+ refreshRevision += 1
44
+ setRevision += 1
45
+ clearThemeState()
46
+ root.removeAttribute('data-dsh-prts-settings')
47
+ style?.remove()
48
+ style = undefined
49
+ }
50
+
51
+ function ensureStyle() {
52
+ if (!style?.isConnected) {
53
+ style = document.createElement('style')
54
+ style.dataset.plugin = 'dsh-theme-prts'
55
+ style.dataset.pluginCss = 'dsh-theme-prts/prts.css'
56
+ document.head.appendChild(style)
57
+ }
58
+ if (style.textContent !== cssText) style.textContent = cssText
59
+ root.setAttribute('data-dsh-prts-settings', '')
60
+ }
61
+
62
+ function sync(value) {
63
+ if (disposed) return
64
+ root.dataset.prtsScheme = resolveScheme(value)
65
+ }
66
+
67
+ function fallbackTheme() {
68
+ return root.dataset.theme || root.getAttribute('data-color-scheme') || root.style.colorScheme || 'light'
69
+ }
70
+
71
+ function refresh() {
72
+ const revision = ++refreshRevision
73
+ let value
74
+ try { value = service?.getTheme?.() } catch { value = undefined }
75
+ if (value && typeof value.then === 'function') {
76
+ return value.then(next => {
77
+ if (!disposed && revision === refreshRevision) sync(next)
78
+ return next
79
+ }).catch(() => {
80
+ if (!disposed && revision === refreshRevision) sync(fallbackTheme())
81
+ })
82
+ }
83
+ sync(value ?? fallbackTheme())
84
+ return value
85
+ }
86
+
87
+ function setTheme(id) {
88
+ const target = id === 'dark' ? 'dark' : 'light'
89
+ const revision = ++setRevision
90
+ try {
91
+ const result = service?.setTheme?.(target)
92
+ if (result && typeof result.then === 'function') {
93
+ return result.then(() => {
94
+ if (!disposed && revision === setRevision) sync(target)
95
+ return target
96
+ }).catch(() => {
97
+ if (!disposed && revision === setRevision) refresh()
98
+ return root.dataset.prtsScheme
99
+ })
100
+ }
101
+ if (!disposed && revision === setRevision) sync(target)
102
+ } catch {
103
+ if (!disposed && revision === setRevision) sync(target)
104
+ }
105
+ return target
106
+ }
107
+
108
+ function toggle() {
109
+ return setTheme(root.dataset.prtsScheme === 'dark' ? 'light' : 'dark')
110
+ }
111
+
112
+ return {
113
+ apply(value) {
114
+ disposed = false
115
+ const preferences = normalizePreferences(value)
116
+ ensureStyle()
117
+ if (!preferences.enabled) {
118
+ clearThemeState()
119
+ return
120
+ }
121
+ root.setAttribute('data-dsh-prts', '')
122
+ root.dataset.prtsTexture = preferences.texture
123
+ root.dataset.prtsGlass = preferences.glass
124
+ root.dataset.prtsMotion = preferences.motion
125
+ root.dataset.prtsParticlePattern = 'orthogonal'
126
+ refresh()
127
+ },
128
+ sync,
129
+ refresh,
130
+ setTheme,
131
+ toggle,
132
+ dispose: removeOwnedState,
133
+ }
134
+ }
@@ -0,0 +1,119 @@
1
+ import { normalizePreferences } from './preferences.js'
2
+
3
+ const ROOT_ATTRIBUTES = [
4
+ 'data-dsh-prts',
5
+ 'data-prts-scheme',
6
+ 'data-prts-texture',
7
+ 'data-prts-glass',
8
+ 'data-prts-motion',
9
+ 'data-prts-particle-pattern',
10
+ ]
11
+
12
+ function themeIdentity(value) {
13
+ if (typeof value === 'string') return value
14
+ if (!value || typeof value !== 'object') return ''
15
+ const active = value.active
16
+ if (active && typeof active === 'object') {
17
+ const resolved = active.colorScheme || active.scheme || active.appearance || active.mode || active.id || active.name
18
+ if (resolved) return resolved
19
+ }
20
+ return value.colorScheme || value.scheme || value.appearance || value.mode || value.id || value.name || value.preference || ''
21
+ }
22
+
23
+ function resolveScheme(value) {
24
+ const identity = String(themeIdentity(value)).toLowerCase()
25
+ return identity.includes('dark') || identity.includes('night') ? 'dark' : 'light'
26
+ }
27
+
28
+ export function createThemeController({ document, window, cssText, service }) {
29
+ if (!document || !window) return { apply() {}, sync() {}, refresh() {}, setTheme() {}, toggle() {}, dispose() {} }
30
+
31
+ const root = document.documentElement
32
+ let style
33
+ let disposed = false
34
+ let refreshRevision = 0
35
+
36
+ function clearThemeState() {
37
+ for (const attribute of ROOT_ATTRIBUTES) root.removeAttribute(attribute)
38
+ }
39
+
40
+ function removeOwnedState() {
41
+ disposed = true
42
+ refreshRevision += 1
43
+ clearThemeState()
44
+ root.removeAttribute('data-dsh-prts-settings')
45
+ style?.remove()
46
+ style = undefined
47
+ }
48
+
49
+ function ensureStyle() {
50
+ if (!style?.isConnected) {
51
+ style = document.createElement('style')
52
+ style.dataset.plugin = 'dsh-theme-prts'
53
+ style.dataset.pluginCss = 'dsh-theme-prts/prts.css'
54
+ document.head.appendChild(style)
55
+ }
56
+ if (style.textContent !== cssText) style.textContent = cssText
57
+ root.setAttribute('data-dsh-prts-settings', '')
58
+ }
59
+
60
+ function sync(value) {
61
+ if (disposed) return
62
+ root.dataset.prtsScheme = resolveScheme(value)
63
+ }
64
+
65
+ function fallbackTheme() {
66
+ return root.dataset.theme || root.getAttribute('data-color-scheme') || root.style.colorScheme || 'light'
67
+ }
68
+
69
+ function refresh() {
70
+ const revision = ++refreshRevision
71
+ let value
72
+ try { value = service?.getTheme?.() } catch { value = undefined }
73
+ if (value && typeof value.then === 'function') {
74
+ value.then(next => {
75
+ if (!disposed && revision === refreshRevision) sync(next)
76
+ }).catch(() => {
77
+ if (!disposed && revision === refreshRevision) sync(fallbackTheme())
78
+ })
79
+ return
80
+ }
81
+ sync(value ?? fallbackTheme())
82
+ }
83
+
84
+ function setTheme(id) {
85
+ const target = id === 'dark' ? 'dark' : 'light'
86
+ sync(target)
87
+ try {
88
+ const result = service?.setTheme?.(target)
89
+ result?.then?.(() => refresh()).catch?.(() => {})
90
+ } catch {}
91
+ }
92
+
93
+ function toggle() {
94
+ setTheme(root.dataset.prtsScheme === 'dark' ? 'light' : 'dark')
95
+ }
96
+
97
+ return {
98
+ apply(value) {
99
+ disposed = false
100
+ const preferences = normalizePreferences(value)
101
+ ensureStyle()
102
+ if (!preferences.enabled) {
103
+ clearThemeState()
104
+ return
105
+ }
106
+ root.setAttribute('data-dsh-prts', '')
107
+ root.dataset.prtsTexture = preferences.texture
108
+ root.dataset.prtsGlass = preferences.glass
109
+ root.dataset.prtsMotion = preferences.motion
110
+ root.dataset.prtsParticlePattern = 'orthogonal'
111
+ refresh()
112
+ },
113
+ sync,
114
+ refresh,
115
+ setTheme,
116
+ toggle,
117
+ dispose: removeOwnedState,
118
+ }
119
+ }