@ash-qw/dsh-theme-prts 0.1.99 → 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.
@@ -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])',
@@ -20,7 +20,14 @@ const COMMON_CONTROLS = [
20
20
  ['glass', '玻璃材质', [['off', '关闭'], ['soft', '柔和'], ['standard', '标准'], ['clear', '清晰']]],
21
21
  ['motion', '动态效果', [['system', '跟随系统'], ['reduced', '减少']]],
22
22
  ['sessionFlow', '当前会话流线', [['false', '关闭'], ['true', '开启']]],
23
- ['conversationStyle', '会话主题', [['native', '原有'], ['deck-chat', '通讯链路']]],
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', '前层'],
24
31
  ]
25
32
 
26
33
  const PARTICLE_OPTIONS = [
@@ -40,8 +47,12 @@ function settingButtons(key, label, options) {
40
47
  ? '<small data-prts-setting-note>立即切换会话背景、头像与消息气泡</small>'
41
48
  : key === 'sessionFlow'
42
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>'
43
54
  : ''
44
- const sample = value => key === 'texture' || key === 'glass'
55
+ const sample = value => key === 'texture' || key === 'glass' || key === 'sessionFlowPalette'
45
56
  ? `<i data-prts-option-sample="${key}" data-prts-option-sample-value="${value}" aria-hidden="true"></i>`
46
57
  : ''
47
58
  return `<div data-prts-setting-row="${key}">
@@ -62,6 +73,33 @@ function particleMarkup() {
62
73
  </button>`).join('')
63
74
  }
64
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
+
65
103
  export function themeSettingsMarkup() {
66
104
  return `<div data-prts-settings-backdrop hidden aria-hidden="true"></div>
67
105
  <dialog id="prts-theme-settings" data-prts-theme-settings aria-label="P.R.T.S. 主题设置" aria-hidden="true">
@@ -83,7 +121,14 @@ export function themeSettingsMarkup() {
83
121
 
84
122
  <section data-prts-settings-common>
85
123
  <header><span><small>常用设置</small><strong>主题表现</strong></span></header>
86
- <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>
87
132
  </section>
88
133
 
89
134
  <details data-prts-settings-advanced>
@@ -143,6 +188,10 @@ export function createThemeSettingsOverlay({
143
188
  let currentPersistence = { phase: 'idle', revision: 0 }
144
189
  let particleTraversalSpeedDraft
145
190
  let particleTraversalSpeedDirty = false
191
+ let sessionFlowSpeedDraft
192
+ let sessionFlowSpeedDirty = false
193
+ let sessionFlowColorDrafts = {}
194
+ const sessionFlowColorDirty = new Set()
146
195
  let savedTimer
147
196
  let feedbackTimer
148
197
  let feedbackTarget
@@ -192,6 +241,8 @@ export function createThemeSettingsOverlay({
192
241
  if (value === isOpen) return isOpen
193
242
  if (!value) {
194
243
  commitParticleTraversalSpeed()
244
+ commitSessionFlowSpeed()
245
+ commitSessionFlowColors()
195
246
  }
196
247
  isOpen = value
197
248
  trigger.setAttribute('aria-expanded', String(isOpen))
@@ -225,6 +276,34 @@ export function createThemeSettingsOverlay({
225
276
  )
226
277
  }
227
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
+
228
307
  function conciseNumber(value) {
229
308
  return String(Number(Number(value).toFixed(2)))
230
309
  }
@@ -243,6 +322,42 @@ export function createThemeSettingsOverlay({
243
322
  setText(panel.querySelector('[data-prts-particle-traversal-output]'), particleTraversalLabel(value))
244
323
  }
245
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
+
246
361
  function setText(node, value) {
247
362
  if (node && node.textContent !== value) node.textContent = value
248
363
  }
@@ -260,6 +375,38 @@ export function createThemeSettingsOverlay({
260
375
  return true
261
376
  }
262
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
+
263
410
  function renderPersistence() {
264
411
  const status = panel?.querySelector?.('[data-prts-persistence-status]')
265
412
  const error = panel?.querySelector?.('[data-prts-persistence-error]')
@@ -286,8 +433,10 @@ export function createThemeSettingsOverlay({
286
433
  const key = button.dataset.prtsSettingKey
287
434
  const actual = key === 'particleDetail'
288
435
  ? particleDetail
289
- : key === 'sessionFlow'
290
- ? currentPreferences.sessionFlow !== false
436
+ : key === 'sessionFlowPalette'
437
+ ? activeSessionFlowPalette()
438
+ : key === 'sessionFlow' || key === 'sessionGlow'
439
+ ? currentPreferences[key] !== false
291
440
  : currentPreferences[key]
292
441
  const selected = String(actual) === button.dataset.prtsSettingValue
293
442
  button.classList.toggle('is-selected', selected)
@@ -305,7 +454,13 @@ export function createThemeSettingsOverlay({
305
454
  const particleError = panel.querySelector('[data-prts-particle-error]')
306
455
  if (particleError) particleError.hidden = currentStatus?.particle?.phase !== 'error'
307
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
+ }
308
461
  renderParticleTraversal()
462
+ renderSessionFlowSpeed()
463
+ renderSessionFlowColors()
309
464
  renderPersistence()
310
465
  }
311
466
 
@@ -329,6 +484,10 @@ export function createThemeSettingsOverlay({
329
484
  if (target.hasAttribute('data-prts-theme-settings-close')) { setOpen(false); return }
330
485
  if (target.hasAttribute('data-prts-setting-key')) {
331
486
  const raw = target.dataset.prtsSettingValue
487
+ if (target.dataset.prtsSettingKey === 'sessionFlowPalette') {
488
+ sessionFlowColorDirty.clear()
489
+ sessionFlowColorDrafts = {}
490
+ }
332
491
  markFeedback(target)
333
492
  onPreferenceChange(target.dataset.prtsSettingKey, raw === 'true' ? true : raw === 'false' ? false : raw)
334
493
  return
@@ -339,6 +498,10 @@ export function createThemeSettingsOverlay({
339
498
  if (target.hasAttribute('data-prts-reset-confirm-action')) {
340
499
  particleTraversalSpeedDirty = false
341
500
  particleTraversalSpeedDraft = undefined
501
+ sessionFlowSpeedDirty = false
502
+ sessionFlowSpeedDraft = undefined
503
+ sessionFlowColorDirty.clear()
504
+ sessionFlowColorDrafts = {}
342
505
  setResetConfirmation(false)
343
506
  onResetVisual()
344
507
  }
@@ -346,6 +509,15 @@ export function createThemeSettingsOverlay({
346
509
 
347
510
  function onPanelInput(event) {
348
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
+ }
349
521
  if (!target?.matches?.('input[data-prts-setting-range]')) return
350
522
  const value = Number(target.value)
351
523
  if (!Number.isFinite(value)) return
@@ -355,6 +527,12 @@ export function createThemeSettingsOverlay({
355
527
  renderParticleTraversal()
356
528
  onPreferencePreview('particleTraversalSpeed', particleTraversalSpeedDraft)
357
529
  return
530
+ } else if (target.dataset.prtsSettingKey === 'sessionFlowSpeed') {
531
+ sessionFlowSpeedDraft = normalizeSessionFlowSpeed(value)
532
+ sessionFlowSpeedDirty = true
533
+ renderSessionFlowSpeed()
534
+ onPreferencePreview('sessionFlowSpeed', sessionFlowSpeedDraft)
535
+ return
358
536
  } else {
359
537
  return
360
538
  }
@@ -378,8 +556,13 @@ export function createThemeSettingsOverlay({
378
556
  const renderMediaState = () => renderState()
379
557
  const commitFromControl = event => {
380
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
+ }
381
563
  if (!target?.matches?.('input[data-prts-setting-range]')) return
382
564
  if (target.dataset.prtsSettingKey === 'particleTraversalSpeed') commitParticleTraversalSpeed()
565
+ if (target.dataset.prtsSettingKey === 'sessionFlowSpeed') commitSessionFlowSpeed()
383
566
  }
384
567
 
385
568
  trigger?.addEventListener('click', toggle)
@@ -400,6 +583,10 @@ export function createThemeSettingsOverlay({
400
583
  currentStatus = status
401
584
  currentPersistence = persistenceState ?? { phase: 'idle', revision: 0 }
402
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
+ }
403
590
  renderState()
404
591
  },
405
592
  open: () => setOpen(true),