@ash-qw/dsh-theme-prts 1.0.3 → 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.
Files changed (120) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.en.md +3 -1
  3. package/README.md +4 -2
  4. package/THIRD_PARTY_NOTICES.md +9 -12
  5. package/THIRD_PARTY_NOTICES.zh-CN.md +45 -0
  6. package/lib/client.js +126 -4
  7. package/package.json +3 -2
  8. package/src/assets/cursors/animation.json +428 -0
  9. package/src/assets/cursors/prts-hand-hold.png +0 -0
  10. package/src/assets/cursors/prts-hand-press-00.png +0 -0
  11. package/src/assets/cursors/prts-hand-press-01.png +0 -0
  12. package/src/assets/cursors/prts-hand-press-02.png +0 -0
  13. package/src/assets/cursors/prts-hand-press-03.png +0 -0
  14. package/src/assets/cursors/prts-hand-press-04.png +0 -0
  15. package/src/assets/cursors/prts-hand-press-05.png +0 -0
  16. package/src/assets/cursors/prts-hand-release-00.png +0 -0
  17. package/src/assets/cursors/prts-hand-release-01.png +0 -0
  18. package/src/assets/cursors/prts-hand-release-02.png +0 -0
  19. package/src/assets/cursors/prts-hand-release-03.png +0 -0
  20. package/src/assets/cursors/prts-hand-release-04.png +0 -0
  21. package/src/assets/cursors/prts-hand-release-05.png +0 -0
  22. package/src/assets/cursors/prts-hand-release-06.png +0 -0
  23. package/src/assets/cursors/prts-hand-release-07.png +0 -0
  24. package/src/assets/cursors/prts-hand-release-08.png +0 -0
  25. package/src/assets/cursors/prts-hand-release-09.png +0 -0
  26. package/src/assets/cursors/prts-hand-release-10.png +0 -0
  27. package/src/assets/cursors/prts-hand-release-11.png +0 -0
  28. package/src/assets/cursors/prts-hand.png +0 -0
  29. package/src/assets/cursors/prts-text-click-00.png +0 -0
  30. package/src/assets/cursors/prts-text-click-01.png +0 -0
  31. package/src/assets/cursors/prts-text-click-02.png +0 -0
  32. package/src/assets/cursors/prts-text-click-03.png +0 -0
  33. package/src/assets/cursors/prts-text-click-04.png +0 -0
  34. package/src/assets/cursors/prts-text-click-05.png +0 -0
  35. package/src/assets/cursors/prts-text-click-06.png +0 -0
  36. package/src/assets/cursors/prts-text-click-07.png +0 -0
  37. package/src/assets/cursors/prts-text-click-08.png +0 -0
  38. package/src/assets/cursors/prts-text-click-09.png +0 -0
  39. package/src/assets/cursors/prts-text-click-10.png +0 -0
  40. package/src/assets/cursors/prts-text-click-11.png +0 -0
  41. package/src/assets/cursors/prts-text-click-12.png +0 -0
  42. package/src/assets/cursors/prts-text-click-13.png +0 -0
  43. package/src/assets/cursors/prts-text-click-14.png +0 -0
  44. package/src/assets/cursors/prts-text-click-15.png +0 -0
  45. package/src/assets/cursors/prts-text-click-16.png +0 -0
  46. package/src/assets/cursors/prts-text-click-17.png +0 -0
  47. package/src/assets/cursors/prts-text-click-18.png +0 -0
  48. package/src/assets/cursors/prts-text-click-19.png +0 -0
  49. package/src/assets/cursors/prts-text-click-20.png +0 -0
  50. package/src/assets/cursors/prts-text-click-21.png +0 -0
  51. package/src/assets/cursors/prts-text-click-22.png +0 -0
  52. package/src/assets/cursors/prts-text-click-23.png +0 -0
  53. package/src/assets/cursors/prts-text-click-24.png +0 -0
  54. package/src/assets/cursors/prts-text-click-25.png +0 -0
  55. package/src/assets/cursors/prts-text-click-26.png +0 -0
  56. package/src/assets/cursors/prts-text-click-27.png +0 -0
  57. package/src/assets/cursors/prts-text-click-28.png +0 -0
  58. package/src/assets/cursors/prts-text-click-29.png +0 -0
  59. package/src/assets/cursors/prts-text-click-30.png +0 -0
  60. package/src/assets/cursors/prts-text-click-31.png +0 -0
  61. package/src/assets/cursors/prts-text-click-32.png +0 -0
  62. package/src/assets/cursors/prts-text-click-33.png +0 -0
  63. package/src/assets/cursors/prts-text-click-34.png +0 -0
  64. package/src/assets/cursors/prts-text-click-35.png +0 -0
  65. package/src/assets/cursors/prts-text-click-36.png +0 -0
  66. package/src/assets/cursors/prts-text-click-37.png +0 -0
  67. package/src/assets/cursors/prts-text-click-38.png +0 -0
  68. package/src/assets/cursors/prts-text-click-39.png +0 -0
  69. package/src/assets/cursors/prts-text-click-40.png +0 -0
  70. package/src/assets/cursors/prts-text-click-41.png +0 -0
  71. package/src/assets/cursors/prts-text-click-42.png +0 -0
  72. package/src/assets/cursors/prts-text-click-43.png +0 -0
  73. package/src/assets/cursors/prts-text-click-44.png +0 -0
  74. package/src/assets/cursors/prts-text-click-45.png +0 -0
  75. package/src/assets/cursors/prts-text-click-46.png +0 -0
  76. package/src/assets/cursors/prts-text-click-47.png +0 -0
  77. package/src/assets/cursors/prts-text-click-48.png +0 -0
  78. package/src/assets/cursors/prts-text-click-49.png +0 -0
  79. package/src/assets/cursors/prts-text-click-50.png +0 -0
  80. package/src/assets/cursors/prts-text-click-51.png +0 -0
  81. package/src/assets/cursors/prts-text-click-52.png +0 -0
  82. package/src/assets/cursors/prts-text-click-53.png +0 -0
  83. package/src/assets/cursors/prts-text-click-54.png +0 -0
  84. package/src/assets/cursors/prts-text-click-55.png +0 -0
  85. package/src/assets/cursors/prts-text-click-56.png +0 -0
  86. package/src/assets/cursors/prts-text-click-57.png +0 -0
  87. package/src/assets/cursors/prts-text-click-58.png +0 -0
  88. package/src/assets/cursors/prts-text-click-59.png +0 -0
  89. package/src/assets/cursors/prts-text-click-60.png +0 -0
  90. package/src/assets/cursors/prts-text-click-61.png +0 -0
  91. package/src/assets/cursors/prts-text-click-62.png +0 -0
  92. package/src/assets/cursors/prts-text-click-63.png +0 -0
  93. package/src/assets/cursors/prts-text-click-64.png +0 -0
  94. package/src/assets/cursors/prts-text-click-65.png +0 -0
  95. package/src/assets/cursors/prts-text-click-66.png +0 -0
  96. package/src/assets/cursors/prts-text-click-67.png +0 -0
  97. package/src/assets/cursors/prts-text-click-68.png +0 -0
  98. package/src/assets/cursors/prts-text-click-69.png +0 -0
  99. package/src/assets/cursors/prts-text-click-70.png +0 -0
  100. package/src/assets/cursors/prts-text-click-71.png +0 -0
  101. package/src/assets/cursors/prts-text-click-72.png +0 -0
  102. package/src/assets/cursors/prts-text-click-73.png +0 -0
  103. package/src/assets/cursors/prts-text-click-74.png +0 -0
  104. package/src/assets/cursors/prts-text-click-75.png +0 -0
  105. package/src/assets/cursors/prts-text-click-76.png +0 -0
  106. package/src/assets/cursors/prts-text-click-77.png +0 -0
  107. package/src/assets/cursors/prts-text-click-78.png +0 -0
  108. package/src/assets/cursors/prts-text-click-79.png +0 -0
  109. package/src/assets/cursors/prts-text-click-80.png +0 -0
  110. package/src/assets/cursors/prts-text-click-81.png +0 -0
  111. package/src/assets/cursors/prts-text-click-82.png +0 -0
  112. package/src/assets/cursors/prts-text-click-83.png +0 -0
  113. package/src/assets/cursors/prts-text-click-84.png +0 -0
  114. package/src/assets/cursors/prts-text.png +0 -0
  115. package/src/client/cursor-adapter.js +115 -0
  116. package/src/client/index.js +1 -0
  117. package/src/client/preferences.js +3 -1
  118. package/src/client/theme-controller-v2.js +7 -1
  119. package/src/client/theme-settings-workbench.js +1 -0
  120. package/src/styles/cursor.css +17 -0
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
+ }
@@ -79,6 +79,7 @@ export function applyPrtsPlugin(ctx, environment) {
79
79
  window,
80
80
  cssText,
81
81
  service: themeService,
82
+ cursorAssets: assets.cursor,
82
83
  onTransitionStateChange(active) {
83
84
  particleField.setSchemeTransitionActive(active)
84
85
  },
@@ -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
+ }