@ash-qw/dsh-theme-prts 0.1.100 → 0.1.101
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 +6 -0
- package/lib/client.js +244 -17
- package/package.json +1 -1
- package/src/client/facility-geometry.js +12 -6
- package/src/client/facility-vector.js +36 -5
- package/src/client/index.js +6 -1
- package/src/client/sidebar-control-adapter.js +201 -4
- package/src/styles/facility-vector.css +235 -0
package/package.json
CHANGED
|
@@ -7,6 +7,9 @@ export const FACILITY_GEOMETRY = Object.freeze({
|
|
|
7
7
|
topOpening: 50,
|
|
8
8
|
topInner: 40,
|
|
9
9
|
topDepth: 10 / 3,
|
|
10
|
+
sessionTopOpening: 50,
|
|
11
|
+
sessionTopInner: 36.5,
|
|
12
|
+
sessionTopDepth: 4.5,
|
|
10
13
|
})
|
|
11
14
|
|
|
12
15
|
function number(value) {
|
|
@@ -17,7 +20,7 @@ function point(x, y) {
|
|
|
17
20
|
return `${number(x)} ${number(y)}`
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
export function createFacilityPath({ width, height, topNotch = false } = {}) {
|
|
23
|
+
export function createFacilityPath({ width, height, topNotch = false, sessionNotch = false } = {}) {
|
|
21
24
|
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 2 || height <= FACILITY_GEOMETRY.sideOpening + 2) return ''
|
|
22
25
|
|
|
23
26
|
const halfStroke = FACILITY_GEOMETRY.stroke / 2
|
|
@@ -29,12 +32,15 @@ export function createFacilityPath({ width, height, topNotch = false } = {}) {
|
|
|
29
32
|
const centerY = height / 2
|
|
30
33
|
const points = [point(left + FACILITY_GEOMETRY.corner, top)]
|
|
31
34
|
|
|
32
|
-
|
|
35
|
+
const topOpening = sessionNotch ? FACILITY_GEOMETRY.sessionTopOpening : FACILITY_GEOMETRY.topOpening
|
|
36
|
+
const topInner = sessionNotch ? FACILITY_GEOMETRY.sessionTopInner : FACILITY_GEOMETRY.topInner
|
|
37
|
+
const topDepth = sessionNotch ? FACILITY_GEOMETRY.sessionTopDepth : FACILITY_GEOMETRY.topDepth
|
|
38
|
+
if (topNotch && width >= topOpening + FACILITY_GEOMETRY.corner * 2 + FACILITY_GEOMETRY.stroke) {
|
|
33
39
|
points.push(
|
|
34
|
-
point(centerX -
|
|
35
|
-
point(centerX -
|
|
36
|
-
point(centerX +
|
|
37
|
-
point(centerX +
|
|
40
|
+
point(centerX - topOpening / 2, top),
|
|
41
|
+
point(centerX - topInner / 2, top + topDepth),
|
|
42
|
+
point(centerX + topInner / 2, top + topDepth),
|
|
43
|
+
point(centerX + topOpening / 2, top),
|
|
38
44
|
)
|
|
39
45
|
}
|
|
40
46
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createFacilityPath } from './facility-geometry.js'
|
|
1
|
+
import { createFacilityPath, FACILITY_GEOMETRY } from './facility-geometry.js'
|
|
2
2
|
import { configureSessionLifeline } from './session-lifeline.js'
|
|
3
3
|
|
|
4
4
|
const SVG_NS = 'http://www.w3.org/2000/svg'
|
|
@@ -167,6 +167,28 @@ function appendSessionPickup(document, graphic) {
|
|
|
167
167
|
graphic.insertBefore(pickup, graphic.querySelector('[data-prts-facility-layer="outline"]'))
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
function appendSessionStatusUnderlay(document, graphic) {
|
|
171
|
+
const surface = graphic.querySelector('[data-prts-facility-layer="surface"]')
|
|
172
|
+
if (!surface) return
|
|
173
|
+
if (!graphic.querySelector('[data-prts-session-status-underlay]')) {
|
|
174
|
+
graphic.insertBefore(svgNode(document, 'path', {
|
|
175
|
+
'data-prts-session-status-underlay': '',
|
|
176
|
+
'aria-hidden': 'true',
|
|
177
|
+
transform: 'translate(3 2)',
|
|
178
|
+
'shape-rendering': 'geometricPrecision',
|
|
179
|
+
}), surface)
|
|
180
|
+
}
|
|
181
|
+
if (!graphic.querySelector('[data-prts-session-idle-indicator]')) {
|
|
182
|
+
graphic.insertBefore(svgNode(document, 'rect', {
|
|
183
|
+
'data-prts-session-idle-indicator': '',
|
|
184
|
+
'aria-hidden': 'true',
|
|
185
|
+
y: '2.5',
|
|
186
|
+
width: '50',
|
|
187
|
+
height: '2.5',
|
|
188
|
+
}), surface)
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
170
192
|
function clamp(value, min, max) {
|
|
171
193
|
return Math.min(max, Math.max(min, value))
|
|
172
194
|
}
|
|
@@ -179,7 +201,7 @@ function sessionLifelineGeometry(width, height) {
|
|
|
179
201
|
return { start, end, baseline, lift }
|
|
180
202
|
}
|
|
181
203
|
|
|
182
|
-
export function ensureFacilityGraphic(document, owner, { kind, topNotch = false, texture = 'none' } = {}) {
|
|
204
|
+
export function ensureFacilityGraphic(document, owner, { kind, topNotch = false, sessionNotch = false, texture = 'none' } = {}) {
|
|
183
205
|
if (!owner) return null
|
|
184
206
|
let graphic = owner.querySelector?.(':scope > svg[data-prts-facility-svg]')
|
|
185
207
|
if (!graphic) {
|
|
@@ -205,9 +227,13 @@ export function ensureFacilityGraphic(document, owner, { kind, topNotch = false,
|
|
|
205
227
|
owner.insertBefore(graphic, owner.firstChild)
|
|
206
228
|
}
|
|
207
229
|
if (texture === 'silhouette' && !graphic.querySelector('[data-prts-facility-silhouette]')) appendSilhouetteScene(document, graphic)
|
|
208
|
-
if (texture === 'pickup'
|
|
230
|
+
if (texture === 'pickup') {
|
|
231
|
+
appendSessionStatusUnderlay(document, graphic)
|
|
232
|
+
if (!graphic.querySelector('[data-prts-session-pickup]')) appendSessionPickup(document, graphic)
|
|
233
|
+
}
|
|
209
234
|
graphic.setAttribute('data-prts-facility-svg', kind)
|
|
210
235
|
graphic.toggleAttribute('data-prts-top-notch', Boolean(topNotch))
|
|
236
|
+
graphic.toggleAttribute('data-prts-session-notch', Boolean(sessionNotch))
|
|
211
237
|
if (!owner.hasAttribute('data-prts-facility-texture')) owner.setAttribute('data-prts-facility-texture', texture)
|
|
212
238
|
return graphic
|
|
213
239
|
}
|
|
@@ -222,19 +248,24 @@ function setReady(owner, ready) {
|
|
|
222
248
|
export function renderFacilityGraphic(owner, { width, height } = {}) {
|
|
223
249
|
const graphic = owner?.querySelector?.(':scope > svg[data-prts-facility-svg]')
|
|
224
250
|
const topNotch = graphic?.hasAttribute('data-prts-top-notch')
|
|
225
|
-
const
|
|
251
|
+
const sessionNotch = graphic?.hasAttribute('data-prts-session-notch')
|
|
252
|
+
const path = createFacilityPath({ width, height, topNotch, sessionNotch })
|
|
226
253
|
if (!graphic || !path) {
|
|
227
254
|
owner?.removeAttribute?.('data-prts-facility-size')
|
|
228
255
|
setReady(owner, false)
|
|
229
256
|
return false
|
|
230
257
|
}
|
|
231
258
|
|
|
232
|
-
const size = `${Number(width.toFixed(3))}x${Number(height.toFixed(3))}`
|
|
259
|
+
const size = `${Number(width.toFixed(3))}x${Number(height.toFixed(3))}:${topNotch ? 't' : '-'}${sessionNotch ? 's' : '-'}`
|
|
233
260
|
const lifeline = graphic.querySelector('[data-prts-session-lifeline]')
|
|
234
261
|
const lifelineGeometry = lifeline ? sessionLifelineGeometry(width, height) : undefined
|
|
235
262
|
if (owner.getAttribute('data-prts-facility-size') !== size) {
|
|
236
263
|
graphic.setAttribute('viewBox', `0 0 ${Number(width.toFixed(3))} ${Number(height.toFixed(3))}`)
|
|
237
264
|
for (const layer of graphic.querySelectorAll('[data-prts-facility-layer], [data-prts-facility-clip]')) layer.setAttribute('d', path)
|
|
265
|
+
const statusUnderlay = graphic.querySelector('[data-prts-session-status-underlay]')
|
|
266
|
+
statusUnderlay?.setAttribute('d', createFacilityPath({ width, height }))
|
|
267
|
+
const idleIndicator = graphic.querySelector('[data-prts-session-idle-indicator]')
|
|
268
|
+
idleIndicator?.setAttribute('x', String(Number((width / 2 - 25).toFixed(3))))
|
|
238
269
|
const silhouette = graphic.querySelector('[data-prts-facility-silhouette]')
|
|
239
270
|
if (silhouette) {
|
|
240
271
|
const availableHeight = Math.max(1, height - 2)
|
package/src/client/index.js
CHANGED
|
@@ -86,7 +86,12 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
86
86
|
const toBottom = createToBottomAdapter({ document, window })
|
|
87
87
|
const conversationControls = createConversationControlAdapter({ document, window })
|
|
88
88
|
const layoutDiagnostics = createLayoutStabilityDiagnostics({ document, window })
|
|
89
|
-
const sidebarControls = createSidebarControlAdapter({
|
|
89
|
+
const sidebarControls = createSidebarControlAdapter({
|
|
90
|
+
document,
|
|
91
|
+
window,
|
|
92
|
+
sessions: contextService(ctx, 'sessions'),
|
|
93
|
+
workspaces: contextService(ctx, 'workspaces'),
|
|
94
|
+
})
|
|
90
95
|
const floatingGlass = createFloatingGlassAdapter({ document, window })
|
|
91
96
|
const adapter = createRc7Adapter({ document })
|
|
92
97
|
const hostGeometry = createHostGeometryAdapter({ document, window })
|
|
@@ -17,6 +17,9 @@ const OWNED_ATTRIBUTES = [
|
|
|
17
17
|
'data-prts-session-menu',
|
|
18
18
|
'data-prts-session-time',
|
|
19
19
|
'data-prts-session-index',
|
|
20
|
+
'data-prts-session-state',
|
|
21
|
+
'data-prts-session-summary-header',
|
|
22
|
+
'data-prts-session-summary-label',
|
|
20
23
|
'data-prts-facility-face',
|
|
21
24
|
'data-prts-facility-spine',
|
|
22
25
|
'data-prts-facility-vector',
|
|
@@ -25,6 +28,54 @@ const OWNED_ATTRIBUTES = [
|
|
|
25
28
|
'data-prts-spine-vector',
|
|
26
29
|
]
|
|
27
30
|
|
|
31
|
+
const SESSION_STATES = Object.freeze(['warning', 'ongoing', 'error', 'done'])
|
|
32
|
+
|
|
33
|
+
function snapshotOf(source) {
|
|
34
|
+
try {
|
|
35
|
+
return source?.getSnapshot?.()
|
|
36
|
+
} catch {
|
|
37
|
+
return undefined
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function nativeSessionState(row) {
|
|
42
|
+
for (const state of SESSION_STATES) {
|
|
43
|
+
if (row.querySelector(`[data-state="${state}"]:not([data-prts-owned-session-status])`)) return state
|
|
44
|
+
}
|
|
45
|
+
return undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolveSectionHeader(root) {
|
|
49
|
+
const tree = root?.querySelector?.('[role="tree"]')
|
|
50
|
+
if (!tree) return {}
|
|
51
|
+
for (const candidate of root.querySelectorAll('div')) {
|
|
52
|
+
if (candidate.contains(tree)) continue
|
|
53
|
+
const label = Array.from(candidate.children).find(child => (
|
|
54
|
+
child.tagName === 'SPAN'
|
|
55
|
+
&& child.textContent.trim()
|
|
56
|
+
&& !child.querySelector('button, input')
|
|
57
|
+
))
|
|
58
|
+
if (!label || candidate.querySelectorAll('button').length < 1) continue
|
|
59
|
+
return { header: candidate, label }
|
|
60
|
+
}
|
|
61
|
+
return {}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function visibleSessionSummaries(sessions, workspaces) {
|
|
65
|
+
const list = snapshotOf(sessions?.list)
|
|
66
|
+
if (!list?.byId) return []
|
|
67
|
+
const workspaceList = snapshotOf(workspaces?.list)
|
|
68
|
+
const archived = new Set(workspaceList?.archivedSessionIds ?? [])
|
|
69
|
+
const ids = Array.isArray(list.ids) ? list.ids : Object.keys(list.byId)
|
|
70
|
+
return ids.flatMap(id => {
|
|
71
|
+
const summary = list.byId[id]
|
|
72
|
+
if (!summary || summary.blank || summary.origin === 'subagent' || archived.has(id)) return []
|
|
73
|
+
if (summary.running) return [{ id, title: summary.displayTitle || id, state: 'ongoing' }]
|
|
74
|
+
if (summary.completed === true) return [{ id, title: summary.displayTitle || id, state: 'done' }]
|
|
75
|
+
return []
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
28
79
|
function markSidebarNode(node, attribute, next) {
|
|
29
80
|
if (!node) return
|
|
30
81
|
node.setAttribute(attribute, '')
|
|
@@ -119,18 +170,134 @@ function ensureFacilitySpine(document, row, kind, next) {
|
|
|
119
170
|
}
|
|
120
171
|
|
|
121
172
|
|
|
122
|
-
export function createSidebarControlAdapter({ document, window }) {
|
|
173
|
+
export function createSidebarControlAdapter({ document, window, sessions, workspaces }) {
|
|
123
174
|
let observer
|
|
124
175
|
let resizeObserver
|
|
125
176
|
let resizeFrame
|
|
126
177
|
let resizeTimer
|
|
127
178
|
let scanFrame
|
|
179
|
+
let stopSessionList = () => {}
|
|
180
|
+
let stopWorkspaceList = () => {}
|
|
181
|
+
let statusSourcesStarted = false
|
|
182
|
+
let summaryRoot
|
|
183
|
+
let summarySignature = ''
|
|
128
184
|
let owned = new Set()
|
|
129
185
|
let vectorOwners = new Set()
|
|
130
186
|
const dirtyVectors = new Map()
|
|
131
187
|
const vectorSizes = new WeakMap()
|
|
132
188
|
const lifelineAnimator = createSessionLifelineAnimator({ document, window })
|
|
133
189
|
|
|
190
|
+
function openSession(sessionId) {
|
|
191
|
+
try {
|
|
192
|
+
if (typeof sessions?.open === 'function') {
|
|
193
|
+
sessions.open(sessionId)
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
} catch {}
|
|
197
|
+
const row = Array.from(document.querySelectorAll('[data-prts-session-row]')).find(candidate => (
|
|
198
|
+
candidate.querySelector('[data-prts-row-title]')?.textContent.trim() === sessionId
|
|
199
|
+
))
|
|
200
|
+
row?.click?.()
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function statusSignal(document, state) {
|
|
204
|
+
const signal = document.createElement('span')
|
|
205
|
+
signal.setAttribute('data-prts-session-summary-signal', state)
|
|
206
|
+
signal.setAttribute('aria-hidden', 'true')
|
|
207
|
+
return signal
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function buildSummaryGroup(state, label, items) {
|
|
211
|
+
const group = document.createElement('span')
|
|
212
|
+
group.setAttribute('data-prts-session-summary-group', state)
|
|
213
|
+
|
|
214
|
+
const trigger = document.createElement('button')
|
|
215
|
+
trigger.type = 'button'
|
|
216
|
+
trigger.setAttribute('data-prts-session-summary-trigger', state)
|
|
217
|
+
trigger.setAttribute('aria-haspopup', 'menu')
|
|
218
|
+
trigger.setAttribute('aria-label', `${label} ${items.length}`)
|
|
219
|
+
trigger.append(statusSignal(document, state))
|
|
220
|
+
const count = document.createElement('span')
|
|
221
|
+
count.setAttribute('data-prts-session-summary-count', '')
|
|
222
|
+
count.textContent = String(items.length)
|
|
223
|
+
trigger.append(count)
|
|
224
|
+
|
|
225
|
+
const popover = document.createElement('span')
|
|
226
|
+
popover.setAttribute('data-prts-session-summary-popover', state)
|
|
227
|
+
popover.setAttribute('role', 'menu')
|
|
228
|
+
popover.setAttribute('aria-label', label)
|
|
229
|
+
const heading = document.createElement('span')
|
|
230
|
+
heading.setAttribute('data-prts-session-summary-heading', '')
|
|
231
|
+
const headingLabel = document.createElement('strong')
|
|
232
|
+
headingLabel.textContent = label
|
|
233
|
+
const hint = document.createElement('small')
|
|
234
|
+
hint.textContent = '选择会话'
|
|
235
|
+
heading.append(headingLabel, hint)
|
|
236
|
+
popover.append(heading)
|
|
237
|
+
for (const item of items) {
|
|
238
|
+
const button = document.createElement('button')
|
|
239
|
+
button.type = 'button'
|
|
240
|
+
button.setAttribute('role', 'menuitem')
|
|
241
|
+
button.setAttribute('data-prts-session-summary-item', item.id)
|
|
242
|
+
button.append(statusSignal(document, state))
|
|
243
|
+
const title = document.createElement('span')
|
|
244
|
+
title.textContent = item.title
|
|
245
|
+
button.append(title)
|
|
246
|
+
button.addEventListener('click', event => {
|
|
247
|
+
event.stopPropagation()
|
|
248
|
+
openSession(item.id)
|
|
249
|
+
})
|
|
250
|
+
popover.append(button)
|
|
251
|
+
}
|
|
252
|
+
group.append(trigger, popover)
|
|
253
|
+
return group
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function syncSummary(root, next) {
|
|
257
|
+
const { header, label } = resolveSectionHeader(root)
|
|
258
|
+
if (!header || !label) {
|
|
259
|
+
summaryRoot?.remove()
|
|
260
|
+
summaryRoot = undefined
|
|
261
|
+
summarySignature = ''
|
|
262
|
+
return
|
|
263
|
+
}
|
|
264
|
+
header.setAttribute('data-prts-session-summary-header', '')
|
|
265
|
+
label.setAttribute('data-prts-session-summary-label', '')
|
|
266
|
+
next.add(header)
|
|
267
|
+
next.add(label)
|
|
268
|
+
const fromService = visibleSessionSummaries(sessions, workspaces)
|
|
269
|
+
const rows = fromService.length ? fromService : Array.from(root.querySelectorAll('[data-prts-session-row]')).flatMap(row => {
|
|
270
|
+
const state = row.getAttribute('data-prts-session-state')
|
|
271
|
+
if (state !== 'ongoing' && state !== 'done') return []
|
|
272
|
+
const title = row.querySelector('[data-prts-row-title]')?.textContent.trim()
|
|
273
|
+
return title ? [{ id: title, title, state }] : []
|
|
274
|
+
})
|
|
275
|
+
const ongoing = rows.filter(item => item.state === 'ongoing')
|
|
276
|
+
const done = rows.filter(item => item.state === 'done')
|
|
277
|
+
const signature = JSON.stringify({
|
|
278
|
+
ongoing: ongoing.map(item => [item.id, item.title]),
|
|
279
|
+
done: done.map(item => [item.id, item.title]),
|
|
280
|
+
})
|
|
281
|
+
if (!summaryRoot) {
|
|
282
|
+
summaryRoot = document.createElement('span')
|
|
283
|
+
summaryRoot.setAttribute('data-prts-owned-session-summary', '')
|
|
284
|
+
summaryRoot.setAttribute('data-prts-session-summary', '')
|
|
285
|
+
summaryRoot.setAttribute('role', 'group')
|
|
286
|
+
summaryRoot.setAttribute('aria-label', '会话状态总览')
|
|
287
|
+
label.insertAdjacentElement('afterend', summaryRoot)
|
|
288
|
+
} else if (summaryRoot.parentElement !== header) {
|
|
289
|
+
label.insertAdjacentElement('afterend', summaryRoot)
|
|
290
|
+
}
|
|
291
|
+
const labelHidden = Array.from(label.classList).some(name => /hidden/i.test(name))
|
|
292
|
+
summaryRoot.toggleAttribute('data-prts-summary-search-hidden', labelHidden)
|
|
293
|
+
summaryRoot.hidden = ongoing.length === 0 && done.length === 0
|
|
294
|
+
if (signature === summarySignature) return
|
|
295
|
+
summarySignature = signature
|
|
296
|
+
summaryRoot.replaceChildren()
|
|
297
|
+
if (ongoing.length) summaryRoot.append(buildSummaryGroup('ongoing', '运行中的会话', ongoing))
|
|
298
|
+
if (done.length) summaryRoot.append(buildSummaryGroup('done', '完成未查看', done))
|
|
299
|
+
}
|
|
300
|
+
|
|
134
301
|
function scheduleVectors() {
|
|
135
302
|
if (resizeFrame !== undefined) return
|
|
136
303
|
const requestFrame = window?.requestAnimationFrame?.bind(window) ?? (callback => window?.setTimeout?.(callback, 0))
|
|
@@ -161,12 +328,17 @@ export function createSidebarControlAdapter({ document, window }) {
|
|
|
161
328
|
|
|
162
329
|
function registerVector(owner, options, next, nextVectorOwners) {
|
|
163
330
|
if (!owner) return
|
|
331
|
+
const previousSessionNotch = owner.querySelector?.(':scope > svg[data-prts-facility-svg]')
|
|
332
|
+
?.hasAttribute('data-prts-session-notch') ?? false
|
|
164
333
|
const graphic = ensureFacilityGraphic(document, owner, options)
|
|
165
334
|
if (!graphic) return
|
|
166
335
|
next.add(graphic)
|
|
167
336
|
nextVectorOwners.add(owner)
|
|
168
337
|
if (!vectorOwners.has(owner)) resizeObserver?.observe(owner)
|
|
169
|
-
if (
|
|
338
|
+
if (previousSessionNotch !== Boolean(options.sessionNotch)) vectorSizes.delete(owner)
|
|
339
|
+
if (!vectorSizes.has(owner)) {
|
|
340
|
+
dirtyVectors.set(owner, undefined)
|
|
341
|
+
}
|
|
170
342
|
}
|
|
171
343
|
|
|
172
344
|
function syncVectorOwners(next) {
|
|
@@ -222,15 +394,22 @@ export function createSidebarControlAdapter({ document, window }) {
|
|
|
222
394
|
for (const row of root.querySelectorAll('div[role="treeitem"][aria-selected]')) {
|
|
223
395
|
const sessionIndex = String(++sessionOrdinal).padStart(2, '0')
|
|
224
396
|
const controls = resolveRowControls(row, 'session')
|
|
397
|
+
const state = nativeSessionState(row)
|
|
225
398
|
const face = ensureFacilityFace(document, row, 'session', next)
|
|
226
399
|
const spine = ensureFacilitySpine(document, row, 'session', next)
|
|
227
|
-
registerVector(face, { kind: 'face', topNotch: true, texture: 'pickup' }, next, nextVectorOwners)
|
|
400
|
+
registerVector(face, { kind: 'face', topNotch: true, sessionNotch: true, texture: 'pickup' }, next, nextVectorOwners)
|
|
228
401
|
registerVector(spine, { kind: 'spine' }, next, nextVectorOwners)
|
|
229
402
|
if (!activeLifelineRow && row.getAttribute('aria-selected') === 'true') {
|
|
230
403
|
activeLifelineRow = row
|
|
231
404
|
activeLifeline = face.querySelector('[data-prts-session-lifeline]')
|
|
232
405
|
}
|
|
233
406
|
markSidebarNode(row, 'data-prts-session-row', next)
|
|
407
|
+
if (state) {
|
|
408
|
+
row.setAttribute('data-prts-session-state', state)
|
|
409
|
+
next.add(row)
|
|
410
|
+
} else {
|
|
411
|
+
row.removeAttribute('data-prts-session-state')
|
|
412
|
+
}
|
|
234
413
|
row.dataset.prtsSessionIndex = sessionIndex
|
|
235
414
|
const title = resolveRowTitle(row, 'session', controls.actions)
|
|
236
415
|
markSidebarNode(title, 'data-prts-row-title', next)
|
|
@@ -243,6 +422,11 @@ export function createSidebarControlAdapter({ document, window }) {
|
|
|
243
422
|
markSidebarNode(controls.menu, 'data-prts-session-menu', next)
|
|
244
423
|
registerVector(controls.menu, { kind: 'button' }, next, nextVectorOwners)
|
|
245
424
|
}
|
|
425
|
+
syncSummary(root, next)
|
|
426
|
+
} else {
|
|
427
|
+
summaryRoot?.remove()
|
|
428
|
+
summaryRoot = undefined
|
|
429
|
+
summarySignature = ''
|
|
246
430
|
}
|
|
247
431
|
lifelineAnimator.setTarget(activeLifelineRow, activeLifeline)
|
|
248
432
|
for (const node of owned) {
|
|
@@ -272,6 +456,9 @@ export function createSidebarControlAdapter({ document, window }) {
|
|
|
272
456
|
vectorOwners.clear()
|
|
273
457
|
dirtyVectors.clear()
|
|
274
458
|
owned.clear()
|
|
459
|
+
summaryRoot?.remove()
|
|
460
|
+
summaryRoot = undefined
|
|
461
|
+
summarySignature = ''
|
|
275
462
|
}
|
|
276
463
|
|
|
277
464
|
return {
|
|
@@ -294,6 +481,11 @@ export function createSidebarControlAdapter({ document, window }) {
|
|
|
294
481
|
})
|
|
295
482
|
}
|
|
296
483
|
scan()
|
|
484
|
+
if (!statusSourcesStarted) {
|
|
485
|
+
statusSourcesStarted = true
|
|
486
|
+
stopSessionList = sessions?.list?.subscribe?.(queueScan) ?? (() => {})
|
|
487
|
+
stopWorkspaceList = workspaces?.list?.subscribe?.(queueScan) ?? (() => {})
|
|
488
|
+
}
|
|
297
489
|
if (observer || !window?.MutationObserver) return
|
|
298
490
|
const observationRoot = document.querySelector('[data-prts-region="sessions"]')
|
|
299
491
|
if (!observationRoot) return
|
|
@@ -302,12 +494,17 @@ export function createSidebarControlAdapter({ document, window }) {
|
|
|
302
494
|
subtree: true,
|
|
303
495
|
childList: true,
|
|
304
496
|
attributes: true,
|
|
305
|
-
attributeFilter: ['aria-expanded', 'aria-selected', 'class'],
|
|
497
|
+
attributeFilter: ['aria-expanded', 'aria-selected', 'class', 'data-state'],
|
|
306
498
|
})
|
|
307
499
|
},
|
|
308
500
|
dispose() {
|
|
309
501
|
observer?.disconnect()
|
|
310
502
|
observer = undefined
|
|
503
|
+
stopSessionList()
|
|
504
|
+
stopSessionList = () => {}
|
|
505
|
+
stopWorkspaceList()
|
|
506
|
+
stopWorkspaceList = () => {}
|
|
507
|
+
statusSourcesStarted = false
|
|
311
508
|
if (scanFrame !== undefined) {
|
|
312
509
|
if (window?.cancelAnimationFrame) window.cancelAnimationFrame(scanFrame)
|
|
313
510
|
else window?.clearTimeout?.(scanFrame)
|
|
@@ -17,6 +17,241 @@ html[data-dsh-prts] [data-prts-facility-svg] {
|
|
|
17
17
|
pointer-events: none;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/* A pure status-colored card underlay sits behind the stable face. Its small
|
|
21
|
+
right-down offset is visible through the top notch and along two outer edges. */
|
|
22
|
+
html[data-dsh-prts] [data-prts-session-status-underlay] {
|
|
23
|
+
fill: var(--prts-session-status-color, transparent);
|
|
24
|
+
opacity: 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
html[data-dsh-prts] [data-prts-session-state="ongoing"] {
|
|
28
|
+
--prts-session-status-color: var(--dsw-static-deepseek-450, var(--prts-cyan));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
html[data-dsh-prts] [data-prts-session-state="done"] {
|
|
32
|
+
--prts-session-status-color: var(--dsw-alias-state-success-primary, var(--prts-green));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
html[data-dsh-prts] [data-prts-session-state="warning"] {
|
|
36
|
+
--prts-session-status-color: var(--dsw-alias-state-warn-primary, var(--prts-yellow));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
html[data-dsh-prts] [data-prts-session-state="error"] {
|
|
40
|
+
--prts-session-status-color: var(--dsw-alias-state-error-primary, var(--prts-red));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
html[data-dsh-prts] [data-prts-session-state] [data-prts-session-status-underlay] {
|
|
44
|
+
opacity: 1;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
html[data-dsh-prts] [data-prts-session-idle-indicator] {
|
|
48
|
+
fill: var(--prts-dim);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
html[data-dsh-prts] [data-prts-session-state] [data-prts-session-idle-indicator] {
|
|
52
|
+
opacity: 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* Workspace status summary. */
|
|
56
|
+
html[data-dsh-prts] [data-prts-session-summary-header] {
|
|
57
|
+
position: relative;
|
|
58
|
+
z-index: 18;
|
|
59
|
+
overflow: visible !important;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
html[data-dsh-prts] [data-prts-session-summary-label] {
|
|
63
|
+
margin-right: 2px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
html[data-dsh-prts] [data-prts-session-summary] {
|
|
67
|
+
position: static;
|
|
68
|
+
z-index: 19;
|
|
69
|
+
display: inline-flex;
|
|
70
|
+
flex: 0 0 auto;
|
|
71
|
+
align-items: center;
|
|
72
|
+
gap: 3px;
|
|
73
|
+
color: var(--prts-muted);
|
|
74
|
+
font-family: var(--prts-mono);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
html[data-dsh-prts] [data-prts-session-summary][data-prts-summary-search-hidden] {
|
|
78
|
+
display: none;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
html[data-dsh-prts] [data-prts-session-summary-group] {
|
|
82
|
+
position: static;
|
|
83
|
+
display: inline-flex;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
html[data-dsh-prts] [data-prts-session-summary-trigger] {
|
|
87
|
+
min-width: 30px;
|
|
88
|
+
height: 22px;
|
|
89
|
+
display: inline-flex;
|
|
90
|
+
align-items: center;
|
|
91
|
+
justify-content: center;
|
|
92
|
+
gap: 5px;
|
|
93
|
+
padding: 0 6px;
|
|
94
|
+
border: 1px solid color-mix(in srgb, currentColor 38%, transparent);
|
|
95
|
+
border-radius: 0;
|
|
96
|
+
background: color-mix(in srgb, currentColor 7%, transparent);
|
|
97
|
+
color: inherit;
|
|
98
|
+
font: 700 9px/1 var(--prts-mono);
|
|
99
|
+
cursor: pointer;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
html[data-dsh-prts] [data-prts-session-summary-group="ongoing"] {
|
|
103
|
+
--prts-summary-state-color: var(--dsw-static-deepseek-450, var(--prts-cyan));
|
|
104
|
+
color: var(--prts-summary-state-color);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
html[data-dsh-prts] [data-prts-session-summary-group="done"] {
|
|
108
|
+
--prts-summary-state-color: var(--dsw-alias-state-success-primary, var(--prts-green));
|
|
109
|
+
color: var(--prts-summary-state-color);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
html[data-dsh-prts] [data-prts-session-summary-trigger]:is(:hover, :focus-visible) {
|
|
113
|
+
background: color-mix(in srgb, currentColor 14%, var(--prts-panel));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
html[data-dsh-prts] [data-prts-session-summary-signal] {
|
|
117
|
+
position: relative;
|
|
118
|
+
width: 9px;
|
|
119
|
+
height: 9px;
|
|
120
|
+
display: inline-block;
|
|
121
|
+
flex: 0 0 9px;
|
|
122
|
+
color: inherit;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
html[data-dsh-prts] [data-prts-session-summary-signal="ongoing"]::before {
|
|
126
|
+
content: "";
|
|
127
|
+
position: absolute;
|
|
128
|
+
top: 0;
|
|
129
|
+
left: 0;
|
|
130
|
+
width: 2px;
|
|
131
|
+
height: 2px;
|
|
132
|
+
background: currentColor;
|
|
133
|
+
box-shadow:
|
|
134
|
+
3.5px 0 currentColor,
|
|
135
|
+
7px 0 currentColor,
|
|
136
|
+
7px 3.5px currentColor,
|
|
137
|
+
7px 7px currentColor,
|
|
138
|
+
3.5px 7px currentColor,
|
|
139
|
+
0 7px currentColor,
|
|
140
|
+
0 3.5px currentColor;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
html[data-dsh-prts] [data-prts-session-summary-signal="done"]::before {
|
|
144
|
+
content: "";
|
|
145
|
+
position: absolute;
|
|
146
|
+
inset: 1px;
|
|
147
|
+
background: currentColor;
|
|
148
|
+
clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
html[data-dsh-prts] [data-prts-session-summary-popover] {
|
|
152
|
+
position: absolute;
|
|
153
|
+
z-index: 40;
|
|
154
|
+
top: calc(100% + 6px);
|
|
155
|
+
right: 0;
|
|
156
|
+
left: 0;
|
|
157
|
+
width: auto;
|
|
158
|
+
display: block;
|
|
159
|
+
padding: 8px;
|
|
160
|
+
border: 1px solid color-mix(in srgb, currentColor 44%, var(--prts-line));
|
|
161
|
+
background: var(--prts-glass-dense, var(--prts-panel));
|
|
162
|
+
box-shadow: var(--prts-shadow-glass-overlay);
|
|
163
|
+
color: var(--prts-ink);
|
|
164
|
+
opacity: 0;
|
|
165
|
+
visibility: hidden;
|
|
166
|
+
transform: translate3d(0, -4px, 0);
|
|
167
|
+
transition: opacity 120ms ease, transform 160ms var(--prts-ease-snap), visibility 0s linear 160ms;
|
|
168
|
+
pointer-events: none;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
html[data-dsh-prts] [data-prts-session-summary-popover]::before {
|
|
172
|
+
content: "";
|
|
173
|
+
position: absolute;
|
|
174
|
+
right: 0;
|
|
175
|
+
bottom: 100%;
|
|
176
|
+
left: 0;
|
|
177
|
+
/* Cover the space from the vertically centered trigger to the popover. */
|
|
178
|
+
height: 16px;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
html[data-dsh-prts] [data-prts-session-summary-group]:is(:hover, :has(:focus-visible)) [data-prts-session-summary-popover] {
|
|
182
|
+
opacity: 1;
|
|
183
|
+
visibility: visible;
|
|
184
|
+
transform: translate3d(0, 0, 0);
|
|
185
|
+
transition-delay: 0s;
|
|
186
|
+
pointer-events: auto;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
@media (hover: none) {
|
|
190
|
+
html[data-dsh-prts] [data-prts-session-summary-group]:focus-within [data-prts-session-summary-popover] {
|
|
191
|
+
opacity: 1;
|
|
192
|
+
visibility: visible;
|
|
193
|
+
transform: translate3d(0, 0, 0);
|
|
194
|
+
transition-delay: 0s;
|
|
195
|
+
pointer-events: auto;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
html[data-dsh-prts] [data-prts-session-summary-heading] {
|
|
200
|
+
height: 28px;
|
|
201
|
+
display: flex;
|
|
202
|
+
align-items: center;
|
|
203
|
+
justify-content: space-between;
|
|
204
|
+
gap: 12px;
|
|
205
|
+
padding: 0 6px 6px;
|
|
206
|
+
color: currentColor;
|
|
207
|
+
font: 700 10px/1 var(--prts-mono);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
html[data-dsh-prts] [data-prts-session-summary-heading] small {
|
|
211
|
+
color: var(--prts-dim);
|
|
212
|
+
font-size: 9px;
|
|
213
|
+
font-weight: 500;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
html[data-dsh-prts] [data-prts-session-summary-item] {
|
|
217
|
+
width: 100%;
|
|
218
|
+
height: 34px;
|
|
219
|
+
display: flex;
|
|
220
|
+
align-items: center;
|
|
221
|
+
gap: 8px;
|
|
222
|
+
padding: 0 7px;
|
|
223
|
+
border: 0;
|
|
224
|
+
border-top: 1px solid var(--prts-line);
|
|
225
|
+
border-radius: 0;
|
|
226
|
+
background: transparent;
|
|
227
|
+
color: var(--prts-ink);
|
|
228
|
+
cursor: pointer;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
html[data-dsh-prts] [data-prts-session-summary-item] [data-prts-session-summary-signal] {
|
|
232
|
+
color: var(--prts-summary-state-color);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
html[data-dsh-prts] [data-prts-session-summary-item] > span:not([data-prts-session-summary-signal]) {
|
|
236
|
+
min-width: 0;
|
|
237
|
+
overflow: hidden;
|
|
238
|
+
font-size: 11px;
|
|
239
|
+
text-align: left;
|
|
240
|
+
text-overflow: ellipsis;
|
|
241
|
+
white-space: nowrap;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
html[data-dsh-prts] [data-prts-session-summary-item]:is(:hover, :focus-visible) {
|
|
245
|
+
background: color-mix(in srgb, currentColor 9%, transparent);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
@media (prefers-reduced-motion: reduce) {
|
|
249
|
+
html[data-dsh-prts] [data-prts-session-summary-popover] {
|
|
250
|
+
animation: none;
|
|
251
|
+
transition-duration: 0s;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
20
255
|
html[data-dsh-prts] [data-prts-facility-layer="surface"] {
|
|
21
256
|
fill: var(--prts-facility-surface, var(--prts-panel-raised));
|
|
22
257
|
}
|