@ash-qw/dsh-theme-prts 1.0.4 → 1.0.5
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/CHANGELOG.md +10 -0
- package/README.en.md +1 -1
- package/README.md +1 -1
- package/THIRD_PARTY_NOTICES.md +4 -4
- package/THIRD_PARTY_NOTICES.zh-CN.md +4 -4
- package/lib/client.js +203 -45
- package/package.json +1 -1
- package/src/assets/cursors/animation.json +83 -1
- package/src/assets/cursors/prts-link-hold.png +0 -0
- package/src/assets/cursors/prts-link-press-00.png +0 -0
- package/src/assets/cursors/prts-link-press-01.png +0 -0
- package/src/assets/cursors/prts-link-press-02.png +0 -0
- package/src/assets/cursors/prts-link-press-03.png +0 -0
- package/src/assets/cursors/prts-link-press-04.png +0 -0
- package/src/assets/cursors/prts-link-press-05.png +0 -0
- package/src/assets/cursors/prts-link-release-00.png +0 -0
- package/src/assets/cursors/prts-link-release-01.png +0 -0
- package/src/assets/cursors/prts-link-release-02.png +0 -0
- package/src/assets/cursors/prts-link-release-03.png +0 -0
- package/src/assets/cursors/prts-link-release-04.png +0 -0
- package/src/assets/cursors/prts-link-release-05.png +0 -0
- package/src/assets/cursors/prts-link-release-06.png +0 -0
- package/src/assets/cursors/prts-link-release-07.png +0 -0
- package/src/assets/cursors/prts-link-release-08.png +0 -0
- package/src/assets/cursors/prts-link-release-09.png +0 -0
- package/src/assets/cursors/prts-link-release-10.png +0 -0
- package/src/assets/cursors/prts-link-release-11.png +0 -0
- package/src/assets/cursors/prts-link.png +0 -0
- package/src/assets/cursors/prts-resize-ew.png +0 -0
- package/src/assets/cursors/prts-resize-nesw.png +0 -0
- package/src/assets/cursors/prts-resize-ns.png +0 -0
- package/src/assets/cursors/prts-resize-nwse.png +0 -0
- package/src/client/cursor-adapter.js +143 -34
- package/src/client/theme-settings-workbench.js +62 -8
- package/src/styles/cursor.css +5 -1
- package/src/styles/prts.css +7 -7
- package/src/styles/theme-settings-workbench.css +162 -0
package/package.json
CHANGED
|
@@ -424,5 +424,87 @@
|
|
|
424
424
|
"file": "prts-text-click-84.png",
|
|
425
425
|
"duration": 60000.0
|
|
426
426
|
}
|
|
427
|
-
]
|
|
427
|
+
],
|
|
428
|
+
"link": {
|
|
429
|
+
"hotspot": [
|
|
430
|
+
7,
|
|
431
|
+
7
|
|
432
|
+
],
|
|
433
|
+
"press": [
|
|
434
|
+
{
|
|
435
|
+
"file": "prts-link-press-00.png",
|
|
436
|
+
"duration": 16.666666666666668
|
|
437
|
+
},
|
|
438
|
+
{
|
|
439
|
+
"file": "prts-link-press-01.png",
|
|
440
|
+
"duration": 16.666666666666668
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
"file": "prts-link-press-02.png",
|
|
444
|
+
"duration": 16.666666666666668
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
"file": "prts-link-press-03.png",
|
|
448
|
+
"duration": 16.666666666666668
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
"file": "prts-link-press-04.png",
|
|
452
|
+
"duration": 16.666666666666668
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
"file": "prts-link-press-05.png",
|
|
456
|
+
"duration": 60000.0
|
|
457
|
+
}
|
|
458
|
+
],
|
|
459
|
+
"release": [
|
|
460
|
+
{
|
|
461
|
+
"file": "prts-link-release-00.png",
|
|
462
|
+
"duration": 16.666666666666668
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
"file": "prts-link-release-01.png",
|
|
466
|
+
"duration": 16.666666666666668
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
"file": "prts-link-release-02.png",
|
|
470
|
+
"duration": 16.666666666666668
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
"file": "prts-link-release-03.png",
|
|
474
|
+
"duration": 16.666666666666668
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
"file": "prts-link-release-04.png",
|
|
478
|
+
"duration": 16.666666666666668
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
"file": "prts-link-release-05.png",
|
|
482
|
+
"duration": 16.666666666666668
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
"file": "prts-link-release-06.png",
|
|
486
|
+
"duration": 16.666666666666668
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
"file": "prts-link-release-07.png",
|
|
490
|
+
"duration": 16.666666666666668
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
"file": "prts-link-release-08.png",
|
|
494
|
+
"duration": 16.666666666666668
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
"file": "prts-link-release-09.png",
|
|
498
|
+
"duration": 16.666666666666668
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
"file": "prts-link-release-10.png",
|
|
502
|
+
"duration": 16.666666666666668
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
"file": "prts-link-release-11.png",
|
|
506
|
+
"duration": 60000.0
|
|
507
|
+
}
|
|
508
|
+
]
|
|
509
|
+
}
|
|
428
510
|
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -4,12 +4,121 @@ export function createCursorAdapter({ document, window, assets }) {
|
|
|
4
4
|
let listening = false
|
|
5
5
|
let pointerId
|
|
6
6
|
let timer
|
|
7
|
-
let mode
|
|
8
|
-
let pressX
|
|
9
|
-
let pressY
|
|
10
7
|
let handPhase
|
|
8
|
+
let gestureAssets = assets
|
|
9
|
+
let textTarget
|
|
10
|
+
let lastPoint
|
|
11
|
+
const contentSelector = '[data-markdown], [data-slot*="markdown" i], [class*="markdown" i], [data-chat-flow-kind="user"] [class$="_bubble"]'
|
|
12
|
+
const controlSelector = 'a[href], button, summary, input, textarea, select, [contenteditable], [role="button"], [role="toolbar"], [role="tab"]'
|
|
13
|
+
function clearTextHit() {
|
|
14
|
+
textTarget?.removeAttribute('data-prts-text-hit')
|
|
15
|
+
textTarget = undefined
|
|
16
|
+
}
|
|
17
|
+
function updateTextHit(event) {
|
|
18
|
+
const nextTarget = findTextHit(event)
|
|
19
|
+
if (nextTarget === textTarget) return
|
|
20
|
+
clearTextHit()
|
|
21
|
+
textTarget = nextTarget
|
|
22
|
+
textTarget?.setAttribute('data-prts-text-hit', '')
|
|
23
|
+
}
|
|
24
|
+
function findTextHit(event) {
|
|
25
|
+
if (event.pointerType !== 'mouse') return
|
|
26
|
+
lastPoint = { clientX: event.clientX, clientY: event.clientY, pointerType: 'mouse' }
|
|
27
|
+
const target = event.target?.nodeType === 1 ? event.target : event.target?.parentElement
|
|
28
|
+
const content = target?.closest?.(contentSelector)
|
|
29
|
+
if (!content?.closest('[data-chat-flow-kind], [data-message-role]') || target.closest(controlSelector)) return
|
|
30
|
+
const style = window.getComputedStyle(target)
|
|
31
|
+
if (style.userSelect === 'none' || !style.cursor.includes('data:image/png')) return
|
|
32
|
+
// Caret APIs return the nearest insertion point even over empty space.
|
|
33
|
+
// Check its adjacent character rectangles before treating it as a text hit.
|
|
34
|
+
const caret = document.caretPositionFromPoint?.(event.clientX, event.clientY)
|
|
35
|
+
const fallback = caret ? undefined : document.caretRangeFromPoint?.(event.clientX, event.clientY)
|
|
36
|
+
const node = caret?.offsetNode ?? fallback?.startContainer
|
|
37
|
+
const offset = caret?.offset ?? fallback?.startOffset
|
|
38
|
+
if (node?.nodeType !== 3 || !content.contains(node) || node.parentElement?.closest(controlSelector)) return
|
|
39
|
+
if (window.getComputedStyle(node.parentElement).userSelect === 'none') return
|
|
40
|
+
const range = document.createRange()
|
|
41
|
+
for (const index of [offset - 1, offset]) {
|
|
42
|
+
if (index < 0 || index >= node.length) continue
|
|
43
|
+
range.setStart(node, index)
|
|
44
|
+
range.setEnd(node, index + 1)
|
|
45
|
+
for (const rect of range.getClientRects()) {
|
|
46
|
+
if (rect.width > 0 && event.clientX >= rect.left && event.clientX < rect.right && event.clientY >= rect.top && event.clientY < rect.bottom) {
|
|
47
|
+
return target
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function refreshTextHit() {
|
|
53
|
+
if (!lastPoint) return
|
|
54
|
+
updateTextHit({ ...lastPoint, target: document.elementFromPoint(lastPoint.clientX, lastPoint.clientY) })
|
|
55
|
+
}
|
|
56
|
+
let resizeTarget
|
|
57
|
+
let resizeOriginal
|
|
58
|
+
let resizePriority
|
|
59
|
+
let resizeApplied
|
|
60
|
+
function clearResize() {
|
|
61
|
+
if (resizeTarget && resizeTarget.style.cursor === resizeApplied) {
|
|
62
|
+
if (resizeOriginal) resizeTarget.style.setProperty('cursor', resizeOriginal, resizePriority)
|
|
63
|
+
else resizeTarget.style.removeProperty('cursor')
|
|
64
|
+
}
|
|
65
|
+
resizeTarget = undefined
|
|
66
|
+
}
|
|
67
|
+
function updateResize(event) {
|
|
68
|
+
if (event.pointerType !== 'mouse' || !assets.resize) return
|
|
69
|
+
const target = event.target?.nodeType === 1 ? event.target : event.target?.parentElement
|
|
70
|
+
if (target === resizeTarget && target.style.cursor === resizeApplied) return
|
|
71
|
+
clearResize()
|
|
72
|
+
if (!target) return
|
|
73
|
+
const cursor = window.getComputedStyle(target).cursor
|
|
74
|
+
const direction = { 'n-resize': 'ns', 's-resize': 'ns', 'ns-resize': 'ns', 'row-resize': 'ns',
|
|
75
|
+
'e-resize': 'ew', 'w-resize': 'ew', 'ew-resize': 'ew', 'col-resize': 'ew',
|
|
76
|
+
'ne-resize': 'nesw', 'sw-resize': 'nesw', 'nesw-resize': 'nesw',
|
|
77
|
+
'nw-resize': 'nwse', 'se-resize': 'nwse', 'nwse-resize': 'nwse' }[cursor]
|
|
78
|
+
if (!direction) return
|
|
79
|
+
resizeTarget = target
|
|
80
|
+
resizeOriginal = target.style.getPropertyValue('cursor')
|
|
81
|
+
resizePriority = target.style.getPropertyPriority('cursor')
|
|
82
|
+
target.style.setProperty('cursor', `url("${assets.resize[direction]}") 16 16, ${cursor}`, resizePriority)
|
|
83
|
+
resizeApplied = target.style.cursor
|
|
84
|
+
}
|
|
11
85
|
const reducedMedia = window?.matchMedia?.('(prefers-reduced-motion: reduce)')
|
|
12
|
-
|
|
86
|
+
let handTarget
|
|
87
|
+
let handCursor
|
|
88
|
+
let originalCursor
|
|
89
|
+
let originalPriority
|
|
90
|
+
let appliedCursor
|
|
91
|
+
function restoreHandTarget() {
|
|
92
|
+
if (handTarget && handTarget.style.cursor === appliedCursor) {
|
|
93
|
+
if (originalCursor) handTarget.style.setProperty('cursor', originalCursor, originalPriority)
|
|
94
|
+
else handTarget.style.removeProperty('cursor')
|
|
95
|
+
}
|
|
96
|
+
handTarget?.removeAttribute('data-prts-hand-active')
|
|
97
|
+
handTarget = undefined
|
|
98
|
+
}
|
|
99
|
+
// Cursor is inherited. Animating a large container still restyles its subtree.
|
|
100
|
+
// Keep those background hits static, while controls retain native animation.
|
|
101
|
+
const maxAnimatedDescendants = 128
|
|
102
|
+
function canAnimate(target) {
|
|
103
|
+
return target && target.getElementsByTagName('*').length <= maxAnimatedDescendants
|
|
104
|
+
}
|
|
105
|
+
function followHand(target) {
|
|
106
|
+
if (target === handTarget && target !== textTarget) return
|
|
107
|
+
restoreHandTarget()
|
|
108
|
+
if (!canAnimate(target) || !handCursor || target === textTarget) return
|
|
109
|
+
const cursor = window.getComputedStyle(target).cursor
|
|
110
|
+
if (!cursor.includes('data:image/png') || /(?:,\s*text|resize)$/.test(cursor)) return
|
|
111
|
+
handTarget = target
|
|
112
|
+
originalCursor = target.style.getPropertyValue('cursor')
|
|
113
|
+
originalPriority = target.style.getPropertyPriority('cursor')
|
|
114
|
+
target.setAttribute('data-prts-hand-active', '')
|
|
115
|
+
applyHandFrame()
|
|
116
|
+
}
|
|
117
|
+
function applyHandFrame() {
|
|
118
|
+
if (!handTarget) return
|
|
119
|
+
handTarget.style.setProperty('cursor', handCursor)
|
|
120
|
+
appliedCursor = handTarget.style.cursor
|
|
121
|
+
}
|
|
13
122
|
function clearTimer() {
|
|
14
123
|
if (timer !== undefined) window.clearTimeout(timer)
|
|
15
124
|
timer = undefined
|
|
@@ -17,14 +126,15 @@ export function createCursorAdapter({ document, window, assets }) {
|
|
|
17
126
|
function reset() {
|
|
18
127
|
clearTimer()
|
|
19
128
|
pointerId = undefined
|
|
20
|
-
|
|
129
|
+
restoreHandTarget()
|
|
130
|
+
handCursor = undefined
|
|
21
131
|
root?.style.removeProperty('--prts-text-frame')
|
|
22
|
-
mode = undefined
|
|
23
132
|
handPhase = undefined
|
|
24
133
|
}
|
|
25
134
|
function show(url) {
|
|
26
|
-
const [x, y] =
|
|
27
|
-
|
|
135
|
+
const [x, y] = gestureAssets.hotspot
|
|
136
|
+
handCursor = `url("${url}") ${x} ${y}, pointer`
|
|
137
|
+
applyHandFrame()
|
|
28
138
|
}
|
|
29
139
|
function reduced() {
|
|
30
140
|
return root.dataset.prtsMotion === 'reduced' || reducedMedia?.matches
|
|
@@ -44,59 +154,58 @@ export function createCursorAdapter({ document, window, assets }) {
|
|
|
44
154
|
}
|
|
45
155
|
function down(event) {
|
|
46
156
|
if (event.pointerType !== 'mouse' || event.button !== 0 || event.isPrimary === false) return
|
|
157
|
+
updateResize(event)
|
|
158
|
+
updateTextHit(event)
|
|
47
159
|
const target = event.target?.nodeType === 1 ? event.target : event.target?.parentElement
|
|
48
|
-
const cursor = target ? window.getComputedStyle(target).cursor : ''
|
|
49
160
|
reset()
|
|
161
|
+
const cursor = target ? window.getComputedStyle(target).cursor : ''
|
|
50
162
|
if (!cursor.includes('data:image/png')) return
|
|
51
|
-
|
|
163
|
+
if (/(?:,\s*text|resize)$/.test(cursor) || !canAnimate(target)) return
|
|
164
|
+
gestureAssets = /,\s*pointer$/.test(cursor) && assets.link ? assets.link : assets
|
|
52
165
|
pointerId = event.pointerId
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (
|
|
56
|
-
if (reduced() || !assets.textClick) { reset(); return }
|
|
57
|
-
play(assets.textClick, reset)
|
|
58
|
-
return
|
|
59
|
-
}
|
|
60
|
-
if (reduced()) { clearTimer(); show(assets.hold); return }
|
|
166
|
+
handCursor = cursor
|
|
167
|
+
followHand(target)
|
|
168
|
+
if (reduced()) { clearTimer(); show(gestureAssets.hold); return }
|
|
61
169
|
handPhase = 'press'
|
|
62
|
-
play(
|
|
170
|
+
play(gestureAssets.press, () => {
|
|
63
171
|
handPhase = 'hold'
|
|
64
|
-
show(
|
|
172
|
+
show(gestureAssets.hold)
|
|
65
173
|
// A quick tap must still reach a visible pressed pose before recovering.
|
|
66
174
|
if (pointerId === undefined) timer = window.setTimeout(releaseHand, 48)
|
|
67
175
|
})
|
|
68
176
|
}
|
|
69
177
|
function releaseHand() {
|
|
70
178
|
handPhase = 'release'
|
|
71
|
-
play(
|
|
179
|
+
play(gestureAssets.release, reset)
|
|
72
180
|
}
|
|
73
181
|
function up(event) {
|
|
74
182
|
if (pointerId === undefined || event.pointerId !== pointerId || event.button !== 0) return
|
|
75
183
|
pointerId = undefined
|
|
76
|
-
if (mode === 'text') return
|
|
77
184
|
if (reduced()) { reset(); return }
|
|
78
185
|
if (handPhase === 'press') return
|
|
79
186
|
releaseHand()
|
|
80
187
|
}
|
|
81
188
|
function move(event) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const selecting = pointerId === event.pointerId && (event.buttons & 1) && Math.hypot(event.clientX - pressX, event.clientY - pressY) > 4
|
|
86
|
-
if (!/,\s*text$/.test(cursor) || selecting) reset()
|
|
87
|
-
return
|
|
88
|
-
}
|
|
189
|
+
updateResize(event)
|
|
190
|
+
updateTextHit(event)
|
|
191
|
+
if (handCursor) followHand(event.target?.nodeType === 1 ? event.target : event.target?.parentElement)
|
|
89
192
|
if (pointerId !== undefined && event.pointerId === pointerId && !(event.buttons & 1)) reset()
|
|
90
193
|
}
|
|
91
|
-
function
|
|
194
|
+
function resetInteraction() {
|
|
195
|
+
reset()
|
|
196
|
+
clearResize()
|
|
197
|
+
clearTextHit()
|
|
198
|
+
lastPoint = undefined
|
|
199
|
+
}
|
|
200
|
+
function leave(event) { if (!event.relatedTarget) resetInteraction() }
|
|
92
201
|
function blur(event) {
|
|
93
202
|
// Captured element blur also reaches window when focus changes inside the app.
|
|
94
|
-
if (event.target === window)
|
|
203
|
+
if (event.target === window) resetInteraction()
|
|
95
204
|
}
|
|
96
|
-
function visibility() { if (document.hidden)
|
|
97
|
-
const events = [['pointerdown', down], ['pointerup', up], ['pointermove', move], ['pointercancel',
|
|
205
|
+
function visibility() { if (document.hidden) resetInteraction() }
|
|
206
|
+
const events = [['pointerdown', down], ['pointerup', up], ['pointermove', move], ['pointercancel', resetInteraction], ['blur', blur], ['dragstart', resetInteraction], ['pointerout', leave], ['scroll', refreshTextHit], ['resize', refreshTextHit]]
|
|
98
207
|
function dispose() {
|
|
99
|
-
|
|
208
|
+
resetInteraction()
|
|
100
209
|
if (!listening) return
|
|
101
210
|
for (const [name, handler] of events) window.removeEventListener(name, handler, true)
|
|
102
211
|
document.removeEventListener('visibilitychange', visibility)
|
|
@@ -15,7 +15,7 @@ const PRESET_OPTIONS = [
|
|
|
15
15
|
]
|
|
16
16
|
|
|
17
17
|
const COMMON_CONTROLS = [
|
|
18
|
-
['cursor', '
|
|
18
|
+
['cursor', 'PRTS 光标', [['system', '关闭'], ['prts', '开启']]],
|
|
19
19
|
['railDefaultHidden', '默认隐藏导航栏', [['false', '关闭'], ['true', '开启']]],
|
|
20
20
|
['texture', '环境底纹', [['off', '关闭'], ['restrained', '克制'], ['full', '完整']]],
|
|
21
21
|
['glass', '玻璃材质', [['off', '关闭'], ['soft', '柔和'], ['standard', '标准'], ['clear', '清晰']]],
|
|
@@ -53,6 +53,10 @@ function settingButtons(key, label, options) {
|
|
|
53
53
|
: key === 'sessionFlowPalette'
|
|
54
54
|
? '<small data-prts-setting-note>同时适配日间与夜间模式</small>'
|
|
55
55
|
: ''
|
|
56
|
+
if (['cursor', 'railDefaultHidden', 'sessionFlow', 'sessionGlow'].includes(key)) return `<div data-prts-setting-row="${key}">
|
|
57
|
+
<span><strong>${label}</strong>${note}</span>
|
|
58
|
+
<button type="button" role="switch" aria-label="${label}" aria-checked="false" data-prts-setting-switch="${key}" ${key === 'cursor' ? 'data-prts-cursor-switch' : ''}><span aria-hidden="true"></span></button>
|
|
59
|
+
</div>`
|
|
56
60
|
const sample = value => key === 'texture' || key === 'glass' || key === 'sessionFlowPalette'
|
|
57
61
|
? `<i data-prts-option-sample="${key}" data-prts-option-sample-value="${value}" aria-hidden="true"></i>`
|
|
58
62
|
: ''
|
|
@@ -114,26 +118,47 @@ export function themeSettingsMarkup() {
|
|
|
114
118
|
<button type="button" data-prts-theme-settings-close aria-label="关闭主题设置">×</button>
|
|
115
119
|
</header>
|
|
116
120
|
|
|
121
|
+
<div data-prts-settings-body>
|
|
122
|
+
<nav data-prts-settings-nav aria-label="设置分区">
|
|
123
|
+
<button type="button" data-prts-settings-jump="appearance">外观</button>
|
|
124
|
+
<button type="button" data-prts-settings-jump="interaction">交互</button>
|
|
125
|
+
<button type="button" data-prts-settings-jump="flow">会话流线</button>
|
|
126
|
+
<button type="button" data-prts-settings-jump="particles">粒子效果</button>
|
|
127
|
+
</nav>
|
|
117
128
|
<div data-prts-theme-settings-scroll>
|
|
118
|
-
<section data-prts-settings-presets>
|
|
119
|
-
<header><span><
|
|
129
|
+
<section data-prts-settings-presets data-prts-settings-section="appearance" tabindex="-1">
|
|
130
|
+
<header><span><strong>视觉预设</strong></span></header>
|
|
120
131
|
<div>${presetMarkup()}</div>
|
|
121
132
|
</section>
|
|
122
133
|
|
|
123
134
|
<section data-prts-settings-common>
|
|
124
|
-
<header><span><
|
|
125
|
-
<div data-prts-settings-common-grid
|
|
135
|
+
<header><span><strong>界面外观</strong></span></header>
|
|
136
|
+
<div data-prts-settings-common-grid>
|
|
137
|
+
${COMMON_CONTROLS.filter(([key]) => ['texture', 'glass'].includes(key)).map(([key, label, options]) => settingButtons(key, label, options)).join('')}
|
|
138
|
+
${settingButtons('conversationStyle', '会话主题', [['native', '原有'], ['deck-chat', '通讯链路']])}
|
|
139
|
+
</div>
|
|
140
|
+
</section>
|
|
141
|
+
<section data-prts-settings-common data-prts-settings-section="interaction" tabindex="-1">
|
|
142
|
+
<header><span><strong>交互</strong></span></header>
|
|
143
|
+
<div data-prts-settings-common-grid>
|
|
144
|
+
${COMMON_CONTROLS.filter(([key]) => ['cursor', 'railDefaultHidden', 'motion'].includes(key)).map(([key, label, options]) => settingButtons(key, label, options)).join('')}
|
|
145
|
+
</div>
|
|
146
|
+
</section>
|
|
147
|
+
<section data-prts-settings-common data-prts-settings-section="flow" tabindex="-1">
|
|
148
|
+
<header><span><strong>会话流线</strong></span></header>
|
|
149
|
+
<div data-prts-settings-common-grid>
|
|
150
|
+
${COMMON_CONTROLS.filter(([key]) => ['sessionFlow', 'sessionGlow', 'sessionFlowPalette'].includes(key)).map(([key, label, options]) => settingButtons(key, label, options)).join('')}
|
|
126
151
|
${sessionFlowColorMarkup()}
|
|
127
152
|
<label data-prts-setting-row="sessionFlowSpeed" data-prts-setting-range-row data-prts-session-flow-speed>
|
|
128
153
|
<span><strong>流线速度</strong><small data-prts-setting-note>控制当前会话流线的形变速度</small></span>
|
|
129
154
|
<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>
|
|
130
155
|
</label>
|
|
131
|
-
|
|
156
|
+
|
|
132
157
|
</div>
|
|
133
158
|
</section>
|
|
134
159
|
|
|
135
|
-
<details data-prts-settings-advanced>
|
|
136
|
-
<summary><span><
|
|
160
|
+
<details data-prts-settings-advanced data-prts-settings-section="particles" tabindex="-1">
|
|
161
|
+
<summary><span><strong>粒子效果</strong></span><output data-prts-advanced-summary>标准 / 1×</output></summary>
|
|
137
162
|
<div data-prts-settings-advanced-content>
|
|
138
163
|
<section data-prts-particle-detail>
|
|
139
164
|
<header><strong>粒子精度</strong><span>同时调整会话与新会话徽记</span></header>
|
|
@@ -163,6 +188,7 @@ export function themeSettingsMarkup() {
|
|
|
163
188
|
</div>
|
|
164
189
|
</footer>
|
|
165
190
|
</div>
|
|
191
|
+
</div>
|
|
166
192
|
</div>
|
|
167
193
|
</dialog>`
|
|
168
194
|
}
|
|
@@ -429,6 +455,18 @@ export function createThemeSettingsOverlay({
|
|
|
429
455
|
|
|
430
456
|
function renderState() {
|
|
431
457
|
if (!panel || !currentPreferences) return
|
|
458
|
+
for (const toggle of panel.querySelectorAll('[data-prts-setting-switch]')) {
|
|
459
|
+
const key = toggle.dataset.prtsSettingSwitch
|
|
460
|
+
const enabled = key === 'cursor' ? currentPreferences.cursor === 'prts'
|
|
461
|
+
: key === 'railDefaultHidden' ? currentPreferences[key] === true : currentPreferences[key] !== false
|
|
462
|
+
toggle.setAttribute('aria-checked', String(enabled))
|
|
463
|
+
toggle.classList.toggle('is-active', enabled)
|
|
464
|
+
}
|
|
465
|
+
for (const key of ['sessionGlow', 'sessionFlowPalette', 'sessionFlowColors', 'sessionFlowSpeed']) {
|
|
466
|
+
const row = panel.querySelector(`[data-prts-setting-row="${key}"]`)
|
|
467
|
+
if (row) row.hidden = currentPreferences.sessionFlow === false
|
|
468
|
+
|| (key === 'sessionFlowColors' && activeSessionFlowPalette() !== 'custom')
|
|
469
|
+
}
|
|
432
470
|
const particleDetail = resolveParticleDetail(currentPreferences)
|
|
433
471
|
for (const button of panel.querySelectorAll('button[data-prts-setting-key]')) {
|
|
434
472
|
const key = button.dataset.prtsSettingKey
|
|
@@ -482,7 +520,23 @@ export function createThemeSettingsOverlay({
|
|
|
482
520
|
if (event.target === panel) { setOpen(false); return }
|
|
483
521
|
const target = event.target?.closest?.('button')
|
|
484
522
|
if (!target) return
|
|
523
|
+
if (target.hasAttribute('data-prts-settings-jump')) {
|
|
524
|
+
const section = panel.querySelector(`[data-prts-settings-section="${target.dataset.prtsSettingsJump}"]`)
|
|
525
|
+
const scroller = panel.querySelector('[data-prts-theme-settings-scroll]')
|
|
526
|
+
if (section && scroller) {
|
|
527
|
+
if (section.tagName === 'DETAILS') section.open = true
|
|
528
|
+
scroller.scrollTo?.({ top: target.dataset.prtsSettingsJump === 'appearance' ? 0 : scroller.scrollTop + section.getBoundingClientRect().top - scroller.getBoundingClientRect().top, behavior: 'instant' })
|
|
529
|
+
section.focus?.({ preventScroll: true })
|
|
530
|
+
}
|
|
531
|
+
return
|
|
532
|
+
}
|
|
485
533
|
if (target.hasAttribute('data-prts-theme-settings-close')) { setOpen(false); return }
|
|
534
|
+
if (target.hasAttribute('data-prts-setting-switch')) {
|
|
535
|
+
const key = target.dataset.prtsSettingSwitch
|
|
536
|
+
const enabled = target.getAttribute('aria-checked') === 'true'
|
|
537
|
+
onPreferenceChange(key, key === 'cursor' ? (enabled ? 'system' : 'prts') : !enabled)
|
|
538
|
+
return
|
|
539
|
+
}
|
|
486
540
|
if (target.hasAttribute('data-prts-setting-key')) {
|
|
487
541
|
const raw = target.dataset.prtsSettingValue
|
|
488
542
|
if (target.dataset.prtsSettingKey === 'sessionFlowPalette') {
|
package/src/styles/cursor.css
CHANGED
|
@@ -3,10 +3,14 @@
|
|
|
3
3
|
html[data-dsh-prts][data-prts-cursor="prts"] {
|
|
4
4
|
--prts-hand-cursor: var(--prts-hand-frame, url("__PRTS_HAND__") 7 6);
|
|
5
5
|
--prts-text-cursor: var(--prts-text-frame, url("__PRTS_TEXT__") 24 24);
|
|
6
|
+
--prts-link-cursor: url("__PRTS_LINK__") 7 7;
|
|
6
7
|
cursor: var(--prts-hand-cursor), default;
|
|
7
8
|
}
|
|
8
9
|
html[data-dsh-prts][data-prts-cursor="prts"] :is(button, a[href], summary, [role="button"], [role="tab"], [role="treeitem"], [role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"], [role="option"], [data-prts-workspace-row], [data-prts-session-row], .dsh-partner-sidebar-row, select, input[type="button"], input[type="submit"], input[type="checkbox"], input[type="radio"], input[type="color"], input[type="range"]) {
|
|
9
|
-
cursor: var(--prts-
|
|
10
|
+
cursor: var(--prts-link-cursor), pointer;
|
|
11
|
+
}
|
|
12
|
+
html[data-dsh-prts][data-prts-cursor="prts"] [data-prts-text-hit] {
|
|
13
|
+
cursor: var(--prts-text-cursor), text;
|
|
10
14
|
}
|
|
11
15
|
html[data-dsh-prts][data-prts-cursor="prts"] :is(textarea, input:not([type]), input[type="text"], input[type="search"], input[type="email"], input[type="url"], input[type="password"], input[type="number"], input[type="tel"], [contenteditable=""], [contenteditable="true"], [contenteditable="plaintext-only"]) {
|
|
12
16
|
cursor: var(--prts-text-cursor), text;
|
package/src/styles/prts.css
CHANGED
|
@@ -1712,7 +1712,7 @@ html[data-dsh-prts-settings] .prts-plugin-settings__copy strong {
|
|
|
1712
1712
|
font-weight: 600;
|
|
1713
1713
|
}
|
|
1714
1714
|
|
|
1715
|
-
html[data-dsh-prts-settings] .prts-plugin-settings button[role='switch'] {
|
|
1715
|
+
html[data-dsh-prts-settings] :is(.prts-plugin-settings button[role='switch'], [data-prts-setting-switch]) {
|
|
1716
1716
|
position: relative;
|
|
1717
1717
|
width: 42px;
|
|
1718
1718
|
height: 24px;
|
|
@@ -1725,7 +1725,7 @@ html[data-dsh-prts-settings] .prts-plugin-settings button[role='switch'] {
|
|
|
1725
1725
|
cursor: pointer;
|
|
1726
1726
|
}
|
|
1727
1727
|
|
|
1728
|
-
html[data-dsh-prts-settings] .prts-plugin-settings button[role='switch'] > span {
|
|
1728
|
+
html[data-dsh-prts-settings] :is(.prts-plugin-settings button[role='switch'], [data-prts-setting-switch]) > span {
|
|
1729
1729
|
position: absolute;
|
|
1730
1730
|
top: 3px;
|
|
1731
1731
|
left: 3px;
|
|
@@ -1736,22 +1736,22 @@ html[data-dsh-prts-settings] .prts-plugin-settings button[role='switch'] > span
|
|
|
1736
1736
|
transition: transform 120ms ease;
|
|
1737
1737
|
}
|
|
1738
1738
|
|
|
1739
|
-
html[data-dsh-prts-settings] .prts-plugin-settings button[role='switch'].is-active {
|
|
1739
|
+
html[data-dsh-prts-settings] :is(.prts-plugin-settings button[role='switch'], [data-prts-setting-switch]).is-active {
|
|
1740
1740
|
background: var(--color-primary, #d9b800);
|
|
1741
1741
|
color: #111;
|
|
1742
1742
|
}
|
|
1743
1743
|
|
|
1744
|
-
html[data-dsh-prts-settings] .prts-plugin-settings button[role='switch'].is-active > span {
|
|
1744
|
+
html[data-dsh-prts-settings] :is(.prts-plugin-settings button[role='switch'], [data-prts-setting-switch]).is-active > span {
|
|
1745
1745
|
transform: translateX(18px);
|
|
1746
1746
|
}
|
|
1747
1747
|
|
|
1748
|
-
html[data-dsh-prts-settings] .prts-plugin-settings button[role='switch']:disabled {
|
|
1748
|
+
html[data-dsh-prts-settings] :is(.prts-plugin-settings button[role='switch'], [data-prts-setting-switch]):disabled {
|
|
1749
1749
|
cursor: not-allowed;
|
|
1750
1750
|
opacity: .45;
|
|
1751
1751
|
}
|
|
1752
1752
|
|
|
1753
1753
|
@media (prefers-reduced-motion: reduce) {
|
|
1754
|
-
html[data-dsh-prts-settings] .prts-plugin-settings button[role='switch'] > span {
|
|
1754
|
+
html[data-dsh-prts-settings] :is(.prts-plugin-settings button[role='switch'], [data-prts-setting-switch]) > span {
|
|
1755
1755
|
transition: none;
|
|
1756
1756
|
}
|
|
1757
1757
|
}
|
|
@@ -1763,7 +1763,7 @@ html[data-dsh-prts-settings] .prts-plugin-settings button[role='switch']:disable
|
|
|
1763
1763
|
gap: 10px;
|
|
1764
1764
|
}
|
|
1765
1765
|
|
|
1766
|
-
html[data-dsh-prts-settings] .prts-plugin-settings button[role='switch'] {
|
|
1766
|
+
html[data-dsh-prts-settings] :is(.prts-plugin-settings button[role='switch'], [data-prts-setting-switch]) {
|
|
1767
1767
|
align-self: flex-end;
|
|
1768
1768
|
}
|
|
1769
1769
|
}
|