@ash-qw/dsh-theme-prts 0.1.98 → 0.1.100

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.
@@ -7,8 +7,17 @@ const ROOT_ATTRIBUTES = [
7
7
  'data-prts-glass',
8
8
  'data-prts-motion',
9
9
  'data-prts-particle-pattern',
10
+ 'data-prts-session-flow',
11
+ 'data-prts-session-glow',
12
+ 'data-prts-session-flow-palette',
13
+ 'data-prts-session-flow-speed',
10
14
  'data-prts-conversation-style',
11
15
  ]
16
+ const SESSION_FLOW_COLOR_PROPERTIES = {
17
+ '--prts-session-flow-back': 'sessionFlowColorBack',
18
+ '--prts-session-flow-core': 'sessionFlowColorCore',
19
+ '--prts-session-flow-front': 'sessionFlowColorFront',
20
+ }
12
21
 
13
22
  function themeIdentity(value) {
14
23
  if (typeof value === 'string') return value
@@ -36,6 +45,7 @@ export function createThemeController({ document, window, cssText, service }) {
36
45
 
37
46
  function clearThemeState() {
38
47
  for (const attribute of ROOT_ATTRIBUTES) root.removeAttribute(attribute)
48
+ for (const property of Object.keys(SESSION_FLOW_COLOR_PROPERTIES)) root.style.removeProperty(property)
39
49
  }
40
50
 
41
51
  function removeOwnedState() {
@@ -109,6 +119,11 @@ export function createThemeController({ document, window, cssText, service }) {
109
119
  root.dataset.prtsGlass = preferences.glass
110
120
  root.dataset.prtsMotion = preferences.motion
111
121
  root.dataset.prtsParticlePattern = 'orthogonal'
122
+ root.dataset.prtsSessionFlow = preferences.sessionFlow ? 'on' : 'off'
123
+ root.dataset.prtsSessionGlow = preferences.sessionGlow ? 'on' : 'off'
124
+ root.dataset.prtsSessionFlowPalette = preferences.sessionFlowPalette
125
+ root.dataset.prtsSessionFlowSpeed = String(preferences.sessionFlowSpeed)
126
+ for (const [property, key] of Object.entries(SESSION_FLOW_COLOR_PROPERTIES)) root.style.setProperty(property, preferences[key])
112
127
  root.dataset.prtsConversationStyle = preferences.conversationStyle
113
128
  refresh()
114
129
  },
@@ -1,4 +1,4 @@
1
- import { PARTICLE_TRAVERSAL_SPEED_DEFAULT, PARTICLE_TRAVERSAL_SPEED_MAX, PARTICLE_TRAVERSAL_SPEED_MIN, PARTICLE_TRAVERSAL_SPEED_STEP, resolveParticleDetail } from './preferences.js'
1
+ import { PARTICLE_TRAVERSAL_SPEED_DEFAULT, PARTICLE_TRAVERSAL_SPEED_MAX, PARTICLE_TRAVERSAL_SPEED_MIN, PARTICLE_TRAVERSAL_SPEED_STEP, SESSION_FLOW_SPEED_DEFAULT, SESSION_FLOW_SPEED_MAX, SESSION_FLOW_SPEED_MIN, SESSION_FLOW_SPEED_STEP, resolveParticleDetail } from './preferences.js'
2
2
 
3
3
  const SETTINGS_FOCUSABLE = [
4
4
  'button:not([disabled])',
@@ -19,7 +19,15 @@ const COMMON_CONTROLS = [
19
19
  ['texture', '环境底纹', [['off', '关闭'], ['restrained', '克制'], ['full', '完整']]],
20
20
  ['glass', '玻璃材质', [['off', '关闭'], ['soft', '柔和'], ['standard', '标准'], ['clear', '清晰']]],
21
21
  ['motion', '动态效果', [['system', '跟随系统'], ['reduced', '减少']]],
22
- ['conversationStyle', '会话主题', [['native', '原有'], ['deck-chat', '通讯链路']]],
22
+ ['sessionFlow', '当前会话流线', [['false', '关闭'], ['true', '开启']]],
23
+ ['sessionGlow', '流线光效', [['false', '关闭'], ['true', '开启']]],
24
+ ['sessionFlowPalette', '流线色调', [['triad', '三色'], ['rhodes', '青蓝'], ['amber', '金黄'], ['alert', '赤红'], ['custom', '自定义']]],
25
+ ]
26
+
27
+ const SESSION_FLOW_COLOR_CONTROLS = [
28
+ ['sessionFlowColorBack', '后层'],
29
+ ['sessionFlowColorCore', '中层'],
30
+ ['sessionFlowColorFront', '前层'],
23
31
  ]
24
32
 
25
33
  const PARTICLE_OPTIONS = [
@@ -37,8 +45,14 @@ function settingButtons(key, label, options) {
37
45
  ? '<small data-prts-setting-note>在任意窗口宽度下默认收起 P.R.T.S. 导航,可从左侧按钮临时展开</small>'
38
46
  : key === 'conversationStyle'
39
47
  ? '<small data-prts-setting-note>立即切换会话背景、头像与消息气泡</small>'
48
+ : key === 'sessionFlow'
49
+ ? '<small data-prts-setting-note>仅控制当前会话卡片的三色流线</small>'
50
+ : key === 'sessionGlow'
51
+ ? '<small data-prts-setting-note>为流线添加柔和的同色辉光</small>'
52
+ : key === 'sessionFlowPalette'
53
+ ? '<small data-prts-setting-note>同时适配日间与夜间模式</small>'
40
54
  : ''
41
- const sample = value => key === 'texture' || key === 'glass'
55
+ const sample = value => key === 'texture' || key === 'glass' || key === 'sessionFlowPalette'
42
56
  ? `<i data-prts-option-sample="${key}" data-prts-option-sample-value="${value}" aria-hidden="true"></i>`
43
57
  : ''
44
58
  return `<div data-prts-setting-row="${key}">
@@ -59,6 +73,33 @@ function particleMarkup() {
59
73
  </button>`).join('')
60
74
  }
61
75
 
76
+ function sessionFlowPreviewMarkup() {
77
+ const filaments = [
78
+ ['back', 'M 3 29 C 34 8 66 36 101 17 C 126 5 151 27 177 13'],
79
+ ['core', 'M 3 24 C 30 35 61 7 94 23 C 124 37 148 7 177 20'],
80
+ ['front', 'M 3 18 C 34 4 66 30 96 12 C 125 -1 148 30 177 24'],
81
+ ]
82
+ return `<svg data-prts-session-flow-preview viewBox="0 0 180 42" aria-label="三色流线预览" role="img">
83
+ ${filaments.map(([strand, path]) => `<g data-prts-session-flow-preview-filament="${strand}">
84
+ <path data-prts-session-flow-preview-layer="halo" d="${path}"></path>
85
+ <path data-prts-session-flow-preview-layer="body" d="${path}"></path>
86
+ <path data-prts-session-flow-preview-layer="core" d="${path}"></path>
87
+ </g>`).join('')}
88
+ </svg>`
89
+ }
90
+
91
+ function sessionFlowColorMarkup() {
92
+ return `<div data-prts-setting-row="sessionFlowColors" data-prts-session-flow-colors>
93
+ <span><strong>三色调色盘</strong><small data-prts-setting-note>任意修改后切换为自定义,日夜模式均保持原色</small></span>
94
+ <div data-prts-session-flow-color-workbench>
95
+ ${sessionFlowPreviewMarkup()}
96
+ <div data-prts-session-flow-color-inputs role="group" aria-label="三色流线调色盘">
97
+ ${SESSION_FLOW_COLOR_CONTROLS.map(([key, label]) => `<label><span>${label}</span><input type="color" value="#ffffff" data-prts-setting-color data-prts-setting-key="${key}" aria-label="${label}流线颜色"><output data-prts-setting-color-output="${key}">#ffffff</output></label>`).join('')}
98
+ </div>
99
+ </div>
100
+ </div>`
101
+ }
102
+
62
103
  export function themeSettingsMarkup() {
63
104
  return `<div data-prts-settings-backdrop hidden aria-hidden="true"></div>
64
105
  <dialog id="prts-theme-settings" data-prts-theme-settings aria-label="P.R.T.S. 主题设置" aria-hidden="true">
@@ -80,7 +121,14 @@ export function themeSettingsMarkup() {
80
121
 
81
122
  <section data-prts-settings-common>
82
123
  <header><span><small>常用设置</small><strong>主题表现</strong></span></header>
83
- <div data-prts-settings-common-grid>${COMMON_CONTROLS.map(([key, label, options]) => settingButtons(key, label, options)).join('')}</div>
124
+ <div data-prts-settings-common-grid>${COMMON_CONTROLS.map(([key, label, options]) => settingButtons(key, label, options)).join('')}
125
+ ${sessionFlowColorMarkup()}
126
+ <label data-prts-setting-row="sessionFlowSpeed" data-prts-setting-range-row data-prts-session-flow-speed>
127
+ <span><strong>流线速度</strong><small data-prts-setting-note>控制当前会话流线的形变速度</small></span>
128
+ <div><input type="range" min="0.25" max="2" step="0.25" value="1" data-prts-setting-range data-prts-setting-key="sessionFlowSpeed" aria-label="当前会话流线速度"><output data-prts-session-flow-speed-output>1× · 标准</output></div>
129
+ </label>
130
+ ${settingButtons('conversationStyle', '会话主题', [['native', '原有'], ['deck-chat', '通讯链路']])}
131
+ </div>
84
132
  </section>
85
133
 
86
134
  <details data-prts-settings-advanced>
@@ -140,6 +188,10 @@ export function createThemeSettingsOverlay({
140
188
  let currentPersistence = { phase: 'idle', revision: 0 }
141
189
  let particleTraversalSpeedDraft
142
190
  let particleTraversalSpeedDirty = false
191
+ let sessionFlowSpeedDraft
192
+ let sessionFlowSpeedDirty = false
193
+ let sessionFlowColorDrafts = {}
194
+ const sessionFlowColorDirty = new Set()
143
195
  let savedTimer
144
196
  let feedbackTimer
145
197
  let feedbackTarget
@@ -189,6 +241,8 @@ export function createThemeSettingsOverlay({
189
241
  if (value === isOpen) return isOpen
190
242
  if (!value) {
191
243
  commitParticleTraversalSpeed()
244
+ commitSessionFlowSpeed()
245
+ commitSessionFlowColors()
192
246
  }
193
247
  isOpen = value
194
248
  trigger.setAttribute('aria-expanded', String(isOpen))
@@ -222,6 +276,34 @@ export function createThemeSettingsOverlay({
222
276
  )
223
277
  }
224
278
 
279
+ function normalizeSessionFlowSpeed(value) {
280
+ const numeric = Number(value)
281
+ if (!Number.isFinite(numeric)) return SESSION_FLOW_SPEED_DEFAULT
282
+ const clamped = Math.min(SESSION_FLOW_SPEED_MAX, Math.max(SESSION_FLOW_SPEED_MIN, numeric))
283
+ return Math.round(clamped / SESSION_FLOW_SPEED_STEP) * SESSION_FLOW_SPEED_STEP
284
+ }
285
+
286
+ function currentSessionFlowSpeed() {
287
+ return normalizeSessionFlowSpeed(
288
+ sessionFlowSpeedDirty ? sessionFlowSpeedDraft : currentPreferences?.sessionFlowSpeed,
289
+ )
290
+ }
291
+
292
+ function normalizeSessionFlowColor(value, fallback = '#ffffff') {
293
+ return typeof value === 'string' && /^#[\da-f]{6}$/i.test(value) ? value.toLowerCase() : fallback
294
+ }
295
+
296
+ function currentSessionFlowColor(key) {
297
+ return normalizeSessionFlowColor(
298
+ sessionFlowColorDirty.has(key) ? sessionFlowColorDrafts[key] : currentPreferences?.[key],
299
+ '#ffffff',
300
+ )
301
+ }
302
+
303
+ function activeSessionFlowPalette() {
304
+ return sessionFlowColorDirty.size > 0 ? 'custom' : currentPreferences?.sessionFlowPalette
305
+ }
306
+
225
307
  function conciseNumber(value) {
226
308
  return String(Number(Number(value).toFixed(2)))
227
309
  }
@@ -240,6 +322,42 @@ export function createThemeSettingsOverlay({
240
322
  setText(panel.querySelector('[data-prts-particle-traversal-output]'), particleTraversalLabel(value))
241
323
  }
242
324
 
325
+ function sessionFlowSpeedLabel(value) {
326
+ const speed = normalizeSessionFlowSpeed(value)
327
+ const description = speed < 1 ? '缓慢' : speed === 1 ? '标准' : speed <= 1.5 ? '快速' : '高速'
328
+ return `${conciseNumber(speed)}× · ${description}`
329
+ }
330
+
331
+ function renderSessionFlowSpeed() {
332
+ if (!panel || !currentPreferences) return
333
+ const value = currentSessionFlowSpeed()
334
+ const range = panel.querySelector('input[data-prts-setting-range][data-prts-setting-key="sessionFlowSpeed"]')
335
+ if (range && range.value !== String(value)) range.value = String(value)
336
+ setText(panel.querySelector('[data-prts-session-flow-speed-output]'), sessionFlowSpeedLabel(value))
337
+ }
338
+
339
+ function renderSessionFlowColors() {
340
+ if (!panel || !currentPreferences) return
341
+ for (const [key] of SESSION_FLOW_COLOR_CONTROLS) {
342
+ const value = currentSessionFlowColor(key)
343
+ const input = panel.querySelector(`input[data-prts-setting-color][data-prts-setting-key="${key}"]`)
344
+ if (input && input.value !== value) input.value = value
345
+ setText(panel.querySelector(`[data-prts-setting-color-output="${key}"]`), value)
346
+ }
347
+ const preview = panel.querySelector('[data-prts-session-flow-preview]')
348
+ if (preview) {
349
+ preview.dataset.prtsSessionFlowPreviewPalette = activeSessionFlowPalette() || 'triad'
350
+ preview.style.setProperty('--prts-session-flow-back', currentSessionFlowColor('sessionFlowColorBack'))
351
+ preview.style.setProperty('--prts-session-flow-core', currentSessionFlowColor('sessionFlowColorCore'))
352
+ preview.style.setProperty('--prts-session-flow-front', currentSessionFlowColor('sessionFlowColorFront'))
353
+ }
354
+ for (const button of panel.querySelectorAll('button[data-prts-setting-key="sessionFlowPalette"]')) {
355
+ const selected = button.dataset.prtsSettingValue === activeSessionFlowPalette()
356
+ button.classList.toggle('is-selected', selected)
357
+ button.setAttribute('aria-pressed', String(selected))
358
+ }
359
+ }
360
+
243
361
  function setText(node, value) {
244
362
  if (node && node.textContent !== value) node.textContent = value
245
363
  }
@@ -257,6 +375,38 @@ export function createThemeSettingsOverlay({
257
375
  return true
258
376
  }
259
377
 
378
+ function commitSessionFlowSpeed() {
379
+ if (!sessionFlowSpeedDirty || !currentPreferences) return false
380
+ const value = currentSessionFlowSpeed()
381
+ sessionFlowSpeedDirty = false
382
+ sessionFlowSpeedDraft = value
383
+ if (value !== normalizeSessionFlowSpeed(currentPreferences.sessionFlowSpeed)) {
384
+ onPreferenceChange('sessionFlowSpeed', value)
385
+ } else {
386
+ renderSessionFlowSpeed()
387
+ }
388
+ return true
389
+ }
390
+
391
+ function commitSessionFlowColor(key) {
392
+ if (!sessionFlowColorDirty.has(key) || !currentPreferences) return false
393
+ const value = currentSessionFlowColor(key)
394
+ sessionFlowColorDirty.delete(key)
395
+ sessionFlowColorDrafts[key] = value
396
+ if (value !== normalizeSessionFlowColor(currentPreferences[key]) || currentPreferences.sessionFlowPalette !== 'custom') {
397
+ onPreferenceChange(key, value)
398
+ } else {
399
+ renderSessionFlowColors()
400
+ }
401
+ return true
402
+ }
403
+
404
+ function commitSessionFlowColors() {
405
+ let committed = false
406
+ for (const key of [...sessionFlowColorDirty]) committed = commitSessionFlowColor(key) || committed
407
+ return committed
408
+ }
409
+
260
410
  function renderPersistence() {
261
411
  const status = panel?.querySelector?.('[data-prts-persistence-status]')
262
412
  const error = panel?.querySelector?.('[data-prts-persistence-error]')
@@ -281,7 +431,13 @@ export function createThemeSettingsOverlay({
281
431
  const particleDetail = resolveParticleDetail(currentPreferences)
282
432
  for (const button of panel.querySelectorAll('button[data-prts-setting-key]')) {
283
433
  const key = button.dataset.prtsSettingKey
284
- const actual = key === 'particleDetail' ? particleDetail : currentPreferences[key]
434
+ const actual = key === 'particleDetail'
435
+ ? particleDetail
436
+ : key === 'sessionFlowPalette'
437
+ ? activeSessionFlowPalette()
438
+ : key === 'sessionFlow' || key === 'sessionGlow'
439
+ ? currentPreferences[key] !== false
440
+ : currentPreferences[key]
285
441
  const selected = String(actual) === button.dataset.prtsSettingValue
286
442
  button.classList.toggle('is-selected', selected)
287
443
  button.setAttribute('aria-pressed', String(selected))
@@ -298,7 +454,13 @@ export function createThemeSettingsOverlay({
298
454
  const particleError = panel.querySelector('[data-prts-particle-error]')
299
455
  if (particleError) particleError.hidden = currentStatus?.particle?.phase !== 'error'
300
456
  if (!particleTraversalSpeedDirty) particleTraversalSpeedDraft = normalizeParticleTraversalSpeed(currentPreferences.particleTraversalSpeed)
457
+ if (!sessionFlowSpeedDirty) sessionFlowSpeedDraft = normalizeSessionFlowSpeed(currentPreferences.sessionFlowSpeed)
458
+ for (const [key] of SESSION_FLOW_COLOR_CONTROLS) {
459
+ if (!sessionFlowColorDirty.has(key)) sessionFlowColorDrafts[key] = normalizeSessionFlowColor(currentPreferences[key])
460
+ }
301
461
  renderParticleTraversal()
462
+ renderSessionFlowSpeed()
463
+ renderSessionFlowColors()
302
464
  renderPersistence()
303
465
  }
304
466
 
@@ -322,6 +484,10 @@ export function createThemeSettingsOverlay({
322
484
  if (target.hasAttribute('data-prts-theme-settings-close')) { setOpen(false); return }
323
485
  if (target.hasAttribute('data-prts-setting-key')) {
324
486
  const raw = target.dataset.prtsSettingValue
487
+ if (target.dataset.prtsSettingKey === 'sessionFlowPalette') {
488
+ sessionFlowColorDirty.clear()
489
+ sessionFlowColorDrafts = {}
490
+ }
325
491
  markFeedback(target)
326
492
  onPreferenceChange(target.dataset.prtsSettingKey, raw === 'true' ? true : raw === 'false' ? false : raw)
327
493
  return
@@ -332,6 +498,10 @@ export function createThemeSettingsOverlay({
332
498
  if (target.hasAttribute('data-prts-reset-confirm-action')) {
333
499
  particleTraversalSpeedDirty = false
334
500
  particleTraversalSpeedDraft = undefined
501
+ sessionFlowSpeedDirty = false
502
+ sessionFlowSpeedDraft = undefined
503
+ sessionFlowColorDirty.clear()
504
+ sessionFlowColorDrafts = {}
335
505
  setResetConfirmation(false)
336
506
  onResetVisual()
337
507
  }
@@ -339,6 +509,15 @@ export function createThemeSettingsOverlay({
339
509
 
340
510
  function onPanelInput(event) {
341
511
  const target = event.target
512
+ if (target?.matches?.('input[data-prts-setting-color]')) {
513
+ const key = target.dataset.prtsSettingKey
514
+ const value = normalizeSessionFlowColor(target.value, currentSessionFlowColor(key))
515
+ sessionFlowColorDrafts[key] = value
516
+ sessionFlowColorDirty.add(key)
517
+ renderSessionFlowColors()
518
+ onPreferencePreview(key, value)
519
+ return
520
+ }
342
521
  if (!target?.matches?.('input[data-prts-setting-range]')) return
343
522
  const value = Number(target.value)
344
523
  if (!Number.isFinite(value)) return
@@ -348,6 +527,12 @@ export function createThemeSettingsOverlay({
348
527
  renderParticleTraversal()
349
528
  onPreferencePreview('particleTraversalSpeed', particleTraversalSpeedDraft)
350
529
  return
530
+ } else if (target.dataset.prtsSettingKey === 'sessionFlowSpeed') {
531
+ sessionFlowSpeedDraft = normalizeSessionFlowSpeed(value)
532
+ sessionFlowSpeedDirty = true
533
+ renderSessionFlowSpeed()
534
+ onPreferencePreview('sessionFlowSpeed', sessionFlowSpeedDraft)
535
+ return
351
536
  } else {
352
537
  return
353
538
  }
@@ -371,8 +556,13 @@ export function createThemeSettingsOverlay({
371
556
  const renderMediaState = () => renderState()
372
557
  const commitFromControl = event => {
373
558
  const target = event.target
559
+ if (target?.matches?.('input[data-prts-setting-color]')) {
560
+ if (event.type === 'change' || event.type === 'focusout') commitSessionFlowColor(target.dataset.prtsSettingKey)
561
+ return
562
+ }
374
563
  if (!target?.matches?.('input[data-prts-setting-range]')) return
375
564
  if (target.dataset.prtsSettingKey === 'particleTraversalSpeed') commitParticleTraversalSpeed()
565
+ if (target.dataset.prtsSettingKey === 'sessionFlowSpeed') commitSessionFlowSpeed()
376
566
  }
377
567
 
378
568
  trigger?.addEventListener('click', toggle)
@@ -393,6 +583,10 @@ export function createThemeSettingsOverlay({
393
583
  currentStatus = status
394
584
  currentPersistence = persistenceState ?? { phase: 'idle', revision: 0 }
395
585
  if (!particleTraversalSpeedDirty) particleTraversalSpeedDraft = normalizeParticleTraversalSpeed(preferences?.particleTraversalSpeed)
586
+ if (!sessionFlowSpeedDirty) sessionFlowSpeedDraft = normalizeSessionFlowSpeed(preferences?.sessionFlowSpeed)
587
+ for (const [key] of SESSION_FLOW_COLOR_CONTROLS) {
588
+ if (!sessionFlowColorDirty.has(key)) sessionFlowColorDrafts[key] = normalizeSessionFlowColor(preferences?.[key])
589
+ }
396
590
  renderState()
397
591
  },
398
592
  open: () => setOpen(true),
@@ -141,6 +141,138 @@ html[data-dsh-prts] [data-prts-session-pickup] {
141
141
  pointer-events: none;
142
142
  }
143
143
 
144
+ html[data-dsh-prts] [data-prts-session-lifeline] {
145
+ --prts-lifeline-pink: #f7ddeb;
146
+ --prts-lifeline-green: #65ddb0;
147
+ --prts-lifeline-blue: #6eb7f3;
148
+ opacity: 0;
149
+ transition: opacity 160ms ease;
150
+ pointer-events: none;
151
+ }
152
+
153
+ html[data-dsh-prts][data-prts-session-flow-palette="rhodes"] [data-prts-session-lifeline] {
154
+ --prts-lifeline-pink: #b9edf4;
155
+ --prts-lifeline-green: #5ccddb;
156
+ --prts-lifeline-blue: #5a9ee6;
157
+ }
158
+
159
+ html[data-dsh-prts][data-prts-session-flow-palette="amber"] [data-prts-session-lifeline] {
160
+ --prts-lifeline-pink: #ffe6a3;
161
+ --prts-lifeline-green: #f0c800;
162
+ --prts-lifeline-blue: #ee8f42;
163
+ }
164
+
165
+ html[data-dsh-prts][data-prts-session-flow-palette="alert"] [data-prts-session-lifeline] {
166
+ --prts-lifeline-pink: #ffc0b8;
167
+ --prts-lifeline-green: #ee625a;
168
+ --prts-lifeline-blue: #db5b8c;
169
+ }
170
+
171
+ html[data-dsh-prts][data-prts-session-flow-palette="custom"] [data-prts-session-lifeline] {
172
+ --prts-lifeline-pink: var(--prts-session-flow-back);
173
+ --prts-lifeline-green: var(--prts-session-flow-core);
174
+ --prts-lifeline-blue: var(--prts-session-flow-front);
175
+ }
176
+
177
+ html[data-dsh-prts] [data-prts-session-lifeline-filament] {
178
+ --prts-lifeline-color: currentColor;
179
+ }
180
+
181
+ html[data-dsh-prts] [data-prts-session-lifeline-layer] {
182
+ fill: none;
183
+ stroke: var(--prts-lifeline-color);
184
+ stroke-linecap: round;
185
+ stroke-linejoin: round;
186
+ vector-effect: non-scaling-stroke;
187
+ shape-rendering: geometricPrecision;
188
+ transition: opacity 160ms ease, stroke-width 160ms ease;
189
+ }
190
+
191
+ html[data-dsh-prts] [data-prts-session-lifeline-filament="back"] {
192
+ --prts-lifeline-color: var(--prts-lifeline-pink);
193
+ opacity: .92;
194
+ }
195
+
196
+ html[data-dsh-prts] [data-prts-session-lifeline-filament="core"] {
197
+ --prts-lifeline-color: var(--prts-lifeline-green);
198
+ opacity: .9;
199
+ }
200
+
201
+ html[data-dsh-prts] [data-prts-session-lifeline-filament="front"] {
202
+ --prts-lifeline-color: var(--prts-lifeline-blue);
203
+ opacity: .98;
204
+ }
205
+
206
+ html[data-dsh-prts] [data-prts-session-lifeline-layer="body"] {
207
+ stroke-width: .9;
208
+ }
209
+
210
+ html[data-dsh-prts] [data-prts-session-lifeline-layer="halo"],
211
+ html[data-dsh-prts] [data-prts-session-lifeline-layer="core"] {
212
+ opacity: 0;
213
+ }
214
+
215
+ html[data-dsh-prts][data-prts-session-glow="off"] :is([data-prts-session-lifeline-layer="halo"], [data-prts-session-lifeline-layer="core"]) {
216
+ opacity: 0;
217
+ }
218
+
219
+ html[data-dsh-prts][data-prts-session-glow="on"] [data-prts-session-lifeline-layer="halo"] {
220
+ stroke-width: 4.25;
221
+ opacity: .28;
222
+ filter: blur(1.15px);
223
+ }
224
+
225
+ html[data-dsh-prts][data-prts-session-glow="on"] [data-prts-session-lifeline-layer="body"] {
226
+ stroke-width: 1.65;
227
+ opacity: .98;
228
+ }
229
+
230
+ html[data-dsh-prts][data-prts-session-glow="on"] [data-prts-session-lifeline-layer="core"] {
231
+ stroke: color-mix(in srgb, var(--prts-lifeline-color) 18%, #fff);
232
+ stroke-width: .58;
233
+ opacity: .96;
234
+ }
235
+
236
+ html[data-dsh-prts][data-prts-scheme="light"][data-prts-session-glow="on"] [data-prts-session-lifeline-layer="halo"] {
237
+ stroke: color-mix(in srgb, var(--prts-lifeline-color) 42%, #fff);
238
+ stroke-width: 4.4;
239
+ opacity: .45;
240
+ filter: blur(1.05px);
241
+ }
242
+
243
+ html[data-dsh-prts][data-prts-scheme="light"][data-prts-session-glow="on"] [data-prts-session-lifeline-layer="body"] {
244
+ stroke-width: 1.5;
245
+ }
246
+
247
+ html[data-dsh-prts][data-prts-scheme="light"][data-prts-session-glow="on"] [data-prts-session-lifeline-layer="core"] {
248
+ stroke: color-mix(in srgb, var(--prts-lifeline-color) 58%, #fff);
249
+ stroke-width: .52;
250
+ }
251
+
252
+ html[data-dsh-prts] [data-prts-session-row][aria-selected="true"] [data-prts-session-lifeline] {
253
+ opacity: .98;
254
+ }
255
+
256
+ html[data-dsh-prts] [data-prts-session-row][aria-selected="true"]:hover [data-prts-session-lifeline] {
257
+ opacity: 0;
258
+ }
259
+
260
+ html[data-dsh-prts][data-prts-session-flow="off"] [data-prts-session-row] [data-prts-session-lifeline] {
261
+ opacity: 0;
262
+ }
263
+
264
+ @media (prefers-reduced-motion: reduce) {
265
+ html[data-dsh-prts] [data-prts-session-lifeline],
266
+ html[data-dsh-prts] [data-prts-session-lifeline-layer] {
267
+ transition-duration: 0s;
268
+ }
269
+ }
270
+
271
+ html[data-dsh-prts][data-prts-motion="reduced"] [data-prts-session-lifeline],
272
+ html[data-dsh-prts][data-prts-motion="reduced"] [data-prts-session-lifeline-layer] {
273
+ transition-duration: 0s;
274
+ }
275
+
144
276
  html[data-dsh-prts] [data-prts-session-row]:hover [data-prts-facility-texture="pickup"] [data-prts-session-pickup] {
145
277
  opacity: .58;
146
278
  transform: translate3d(0, 0, 0);
@@ -809,7 +809,6 @@ html[data-dsh-prts] [data-prts-region="operation"] {
809
809
  html[data-dsh-prts] [data-prts-region="sessions"] {
810
810
  box-sizing: border-box;
811
811
  position: relative;
812
- z-index: 4;
813
812
  overflow: hidden;
814
813
  border-right: 1px solid var(--prts-line);
815
814
  background: var(--prts-panel);
@@ -818,7 +817,6 @@ html[data-dsh-prts] [data-prts-region="sessions"] {
818
817
 
819
818
  html[data-dsh-prts] [data-prts-region="operation"] {
820
819
  position: relative;
821
- z-index: 1;
822
820
  overflow: hidden;
823
821
  background: var(--prts-canvas);
824
822
  color: var(--prts-ink);
@@ -987,6 +985,14 @@ html[data-dsh-prts] [data-prts-session-row] [data-prts-row-title]::before {
987
985
  letter-spacing: .08em;
988
986
  }
989
987
 
988
+ html[data-dsh-prts] [data-prts-session-row][aria-selected="true"] [data-prts-row-title] {
989
+ font-weight: 700;
990
+ }
991
+
992
+ html[data-dsh-prts] [data-prts-session-row][aria-selected="true"] [data-prts-row-title]::before {
993
+ color: var(--prts-yellow);
994
+ }
995
+
990
996
  html[data-dsh-prts] [data-prts-workspace-actions],
991
997
  html[data-dsh-prts] [data-prts-session-actions] {
992
998
  position: absolute;
@@ -1516,7 +1522,7 @@ html[data-dsh-prts] [data-prts-composer-fallback] :is(textarea, [data-composer-i
1516
1522
  caret-color: var(--prts-yellow);
1517
1523
  }
1518
1524
 
1519
- html[data-dsh-prts] [data-composer-card] button:not([data-slot="conversation.input.attachments"] *),
1525
+ html[data-dsh-prts] [data-composer-card] button:not([data-slot="conversation.input.attachments"] *):not([data-prts-glass-menu] *):not([data-prts-floating-glass] *):not([role="menu"] *):not([role="listbox"] *),
1520
1526
  html[data-dsh-prts] [data-prts-conversation-control] {
1521
1527
  border: 1px solid var(--prts-line) !important;
1522
1528
  border-radius: var(--prts-radius-control) !important;
@@ -1526,7 +1532,7 @@ html[data-dsh-prts] [data-prts-conversation-control] {
1526
1532
  }
1527
1533
 
1528
1534
 
1529
- html[data-dsh-prts] [data-composer-card] button:not([data-slot="conversation.input.attachments"] *):is(:hover, :focus-visible),
1535
+ html[data-dsh-prts] [data-composer-card] button:not([data-slot="conversation.input.attachments"] *):not([data-prts-glass-menu] *):not([data-prts-floating-glass] *):not([role="menu"] *):not([role="listbox"] *):is(:hover, :focus-visible),
1530
1536
  html[data-dsh-prts] [data-prts-conversation-control]:is(:hover, :focus-visible) {
1531
1537
  border-color: var(--prts-yellow) !important;
1532
1538
  }
@@ -1537,7 +1543,7 @@ html[data-dsh-prts] [data-composer-card] button[type="submit"] {
1537
1543
  color: var(--prts-yellow-ink) !important;
1538
1544
  }
1539
1545
 
1540
- html[data-dsh-prts] [data-composer-card] button:not([data-slot="conversation.input.attachments"] *):active,
1546
+ html[data-dsh-prts] [data-composer-card] button:not([data-slot="conversation.input.attachments"] *):not([data-prts-glass-menu] *):not([data-prts-floating-glass] *):not([role="menu"] *):not([role="listbox"] *):active,
1541
1547
  html[data-dsh-prts] [data-prts-conversation-control]:active {
1542
1548
  transform: translateY(1px) scale(.98);
1543
1549
  }
@@ -11,7 +11,6 @@ html[data-dsh-prts] [data-prts-region="frame"] {
11
11
 
12
12
  html[data-dsh-prts] [data-prts-region="frame"] > [data-shell-overlay] {
13
13
  position: absolute !important;
14
- z-index: 120;
15
14
  inset: 0;
16
15
  min-width: 0;
17
16
  min-height: 0;
@@ -155,6 +154,10 @@ html[data-dsh-prts] [data-prts-session-row]:hover [data-prts-session-time] {
155
154
  opacity: 0;
156
155
  }
157
156
 
157
+ html[data-dsh-prts] [data-prts-session-row][aria-selected="true"] [data-prts-session-time] {
158
+ opacity: 0;
159
+ }
160
+
158
161
  /* Restore the notched P.R.T.S. composer with one complete layered contour. */
159
162
  html[data-dsh-prts] [data-composer-card],
160
163
  html[data-dsh-prts] [data-prts-composer-fallback] {
@@ -417,7 +420,7 @@ html[data-dsh-prts] [data-prts-hero-active] [data-prts-composer-fallback] textar
417
420
  resize: vertical;
418
421
  }
419
422
 
420
- html[data-dsh-prts] [data-prts-hero-active] [data-composer-card] button:not([data-slot="conversation.input.attachments"] *),
423
+ html[data-dsh-prts] [data-prts-hero-active] [data-composer-card] button:not([data-slot="conversation.input.attachments"] *):not([data-prts-glass-menu] *):not([data-prts-floating-glass] *):not([role="menu"] *):not([role="listbox"] *),
421
424
  html[data-dsh-prts] [data-prts-hero-active] [data-prts-glass-control] {
422
425
  min-height: 36px;
423
426
  }
@@ -137,7 +137,7 @@ html[data-dsh-prts] [data-prts-nav-bottom] b {
137
137
  /* Complete structural outlines; decorative accents are separate edges. */
138
138
  html[data-dsh-prts] [data-composer-card],
139
139
  html[data-dsh-prts] [data-prts-composer-fallback],
140
- html[data-dsh-prts] [data-composer-card] button:not([data-slot="conversation.input.attachments"] *),
140
+ html[data-dsh-prts] [data-composer-card] button:not([data-slot="conversation.input.attachments"] *):not([data-prts-glass-menu] *):not([data-prts-floating-glass] *):not([role="menu"] *):not([role="listbox"] *),
141
141
  html[data-dsh-prts] [data-prts-conversation-control],
142
142
  html[data-dsh-prts] [data-produced-files-row] > button[type="button"],
143
143
  html[data-dsh-prts] [data-prts-to-bottom] {