@ash-qw/dsh-theme-prts 0.1.93 → 0.1.94

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": "0.1.93",
3
+ "version": "0.1.94",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Unofficial Arknights P.R.T.S.-inspired UI theme for DeepSeek Harness Web",
@@ -107,7 +107,8 @@ const PREVIEW_TOUCH_DURATION = 1200
107
107
  const SNAP_DELAY = 110
108
108
  const TOUCH_WAVE_DURATION = 620
109
109
  const HISTORY_PAGE_SETTLE_LIMIT = 4000
110
- const GEOMETRY_SETTLE_FRAME_LIMIT = 24
110
+ const GEOMETRY_SETTLE_FRAME_LIMIT = 8
111
+ const GEOMETRY_SETTLE_STABLE_FRAMES = 2
111
112
  let scaleInstanceSeed = 0
112
113
 
113
114
  function clamp(value, minimum, maximum) {
@@ -767,6 +768,7 @@ export function createConversationScaleAdapter({ document, window, sessions }) {
767
768
  let geometryDirty = true
768
769
  let geometrySettleFrame
769
770
  let geometrySettleFramesRemaining = 0
771
+ let geometrySettleStableFrames = 0
770
772
  let geometrySettleSignature
771
773
  let observedOperationWidth
772
774
  let observedOperationHeight
@@ -789,6 +791,7 @@ export function createConversationScaleAdapter({ document, window, sessions }) {
789
791
  const currentTime = timestamp => Number.isFinite(timestamp)
790
792
  ? timestamp
791
793
  : window?.performance?.now?.() ?? Date.now()
794
+ const phoneScaleHidden = () => Number(window?.innerWidth) <= 640
792
795
 
793
796
  function currentGeometrySignature() {
794
797
  if (!operation || !scroller) return undefined
@@ -817,11 +820,20 @@ export function createConversationScaleAdapter({ document, window, sessions }) {
817
820
  if (!started || failed || geometrySettleFramesRemaining <= 0) return
818
821
  geometrySettleFramesRemaining -= 1
819
822
  const signature = currentGeometrySignature()
820
- if (signature !== undefined && signature !== geometrySettleSignature) {
823
+ if (signature === undefined) {
824
+ geometrySettleStableFrames = 0
825
+ } else if (signature !== geometrySettleSignature) {
821
826
  geometrySettleSignature = signature
827
+ geometrySettleStableFrames = 1
822
828
  geometryDirty = true
823
829
  obstructionDirty = true
824
830
  schedule()
831
+ } else {
832
+ geometrySettleStableFrames += 1
833
+ }
834
+ if (geometrySettleStableFrames >= GEOMETRY_SETTLE_STABLE_FRAMES) {
835
+ geometrySettleFramesRemaining = 0
836
+ return
825
837
  }
826
838
  if (geometrySettleFramesRemaining > 0) {
827
839
  geometrySettleFrame = requestFrame(sampleGeometryStabilization)
@@ -829,8 +841,9 @@ export function createConversationScaleAdapter({ document, window, sessions }) {
829
841
  }
830
842
 
831
843
  function requestGeometryStabilization() {
832
- if (!started || failed) return
844
+ if (!started || failed || phoneScaleHidden()) return
833
845
  geometrySettleFramesRemaining = GEOMETRY_SETTLE_FRAME_LIMIT
846
+ geometrySettleStableFrames = 0
834
847
  if (geometrySettleFrame === undefined) {
835
848
  geometrySettleFrame = requestFrame(sampleGeometryStabilization)
836
849
  }
@@ -840,11 +853,17 @@ export function createConversationScaleAdapter({ document, window, sessions }) {
840
853
  cancelFrame(geometrySettleFrame)
841
854
  geometrySettleFrame = undefined
842
855
  geometrySettleFramesRemaining = 0
856
+ geometrySettleStableFrames = 0
843
857
  geometrySettleSignature = undefined
844
858
  }
845
859
 
846
860
  function onHostGeometryChange() {
861
+ if (phoneScaleHidden()) {
862
+ stopGeometryStabilization()
863
+ return
864
+ }
847
865
  requestGeometryStabilization()
866
+ schedule(true)
848
867
  }
849
868
 
850
869
  function reducedMotion() {
@@ -1058,7 +1077,6 @@ export function createConversationScaleAdapter({ document, window, sessions }) {
1058
1077
  scanPending = true
1059
1078
  fullScanPending = true
1060
1079
  }
1061
- if (resizeActive) return
1062
1080
  if (frame !== undefined || failed) return
1063
1081
  frame = requestFrame(flush)
1064
1082
  }
@@ -2200,6 +2218,13 @@ export function createConversationScaleAdapter({ document, window, sessions }) {
2200
2218
  if (typeof ResizeObserver === 'function') {
2201
2219
  resizeObserver = new ResizeObserver((entries = []) => {
2202
2220
  geometryDirty = true
2221
+ if (phoneScaleHidden()) {
2222
+ stopGeometryStabilization()
2223
+ if (resizeSettleTimer !== undefined) window?.clearTimeout?.(resizeSettleTimer)
2224
+ resizeSettleTimer = undefined
2225
+ setScaleResizeState(false)
2226
+ return
2227
+ }
2203
2228
  requestGeometryStabilization()
2204
2229
  const operationEntry = entries.find(entry => entry.target === operation)
2205
2230
  const width = Number(operationEntry?.contentRect?.width)
@@ -2219,12 +2244,8 @@ export function createConversationScaleAdapter({ document, window, sessions }) {
2219
2244
  return
2220
2245
  }
2221
2246
  if (!resizeActive) {
2247
+ schedule()
2222
2248
  setScaleResizeState(true)
2223
- if (frame !== undefined) {
2224
- if (window?.cancelAnimationFrame) window.cancelAnimationFrame(frame)
2225
- else window?.clearTimeout?.(frame)
2226
- frame = undefined
2227
- }
2228
2249
  }
2229
2250
  if (resizeSettleTimer !== undefined) window?.clearTimeout?.(resizeSettleTimer)
2230
2251
  resizeSettleTimer = window?.setTimeout?.(() => {
@@ -2241,7 +2262,13 @@ export function createConversationScaleAdapter({ document, window, sessions }) {
2241
2262
  const external = mutations.filter(mutation =>
2242
2263
  !mutation.target?.closest?.('[data-prts-conversation-scale], [data-prts-conversation-preview], [data-prts-conversation-history-hint], [data-prts-conversation-history-status]')
2243
2264
  )
2244
- if (external.length) requestGeometryStabilization()
2265
+ if (phoneScaleHidden()) return
2266
+ if (external.some(mutation =>
2267
+ operation?.contains?.(mutation.target)
2268
+ || mutation.target?.contains?.(operation)
2269
+ || mutation.target?.matches?.('header, [data-host-header], [data-slot*="header" i]')
2270
+ || mutationAffectsOverlay(mutation)
2271
+ )) requestGeometryStabilization()
2245
2272
  const historyRelevant = external.some(mutation =>
2246
2273
  mutation.type === 'childList'
2247
2274
  && operation?.contains?.(mutation.target)
@@ -1,6 +1,12 @@
1
1
  import { createThemeSettingsOverlay, themeSettingsMarkup } from './theme-settings-workbench.js'
2
2
  import { createResizeShieldAdapter } from './resize-shield-adapter.js'
3
3
 
4
+ const PHONE_RAIL_BREAKPOINT = 640
5
+ const RAIL_HOLD_DELAY_MS = 350
6
+ const RAIL_HOLD_MOVE_TOLERANCE = 8
7
+ const RAIL_SWIPE_THRESHOLD = 32
8
+ const RAIL_SWIPE_AXIS_RATIO = 1.25
9
+
4
10
  function setText(root, selector, value) {
5
11
  const node = root?.querySelector(selector)
6
12
  const next = String(value ?? '')
@@ -9,7 +15,7 @@ function setText(root, selector, value) {
9
15
 
10
16
  function markup(emblem) {
11
17
  return `<div data-prts-shell data-plugin="dsh-theme-prts">
12
- <button type="button" data-prts-rail-launcher aria-label="打开 P.R.T.S. 导航" aria-controls="prts-nav-rail" aria-expanded="false"><span aria-hidden="true">P.R.T.S.</span></button>
18
+ <button type="button" data-prts-rail-launcher aria-label="打开 P.R.T.S. 导航" aria-controls="prts-nav-rail" aria-expanded="false"><span aria-hidden="true">P.R.T.S.</span><i data-prts-rail-gesture-cue aria-hidden="true">⌄<br>⌄</i></button>
13
19
  <nav id="prts-nav-rail" data-prts-nav-rail aria-label="P.R.T.S. 导航">
14
20
  <button type="button" data-prts-rail-brand aria-label="P.R.T.S. 终端设置" aria-controls="prts-theme-settings" aria-expanded="false">${emblem || ''}<span>P.R.T.S.</span></button>
15
21
  <div data-prts-nav-bottom>
@@ -52,6 +58,10 @@ export function createOperationsShell({
52
58
  let railViewport
53
59
  let railDefaultHidden = false
54
60
  let railViewportQuery
61
+ let railPointerQuery
62
+ let railHoverQuery
63
+ let railPointer
64
+ let railHoldTimer
55
65
  let settingsOverlay
56
66
  let schemeToggle
57
67
  let schemeIntent
@@ -72,6 +82,89 @@ export function createOperationsShell({
72
82
  try { return window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches === true } catch { return false }
73
83
  }
74
84
 
85
+ function phoneRailGesture() {
86
+ if (railMode !== 'overlay' || Number(window.innerWidth) > PHONE_RAIL_BREAKPOINT) return false
87
+ return railPointerQuery?.matches === true
88
+ || railHoverQuery?.matches === true
89
+ || Number(window.navigator?.maxTouchPoints) > 0
90
+ }
91
+
92
+ function clearRailGesture({ release = true } = {}) {
93
+ if (railHoldTimer !== undefined) window.clearTimeout(railHoldTimer)
94
+ railHoldTimer = undefined
95
+ const pointer = railPointer
96
+ railPointer = undefined
97
+ if (release && pointer) {
98
+ try { railLauncher?.releasePointerCapture?.(pointer.pointerId) } catch {}
99
+ }
100
+ railLauncher?.removeAttribute('data-prts-rail-gesture-active')
101
+ railLauncher?.removeAttribute('data-prts-rail-gesture-ready')
102
+ railLauncher?.style?.removeProperty('--prts-rail-gesture-progress')
103
+ }
104
+
105
+ function armRailGesture() {
106
+ railHoldTimer = undefined
107
+ if (!railPointer || !phoneRailGesture()) return
108
+ railPointer.armed = true
109
+ railLauncher?.setAttribute('data-prts-rail-gesture-active', '')
110
+ }
111
+
112
+ function onRailPointerDown(event) {
113
+ if (!phoneRailGesture() || railPointer || (event.button ?? 0) !== 0) return
114
+ const pointerId = event.pointerId ?? 1
115
+ railPointer = {
116
+ pointerId,
117
+ startX: Number(event.clientX) || 0,
118
+ startY: Number(event.clientY) || 0,
119
+ armed: false,
120
+ ready: false,
121
+ }
122
+ try { railLauncher?.setPointerCapture?.(pointerId) } catch {}
123
+ railHoldTimer = window.setTimeout(armRailGesture, RAIL_HOLD_DELAY_MS)
124
+ }
125
+
126
+ function onRailPointerMove(event) {
127
+ const pointer = railPointer
128
+ if (!pointer || (event.pointerId ?? 1) !== pointer.pointerId) return
129
+ const deltaX = (Number(event.clientX) || 0) - pointer.startX
130
+ const deltaY = (Number(event.clientY) || 0) - pointer.startY
131
+ if (!pointer.armed) {
132
+ if (Math.hypot(deltaX, deltaY) > RAIL_HOLD_MOVE_TOLERANCE) clearRailGesture()
133
+ return
134
+ }
135
+ event.preventDefault?.()
136
+ if (Math.abs(deltaX) > Math.max(RAIL_HOLD_MOVE_TOLERANCE, Math.max(0, deltaY) / RAIL_SWIPE_AXIS_RATIO)) {
137
+ clearRailGesture()
138
+ return
139
+ }
140
+ const progress = Math.min(1, Math.max(0, deltaY) / RAIL_SWIPE_THRESHOLD)
141
+ pointer.ready = deltaY >= RAIL_SWIPE_THRESHOLD
142
+ && deltaY >= Math.abs(deltaX) * RAIL_SWIPE_AXIS_RATIO
143
+ railLauncher?.style?.setProperty('--prts-rail-gesture-progress', Math.round(progress * 12) + 'px')
144
+ railLauncher?.toggleAttribute('data-prts-rail-gesture-ready', pointer.ready)
145
+ }
146
+
147
+ function onRailPointerUp(event) {
148
+ const pointer = railPointer
149
+ if (!pointer || (event.pointerId ?? 1) !== pointer.pointerId) return
150
+ const shouldOpen = pointer.armed && pointer.ready
151
+ clearRailGesture()
152
+ if (shouldOpen) openRail()
153
+ }
154
+
155
+ function onRailLostPointerCapture(event) {
156
+ if (!railPointer || (event.pointerId ?? 1) !== railPointer.pointerId) return
157
+ clearRailGesture({ release: false })
158
+ }
159
+
160
+ function onRailClick(event) {
161
+ if (phoneRailGesture() && Number(event?.detail) > 0) {
162
+ event.preventDefault?.()
163
+ return
164
+ }
165
+ toggleRail()
166
+ }
167
+
75
168
  function playSchemePress() {
76
169
  if (!schemeToggle || reducedMotion()) return
77
170
  const revision = ++schemePressRevision
@@ -257,7 +350,8 @@ export function createOperationsShell({
257
350
  const overlay = railMode === 'overlay'
258
351
  const open = overlay && root.hasAttribute('data-prts-rail-open')
259
352
  railLauncher.setAttribute('aria-expanded', String(open))
260
- railLauncher.setAttribute('aria-label', open ? '关闭 P.R.T.S. 导航' : '打开 P.R.T.S. 导航')
353
+ const closedLabel = phoneRailGesture() ? '长按并向下滑动打开 P.R.T.S. 导航' : '打开 P.R.T.S. 导航'
354
+ railLauncher.setAttribute('aria-label', open ? '关闭 P.R.T.S. 导航' : closedLabel)
261
355
  if (overlay && !open) {
262
356
  navRail.setAttribute('aria-hidden', 'true')
263
357
  navRail.setAttribute('inert', '')
@@ -294,6 +388,7 @@ export function createOperationsShell({
294
388
  if (!viewportChanged && !modeChanged && !forceClose) return railMode
295
389
  railViewport = nextViewport
296
390
  railMode = nextMode
391
+ clearRailGesture()
297
392
  root.dataset.prtsRailMode = nextMode
298
393
  root.removeAttribute('data-prts-rail-open')
299
394
  syncRailState()
@@ -320,13 +415,19 @@ export function createOperationsShell({
320
415
  }
321
416
 
322
417
  function onResponsiveChange() {
418
+ clearRailGesture()
323
419
  setRailViewport(window.innerWidth)
420
+ syncRailState()
324
421
  settingsOverlay?.scheduleScalePreviewRefresh?.()
325
422
  }
326
423
 
327
424
  function bindResponsiveQueries() {
328
425
  railViewportQuery = window.matchMedia?.('(min-width: 1180px)')
426
+ railPointerQuery = window.matchMedia?.('(pointer: coarse)')
427
+ railHoverQuery = window.matchMedia?.('(hover: none)')
329
428
  railViewportQuery?.addEventListener?.('change', onResponsiveChange)
429
+ railPointerQuery?.addEventListener?.('change', onResponsiveChange)
430
+ railHoverQuery?.addEventListener?.('change', onResponsiveChange)
330
431
  }
331
432
  function disableTheme() { onThemeDisable() }
332
433
 
@@ -367,7 +468,12 @@ export function createOperationsShell({
367
468
  conversationObserver.observe(operationRegion, { childList: true, subtree: true, attributes: true, attributeFilter: ['hidden', 'data-chat-flow-kind', 'data-message-role'] })
368
469
  schemeObserver = new window.MutationObserver(syncSchemeToggle)
369
470
  schemeObserver.observe(root, { attributes: true, attributeFilter: ['data-prts-scheme'] })
370
- railLauncher.addEventListener('click', toggleRail)
471
+ railLauncher.addEventListener('click', onRailClick)
472
+ railLauncher.addEventListener('pointerdown', onRailPointerDown)
473
+ railLauncher.addEventListener('pointermove', onRailPointerMove)
474
+ railLauncher.addEventListener('pointerup', onRailPointerUp)
475
+ railLauncher.addEventListener('pointercancel', clearRailGesture)
476
+ railLauncher.addEventListener('lostpointercapture', onRailLostPointerCapture)
371
477
  document.addEventListener('keydown', onRailKeydown)
372
478
  document.addEventListener('click', onDocumentClick)
373
479
  schemeToggle.addEventListener('click', toggleScheme)
@@ -384,10 +490,16 @@ export function createOperationsShell({
384
490
  }
385
491
 
386
492
  function dispose() {
493
+ clearRailGesture()
387
494
  cancelSchemePointer()
388
495
  clearSchemeClickSuppression()
389
496
  clearSchemePressDelays()
390
- railLauncher?.removeEventListener('click', toggleRail)
497
+ railLauncher?.removeEventListener('click', onRailClick)
498
+ railLauncher?.removeEventListener('pointerdown', onRailPointerDown)
499
+ railLauncher?.removeEventListener('pointermove', onRailPointerMove)
500
+ railLauncher?.removeEventListener('pointerup', onRailPointerUp)
501
+ railLauncher?.removeEventListener('pointercancel', clearRailGesture)
502
+ railLauncher?.removeEventListener('lostpointercapture', onRailLostPointerCapture)
391
503
  document.removeEventListener('keydown', onRailKeydown)
392
504
  document.removeEventListener('click', onDocumentClick)
393
505
  schemeToggle?.removeEventListener('click', toggleScheme)
@@ -399,7 +511,11 @@ export function createOperationsShell({
399
511
  window.removeEventListener?.('blur', cancelSchemePointer)
400
512
  themeDisable?.removeEventListener('click', disableTheme)
401
513
  railViewportQuery?.removeEventListener?.('change', onResponsiveChange)
514
+ railPointerQuery?.removeEventListener?.('change', onResponsiveChange)
515
+ railHoverQuery?.removeEventListener?.('change', onResponsiveChange)
402
516
  railViewportQuery = undefined
517
+ railPointerQuery = undefined
518
+ railHoverQuery = undefined
403
519
  resizeShield.dispose()
404
520
  settingsOverlay?.dispose()
405
521
  conversationObserver?.disconnect()
@@ -557,6 +557,7 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
557
557
 
558
558
 
559
559
  function particleTraversalSpeed(value = preferences) {
560
+ if (phoneStatic()) return 0
560
561
  const numeric = Number(value?.particleTraversalSpeed)
561
562
  return Number.isFinite(numeric) ? clamp(numeric, 0, 2) : 1
562
563
  }
@@ -1123,6 +1124,19 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
1123
1124
  const entry = entries.find(candidate => candidate.target === operation) ?? entries[0]
1124
1125
  const contentRect = entry?.contentRect
1125
1126
  pendingResizeSize = contentRect ? sizeFromRect(contentRect) : readOperationSize()
1127
+ if (phoneStatic()) {
1128
+ const nextSize = pendingResizeSize
1129
+ clearResizeState()
1130
+ const changed = applyFieldSize(nextSize)
1131
+ if (changed && particles.length) snapParticlesToCurrentLayout()
1132
+ resizeCommits += changed ? 1 : 0
1133
+ syncCanvasBackingStore()
1134
+ geometryDirty = false
1135
+ cancelFrame()
1136
+ draw(Number(window.performance?.now?.()) || 0)
1137
+ cancelFrame()
1138
+ return
1139
+ }
1126
1140
  if (resizeFrame !== undefined) return
1127
1141
  resizeFrame = window.requestAnimationFrame?.(() => {
1128
1142
  resizeFrame = undefined
@@ -1532,7 +1546,7 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
1532
1546
  if (heroActive) leaveHero()
1533
1547
  }
1534
1548
  function onScroll() {
1535
- if (!scroller) return
1549
+ if (!scroller || phoneStatic()) return
1536
1550
  if (heroActive) {
1537
1551
  scheduleFrame()
1538
1552
  return
@@ -1581,6 +1595,7 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
1581
1595
  }
1582
1596
 
1583
1597
  function registerContentMutations(mutations) {
1598
+ if (phoneStatic()) return
1584
1599
  let weight = mutations.length
1585
1600
  for (const mutation of mutations) {
1586
1601
  for (const node of [...mutation.addedNodes, ...mutation.removedNodes]) {
@@ -1693,8 +1708,35 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
1693
1708
  cancelFrame()
1694
1709
  }
1695
1710
 
1711
+ function rebindScroller(nextScroller) {
1712
+ scroller?.removeAttribute('data-prts-hero-active')
1713
+ scroller?.removeEventListener('scroll', onScroll)
1714
+ cancelHydrationResume()
1715
+ hydrationPaused = false
1716
+ mutationBurstScore = 0
1717
+ if (mutationBurstTimer !== undefined) window.clearTimeout?.(mutationBurstTimer)
1718
+ mutationBurstTimer = undefined
1719
+ heroMarker = undefined
1720
+ heroActive = false
1721
+ scroller = nextScroller
1722
+ scroller.addEventListener('scroll', onScroll, { passive: true })
1723
+ syncHeroState()
1724
+ if (!heroActive) {
1725
+ resetTraversal()
1726
+ if (!phoneStatic()) restoreTraversalFromScroll()
1727
+ commitTransitionTargets(segment, true)
1728
+ }
1729
+ scheduleFrame()
1730
+ }
1731
+
1696
1732
  function mount(nextOperation, nextScroller) {
1697
- if (operation === nextOperation && scroller === nextScroller && ambient?.isConnected && canvas?.isConnected) return
1733
+ if (operation === nextOperation && ambient?.isConnected && canvas?.isConnected) {
1734
+ if (scroller === nextScroller) return
1735
+ if (nextScroller) {
1736
+ rebindScroller(nextScroller)
1737
+ return
1738
+ }
1739
+ }
1698
1740
  unmount()
1699
1741
  operation = nextOperation
1700
1742
  scroller = nextScroller
@@ -1705,7 +1747,7 @@ export function createParticleFieldAdapter({ document, window, emblem = '', embl
1705
1747
  glow = created.glow
1706
1748
  canvas = document.createElement('canvas')
1707
1749
  canvas.setAttribute('data-prts-particle-layer', '')
1708
- canvas.setAttribute('aria-label', '随对话滚动轮换阵营标识的粒子画布')
1750
+ canvas.setAttribute('aria-label', phoneStatic() ? '静态阵营标识粒子画布' : '随对话滚动轮换阵营标识的粒子画布')
1709
1751
  canvas.style.visibility = 'hidden'
1710
1752
  if (typeof window.CanvasRenderingContext2D === 'function') context = canvas.getContext('2d')
1711
1753
  targetCache.clear()
@@ -29,6 +29,11 @@ export function createResizeShieldAdapter({
29
29
 
30
30
  function onResize() {
31
31
  if (!started) return
32
+ if (Number(window?.innerWidth) <= 640) {
33
+ clearTimer()
34
+ setActive(false)
35
+ return
36
+ }
32
37
  setActive(true)
33
38
  clearTimer()
34
39
  settleTimer = window?.setTimeout?.(settle, settleDelay)
@@ -294,18 +294,23 @@ html[data-dsh-prts][data-prts-rail-mode="overlay"] [data-prts-rail-launcher]:is(
294
294
 
295
295
  @media (hover: none), (pointer: coarse) {
296
296
  html[data-dsh-prts][data-prts-rail-mode="overlay"] [data-prts-rail-launcher] {
297
- top: calc(var(--prts-host-top-inset) + 12px);
298
- width: 40px;
299
- height: 40px;
300
- opacity: .92;
301
- transform: none;
297
+ top: 50%; width: 24px; height: 88px; opacity: .58;
298
+ transform: translateY(-50%); touch-action: none;
302
299
  }
303
-
304
- html[data-dsh-prts][data-prts-rail-mode="overlay"] [data-prts-rail-launcher] > span {
305
- opacity: 1;
300
+ html[data-dsh-prts] [data-prts-rail-launcher] > span { opacity: 0; }
301
+ html[data-dsh-prts] [data-prts-rail-launcher][data-prts-rail-gesture-active] { width: 40px; opacity: 1; }
302
+ html[data-dsh-prts] [data-prts-rail-gesture-cue] {
303
+ position: absolute; inset: calc(27px + var(--prts-rail-gesture-progress, 0px)) 10px auto auto;
304
+ display: block; opacity: 0; font: 700 14px/8px sans-serif;
305
+ }
306
+ html[data-dsh-prts] [data-prts-rail-gesture-active] [data-prts-rail-gesture-cue] {
307
+ opacity: 1; animation: prts-rail-swipe-cue 720ms ease-in-out infinite;
306
308
  }
309
+ html[data-dsh-prts] [data-prts-rail-gesture-ready] [data-prts-rail-gesture-cue] { filter: drop-shadow(0 0 3px currentColor); }
307
310
  }
308
311
 
312
+ @keyframes prts-rail-swipe-cue { 50% { opacity: .45; } }
313
+
309
314
  html[data-dsh-prts] [data-prts-rail-brand] {
310
315
  height: 64px;
311
316
  display: grid;
@@ -811,12 +816,18 @@ html[data-dsh-prts] [data-prts-region="operation"] {
811
816
  color: var(--prts-ink);
812
817
  }
813
818
 
814
- html[data-dsh-prts][data-prts-sessions-collapsed] [data-prts-region="sessions"] [data-slot="sidebar.workspaces"],
815
- html[data-dsh-prts][data-prts-sessions-collapsed] [data-prts-region="sessions"] [data-slot="sidebar.settings"] {
819
+ html[data-dsh-prts] [data-prts-region="frame"][data-sidebar-collapsed] [data-prts-region="sessions"] :is(
820
+ [data-slot="sidebar.workspaces"],
821
+ [data-slot="sidebar.settings"]
822
+ ) {
816
823
  opacity: 0;
817
824
  pointer-events: none;
818
825
  }
819
826
 
827
+ html[data-dsh-prts] [data-sidebar-collapsed] :is([data-prts-owned-facility], [data-prts-owned-facility-spine], [data-prts-owned-facility-vector]) {
828
+ visibility: hidden; content-visibility: hidden;
829
+ }
830
+
820
831
  html[data-dsh-prts] [data-prts-region="sessions"] > *,
821
832
  html[data-dsh-prts] [data-prts-region="sessions"] [data-slot="sidebar"] {
822
833
  height: 100%;
@@ -827,11 +838,15 @@ html[data-dsh-prts] [data-prts-region="sessions"] [data-rc7-sidebar-root] {
827
838
  width: 100% !important;
828
839
  min-width: var(--prts-sessions-width);
829
840
  padding: 14px 10px;
830
- overflow-y: auto;
841
+ overflow: hidden auto;
831
842
  background: transparent !important;
832
843
  color: var(--prts-ink);
833
844
  }
834
845
 
846
+ html[data-dsh-prts] [data-prts-region="sessions"] [data-slot="sidebar.workspaces"] [role="tree"] {
847
+ overflow-x: hidden;
848
+ }
849
+
835
850
  html[data-dsh-prts] [data-prts-region="sessions"] > * > [data-slot="sidebar"] > :first-child > button[aria-label*="new session" i] {
836
851
  display: none !important;
837
852
  }
@@ -1362,6 +1377,7 @@ html[data-dsh-prts-settings] .prts-plugin-settings {
1362
1377
  width: 100%;
1363
1378
  display: grid;
1364
1379
  gap: 16px;
1380
+ container: prts-plugin-settings / inline-size;
1365
1381
  color: inherit;
1366
1382
  font: inherit;
1367
1383
  }
@@ -1474,6 +1490,19 @@ html[data-dsh-prts-settings] .prts-plugin-settings button[role='switch']:disable
1474
1490
  }
1475
1491
  }
1476
1492
 
1493
+ @container prts-plugin-settings (max-width: 22rem) {
1494
+ html[data-dsh-prts-settings] .prts-plugin-settings__row {
1495
+ flex-direction: column;
1496
+ align-items: stretch;
1497
+ gap: 10px;
1498
+ }
1499
+
1500
+ html[data-dsh-prts-settings] .prts-plugin-settings button[role='switch'] {
1501
+ align-self: flex-end;
1502
+ }
1503
+ }
1504
+
1505
+
1477
1506
 
1478
1507
  @media (max-width: 640px) {
1479
1508
  html[data-dsh-prts] {