@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.
- package/LICENSE +24 -0
- package/README.en.md +103 -0
- package/README.md +107 -0
- package/THIRD_PARTY_NOTICES.md +48 -0
- package/cordis.patch.yml +4 -0
- package/lib/client.js +7466 -0
- package/lib/index.js +3 -0
- package/package.json +64 -0
- package/src/assets/faction-emblems/lungmen.png +0 -0
- package/src/assets/faction-emblems/penguin-logistics.png +0 -0
- package/src/assets/faction-emblems/reunion.png +0 -0
- package/src/assets/faction-emblems/rhine-lab.png +0 -0
- package/src/assets/faction-emblems/rhodes-island.png +0 -0
- package/src/assets/prts-original-avatar.png +0 -0
- package/src/assets/rhodes-dsh.svg +6 -0
- package/src/assets/topographic-map.svg +72 -0
- package/src/assets/topographic-tech.svg +19 -0
- package/src/client/assistant-glass-adapter.js +231 -0
- package/src/client/composer-glass-adapter.js +210 -0
- package/src/client/conversation-control-adapter.js +109 -0
- package/src/client/conversation-scale-adapter.js +2243 -0
- package/src/client/facility-geometry.js +60 -0
- package/src/client/facility-vector.js +230 -0
- package/src/client/floating-glass-adapter.js +177 -0
- package/src/client/host-geometry-adapter.js +123 -0
- package/src/client/index.js +359 -0
- package/src/client/layout-stability-diagnostics.js +162 -0
- package/src/client/operations-shell-v2.js +248 -0
- package/src/client/particle-field-adapter.js +1840 -0
- package/src/client/preferences.js +239 -0
- package/src/client/rc7-adapter.js +83 -0
- package/src/client/resize-shield-adapter.js +64 -0
- package/src/client/settings-page.js +62 -0
- package/src/client/sidebar-control-adapter.js +317 -0
- package/src/client/startup-sequence.js +366 -0
- package/src/client/status-projection.js +79 -0
- package/src/client/terrain-motion-adapter.js +188 -0
- package/src/client/theme-controller-v2.js +134 -0
- package/src/client/theme-controller.js +119 -0
- package/src/client/theme-settings-workbench.js +583 -0
- package/src/client/to-bottom-adapter.js +85 -0
- package/src/client/ui-store.js +23 -0
- package/src/index.js +4 -0
- package/src/styles/conversation-scale.css +471 -0
- package/src/styles/facility-vector.css +292 -0
- package/src/styles/prts.css +1223 -0
- package/src/styles/refinement.css +429 -0
- package/src/styles/stabilization.css +161 -0
- package/src/styles/startup-sequence.css +496 -0
- package/src/styles/theme-settings-workbench.css +803 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export const FACILITY_GEOMETRY = Object.freeze({
|
|
2
|
+
stroke: 1,
|
|
3
|
+
corner: 1,
|
|
4
|
+
sideDepth: 2,
|
|
5
|
+
sideOpening: 10,
|
|
6
|
+
sideInner: 6,
|
|
7
|
+
topOpening: 50,
|
|
8
|
+
topInner: 40,
|
|
9
|
+
topDepth: 10 / 3,
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
function number(value) {
|
|
13
|
+
return Number(value.toFixed(3)).toString()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function point(x, y) {
|
|
17
|
+
return `${number(x)} ${number(y)}`
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function createFacilityPath({ width, height, topNotch = false } = {}) {
|
|
21
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 2 || height <= FACILITY_GEOMETRY.sideOpening + 2) return ''
|
|
22
|
+
|
|
23
|
+
const halfStroke = FACILITY_GEOMETRY.stroke / 2
|
|
24
|
+
const left = halfStroke
|
|
25
|
+
const right = width - halfStroke
|
|
26
|
+
const top = halfStroke
|
|
27
|
+
const bottom = height - halfStroke
|
|
28
|
+
const centerX = width / 2
|
|
29
|
+
const centerY = height / 2
|
|
30
|
+
const points = [point(left + FACILITY_GEOMETRY.corner, top)]
|
|
31
|
+
|
|
32
|
+
if (topNotch && width >= FACILITY_GEOMETRY.topOpening + FACILITY_GEOMETRY.corner * 2 + FACILITY_GEOMETRY.stroke) {
|
|
33
|
+
points.push(
|
|
34
|
+
point(centerX - FACILITY_GEOMETRY.topOpening / 2, top),
|
|
35
|
+
point(centerX - FACILITY_GEOMETRY.topInner / 2, top + FACILITY_GEOMETRY.topDepth),
|
|
36
|
+
point(centerX + FACILITY_GEOMETRY.topInner / 2, top + FACILITY_GEOMETRY.topDepth),
|
|
37
|
+
point(centerX + FACILITY_GEOMETRY.topOpening / 2, top),
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
points.push(
|
|
42
|
+
point(right - FACILITY_GEOMETRY.corner, top),
|
|
43
|
+
point(right, top + FACILITY_GEOMETRY.corner),
|
|
44
|
+
point(right, centerY - FACILITY_GEOMETRY.sideOpening / 2),
|
|
45
|
+
point(right - FACILITY_GEOMETRY.sideDepth, centerY - FACILITY_GEOMETRY.sideInner / 2),
|
|
46
|
+
point(right - FACILITY_GEOMETRY.sideDepth, centerY + FACILITY_GEOMETRY.sideInner / 2),
|
|
47
|
+
point(right, centerY + FACILITY_GEOMETRY.sideOpening / 2),
|
|
48
|
+
point(right, bottom - FACILITY_GEOMETRY.corner),
|
|
49
|
+
point(right - FACILITY_GEOMETRY.corner, bottom),
|
|
50
|
+
point(left + FACILITY_GEOMETRY.corner, bottom),
|
|
51
|
+
point(left, bottom - FACILITY_GEOMETRY.corner),
|
|
52
|
+
point(left, centerY + FACILITY_GEOMETRY.sideOpening / 2),
|
|
53
|
+
point(left + FACILITY_GEOMETRY.sideDepth, centerY + FACILITY_GEOMETRY.sideInner / 2),
|
|
54
|
+
point(left + FACILITY_GEOMETRY.sideDepth, centerY - FACILITY_GEOMETRY.sideInner / 2),
|
|
55
|
+
point(left, centerY - FACILITY_GEOMETRY.sideOpening / 2),
|
|
56
|
+
point(left, top + FACILITY_GEOMETRY.corner),
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
return `M ${points.join(' L ')} Z`
|
|
60
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { createFacilityPath } from './facility-geometry.js'
|
|
2
|
+
|
|
3
|
+
const SVG_NS = 'http://www.w3.org/2000/svg'
|
|
4
|
+
const SILHOUETTE_TRACK_WIDTH = 512
|
|
5
|
+
const SESSION_PICKUP_HEIGHTS = Object.freeze([5, 11, 7, 17, 11, 20, 13, 7, 15, 9, 5])
|
|
6
|
+
|
|
7
|
+
const SILHOUETTE_PATHS = Object.freeze({
|
|
8
|
+
far: [
|
|
9
|
+
'M0 38V26H14V22H30V25H43V18H60V22H73V15H84V11H89V6H93V2H97V6H101V11H106V20H121V16H138V22H151V13H158V9H163V4H168V9H173V13H180V21H196V17H211V12H226V20H241V23H253V15H270V19H285V11H293V7H297V3H301V7H305V11H313V18H329V21H342V14H360V19H374V22H388V16H401V12H405V8H409V12H413V16H422V20H438V13H454V18H470V24H484V20H499V23H512V26H512V38Z',
|
|
10
|
+
'M326 19V7H329V19ZM317 7H346V9H317ZM340 9L350 16H347L337 9Z',
|
|
11
|
+
'M218 12V7H220V12ZM214 7H224V9H214Z',
|
|
12
|
+
].join(' '),
|
|
13
|
+
near: [
|
|
14
|
+
'M0 38V32H16V29H31V33H48V26H61V31H78V28H94V34H112V30H126V32H141V25H156V29H174V33H191V27H206V31H223V34H240V29H256V26H270V32H289V30H305V34H324V27H340V31H357V29H374V34H392V30H410V26H425V31H443V33H460V28H477V31H494V27H512V32H512V38Z',
|
|
15
|
+
'M94 28V20H97V28ZM90 20H102V22H90Z',
|
|
16
|
+
'M203 27V23H219V25H205V27Z',
|
|
17
|
+
'M267 26V18H270V26ZM263 18H274V20H263Z',
|
|
18
|
+
'M418 26V21H421V26ZM414 21H425V23H414Z',
|
|
19
|
+
].join(' '),
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
let silhouetteId = 0
|
|
23
|
+
|
|
24
|
+
export const FACILITY_TEXTURES = Object.freeze(['none', 'grid', 'scanline', 'silhouette', 'pickup'])
|
|
25
|
+
|
|
26
|
+
function svgNode(document, name, attributes = {}) {
|
|
27
|
+
const node = document.createElementNS(SVG_NS, name)
|
|
28
|
+
for (const [key, value] of Object.entries(attributes)) node.setAttribute(key, value)
|
|
29
|
+
return node
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function appendPattern(document, defs, { id, width, height, path }) {
|
|
33
|
+
const pattern = svgNode(document, 'pattern', {
|
|
34
|
+
id: `prts-facility-${id}-pattern`,
|
|
35
|
+
patternUnits: 'userSpaceOnUse',
|
|
36
|
+
width,
|
|
37
|
+
height,
|
|
38
|
+
})
|
|
39
|
+
pattern.append(svgNode(document, 'path', {
|
|
40
|
+
d: path,
|
|
41
|
+
fill: 'none',
|
|
42
|
+
stroke: '#fff',
|
|
43
|
+
'stroke-width': '1',
|
|
44
|
+
opacity: '.72',
|
|
45
|
+
}))
|
|
46
|
+
defs.append(pattern)
|
|
47
|
+
|
|
48
|
+
const mask = svgNode(document, 'mask', {
|
|
49
|
+
id: `prts-facility-${id}-mask`,
|
|
50
|
+
maskUnits: 'userSpaceOnUse',
|
|
51
|
+
x: '0',
|
|
52
|
+
y: '0',
|
|
53
|
+
width: '4096',
|
|
54
|
+
height: '4096',
|
|
55
|
+
})
|
|
56
|
+
mask.append(svgNode(document, 'rect', {
|
|
57
|
+
x: '0',
|
|
58
|
+
y: '0',
|
|
59
|
+
width: '4096',
|
|
60
|
+
height: '4096',
|
|
61
|
+
fill: `url(#prts-facility-${id}-pattern)`,
|
|
62
|
+
}))
|
|
63
|
+
defs.append(mask)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function ensureFacilityTextureDefs(document) {
|
|
67
|
+
let root = document?.querySelector?.('svg[data-prts-facility-defs]')
|
|
68
|
+
if (root) return root
|
|
69
|
+
if (!document?.body) return null
|
|
70
|
+
|
|
71
|
+
root = svgNode(document, 'svg', {
|
|
72
|
+
'data-prts-facility-defs': '',
|
|
73
|
+
'data-prts-owned-facility-defs': '',
|
|
74
|
+
'aria-hidden': 'true',
|
|
75
|
+
focusable: 'false',
|
|
76
|
+
width: '0',
|
|
77
|
+
height: '0',
|
|
78
|
+
})
|
|
79
|
+
const defs = svgNode(document, 'defs')
|
|
80
|
+
appendPattern(document, defs, { id: 'grid', width: '8', height: '8', path: 'M 0 .5 H 8 M .5 0 V 8' })
|
|
81
|
+
appendPattern(document, defs, { id: 'scanline', width: '4', height: '4', path: 'M 0 .5 H 4' })
|
|
82
|
+
root.append(defs)
|
|
83
|
+
document.body.append(root)
|
|
84
|
+
return root
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function appendSilhouetteScene(document, graphic) {
|
|
88
|
+
const clipId = `prts-facility-silhouette-clip-${++silhouetteId}`
|
|
89
|
+
const defs = svgNode(document, 'defs')
|
|
90
|
+
const clip = svgNode(document, 'clipPath', { id: clipId, clipPathUnits: 'userSpaceOnUse' })
|
|
91
|
+
clip.append(svgNode(document, 'path', { 'data-prts-facility-clip': '' }))
|
|
92
|
+
defs.append(clip)
|
|
93
|
+
|
|
94
|
+
const scene = svgNode(document, 'g', {
|
|
95
|
+
'data-prts-facility-silhouette': '',
|
|
96
|
+
'clip-path': `url(#${clipId})`,
|
|
97
|
+
})
|
|
98
|
+
for (const layer of ['far', 'near']) {
|
|
99
|
+
const track = svgNode(document, 'g', { 'data-prts-silhouette-layer': layer })
|
|
100
|
+
for (const offset of [0, SILHOUETTE_TRACK_WIDTH]) {
|
|
101
|
+
track.append(svgNode(document, 'path', {
|
|
102
|
+
d: SILHOUETTE_PATHS[layer],
|
|
103
|
+
transform: `translate(${offset} 0)`,
|
|
104
|
+
}))
|
|
105
|
+
}
|
|
106
|
+
scene.append(track)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
graphic.insertBefore(defs, graphic.firstChild)
|
|
110
|
+
graphic.insertBefore(scene, graphic.querySelector('[data-prts-facility-layer="outline"]'))
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function appendSessionPickup(document, graphic) {
|
|
114
|
+
const clipId = `prts-facility-pickup-clip-${++silhouetteId}`
|
|
115
|
+
const defs = svgNode(document, 'defs')
|
|
116
|
+
const clip = svgNode(document, 'clipPath', { id: clipId, clipPathUnits: 'userSpaceOnUse' })
|
|
117
|
+
clip.append(svgNode(document, 'path', { 'data-prts-facility-clip': '' }))
|
|
118
|
+
defs.append(clip)
|
|
119
|
+
|
|
120
|
+
const pickup = svgNode(document, 'g', {
|
|
121
|
+
'data-prts-session-pickup': '',
|
|
122
|
+
'clip-path': `url(#${clipId})`,
|
|
123
|
+
})
|
|
124
|
+
pickup.append(svgNode(document, 'line', {
|
|
125
|
+
'data-prts-session-pickup-baseline': '',
|
|
126
|
+
x1: '60%',
|
|
127
|
+
x2: '98%',
|
|
128
|
+
y1: '19',
|
|
129
|
+
y2: '19',
|
|
130
|
+
}))
|
|
131
|
+
pickup.append(svgNode(document, 'circle', {
|
|
132
|
+
'data-prts-session-pickup-indicator': '',
|
|
133
|
+
cx: '56%',
|
|
134
|
+
cy: '19',
|
|
135
|
+
r: '2.5',
|
|
136
|
+
}))
|
|
137
|
+
SESSION_PICKUP_HEIGHTS.forEach((height, index) => {
|
|
138
|
+
pickup.append(svgNode(document, 'rect', {
|
|
139
|
+
'data-prts-session-pickup-bar': '',
|
|
140
|
+
x: `${76 + index * 2.15}%`,
|
|
141
|
+
y: String((38 - height) / 2),
|
|
142
|
+
width: '1.7',
|
|
143
|
+
height: String(height),
|
|
144
|
+
rx: '.4',
|
|
145
|
+
}))
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
graphic.insertBefore(defs, graphic.firstChild)
|
|
149
|
+
graphic.insertBefore(pickup, graphic.querySelector('[data-prts-facility-layer="outline"]'))
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function ensureFacilityGraphic(document, owner, { kind, topNotch = false, texture = 'none' } = {}) {
|
|
153
|
+
if (!owner) return null
|
|
154
|
+
let graphic = owner.querySelector?.(':scope > svg[data-prts-facility-svg]')
|
|
155
|
+
if (!graphic) {
|
|
156
|
+
graphic = svgNode(document, 'svg', {
|
|
157
|
+
'data-prts-facility-svg': kind,
|
|
158
|
+
'data-prts-owned-facility-vector': '',
|
|
159
|
+
'aria-hidden': 'true',
|
|
160
|
+
focusable: 'false',
|
|
161
|
+
preserveAspectRatio: 'none',
|
|
162
|
+
})
|
|
163
|
+
for (const layer of ['surface', 'texture', 'outline']) {
|
|
164
|
+
const path = svgNode(document, 'path', { 'data-prts-facility-layer': layer })
|
|
165
|
+
if (layer === 'outline') {
|
|
166
|
+
path.setAttribute('fill', 'none')
|
|
167
|
+
path.setAttribute('stroke-width', '1')
|
|
168
|
+
path.setAttribute('stroke-linejoin', 'miter')
|
|
169
|
+
path.setAttribute('stroke-miterlimit', '2')
|
|
170
|
+
path.setAttribute('vector-effect', 'non-scaling-stroke')
|
|
171
|
+
path.setAttribute('shape-rendering', 'geometricPrecision')
|
|
172
|
+
}
|
|
173
|
+
graphic.append(path)
|
|
174
|
+
}
|
|
175
|
+
owner.insertBefore(graphic, owner.firstChild)
|
|
176
|
+
}
|
|
177
|
+
if (texture === 'silhouette' && !graphic.querySelector('[data-prts-facility-silhouette]')) appendSilhouetteScene(document, graphic)
|
|
178
|
+
if (texture === 'pickup' && !graphic.querySelector('[data-prts-session-pickup]')) appendSessionPickup(document, graphic)
|
|
179
|
+
graphic.setAttribute('data-prts-facility-svg', kind)
|
|
180
|
+
graphic.toggleAttribute('data-prts-top-notch', Boolean(topNotch))
|
|
181
|
+
if (!owner.hasAttribute('data-prts-facility-texture')) owner.setAttribute('data-prts-facility-texture', texture)
|
|
182
|
+
return graphic
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function setReady(owner, ready) {
|
|
186
|
+
owner.toggleAttribute('data-prts-facility-vector', ready)
|
|
187
|
+
if (owner.hasAttribute('data-prts-facility-spine')) {
|
|
188
|
+
owner.parentElement?.toggleAttribute('data-prts-spine-vector', ready)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function renderFacilityGraphic(owner, { width, height } = {}) {
|
|
193
|
+
const graphic = owner?.querySelector?.(':scope > svg[data-prts-facility-svg]')
|
|
194
|
+
const topNotch = graphic?.hasAttribute('data-prts-top-notch')
|
|
195
|
+
const path = createFacilityPath({ width, height, topNotch })
|
|
196
|
+
if (!graphic || !path) {
|
|
197
|
+
owner?.removeAttribute?.('data-prts-facility-size')
|
|
198
|
+
setReady(owner, false)
|
|
199
|
+
return false
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const size = `${Number(width.toFixed(3))}x${Number(height.toFixed(3))}`
|
|
203
|
+
if (owner.getAttribute('data-prts-facility-size') !== size) {
|
|
204
|
+
graphic.setAttribute('viewBox', `0 0 ${Number(width.toFixed(3))} ${Number(height.toFixed(3))}`)
|
|
205
|
+
for (const layer of graphic.querySelectorAll('[data-prts-facility-layer], [data-prts-facility-clip]')) layer.setAttribute('d', path)
|
|
206
|
+
const silhouette = graphic.querySelector('[data-prts-facility-silhouette]')
|
|
207
|
+
if (silhouette) {
|
|
208
|
+
const availableHeight = Math.max(1, height - 2)
|
|
209
|
+
silhouette.setAttribute('transform', `translate(0 1) scale(1 ${Number((availableHeight / 38).toFixed(5))})`)
|
|
210
|
+
}
|
|
211
|
+
const pickup = graphic.querySelector('[data-prts-session-pickup]')
|
|
212
|
+
if (pickup) {
|
|
213
|
+
const center = Number((height / 2).toFixed(3))
|
|
214
|
+
for (const baseline of pickup.querySelectorAll('[data-prts-session-pickup-baseline]')) {
|
|
215
|
+
baseline.setAttribute('y1', String(center))
|
|
216
|
+
baseline.setAttribute('y2', String(center))
|
|
217
|
+
}
|
|
218
|
+
for (const indicator of pickup.querySelectorAll('[data-prts-session-pickup-indicator]')) {
|
|
219
|
+
indicator.setAttribute('cy', String(center))
|
|
220
|
+
}
|
|
221
|
+
for (const bar of pickup.querySelectorAll('[data-prts-session-pickup-bar]')) {
|
|
222
|
+
const barHeight = Number(bar.getAttribute('height')) || 0
|
|
223
|
+
bar.setAttribute('y', String(Number((center - barHeight / 2).toFixed(3))))
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
owner.setAttribute('data-prts-facility-size', size)
|
|
227
|
+
}
|
|
228
|
+
setReady(owner, true)
|
|
229
|
+
return true
|
|
230
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
const FLOATING_GLASS_OWNED_ATTRIBUTES = [
|
|
2
|
+
'data-prts-floating-glass',
|
|
3
|
+
'data-prts-floating-kind',
|
|
4
|
+
'data-prts-floating-item',
|
|
5
|
+
'data-prts-floating-input',
|
|
6
|
+
'data-prts-floating-danger',
|
|
7
|
+
'data-prts-floating-scrim',
|
|
8
|
+
'data-prts-floating-nested',
|
|
9
|
+
'data-prts-floating-preserved',
|
|
10
|
+
]
|
|
11
|
+
const FLOATING_GLASS_OWNED_SELECTOR = FLOATING_GLASS_OWNED_ATTRIBUTES.map(attribute => `[${attribute}]`).join(', ')
|
|
12
|
+
|
|
13
|
+
const FLOATING_GLASS_POPUP_SELECTOR = [
|
|
14
|
+
'[role="menu"]',
|
|
15
|
+
'[role="listbox"]',
|
|
16
|
+
'[role="dialog"][aria-modal="true"]',
|
|
17
|
+
'[popover]',
|
|
18
|
+
].join(', ')
|
|
19
|
+
|
|
20
|
+
const FLOATING_GLASS_PORTAL_ROOT_SELECTOR = [
|
|
21
|
+
'[role="presentation"]',
|
|
22
|
+
'[class*="_portal_"]',
|
|
23
|
+
'[data-floating-ui-portal]',
|
|
24
|
+
'[data-radix-portal]',
|
|
25
|
+
].join(', ')
|
|
26
|
+
|
|
27
|
+
function markFloatingGlass(node, attribute, value = '') {
|
|
28
|
+
if (!node?.setAttribute) return
|
|
29
|
+
node.setAttribute(attribute, value)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function floatingClassHas(node, fragment) {
|
|
33
|
+
return [...(node?.classList ?? [])].some(token => token.includes(fragment))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function floatingPopupKind(node) {
|
|
37
|
+
if (node.matches('[role="dialog"]')) return 'dialog'
|
|
38
|
+
if (node.matches('[role="listbox"]')) return 'listbox'
|
|
39
|
+
if (node.matches('[role="menu"]')) return 'menu'
|
|
40
|
+
return 'popover'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function floatingVisible(node) {
|
|
44
|
+
return !node.hidden
|
|
45
|
+
&& node.getAttribute('aria-hidden') !== 'true'
|
|
46
|
+
&& node.style?.display !== 'none'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function floatingForeignPluginOwner(node) {
|
|
50
|
+
const owner = node.closest?.('[data-plugin]')
|
|
51
|
+
if (!owner) return null
|
|
52
|
+
const id = owner.getAttribute('data-plugin')
|
|
53
|
+
return id && id !== 'dsh-theme-prts' ? owner : null
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function floatingHasCompleteCustomMaterial(window, node) {
|
|
57
|
+
let style
|
|
58
|
+
try {
|
|
59
|
+
style = window.getComputedStyle(node)
|
|
60
|
+
} catch {
|
|
61
|
+
return false
|
|
62
|
+
}
|
|
63
|
+
const radius = Number.parseFloat(style.borderRadius) || 0
|
|
64
|
+
const hasDepth = style.boxShadow !== 'none'
|
|
65
|
+
|| style.backdropFilter !== 'none'
|
|
66
|
+
|| style.webkitBackdropFilter !== 'none'
|
|
67
|
+
|| style.backgroundImage !== 'none'
|
|
68
|
+
return radius >= 10 && hasDepth
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function floatingCandidatesForBodyChild(node, body) {
|
|
72
|
+
if (node?.nodeType !== 1 || node.parentElement !== body) return []
|
|
73
|
+
if (node.matches(FLOATING_GLASS_POPUP_SELECTOR)) return [node]
|
|
74
|
+
if (!node.matches(FLOATING_GLASS_PORTAL_ROOT_SELECTOR)) return []
|
|
75
|
+
return [...node.querySelectorAll(FLOATING_GLASS_POPUP_SELECTOR)]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function createFloatingGlassAdapter({ document, window }) {
|
|
79
|
+
let observer
|
|
80
|
+
const owned = new Set()
|
|
81
|
+
const classified = new WeakSet()
|
|
82
|
+
|
|
83
|
+
function own(node, attribute, value = '') {
|
|
84
|
+
markFloatingGlass(node, attribute, value)
|
|
85
|
+
owned.add(node)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function preserveThirdParty(node) {
|
|
89
|
+
if (node.closest?.('[data-prts-preserve-popup-style]')) return true
|
|
90
|
+
if (!floatingForeignPluginOwner(node) || !floatingHasCompleteCustomMaterial(window, node)) return false
|
|
91
|
+
own(node, 'data-prts-floating-preserved', 'custom')
|
|
92
|
+
return true
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function classify(node) {
|
|
96
|
+
if (!floatingVisible(node) || classified.has(node)) return
|
|
97
|
+
classified.add(node)
|
|
98
|
+
if (preserveThirdParty(node)) return
|
|
99
|
+
|
|
100
|
+
const kind = floatingPopupKind(node)
|
|
101
|
+
own(node, 'data-prts-floating-glass', kind)
|
|
102
|
+
own(node, 'data-prts-floating-kind', kind)
|
|
103
|
+
if (node.parentElement?.closest?.(FLOATING_GLASS_POPUP_SELECTOR)) own(node, 'data-prts-floating-nested')
|
|
104
|
+
|
|
105
|
+
for (const item of node.querySelectorAll('[role="menuitem"], [role="menuitemradio"], [role="menuitemcheckbox"], [role="option"]')) {
|
|
106
|
+
own(item, 'data-prts-floating-item')
|
|
107
|
+
if (floatingClassHas(item, '_danger_') || item.matches('[data-variant="danger"], [data-danger="true"]')) {
|
|
108
|
+
own(item, 'data-prts-floating-danger')
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (kind === 'dialog') {
|
|
113
|
+
const scrim = node.parentElement?.matches?.('[role="presentation"]') ? node.parentElement : null
|
|
114
|
+
if (scrim) own(scrim, 'data-prts-floating-scrim')
|
|
115
|
+
for (const input of node.querySelectorAll('input, textarea, select, [contenteditable="true"]')) {
|
|
116
|
+
own(input, 'data-prts-floating-input')
|
|
117
|
+
}
|
|
118
|
+
for (const danger of node.querySelectorAll('[class*="_danger_"], [class*="_deleteAction"], [data-variant="danger"], [data-danger="true"]')) {
|
|
119
|
+
own(danger, 'data-prts-floating-danger')
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
for (const input of node.querySelectorAll('input, textarea, select, [contenteditable="true"]')) {
|
|
123
|
+
own(input, 'data-prts-floating-input')
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function scanBodyChild(node) {
|
|
129
|
+
for (const candidate of floatingCandidatesForBodyChild(node, document.body)) classify(candidate)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function scan() {
|
|
133
|
+
for (const child of document?.body?.children ?? []) scanBodyChild(child)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function releaseTree(node) {
|
|
137
|
+
if (node?.nodeType !== 1) return
|
|
138
|
+
const candidates = [
|
|
139
|
+
node,
|
|
140
|
+
...(node.querySelectorAll?.(FLOATING_GLASS_OWNED_SELECTOR) ?? []),
|
|
141
|
+
]
|
|
142
|
+
for (const candidate of candidates) {
|
|
143
|
+
if (!owned.has(candidate)) continue
|
|
144
|
+
for (const attribute of FLOATING_GLASS_OWNED_ATTRIBUTES) candidate.removeAttribute?.(attribute)
|
|
145
|
+
owned.delete(candidate)
|
|
146
|
+
classified.delete(candidate)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function clear() {
|
|
151
|
+
for (const node of owned) {
|
|
152
|
+
for (const attribute of FLOATING_GLASS_OWNED_ATTRIBUTES) node.removeAttribute?.(attribute)
|
|
153
|
+
}
|
|
154
|
+
owned.clear()
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
start() {
|
|
159
|
+
if (!document?.body || observer) return
|
|
160
|
+
scan()
|
|
161
|
+
const Observer = window?.MutationObserver
|
|
162
|
+
if (!Observer) return
|
|
163
|
+
observer = new Observer(records => {
|
|
164
|
+
for (const record of records) {
|
|
165
|
+
for (const node of record.removedNodes) releaseTree(node)
|
|
166
|
+
for (const node of record.addedNodes) scanBodyChild(node)
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
observer.observe(document.body, { childList: true })
|
|
170
|
+
},
|
|
171
|
+
dispose() {
|
|
172
|
+
observer?.disconnect()
|
|
173
|
+
observer = undefined
|
|
174
|
+
clear()
|
|
175
|
+
},
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const TOP_INSET_PROPERTY = '--prts-host-top-inset'
|
|
2
|
+
const RESIZE_SETTLE_DELAY = 160
|
|
3
|
+
|
|
4
|
+
function finiteInset(value, viewportHeight) {
|
|
5
|
+
const next = Number(value)
|
|
6
|
+
if (!Number.isFinite(next)) return 0
|
|
7
|
+
const limit = Math.max(0, Number(viewportHeight) || 0)
|
|
8
|
+
return Math.min(Math.max(0, next), limit)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function cssPixels(value) {
|
|
12
|
+
const rounded = Math.round(value * 100) / 100
|
|
13
|
+
return `${Object.is(rounded, -0) ? 0 : rounded}px`
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function createHostGeometryAdapter({ document, window }) {
|
|
17
|
+
const root = document?.documentElement
|
|
18
|
+
let frame
|
|
19
|
+
let resizeObserver
|
|
20
|
+
let frameRequest
|
|
21
|
+
let resizeSettleTimer
|
|
22
|
+
let observedFrameHeight
|
|
23
|
+
let previousValue
|
|
24
|
+
let previousPriority
|
|
25
|
+
let started = false
|
|
26
|
+
|
|
27
|
+
function measure() {
|
|
28
|
+
if (!started || !root || !frame?.isConnected) return 0
|
|
29
|
+
const top = finiteInset(frame.getBoundingClientRect?.().top, window?.innerHeight)
|
|
30
|
+
const value = cssPixels(top)
|
|
31
|
+
if (root.style.getPropertyValue(TOP_INSET_PROPERTY) !== value) {
|
|
32
|
+
root.style.setProperty(TOP_INSET_PROPERTY, value)
|
|
33
|
+
}
|
|
34
|
+
return top
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function scheduleMeasure() {
|
|
38
|
+
if (!started || frameRequest !== undefined) return
|
|
39
|
+
if (typeof window?.requestAnimationFrame !== 'function') {
|
|
40
|
+
measure()
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
frameRequest = window.requestAnimationFrame(() => {
|
|
44
|
+
frameRequest = undefined
|
|
45
|
+
measure()
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function scheduleSettledMeasure() {
|
|
50
|
+
scheduleMeasure()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function onWindowResize() {
|
|
54
|
+
if (!started) return
|
|
55
|
+
if (resizeSettleTimer !== undefined) window?.clearTimeout?.(resizeSettleTimer)
|
|
56
|
+
resizeSettleTimer = window?.setTimeout?.(() => {
|
|
57
|
+
resizeSettleTimer = undefined
|
|
58
|
+
scheduleMeasure()
|
|
59
|
+
}, RESIZE_SETTLE_DELAY)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function onGeometryResize(entries = []) {
|
|
63
|
+
let shouldMeasure = false
|
|
64
|
+
for (const entry of entries) {
|
|
65
|
+
if (entry.target !== frame) continue
|
|
66
|
+
const height = Number(entry.contentRect?.height)
|
|
67
|
+
if (!Number.isFinite(height) || height <= 0) {
|
|
68
|
+
shouldMeasure = true
|
|
69
|
+
} else if (height !== observedFrameHeight) {
|
|
70
|
+
observedFrameHeight = height
|
|
71
|
+
shouldMeasure = true
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (shouldMeasure || !entries.length) scheduleMeasure()
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function stopObservation() {
|
|
78
|
+
resizeObserver?.disconnect?.()
|
|
79
|
+
resizeObserver = undefined
|
|
80
|
+
window?.removeEventListener?.('resize', onWindowResize)
|
|
81
|
+
if (resizeSettleTimer !== undefined) window?.clearTimeout?.(resizeSettleTimer)
|
|
82
|
+
resizeSettleTimer = undefined
|
|
83
|
+
if (frameRequest !== undefined) window?.cancelAnimationFrame?.(frameRequest)
|
|
84
|
+
frameRequest = undefined
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function dispose() {
|
|
88
|
+
if (!started) return
|
|
89
|
+
stopObservation()
|
|
90
|
+
if (previousValue) root.style.setProperty(TOP_INSET_PROPERTY, previousValue, previousPriority)
|
|
91
|
+
else root.style.removeProperty(TOP_INSET_PROPERTY)
|
|
92
|
+
previousValue = undefined
|
|
93
|
+
previousPriority = undefined
|
|
94
|
+
frame = undefined
|
|
95
|
+
observedFrameHeight = undefined
|
|
96
|
+
started = false
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
start(nextFrame) {
|
|
101
|
+
if (!root || !nextFrame) return false
|
|
102
|
+
if (started && frame === nextFrame) {
|
|
103
|
+
scheduleSettledMeasure()
|
|
104
|
+
return true
|
|
105
|
+
}
|
|
106
|
+
if (started) dispose()
|
|
107
|
+
frame = nextFrame
|
|
108
|
+
previousValue = root.style.getPropertyValue(TOP_INSET_PROPERTY)
|
|
109
|
+
previousPriority = root.style.getPropertyPriority(TOP_INSET_PROPERTY)
|
|
110
|
+
started = true
|
|
111
|
+
measure()
|
|
112
|
+
if (typeof window?.ResizeObserver === 'function') {
|
|
113
|
+
resizeObserver = new window.ResizeObserver(onGeometryResize)
|
|
114
|
+
resizeObserver.observe(frame)
|
|
115
|
+
}
|
|
116
|
+
window?.addEventListener?.('resize', onWindowResize, { passive: true })
|
|
117
|
+
scheduleSettledMeasure()
|
|
118
|
+
return true
|
|
119
|
+
},
|
|
120
|
+
refresh: measure,
|
|
121
|
+
dispose,
|
|
122
|
+
}
|
|
123
|
+
}
|