@ash-qw/dsh-theme-prts 1.4.1 → 1.4.2

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.4.1",
3
+ "version": "1.4.2",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Unofficial Arknights P.R.T.S.-inspired UI theme for DeepSeek Harness Web and the official desktop client",
@@ -96,11 +96,10 @@ export function applyPrtsPlugin(ctx, environment) {
96
96
  const sidebarControls = createSidebarControlAdapter({
97
97
  document,
98
98
  window,
99
- sessions: contextService(ctx, 'sessions'),
100
- workspaces: contextService(ctx, 'workspaces'),
101
99
  getSessionStatus: () => contextService(ctx, 'uiSession')?.sessionStatus,
102
100
  getUiWorkspace: () => contextService(ctx, 'uiWorkspace'),
103
101
  getSessions: () => contextService(ctx, 'sessions'),
102
+ getWorkspaces: () => contextService(ctx, 'workspaces'),
104
103
  getUiConversation: () => contextService(ctx, 'uiConversation'),
105
104
  })
106
105
  const floatingGlass = createFloatingGlassAdapter({ document, window })
@@ -138,7 +138,7 @@ export function createSessionLifelineAnimator({ document, window }) {
138
138
  return Boolean(
139
139
  row?.isConnected
140
140
  && lifeline?.isConnected
141
- && row.getAttribute('aria-selected') === 'true'
141
+ && (row.hasAttribute('data-prts-selection-target') ? row.getAttribute('data-prts-selection-target') === 'true' : row.getAttribute('aria-selected') === 'true')
142
142
  && document?.visibilityState !== 'hidden'
143
143
  && document?.documentElement?.dataset?.prtsSessionFlow !== 'off'
144
144
  && document?.documentElement?.dataset?.prtsMotion !== 'reduced'
@@ -91,7 +91,25 @@ function previewStatusFields(status) {
91
91
  const pending = status?.pendingInteraction
92
92
  return [status?.running, status?.completionUnread, pending, pending?.answer,
93
93
  JSON.stringify([pending?.kind, pending?.answerable, pending?.getSnapshot?.().closed,
94
- pending?.questions, pending?.toolName, pending?.reason, pending?.displayReason])]
94
+ pending?.questions, pending?.callId, pending?.toolName, pending?.reason, pending?.displayReason])]
95
+ }
96
+
97
+ export function approvalCallDetails(snapshot, pending) {
98
+ if (!pending?.callId) return undefined
99
+ for (const node of snapshot?.nodes?.values?.() ?? []) {
100
+ const root = node.kind === 'tool-call' ? node.data?.root : undefined
101
+ if (!root || root.callId !== pending.callId || 'kind' in root || root.phase !== 'start') continue
102
+ const raw = root.argsRaw
103
+ if (typeof raw !== 'string') return undefined
104
+ try {
105
+ const args = JSON.parse(raw)
106
+ if (args && typeof args === 'object' && !Array.isArray(args) && typeof args.command === 'string') {
107
+ const {command, ...rest} = args
108
+ return {command, parameters: Object.keys(rest).length ? JSON.stringify(rest, null, 2) : ''}
109
+ }
110
+ return {parameters: JSON.stringify(args, null, 2)}
111
+ } catch { return {parameters: raw} }
112
+ }
95
113
  }
96
114
 
97
115
  export function createSessionSummaryPreview({ document, window, sessions, getSessions, getUiConversation, getSessionStatus, openSession, getPreviewPreferences, onPreviewClosed, onAnswerSubmitted }) {
@@ -211,6 +229,73 @@ export function createSessionSummaryPreview({ document, window, sessions, getSes
211
229
  panel.ownerDocument.body.append(panel)
212
230
  current.detached = true
213
231
  panel.showPopover?.()
232
+ let positionFrame
233
+ const positionPanels = () => {
234
+ positionFrame = undefined
235
+ if (active !== current) return
236
+ const viewport = window.visualViewport
237
+ const left = (viewport?.offsetLeft ?? 0) + 8, top = (viewport?.offsetTop ?? 0) + 8
238
+ const width = Math.max(1, (viewport?.width ?? window.innerWidth) - 16)
239
+ const height = Math.max(1, (viewport?.height ?? window.innerHeight) - 16)
240
+ const place = (surface, reference, preferredTop, noticeOwner, anchorBottom = preferredTop) => {
241
+ surface.style.maxWidth = 'none'
242
+ surface.style.maxHeight = 'none'
243
+ const bounds = surface.getBoundingClientRect()
244
+ const x = reference ? reference.right + 10 : left
245
+ const fitsVertically = y => y >= top && y + bounds.height <= top + height
246
+ const above = anchorBottom - bounds.height
247
+ const y = !fitsVertically(preferredTop) && fitsVertically(above) ? above : preferredTop
248
+ const insufficient = Boolean(reference) && (x < left || x + bounds.width > left + width || !fitsVertically(y))
249
+ surface.toggleAttribute('data-prts-preview-no-space', insufficient)
250
+ const noticeKey = surface === panel ? 'spaceNotice' : 'detailSpaceNotice'
251
+ if (insufficient && noticeOwner) {
252
+ let notice = current[noticeKey]
253
+ if (!notice) {
254
+ notice = panel.ownerDocument.createElement('div')
255
+ notice.setAttribute('data-prts-preview-space-notice', '')
256
+ notice.setAttribute('role', 'status')
257
+ const guidance = '空间不足,无法展开预览。可点击会话进入;建议扩大窗口,或在辅助功能中关闭对应预览。'
258
+ notice.textContent = surface === panel ? '空间不足,无法展开正文预览' : guidance
259
+ notice.title = guidance
260
+ current[noticeKey] = notice
261
+ }
262
+ if (notice.parentElement !== noticeOwner) noticeOwner.append(notice)
263
+ } else { current[noticeKey]?.remove(); current[noticeKey] = undefined }
264
+ surface.style.left = x + 'px'
265
+ surface.style.top = y + 'px'
266
+ return !insufficient
267
+ }
268
+ const menu = current.anchor?.closest('[data-prts-session-summary-popover]')
269
+ const reference = menu?.getBoundingClientRect()
270
+ const row = current.anchor?.getBoundingClientRect()
271
+ const visible = place(panel, reference, row?.top ?? (parseFloat(panel.style.top) || top), menu, row?.bottom)
272
+ if (current.detail && !current.detail.hidden) {
273
+ if (visible) place(current.detail, panel.getBoundingClientRect(), panel.getBoundingClientRect().top, panel, panel.getBoundingClientRect().bottom)
274
+ else current.detail.setAttribute('data-prts-preview-no-space', '')
275
+ }
276
+
277
+ }
278
+ const schedulePosition = () => {
279
+ if (positionFrame === undefined) positionFrame = window.requestAnimationFrame(positionPanels)
280
+ }
281
+ const positionObserver = window.ResizeObserver ? new window.ResizeObserver(schedulePosition) : undefined
282
+ positionObserver?.observe(panel)
283
+ window.addEventListener('resize', schedulePosition)
284
+ window.visualViewport?.addEventListener('resize', schedulePosition)
285
+ window.visualViewport?.addEventListener('scroll', schedulePosition)
286
+ const cleanupHover = current.cleanupHover
287
+ current.cleanupHover = () => {
288
+ cleanupHover()
289
+ current.spaceNotice?.remove()
290
+ current.detailSpaceNotice?.remove()
291
+ panel.removeAttribute('data-prts-preview-no-space')
292
+ positionObserver?.disconnect()
293
+ window.cancelAnimationFrame(positionFrame)
294
+ window.removeEventListener('resize', schedulePosition)
295
+ window.visualViewport?.removeEventListener('resize', schedulePosition)
296
+ window.visualViewport?.removeEventListener('scroll', schedulePosition)
297
+ }
298
+ schedulePosition()
214
299
  const label = panel.querySelector('strong')
215
300
  let sessionTitle = panel.querySelector('[data-prts-preview-session-title]')
216
301
  if (!sessionTitle) {
@@ -248,6 +333,7 @@ export function createSessionSummaryPreview({ document, window, sessions, getSes
248
333
  const pending = pendingNow()
249
334
  if (!canAnswerPreview(pending)) return
250
335
  current.editor = true
336
+ current.editingPending = pending
251
337
  // Detach from the hover menu before promoting again to the top layer.
252
338
  panel.setAttribute('data-prts-preview-editing', '')
253
339
  panel.setAttribute('role', 'dialog')
@@ -358,6 +444,8 @@ export function createSessionSummaryPreview({ document, window, sessions, getSes
358
444
  const hideDetail = () => {
359
445
  current.detail?.hidePopover?.()
360
446
  if (current.detail) current.detail.hidden = true
447
+ current.detailSpaceNotice?.remove()
448
+ current.detailSpaceNotice = undefined
361
449
  detailKey = undefined
362
450
  detailEntry?.removeAttribute('data-prts-reply-active')
363
451
  detailEntry = undefined
@@ -377,6 +465,7 @@ export function createSessionSummaryPreview({ document, window, sessions, getSes
377
465
  detail.append(heading, content)
378
466
  panel.append(detail)
379
467
  current.detail = detail
468
+ positionObserver?.observe(detail)
380
469
  }
381
470
  const detail = current.detail
382
471
  detailKey = reply.key
@@ -395,14 +484,38 @@ export function createSessionSummaryPreview({ document, window, sessions, getSes
395
484
  detail.style.left = (beside ? rect.right + 10 : Math.max(8, Math.min(rect.left, window.innerWidth - width - 8))) + 'px'
396
485
  detail.style.top = Math.max(8, Math.min(beside ? rect.top : rect.bottom + 8, window.innerHeight - 240)) + 'px'
397
486
  detail.showPopover?.()
487
+ schedulePosition()
488
+ }
489
+ const renderApprovalDetails = pending => {
490
+ if (pending?.kind !== 'approval') return
491
+ const details = approvalCallDetails(source?.getSnapshot(), pending)
492
+ let section = body.querySelector('[data-prts-approval-details]')
493
+ const signature = JSON.stringify(details ?? null)
494
+ if (section?.dataset.signature === signature) return
495
+ if (!section) { section = document.createElement('div'); section.setAttribute('data-prts-approval-details', ''); body.append(section) }
496
+ section.dataset.signature = signature
497
+ section.replaceChildren()
498
+ if (!details) {
499
+ section.textContent = source ? '暂无可用的操作参数,请进入会话核对详情。' : '正在读取操作详情…'
500
+ return
501
+ }
502
+ for (const [name, value] of [['命令', details.command], ['参数', details.parameters]]) {
503
+ if (!value) continue
504
+ const heading = document.createElement('div')
505
+ heading.textContent = name
506
+ const code = document.createElement('pre')
507
+ code.textContent = value
508
+ section.append(heading, code)
509
+ }
398
510
  }
399
511
  const render = () => {
400
512
  if (active !== current) return
401
- if (current.editor) { current.validate?.(); return }
513
+ if (current.editor) { current.validate?.(); if (pendingNow() === current.editingPending) renderApprovalDetails(current.editingPending); return }
402
514
  const status = getSessionStatus?.()?.getSnapshot().get(id)
403
515
  const pending = status?.pendingInteraction === current.answeredPending
404
516
  ? undefined : status?.pendingInteraction
405
517
  const approval = pending?.kind === 'approval'
518
+ panel.toggleAttribute('data-prts-preview-approval', approval)
406
519
  const question = approval || pending?.kind === 'question' || pending?.kind === 'plan-review'
407
520
  const state = question ? 'warning' : status?.running || (current.answeredPending && status?.pendingInteraction === current.answeredPending) ? 'ongoing'
408
521
  : status?.completionUnread || status?.running === false ? 'done' : current.group?.getAttribute('data-prts-session-summary-group') || ''
@@ -506,6 +619,7 @@ export function createSessionSummaryPreview({ document, window, sessions, getSes
506
619
  previousQuestion = question
507
620
  if (!extendsReply) body.scrollTop = 0
508
621
  }
622
+ if (approval) renderApprovalDetails(pending)
509
623
  if (canAnswerPreview(pending)) enableAnswer()
510
624
  }
511
625
  let statusFields = previewStatusFields(getSessionStatus?.()?.getSnapshot().get(id))