@ash-qw/dsh-theme-prts 1.0.0 → 1.0.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ash-qw/dsh-theme-prts",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Unofficial Arknights P.R.T.S.-inspired UI theme for DeepSeek Harness Web",
@@ -91,6 +91,8 @@ export function applyPrtsPlugin(ctx, environment) {
91
91
  window,
92
92
  sessions: contextService(ctx, 'sessions'),
93
93
  workspaces: contextService(ctx, 'workspaces'),
94
+ getSessionStatus: () => contextService(ctx, 'uiSession')?.sessionStatus,
95
+ getUiWorkspace: () => contextService(ctx, 'uiWorkspace'),
94
96
  })
95
97
  const floatingGlass = createFloatingGlassAdapter({ document, window })
96
98
  const adapter = createRc7Adapter({ document })
@@ -30,6 +30,11 @@ const OWNED_ATTRIBUTES = [
30
30
  ]
31
31
 
32
32
  const SESSION_STATES = Object.freeze(['warning', 'ongoing', 'error', 'done'])
33
+ const PENDING_LABELS = Object.freeze({
34
+ approval: '待授权',
35
+ 'plan-review': '待审阅',
36
+ question: '待回答',
37
+ })
33
38
 
34
39
  function snapshotOf(source) {
35
40
  try {
@@ -62,17 +67,27 @@ function resolveSectionHeader(root) {
62
67
  return {}
63
68
  }
64
69
 
65
- function visibleSessionSummaries(sessions, workspaces) {
70
+ function visibleSessionSummaries(sessions, workspaces, sessionStatus) {
66
71
  const list = snapshotOf(sessions?.list)
67
72
  if (!list?.byId) return []
73
+ const statuses = snapshotOf(sessionStatus)
68
74
  const workspaceList = snapshotOf(workspaces?.list)
69
75
  const archived = new Set(workspaceList?.archivedSessionIds ?? [])
70
76
  const ids = Array.isArray(list.ids) ? list.ids : Object.keys(list.byId)
71
77
  return ids.flatMap(id => {
72
78
  const summary = list.byId[id]
73
79
  if (!summary || summary.blank || summary.origin === 'subagent' || archived.has(id)) return []
74
- if (summary.running) return [{ id, title: summary.displayTitle || id, state: 'ongoing' }]
75
- if (summary.completed === true) return [{ id, title: summary.displayTitle || id, state: 'done' }]
80
+ const status = statuses?.get?.(id)
81
+ const pendingLabel = PENDING_LABELS[status?.pendingInteraction?.kind]
82
+ if (pendingLabel) return [{ id, title: summary.displayTitle || id, state: 'warning', statusLabel: pendingLabel }]
83
+ const runningSubagent = list.projectionsBySession?.[id]?.values?.subagentCatalog?.some(child =>
84
+ (statuses?.get?.(child.id)?.running ?? list.byId[child.id]?.running) === true)
85
+ if ((status?.running ?? summary.running) || runningSubagent) {
86
+ return [{ id, title: summary.displayTitle || id, state: 'ongoing' }]
87
+ }
88
+ if (status ? status.completionUnread === true : summary.completed === true) {
89
+ return [{ id, title: summary.displayTitle || id, state: 'done' }]
90
+ }
76
91
  return []
77
92
  })
78
93
  }
@@ -139,11 +154,11 @@ function resolveSessionTime(row, title, actions) {
139
154
  && node !== actions
140
155
  && !node.hasAttribute('data-prts-facility-face')
141
156
  && !node.hasAttribute('data-prts-row-projection')
142
- && node.getAttribute('aria-hidden') !== 'true'
143
157
  && !node.querySelector('button')
144
158
  && node.textContent.trim()
145
159
  ))
146
- return candidates.find(node => node.matches('[class*="time" i], time, [data-session-time]')) ?? candidates.at(-1)
160
+ return candidates.find(node => node.matches('[class*="time" i], time, [data-session-time]'))
161
+ ?? candidates.filter(node => node.getAttribute('aria-hidden') !== 'true' && !node.querySelector('[data-state]')).at(-1)
147
162
  }
148
163
 
149
164
  function ensureFacilityFace(document, row, kind, next) {
@@ -174,7 +189,7 @@ function ensureFacilitySpine(document, row, kind, next) {
174
189
  }
175
190
 
176
191
 
177
- export function createSidebarControlAdapter({ document, window, sessions, workspaces }) {
192
+ export function createSidebarControlAdapter({ document, window, sessions, workspaces, getSessionStatus, getUiWorkspace }) {
178
193
  let observer
179
194
  let resizeObserver
180
195
  let resizeFrame
@@ -182,6 +197,8 @@ export function createSidebarControlAdapter({ document, window, sessions, worksp
182
197
  let scanFrame
183
198
  let stopSessionList = () => {}
184
199
  let stopWorkspaceList = () => {}
200
+ let stopSessionStatus = () => {}
201
+ let sessionStatus
185
202
  let statusSourcesStarted = false
186
203
  let summaryRoot
187
204
  let summarySignature = ''
@@ -191,15 +208,30 @@ export function createSidebarControlAdapter({ document, window, sessions, worksp
191
208
  const vectorSizes = new WeakMap()
192
209
  const lifelineAnimator = createSessionLifelineAnimator({ document, window })
193
210
 
211
+ function connectSessionStatus() {
212
+ const source = getSessionStatus?.()
213
+ if (source === sessionStatus) return
214
+ stopSessionStatus()
215
+ sessionStatus = source
216
+ stopSessionStatus = source?.subscribe?.(queueScan) ?? (() => {})
217
+ }
218
+
194
219
  function openSession(sessionId) {
195
220
  try {
221
+ const uiWorkspace = getUiWorkspace?.()
222
+ if (typeof uiWorkspace?.openSession === 'function') {
223
+ uiWorkspace.openSession(sessionId)
224
+ return
225
+ }
196
226
  if (typeof sessions?.open === 'function') {
197
227
  sessions.open(sessionId)
198
228
  return
199
229
  }
200
230
  } catch {}
201
231
  const row = Array.from(document.querySelectorAll('[data-prts-session-row]')).find(candidate => (
202
- candidate.querySelector('[data-prts-row-title]')?.textContent.trim() === sessionId
232
+ candidate.getAttribute('data-row-key') === `session:${sessionId}`
233
+ || candidate.getAttribute('data-session-id') === sessionId
234
+ || candidate.querySelector('[data-prts-row-title]')?.textContent.trim() === sessionId
203
235
  ))
204
236
  row?.click?.()
205
237
  }
@@ -247,6 +279,13 @@ export function createSidebarControlAdapter({ document, window, sessions, worksp
247
279
  const title = document.createElement('span')
248
280
  title.textContent = item.title
249
281
  button.append(title)
282
+ if (item.statusLabel) {
283
+ const detail = document.createElement('small')
284
+ detail.setAttribute('data-prts-session-summary-item-status', '')
285
+ detail.textContent = item.statusLabel
286
+ button.append(detail)
287
+ button.setAttribute('aria-label', `${item.title},${item.statusLabel}`)
288
+ }
250
289
  button.addEventListener('click', event => {
251
290
  event.stopPropagation()
252
291
  openSession(item.id)
@@ -269,16 +308,30 @@ export function createSidebarControlAdapter({ document, window, sessions, worksp
269
308
  label.setAttribute('data-prts-session-summary-label', '')
270
309
  next.add(header)
271
310
  next.add(label)
272
- const fromService = visibleSessionSummaries(sessions, workspaces)
273
- const rows = fromService.length ? fromService : Array.from(root.querySelectorAll('[data-prts-session-row]')).flatMap(row => {
311
+ const fromService = visibleSessionSummaries(sessions, workspaces, sessionStatus)
312
+ const nativeRows = Array.from(root.querySelectorAll('[data-prts-session-row]')).flatMap(row => {
274
313
  const state = row.getAttribute('data-prts-session-state')
275
- if (state !== 'ongoing' && state !== 'done') return []
314
+ if (state !== 'warning' && state !== 'ongoing' && state !== 'done') return []
276
315
  const title = row.querySelector('[data-prts-row-title]')?.textContent.trim()
277
- return title ? [{ id: title, title, state }] : []
316
+ const rowKey = row.getAttribute('data-row-key')
317
+ const id = row.getAttribute('data-session-id') || (rowKey?.startsWith('session:') ? rowKey.slice(8) : title)
318
+ return title ? [{ id, title, state }] : []
278
319
  })
320
+ const rows = [...fromService]
321
+ const statePriority = { done: 1, ongoing: 2, warning: 3 }
322
+ for (const nativeRow of nativeRows) {
323
+ const index = rows.findIndex(item => item.id === nativeRow.id || item.title === nativeRow.title)
324
+ if (index === -1) {
325
+ rows.push(nativeRow)
326
+ } else if (statePriority[nativeRow.state] > statePriority[rows[index].state]) {
327
+ rows[index] = { ...rows[index], state: nativeRow.state }
328
+ }
329
+ }
330
+ const warning = rows.filter(item => item.state === 'warning')
279
331
  const ongoing = rows.filter(item => item.state === 'ongoing')
280
332
  const done = rows.filter(item => item.state === 'done')
281
333
  const signature = JSON.stringify({
334
+ warning: warning.map(item => [item.id, item.title, item.statusLabel]),
282
335
  ongoing: ongoing.map(item => [item.id, item.title]),
283
336
  done: done.map(item => [item.id, item.title]),
284
337
  })
@@ -294,10 +347,11 @@ export function createSidebarControlAdapter({ document, window, sessions, worksp
294
347
  }
295
348
  const labelHidden = Array.from(label.classList).some(name => /hidden/i.test(name))
296
349
  summaryRoot.toggleAttribute('data-prts-summary-search-hidden', labelHidden)
297
- summaryRoot.hidden = ongoing.length === 0 && done.length === 0
350
+ summaryRoot.hidden = warning.length === 0 && ongoing.length === 0 && done.length === 0
298
351
  if (signature === summarySignature) return
299
352
  summarySignature = signature
300
353
  summaryRoot.replaceChildren()
354
+ if (warning.length) summaryRoot.append(buildSummaryGroup('warning', '等待处理的会话', warning))
301
355
  if (ongoing.length) summaryRoot.append(buildSummaryGroup('ongoing', '运行中的会话', ongoing))
302
356
  if (done.length) summaryRoot.append(buildSummaryGroup('done', '完成未查看', done))
303
357
  }
@@ -366,6 +420,7 @@ export function createSidebarControlAdapter({ document, window, sessions, worksp
366
420
  }
367
421
 
368
422
  function scan() {
423
+ connectSessionStatus()
369
424
  const root = document?.querySelector?.('[data-prts-region="sessions"] [data-slot="sidebar.workspaces"]')
370
425
  const next = new Set()
371
426
  const nextVectorOwners = new Set()
@@ -509,6 +564,9 @@ export function createSidebarControlAdapter({ document, window, sessions, worksp
509
564
  stopSessionList = () => {}
510
565
  stopWorkspaceList()
511
566
  stopWorkspaceList = () => {}
567
+ stopSessionStatus()
568
+ stopSessionStatus = () => {}
569
+ sessionStatus = undefined
512
570
  statusSourcesStarted = false
513
571
  if (scanFrame !== undefined) {
514
572
  if (window?.cancelAnimationFrame) window.cancelAnimationFrame(scanFrame)
@@ -104,6 +104,11 @@ html[data-dsh-prts] [data-prts-session-summary-group="ongoing"] {
104
104
  color: var(--prts-summary-state-color);
105
105
  }
106
106
 
107
+ html[data-dsh-prts] [data-prts-session-summary-group="warning"] {
108
+ --prts-summary-state-color: var(--dsw-alias-state-warn-primary, var(--prts-yellow));
109
+ color: var(--prts-summary-state-color);
110
+ }
111
+
107
112
  html[data-dsh-prts] [data-prts-session-summary-group="done"] {
108
113
  --prts-summary-state-color: var(--dsw-alias-state-success-primary, var(--prts-green));
109
114
  color: var(--prts-summary-state-color);
@@ -148,6 +153,24 @@ html[data-dsh-prts] [data-prts-session-summary-signal="done"]::before {
148
153
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
149
154
  }
150
155
 
156
+ html[data-dsh-prts] [data-prts-session-summary-signal="warning"]::before {
157
+ content: "";
158
+ position: absolute;
159
+ inset: 0;
160
+ border: 1px solid currentColor;
161
+ }
162
+
163
+ html[data-dsh-prts] [data-prts-session-summary-signal="warning"]::after {
164
+ content: "";
165
+ position: absolute;
166
+ top: 2px;
167
+ left: 4px;
168
+ width: 1px;
169
+ height: 3px;
170
+ background: currentColor;
171
+ box-shadow: 0 3px currentColor;
172
+ }
173
+
151
174
  html[data-dsh-prts] [data-prts-session-summary-popover] {
152
175
  position: absolute;
153
176
  z-index: 40;
@@ -241,6 +264,13 @@ html[data-dsh-prts] [data-prts-session-summary-item] > span:not([data-prts-sessi
241
264
  white-space: nowrap;
242
265
  }
243
266
 
267
+ html[data-dsh-prts] [data-prts-session-summary-item-status] {
268
+ flex: 0 0 auto;
269
+ margin-left: auto;
270
+ color: var(--prts-summary-state-color);
271
+ font: 700 9px/1 var(--prts-mono);
272
+ }
273
+
244
274
  html[data-dsh-prts] [data-prts-session-summary-item]:is(:hover, :focus-visible) {
245
275
  background: color-mix(in srgb, currentColor 9%, transparent);
246
276
  }
@@ -976,6 +976,10 @@ html[data-dsh-prts] [data-prts-row-title] {
976
976
  white-space: nowrap;
977
977
  }
978
978
 
979
+ html[data-dsh-prts] [data-prts-session-row] > span:has(> [data-state]) {
980
+ padding-left: 0 !important;
981
+ }
982
+
979
983
  html[data-dsh-prts] [data-prts-session-row] [data-prts-row-title]::before {
980
984
  content: attr(data-prts-session-index);
981
985
  margin-right: 8px;