@ash-qw/dsh-theme-prts 1.0.2 → 1.0.4
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 +11 -0
- package/README.en.md +6 -4
- package/README.md +7 -5
- package/THIRD_PARTY_NOTICES.md +9 -12
- package/THIRD_PARTY_NOTICES.zh-CN.md +45 -0
- package/lib/client.js +126 -4
- package/package.json +4 -3
- package/src/assets/cursors/animation.json +428 -0
- package/src/assets/cursors/prts-hand-hold.png +0 -0
- package/src/assets/cursors/prts-hand-press-00.png +0 -0
- package/src/assets/cursors/prts-hand-press-01.png +0 -0
- package/src/assets/cursors/prts-hand-press-02.png +0 -0
- package/src/assets/cursors/prts-hand-press-03.png +0 -0
- package/src/assets/cursors/prts-hand-press-04.png +0 -0
- package/src/assets/cursors/prts-hand-press-05.png +0 -0
- package/src/assets/cursors/prts-hand-release-00.png +0 -0
- package/src/assets/cursors/prts-hand-release-01.png +0 -0
- package/src/assets/cursors/prts-hand-release-02.png +0 -0
- package/src/assets/cursors/prts-hand-release-03.png +0 -0
- package/src/assets/cursors/prts-hand-release-04.png +0 -0
- package/src/assets/cursors/prts-hand-release-05.png +0 -0
- package/src/assets/cursors/prts-hand-release-06.png +0 -0
- package/src/assets/cursors/prts-hand-release-07.png +0 -0
- package/src/assets/cursors/prts-hand-release-08.png +0 -0
- package/src/assets/cursors/prts-hand-release-09.png +0 -0
- package/src/assets/cursors/prts-hand-release-10.png +0 -0
- package/src/assets/cursors/prts-hand-release-11.png +0 -0
- package/src/assets/cursors/prts-hand.png +0 -0
- package/src/assets/cursors/prts-text-click-00.png +0 -0
- package/src/assets/cursors/prts-text-click-01.png +0 -0
- package/src/assets/cursors/prts-text-click-02.png +0 -0
- package/src/assets/cursors/prts-text-click-03.png +0 -0
- package/src/assets/cursors/prts-text-click-04.png +0 -0
- package/src/assets/cursors/prts-text-click-05.png +0 -0
- package/src/assets/cursors/prts-text-click-06.png +0 -0
- package/src/assets/cursors/prts-text-click-07.png +0 -0
- package/src/assets/cursors/prts-text-click-08.png +0 -0
- package/src/assets/cursors/prts-text-click-09.png +0 -0
- package/src/assets/cursors/prts-text-click-10.png +0 -0
- package/src/assets/cursors/prts-text-click-11.png +0 -0
- package/src/assets/cursors/prts-text-click-12.png +0 -0
- package/src/assets/cursors/prts-text-click-13.png +0 -0
- package/src/assets/cursors/prts-text-click-14.png +0 -0
- package/src/assets/cursors/prts-text-click-15.png +0 -0
- package/src/assets/cursors/prts-text-click-16.png +0 -0
- package/src/assets/cursors/prts-text-click-17.png +0 -0
- package/src/assets/cursors/prts-text-click-18.png +0 -0
- package/src/assets/cursors/prts-text-click-19.png +0 -0
- package/src/assets/cursors/prts-text-click-20.png +0 -0
- package/src/assets/cursors/prts-text-click-21.png +0 -0
- package/src/assets/cursors/prts-text-click-22.png +0 -0
- package/src/assets/cursors/prts-text-click-23.png +0 -0
- package/src/assets/cursors/prts-text-click-24.png +0 -0
- package/src/assets/cursors/prts-text-click-25.png +0 -0
- package/src/assets/cursors/prts-text-click-26.png +0 -0
- package/src/assets/cursors/prts-text-click-27.png +0 -0
- package/src/assets/cursors/prts-text-click-28.png +0 -0
- package/src/assets/cursors/prts-text-click-29.png +0 -0
- package/src/assets/cursors/prts-text-click-30.png +0 -0
- package/src/assets/cursors/prts-text-click-31.png +0 -0
- package/src/assets/cursors/prts-text-click-32.png +0 -0
- package/src/assets/cursors/prts-text-click-33.png +0 -0
- package/src/assets/cursors/prts-text-click-34.png +0 -0
- package/src/assets/cursors/prts-text-click-35.png +0 -0
- package/src/assets/cursors/prts-text-click-36.png +0 -0
- package/src/assets/cursors/prts-text-click-37.png +0 -0
- package/src/assets/cursors/prts-text-click-38.png +0 -0
- package/src/assets/cursors/prts-text-click-39.png +0 -0
- package/src/assets/cursors/prts-text-click-40.png +0 -0
- package/src/assets/cursors/prts-text-click-41.png +0 -0
- package/src/assets/cursors/prts-text-click-42.png +0 -0
- package/src/assets/cursors/prts-text-click-43.png +0 -0
- package/src/assets/cursors/prts-text-click-44.png +0 -0
- package/src/assets/cursors/prts-text-click-45.png +0 -0
- package/src/assets/cursors/prts-text-click-46.png +0 -0
- package/src/assets/cursors/prts-text-click-47.png +0 -0
- package/src/assets/cursors/prts-text-click-48.png +0 -0
- package/src/assets/cursors/prts-text-click-49.png +0 -0
- package/src/assets/cursors/prts-text-click-50.png +0 -0
- package/src/assets/cursors/prts-text-click-51.png +0 -0
- package/src/assets/cursors/prts-text-click-52.png +0 -0
- package/src/assets/cursors/prts-text-click-53.png +0 -0
- package/src/assets/cursors/prts-text-click-54.png +0 -0
- package/src/assets/cursors/prts-text-click-55.png +0 -0
- package/src/assets/cursors/prts-text-click-56.png +0 -0
- package/src/assets/cursors/prts-text-click-57.png +0 -0
- package/src/assets/cursors/prts-text-click-58.png +0 -0
- package/src/assets/cursors/prts-text-click-59.png +0 -0
- package/src/assets/cursors/prts-text-click-60.png +0 -0
- package/src/assets/cursors/prts-text-click-61.png +0 -0
- package/src/assets/cursors/prts-text-click-62.png +0 -0
- package/src/assets/cursors/prts-text-click-63.png +0 -0
- package/src/assets/cursors/prts-text-click-64.png +0 -0
- package/src/assets/cursors/prts-text-click-65.png +0 -0
- package/src/assets/cursors/prts-text-click-66.png +0 -0
- package/src/assets/cursors/prts-text-click-67.png +0 -0
- package/src/assets/cursors/prts-text-click-68.png +0 -0
- package/src/assets/cursors/prts-text-click-69.png +0 -0
- package/src/assets/cursors/prts-text-click-70.png +0 -0
- package/src/assets/cursors/prts-text-click-71.png +0 -0
- package/src/assets/cursors/prts-text-click-72.png +0 -0
- package/src/assets/cursors/prts-text-click-73.png +0 -0
- package/src/assets/cursors/prts-text-click-74.png +0 -0
- package/src/assets/cursors/prts-text-click-75.png +0 -0
- package/src/assets/cursors/prts-text-click-76.png +0 -0
- package/src/assets/cursors/prts-text-click-77.png +0 -0
- package/src/assets/cursors/prts-text-click-78.png +0 -0
- package/src/assets/cursors/prts-text-click-79.png +0 -0
- package/src/assets/cursors/prts-text-click-80.png +0 -0
- package/src/assets/cursors/prts-text-click-81.png +0 -0
- package/src/assets/cursors/prts-text-click-82.png +0 -0
- package/src/assets/cursors/prts-text-click-83.png +0 -0
- package/src/assets/cursors/prts-text-click-84.png +0 -0
- package/src/assets/cursors/prts-text.png +0 -0
- package/src/client/cursor-adapter.js +115 -0
- package/src/client/index.js +1 -0
- package/src/client/preferences.js +3 -1
- package/src/client/theme-controller-v2.js +7 -1
- package/src/client/theme-settings-workbench.js +1 -0
- package/src/styles/cursor.css +17 -0
|
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
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Keep the native cursor and change its image only during a primary mouse gesture.
|
|
2
|
+
export function createCursorAdapter({ document, window, assets }) {
|
|
3
|
+
const root = document?.documentElement
|
|
4
|
+
let listening = false
|
|
5
|
+
let pointerId
|
|
6
|
+
let timer
|
|
7
|
+
let mode
|
|
8
|
+
let pressX
|
|
9
|
+
let pressY
|
|
10
|
+
let handPhase
|
|
11
|
+
const reducedMedia = window?.matchMedia?.('(prefers-reduced-motion: reduce)')
|
|
12
|
+
const frameProperty = '--prts-hand-frame'
|
|
13
|
+
function clearTimer() {
|
|
14
|
+
if (timer !== undefined) window.clearTimeout(timer)
|
|
15
|
+
timer = undefined
|
|
16
|
+
}
|
|
17
|
+
function reset() {
|
|
18
|
+
clearTimer()
|
|
19
|
+
pointerId = undefined
|
|
20
|
+
root?.style.removeProperty(frameProperty)
|
|
21
|
+
root?.style.removeProperty('--prts-text-frame')
|
|
22
|
+
mode = undefined
|
|
23
|
+
handPhase = undefined
|
|
24
|
+
}
|
|
25
|
+
function show(url) {
|
|
26
|
+
const [x, y] = mode === 'text' ? assets.textHotspot : assets.hotspot
|
|
27
|
+
root.style.setProperty(mode === 'text' ? '--prts-text-frame' : frameProperty, `url("${url}") ${x} ${y}`)
|
|
28
|
+
}
|
|
29
|
+
function reduced() {
|
|
30
|
+
return root.dataset.prtsMotion === 'reduced' || reducedMedia?.matches
|
|
31
|
+
}
|
|
32
|
+
function play(frames, done) {
|
|
33
|
+
clearTimer()
|
|
34
|
+
let index = 0
|
|
35
|
+
function next() {
|
|
36
|
+
timer = undefined
|
|
37
|
+
// The source ANI ends with a 60-second hold frame. Use the static state instead.
|
|
38
|
+
if (index >= frames.length - 1) { done(); return }
|
|
39
|
+
const frame = frames[index++]
|
|
40
|
+
show(frame.url)
|
|
41
|
+
timer = window.setTimeout(next, frame.duration)
|
|
42
|
+
}
|
|
43
|
+
next()
|
|
44
|
+
}
|
|
45
|
+
function down(event) {
|
|
46
|
+
if (event.pointerType !== 'mouse' || event.button !== 0 || event.isPrimary === false) return
|
|
47
|
+
const target = event.target?.nodeType === 1 ? event.target : event.target?.parentElement
|
|
48
|
+
const cursor = target ? window.getComputedStyle(target).cursor : ''
|
|
49
|
+
reset()
|
|
50
|
+
if (!cursor.includes('data:image/png')) return
|
|
51
|
+
mode = /,\s*text$/.test(cursor) ? 'text' : 'hand'
|
|
52
|
+
pointerId = event.pointerId
|
|
53
|
+
pressX = event.clientX
|
|
54
|
+
pressY = event.clientY
|
|
55
|
+
if (mode === 'text') {
|
|
56
|
+
if (reduced() || !assets.textClick) { reset(); return }
|
|
57
|
+
play(assets.textClick, reset)
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
if (reduced()) { clearTimer(); show(assets.hold); return }
|
|
61
|
+
handPhase = 'press'
|
|
62
|
+
play(assets.press, () => {
|
|
63
|
+
handPhase = 'hold'
|
|
64
|
+
show(assets.hold)
|
|
65
|
+
// A quick tap must still reach a visible pressed pose before recovering.
|
|
66
|
+
if (pointerId === undefined) timer = window.setTimeout(releaseHand, 48)
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
function releaseHand() {
|
|
70
|
+
handPhase = 'release'
|
|
71
|
+
play(assets.release, reset)
|
|
72
|
+
}
|
|
73
|
+
function up(event) {
|
|
74
|
+
if (pointerId === undefined || event.pointerId !== pointerId || event.button !== 0) return
|
|
75
|
+
pointerId = undefined
|
|
76
|
+
if (mode === 'text') return
|
|
77
|
+
if (reduced()) { reset(); return }
|
|
78
|
+
if (handPhase === 'press') return
|
|
79
|
+
releaseHand()
|
|
80
|
+
}
|
|
81
|
+
function move(event) {
|
|
82
|
+
if (mode === 'text') {
|
|
83
|
+
const target = event.target?.nodeType === 1 ? event.target : event.target?.parentElement
|
|
84
|
+
const cursor = target ? window.getComputedStyle(target).cursor : ''
|
|
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
|
+
}
|
|
89
|
+
if (pointerId !== undefined && event.pointerId === pointerId && !(event.buttons & 1)) reset()
|
|
90
|
+
}
|
|
91
|
+
function leave(event) { if (!event.relatedTarget) reset() }
|
|
92
|
+
function blur(event) {
|
|
93
|
+
// Captured element blur also reaches window when focus changes inside the app.
|
|
94
|
+
if (event.target === window) reset()
|
|
95
|
+
}
|
|
96
|
+
function visibility() { if (document.hidden) reset() }
|
|
97
|
+
const events = [['pointerdown', down], ['pointerup', up], ['pointermove', move], ['pointercancel', reset], ['blur', blur], ['dragstart', reset], ['pointerout', leave]]
|
|
98
|
+
function dispose() {
|
|
99
|
+
reset()
|
|
100
|
+
if (!listening) return
|
|
101
|
+
for (const [name, handler] of events) window.removeEventListener(name, handler, true)
|
|
102
|
+
document.removeEventListener('visibilitychange', visibility)
|
|
103
|
+
listening = false
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
update(preferences) {
|
|
107
|
+
if (!root || !assets || !preferences.enabled || preferences.cursor !== 'prts') { dispose(); return }
|
|
108
|
+
if (listening) return
|
|
109
|
+
for (const [name, handler] of events) window.addEventListener(name, handler, true)
|
|
110
|
+
document.addEventListener('visibilitychange', visibility)
|
|
111
|
+
listening = true
|
|
112
|
+
},
|
|
113
|
+
dispose,
|
|
114
|
+
}
|
|
115
|
+
}
|
package/src/client/index.js
CHANGED
|
@@ -24,6 +24,7 @@ export const DEFAULT_PREFERENCES = Object.freeze({
|
|
|
24
24
|
glass: 'standard',
|
|
25
25
|
motion: 'system',
|
|
26
26
|
bootAnimation: true,
|
|
27
|
+
cursor: 'prts',
|
|
27
28
|
conversationParticleDensity: 'sparse',
|
|
28
29
|
heroParticleDensity: 'light',
|
|
29
30
|
particleTraversalSpeed: PARTICLE_TRAVERSAL_SPEED_DEFAULT,
|
|
@@ -76,7 +77,7 @@ export const PREFERENCE_GROUPS = Object.freeze({
|
|
|
76
77
|
background: Object.freeze(['texture']),
|
|
77
78
|
particles: Object.freeze(['conversationParticleDensity', 'heroParticleDensity', 'particleTraversalSpeed']),
|
|
78
79
|
material: Object.freeze(['glass']),
|
|
79
|
-
accessibility: Object.freeze(['motion', 'bootAnimation']),
|
|
80
|
+
accessibility: Object.freeze(['motion', 'bootAnimation', 'cursor']),
|
|
80
81
|
navigation: Object.freeze(['railDefaultHidden']),
|
|
81
82
|
conversation: Object.freeze(['sessionFlow', 'sessionGlow', 'sessionFlowPalette', 'sessionFlowColorBack', 'sessionFlowColorCore', 'sessionFlowColorFront', 'sessionFlowSpeed', 'conversationStyle']),
|
|
82
83
|
})
|
|
@@ -178,6 +179,7 @@ export function normalizePreferences(value) {
|
|
|
178
179
|
glass: normalizeGlass(input),
|
|
179
180
|
motion: input.motion === 'full' ? 'system' : enumOr(input.motion, MOTIONS, DEFAULT_PREFERENCES.motion),
|
|
180
181
|
bootAnimation: booleanOr(input.bootAnimation, DEFAULT_PREFERENCES.bootAnimation),
|
|
182
|
+
cursor: input.cursor === 'system' ? 'system' : DEFAULT_PREFERENCES.cursor,
|
|
181
183
|
conversationParticleDensity: enumOr(legacyDensities.conversation, PARTICLE_DENSITIES, particlePreset.conversationParticleDensity),
|
|
182
184
|
heroParticleDensity: enumOr(legacyDensities.hero, PARTICLE_DENSITIES, particlePreset.heroParticleDensity),
|
|
183
185
|
particleTraversalSpeed: particleTraversalSpeedOr(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createCursorAdapter } from './cursor-adapter.js'
|
|
1
2
|
import { normalizePreferences } from './preferences.js'
|
|
2
3
|
|
|
3
4
|
const ROOT_ATTRIBUTES = [
|
|
@@ -6,6 +7,7 @@ const ROOT_ATTRIBUTES = [
|
|
|
6
7
|
'data-prts-texture',
|
|
7
8
|
'data-prts-glass',
|
|
8
9
|
'data-prts-motion',
|
|
10
|
+
'data-prts-cursor',
|
|
9
11
|
'data-prts-particle-pattern',
|
|
10
12
|
'data-prts-session-flow',
|
|
11
13
|
'data-prts-session-glow',
|
|
@@ -67,12 +69,13 @@ function resolveScheme(value) {
|
|
|
67
69
|
return identity.includes('dark') || identity.includes('night') ? 'dark' : 'light'
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
export function createThemeController({ document, window, cssText, service, onTransitionStateChange = () => {} }) {
|
|
72
|
+
export function createThemeController({ document, window, cssText, service, cursorAssets, onTransitionStateChange = () => {} }) {
|
|
71
73
|
if (!document || !window) {
|
|
72
74
|
const gesture = { update() {}, finish() {}, cancel() {} }
|
|
73
75
|
return { apply() {}, sync() {}, refresh() {}, setTheme() {}, beginThemeTransition() { return gesture }, toggle() {}, dispose() {} }
|
|
74
76
|
}
|
|
75
77
|
|
|
78
|
+
const cursor = createCursorAdapter({ document, window, assets: cursorAssets })
|
|
76
79
|
const root = document.documentElement
|
|
77
80
|
let style
|
|
78
81
|
let disposed = false
|
|
@@ -115,6 +118,7 @@ export function createThemeController({ document, window, cssText, service, onTr
|
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
function removeOwnedState() {
|
|
121
|
+
cursor.dispose()
|
|
118
122
|
disposed = true
|
|
119
123
|
refreshRevision += 1
|
|
120
124
|
setRevision += 1
|
|
@@ -712,6 +716,7 @@ export function createThemeController({ document, window, cssText, service, onTr
|
|
|
712
716
|
disposed = false
|
|
713
717
|
const preferences = normalizePreferences(value)
|
|
714
718
|
ensureStyle()
|
|
719
|
+
cursor.update(preferences)
|
|
715
720
|
if (!preferences.enabled) {
|
|
716
721
|
cancelActiveTransition()
|
|
717
722
|
clearThemeState()
|
|
@@ -721,6 +726,7 @@ export function createThemeController({ document, window, cssText, service, onTr
|
|
|
721
726
|
root.dataset.prtsTexture = preferences.texture
|
|
722
727
|
root.dataset.prtsGlass = preferences.glass
|
|
723
728
|
root.dataset.prtsMotion = preferences.motion
|
|
729
|
+
root.dataset.prtsCursor = preferences.cursor
|
|
724
730
|
root.dataset.prtsParticlePattern = 'orthogonal'
|
|
725
731
|
root.dataset.prtsSessionFlow = preferences.sessionFlow ? 'on' : 'off'
|
|
726
732
|
root.dataset.prtsSessionGlow = preferences.sessionGlow ? 'on' : 'off'
|
|
@@ -15,6 +15,7 @@ const PRESET_OPTIONS = [
|
|
|
15
15
|
]
|
|
16
16
|
|
|
17
17
|
const COMMON_CONTROLS = [
|
|
18
|
+
['cursor', '鼠标样式', [['system', '系统'], ['prts', 'PRTS 手形']]],
|
|
18
19
|
['railDefaultHidden', '默认隐藏导航栏', [['false', '关闭'], ['true', '开启']]],
|
|
19
20
|
['texture', '环境底纹', [['off', '关闭'], ['restrained', '克制'], ['full', '完整']]],
|
|
20
21
|
['glass', '玻璃材质', [['off', '关闭'], ['soft', '柔和'], ['standard', '标准'], ['clear', '清晰']]],
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* Native cursor; gesture frames override the resting image without a DOM overlay. */
|
|
2
|
+
@media (any-pointer: fine) {
|
|
3
|
+
html[data-dsh-prts][data-prts-cursor="prts"] {
|
|
4
|
+
--prts-hand-cursor: var(--prts-hand-frame, url("__PRTS_HAND__") 7 6);
|
|
5
|
+
--prts-text-cursor: var(--prts-text-frame, url("__PRTS_TEXT__") 24 24);
|
|
6
|
+
cursor: var(--prts-hand-cursor), default;
|
|
7
|
+
}
|
|
8
|
+
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-hand-cursor), pointer;
|
|
10
|
+
}
|
|
11
|
+
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
|
+
cursor: var(--prts-text-cursor), text;
|
|
13
|
+
}
|
|
14
|
+
html[data-dsh-prts][data-prts-cursor="prts"] :is(:disabled, [aria-disabled="true"]) {
|
|
15
|
+
cursor: not-allowed !important;
|
|
16
|
+
}
|
|
17
|
+
}
|