@ash-qw/dsh-theme-prts 2.0.0 → 2.1.1
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 +20 -0
- package/README.en.md +12 -2
- package/README.md +16 -4
- package/THIRD_PARTY_NOTICES.md +17 -0
- package/THIRD_PARTY_NOTICES.zh-CN.md +17 -0
- package/lib/client.js +1512 -65
- package/lib/index.js +1 -1
- package/package.json +3 -1
- package/src/client/assistant-glass-adapter.js +45 -2
- package/src/client/composer-glass-adapter.js +20 -1
- package/src/client/composer-glass-surface.js +85 -0
- package/src/client/custom-background.js +143 -0
- package/src/client/host-geometry-adapter.js +13 -0
- package/src/client/index.js +58 -31
- package/src/client/native-preferences.js +92 -0
- package/src/client/opacity-dial.js +137 -0
- package/src/client/operations-shell-v2.js +39 -10
- package/src/client/preferences.js +33 -3
- package/src/client/settings-page.js +7 -2
- package/src/client/theme-controller-v2.js +52 -13
- package/src/client/theme-settings-workbench.js +25 -8
- package/src/index.js +16 -2
- package/src/server/backgrounds.js +97 -0
- package/src/styles/cursor.css +2 -0
- package/src/styles/custom-background.css +331 -0
- package/src/styles/prts.css +112 -16
- package/src/styles/refinement.css +9 -0
- package/src/styles/theme-settings-workbench.css +3 -0
- package/src/vendor/react-bits/GlassSurface.css +168 -0
- package/src/vendor/react-bits/GlassSurface.jsx +818 -0
- package/src/vendor/react-bits/LICENSE.md +25 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { DEFAULT_PREFERENCES, PRTS_STORAGE_KEY, loadPreferences, normalizePreferences } from './preferences.js'
|
|
2
|
+
|
|
3
|
+
// Host snapshots are authoritative. This cache must never become a migration source.
|
|
4
|
+
const NATIVE_CACHE_KEY = 'dsh.ui.prts.host-cache.v1'
|
|
5
|
+
export function createNativePreferences({ storage, onChange }) {
|
|
6
|
+
let current = loadPreferences(storage)
|
|
7
|
+
let legacy = null
|
|
8
|
+
try {
|
|
9
|
+
const raw = JSON.parse(storage?.getItem(PRTS_STORAGE_KEY) || 'null')
|
|
10
|
+
if (raw && Number.isInteger(raw.version) && raw.version >= 1 && raw.version <= 12) legacy = normalizePreferences(raw)
|
|
11
|
+
} catch {}
|
|
12
|
+
try {
|
|
13
|
+
const cache = JSON.parse(storage?.getItem(NATIVE_CACHE_KEY) || 'null')
|
|
14
|
+
if (cache) { current = normalizePreferences(cache); legacy = null }
|
|
15
|
+
} catch {}
|
|
16
|
+
let form, unsubscribe, disposed = false, busy = false, revision = 0
|
|
17
|
+
let pending = {}, inFlight = {}, failed = {}, phase = 'idle', migrationAttempted = false
|
|
18
|
+
const emit = () => { if (!disposed) onChange(current, { phase, revision: ++revision }) }
|
|
19
|
+
const cache = value => { try { storage?.setItem(NATIVE_CACHE_KEY, JSON.stringify(value)) } catch {} }
|
|
20
|
+
const snapshot = () => { try { return form?.getSnapshot() } catch { return undefined } }
|
|
21
|
+
const writable = s => s?.status === 'ready' && s.writable && s.mode !== 'memory'
|
|
22
|
+
function receive() {
|
|
23
|
+
if (disposed) return
|
|
24
|
+
const s = snapshot()
|
|
25
|
+
if (s?.status !== 'ready') { emit(); return }
|
|
26
|
+
const host = normalizePreferences(s.value)
|
|
27
|
+
if (!s.value?.preferencesInitialized && legacy && !migrationAttempted && writable(s)) {
|
|
28
|
+
migrationAttempted = true
|
|
29
|
+
pending = { ...legacy, ...pending }
|
|
30
|
+
}
|
|
31
|
+
current = normalizePreferences({ ...host, ...inFlight, ...pending })
|
|
32
|
+
if (s.value?.preferencesInitialized) {
|
|
33
|
+
legacy = null
|
|
34
|
+
cache(host)
|
|
35
|
+
// Once the Host has settings, an obsolete browser must not re-migrate them.
|
|
36
|
+
try { storage?.removeItem(PRTS_STORAGE_KEY) } catch {}
|
|
37
|
+
}
|
|
38
|
+
emit()
|
|
39
|
+
void flush()
|
|
40
|
+
}
|
|
41
|
+
async function flush() {
|
|
42
|
+
if (disposed || busy || !Object.keys(pending).length) return
|
|
43
|
+
const s = snapshot()
|
|
44
|
+
if (!writable(s)) { failed = { ...failed, ...pending }; pending = {}; phase = 'error'; emit(); return }
|
|
45
|
+
busy = true
|
|
46
|
+
const patch = pending
|
|
47
|
+
inFlight = patch
|
|
48
|
+
pending = {}
|
|
49
|
+
phase = 'saving'; emit()
|
|
50
|
+
try {
|
|
51
|
+
const ops = Object.entries(patch).map(([key, value]) => ({ op: 'set', path: [key], value }))
|
|
52
|
+
ops.push({ op: 'set', path: ['preferencesInitialized'], value: true })
|
|
53
|
+
// Fixed revision: conflicts are recovered by DSH, never blindly replayed.
|
|
54
|
+
const accepted = await form.mutate(ops, s.revision)
|
|
55
|
+
if (disposed) return
|
|
56
|
+
if (accepted !== true) throw Error('Host refused settings mutation')
|
|
57
|
+
for (const key of Object.keys(patch)) delete failed[key]
|
|
58
|
+
phase = Object.keys(failed).length ? 'error' : 'saved'
|
|
59
|
+
} catch {
|
|
60
|
+
if (disposed) return
|
|
61
|
+
failed = { ...failed, ...patch }
|
|
62
|
+
for (const key of Object.keys(pending)) delete failed[key]
|
|
63
|
+
phase = 'error'
|
|
64
|
+
} finally {
|
|
65
|
+
busy = false
|
|
66
|
+
inFlight = {}
|
|
67
|
+
if (!disposed) receive()
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
get value() { return current },
|
|
72
|
+
bind(next) {
|
|
73
|
+
unsubscribe?.(); form = next
|
|
74
|
+
unsubscribe = form.subscribe(receive)
|
|
75
|
+
receive()
|
|
76
|
+
return () => { unsubscribe?.(); unsubscribe = null; form = null }
|
|
77
|
+
},
|
|
78
|
+
update(next) {
|
|
79
|
+
if (disposed) return
|
|
80
|
+
const normalized = normalizePreferences(next)
|
|
81
|
+
for (const key of Object.keys(DEFAULT_PREFERENCES)) {
|
|
82
|
+
if (normalized[key] !== current[key]) { pending[key] = normalized[key]; delete failed[key] }
|
|
83
|
+
}
|
|
84
|
+
if (!Object.keys(pending).length) return
|
|
85
|
+
current = normalized
|
|
86
|
+
phase = 'saving'; emit()
|
|
87
|
+
void flush()
|
|
88
|
+
},
|
|
89
|
+
retry() { pending = { ...failed, ...pending }; failed = {}; void flush() },
|
|
90
|
+
dispose() { disposed = true; unsubscribe?.() },
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Ring geometry adapted from React Bits CometDial (David Haz).
|
|
2
|
+
// See src/vendor/react-bits/LICENSE.md and THIRD_PARTY_NOTICES.md.
|
|
3
|
+
export const BACKGROUND_OPACITY_CONTROLS = Object.freeze([
|
|
4
|
+
{ key: 'sidebarOpacity', label: '会话列表侧栏', property: '--prts-sidebar-opacity', value: 94, blurKey: 'sidebarBlur', blurProperty: '--prts-sidebar-blur', blurDefault: 8 },
|
|
5
|
+
{ key: 'railOpacity', label: '设置侧栏', property: '--prts-rail-opacity', value: 70, blurKey: 'railBlur', blurProperty: '--prts-rail-blur', blurDefault: 12 },
|
|
6
|
+
{ key: 'bakerOpacity', label: 'Baker 气泡', property: '--prts-baker-opacity', value: 50, blurKey: 'bakerBlur', blurProperty: '--prts-baker-blur', blurDefault: 8, theme: 'deck-chat' },
|
|
7
|
+
{ key: 'classicUserOpacity', label: '经典用户消息', property: '--prts-classic-user-opacity', value: 50, blurKey: 'classicUserBlur', blurProperty: '--prts-classic-user-blur', blurDefault: 8, theme: 'native' },
|
|
8
|
+
])
|
|
9
|
+
const dialClamp = value => Math.min(100, Math.max(0, Math.round(value)))
|
|
10
|
+
const dialPoint = angle => [100 + Math.cos(angle * Math.PI / 180) * 80, 100 + Math.sin(angle * Math.PI / 180) * 80]
|
|
11
|
+
const dialArc = (start, end) => {
|
|
12
|
+
const [x, y] = dialPoint(start), [ex, ey] = dialPoint(end)
|
|
13
|
+
return `M ${x} ${y} A 80 80 0 ${end - start > 180 ? 1 : 0} 1 ${ex} ${ey}`
|
|
14
|
+
}
|
|
15
|
+
export function opacityDialMarkup() {
|
|
16
|
+
const controls = BACKGROUND_OPACITY_CONTROLS.map(({key,label,value,blurDefault}) => {
|
|
17
|
+
const sample = key === 'bakerOpacity' ? '<span>这是一条回复</span><i></i>'
|
|
18
|
+
: key === 'classicUserOpacity' ? '<span>这是我发送的消息</span><i></i>'
|
|
19
|
+
: key === 'railOpacity' ? '<b>◇</b><b>☼</b><b>⚙</b>' : '<span>工作区</span><i></i><i></i>'
|
|
20
|
+
return `<div data-prts-opacity-control="${key}">
|
|
21
|
+
<div data-prts-opacity-heading><strong>${label}</strong><button type="button" data-prts-opacity-reset data-prts-opacity-reset-tooltip="恢复到 ${value}%" aria-label="恢复${label}透明度到 ${value}%"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4.5 10a7.5 7.5 0 1 1 .7 5.2M4.5 4.5V10H10"/></svg></button></div>
|
|
22
|
+
<div data-prts-opacity-dial><svg viewBox="0 0 200 200" role="slider" tabindex="0" aria-label="${label}透明度" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${value}" aria-valuetext="${value}%"><circle data-dial-focus cx="100" cy="100" r="97"/><path data-dial-track d="${dialArc(110,430)}"/><path data-dial-lit/><path data-dial-tail/><circle data-dial-head r="8"/></svg><label data-prts-opacity-number><input type="number" min="0" max="100" step="1" value="${value}" aria-label="${label}透明度百分比"><span>%</span></label></div>
|
|
23
|
+
<div data-prts-material-preview="${key}" aria-hidden="true"><div data-prts-material-preview-surface>${sample}</div></div>
|
|
24
|
+
<div data-prts-blur-control hidden><label><span>模糊</span><input type="range" min="0" max="32" step="1" value="${blurDefault}" aria-label="${label}模糊度"></label><output>${blurDefault}px</output><button type="button" data-prts-blur-reset title="恢复到 ${blurDefault}px" aria-label="恢复${label}模糊度到 ${blurDefault}px"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4.5 10a7.5 7.5 0 1 1 .7 5.2M4.5 4.5V10H10"/></svg></button></div>
|
|
25
|
+
<small data-prts-opacity-note></small>
|
|
26
|
+
</div>`
|
|
27
|
+
}).join('')
|
|
28
|
+
return `<fieldset data-prts-opacity-settings hidden><legend>材质透明度与模糊度</legend><small>透明度数值越高越透明;模糊度以像素计。调整时预览,松手后保存。</small><div data-prts-opacity-grid>${controls}</div></fieldset>`
|
|
29
|
+
}
|
|
30
|
+
export function mountOpacityDials({ section, window, onChange }) {
|
|
31
|
+
const root = section.ownerDocument.documentElement
|
|
32
|
+
const controls = BACKGROUND_OPACITY_CONTROLS.map(definition => {
|
|
33
|
+
const container = section.querySelector(`[data-prts-opacity-control="${definition.key}"]`)
|
|
34
|
+
const svg = container.querySelector('[data-prts-opacity-dial] svg'), input = container.querySelector('[data-prts-opacity-number] input'), tail = svg.querySelector('[data-dial-tail]')
|
|
35
|
+
const blurControl = container.querySelector('[data-prts-blur-control]')
|
|
36
|
+
const blurInput = blurControl.querySelector('input'), blurOutput = blurControl.querySelector('output')
|
|
37
|
+
const materialPreview = container.querySelector('[data-prts-material-preview]')
|
|
38
|
+
let value = definition.value, saved = value, grip = null, timer
|
|
39
|
+
let blurValue = definition.blurDefault, savedBlur = blurValue
|
|
40
|
+
const paint = next => {
|
|
41
|
+
const previous = value
|
|
42
|
+
value = dialClamp(next)
|
|
43
|
+
const angle = 110 + value * 3.2, [x,y] = dialPoint(angle)
|
|
44
|
+
svg.querySelector('[data-dial-lit]').setAttribute('d', value ? dialArc(110,angle) : '')
|
|
45
|
+
const head = svg.querySelector('[data-dial-head]');head.setAttribute('cx', x);head.setAttribute('cy', y)
|
|
46
|
+
svg.setAttribute('aria-valuenow', value);svg.setAttribute('aria-valuetext', `${value}%`)
|
|
47
|
+
input.value = value
|
|
48
|
+
window.clearTimeout(timer)
|
|
49
|
+
const reach = Math.min(65, Math.abs(value - previous) * 3.2)
|
|
50
|
+
tail.setAttribute('d', reach && value !== previous ? dialArc(Math.max(110, value > previous ? angle-reach : angle), Math.min(430, value > previous ? angle : angle+reach)) : '')
|
|
51
|
+
tail.style.opacity = reach ? '.5' : '0'
|
|
52
|
+
if (reach) timer = window.setTimeout(() => {tail.style.opacity = '0'}, 160)
|
|
53
|
+
}
|
|
54
|
+
const preview = next => { paint(next);root.style.setProperty(definition.property, `${100 - value}%`) }
|
|
55
|
+
const commit = () => { if (value !== saved) { saved = value;onChange(definition.key, (100 - value) / 100) } }
|
|
56
|
+
const position = event => {
|
|
57
|
+
const b = svg.getBoundingClientRect()
|
|
58
|
+
const x = (event.clientX-b.left)/b.width*200-100, y = (event.clientY-b.top)/b.height*200-100
|
|
59
|
+
if (Math.hypot(x,y) < 35) return value
|
|
60
|
+
let angle = (Math.atan2(y,x)*180/Math.PI-110+720)%360
|
|
61
|
+
// Keep a drag on its own side of the dead zone, avoiding 100 -> 0 jumps.
|
|
62
|
+
if (angle > 320) angle = grip?.side === 'high' ? 320 : grip?.side === 'low' ? 0 : angle < 340 ? 320 : 0
|
|
63
|
+
else if (grip) grip.side = angle > 160 ? 'high' : 'low'
|
|
64
|
+
return angle/3.2
|
|
65
|
+
}
|
|
66
|
+
const down = event => {
|
|
67
|
+
if (event.button > 0 || grip) return
|
|
68
|
+
event.preventDefault();svg.focus({preventScroll:true})
|
|
69
|
+
grip = { id: event.pointerId, side: null }
|
|
70
|
+
svg.setPointerCapture?.(event.pointerId);container.setAttribute('data-dragging','')
|
|
71
|
+
preview(position(event))
|
|
72
|
+
}
|
|
73
|
+
const move = event => { if (grip?.id === event.pointerId) preview(position(event)) }
|
|
74
|
+
const finish = (event, cancel = false) => {
|
|
75
|
+
if (!grip || (event.pointerId !== undefined && grip.id !== event.pointerId)) return
|
|
76
|
+
const id = grip.id;grip = null;container.removeAttribute('data-dragging')
|
|
77
|
+
if (svg.hasPointerCapture?.(id)) svg.releasePointerCapture(id)
|
|
78
|
+
if (cancel) preview(saved);else commit()
|
|
79
|
+
}
|
|
80
|
+
const up = event => finish(event)
|
|
81
|
+
const cancel = event => finish(event,true)
|
|
82
|
+
const key = event => {
|
|
83
|
+
if (event.key === 'Escape') { if(grip){event.preventDefault();event.stopPropagation();finish(event,true)}return }
|
|
84
|
+
const step = event.shiftKey ? 10 : 1
|
|
85
|
+
const next = {ArrowRight:value+step,ArrowUp:value+step,ArrowLeft:value-step,ArrowDown:value-step,PageUp:value+10,PageDown:value-10,Home:0,End:100}[event.key]
|
|
86
|
+
if (next === undefined) return
|
|
87
|
+
event.preventDefault();preview(next);commit()
|
|
88
|
+
}
|
|
89
|
+
const change = () => { const n = input.valueAsNumber;if(!Number.isFinite(n)){paint(saved);return}preview(n);commit() }
|
|
90
|
+
const reset = () => { preview(definition.value);commit() }
|
|
91
|
+
const paintBlur = next => {
|
|
92
|
+
blurValue = Math.min(32, Math.max(0, Math.round(next)))
|
|
93
|
+
blurInput.value = blurValue
|
|
94
|
+
blurOutput.value = `${blurValue}px`
|
|
95
|
+
materialPreview.style.setProperty(definition.blurProperty, `${blurValue}px`)
|
|
96
|
+
}
|
|
97
|
+
const previewBlur = next => {
|
|
98
|
+
paintBlur(next)
|
|
99
|
+
if (root.hasAttribute('data-dsh-prts')) root.style.setProperty(definition.blurProperty, `${blurValue}px`)
|
|
100
|
+
}
|
|
101
|
+
const commitBlur = () => { if (blurValue !== savedBlur) { savedBlur = blurValue;onChange(definition.blurKey, blurValue) } }
|
|
102
|
+
const blurChange = () => { previewBlur(blurInput.valueAsNumber);commitBlur() }
|
|
103
|
+
const blurCancel = event => { if (event.key === 'Escape') { event.preventDefault();previewBlur(savedBlur);blurInput.blur() } }
|
|
104
|
+
const resetBlur = () => { previewBlur(definition.blurDefault);commitBlur() }
|
|
105
|
+
const resetButton = container.querySelector('[data-prts-opacity-reset]')
|
|
106
|
+
const blurResetButton = blurControl.querySelector('[data-prts-blur-reset]')
|
|
107
|
+
const bindings = [[svg,'pointerdown',down],[svg,'pointermove',move],[svg,'pointerup',up],[svg,'pointercancel',cancel],[svg,'lostpointercapture',cancel],[svg,'keydown',key],[input,'change',change],[resetButton,'click',reset],[blurInput,'input',event=>previewBlur(event.target.valueAsNumber)],[blurInput,'change',blurChange],[blurInput,'keydown',blurCancel],[blurResetButton,'click',resetBlur],[window,'blur',cancel]]
|
|
108
|
+
for(const [el,type,fn] of bindings)el.addEventListener(type,fn)
|
|
109
|
+
paint(value)
|
|
110
|
+
return {
|
|
111
|
+
update(preferences, active) {
|
|
112
|
+
container.hidden = Boolean(definition.theme && preferences.conversationStyle !== definition.theme)
|
|
113
|
+
saved = 100 - Math.round((preferences[definition.key] ?? (100 - definition.value) / 100) * 100)
|
|
114
|
+
if (!grip && section.ownerDocument.activeElement !== input) paint(saved)
|
|
115
|
+
savedBlur = preferences[definition.blurKey] ?? definition.blurDefault
|
|
116
|
+
if (section.ownerDocument.activeElement !== blurInput) paintBlur(savedBlur)
|
|
117
|
+
blurControl.hidden = !active
|
|
118
|
+
container.querySelector('[data-prts-opacity-note]').textContent = active ? '' : '当前未启用此材质'
|
|
119
|
+
},
|
|
120
|
+
dispose() { if(grip)finish({},true);window.clearTimeout(timer);for(const [el,type,fn] of bindings)el.removeEventListener(type,fn) },
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
return {
|
|
124
|
+
update(preferences, state) {
|
|
125
|
+
const full = preferences.backgroundMode === 'full' && Boolean(state.full)
|
|
126
|
+
const split = preferences.backgroundMode === 'split'
|
|
127
|
+
section.querySelector('[data-prts-opacity-settings]').hidden = preferences.backgroundMode === 'off'
|
|
128
|
+
const conversationImage = full || (split && Boolean(state.conversation))
|
|
129
|
+
const sidebarImage = full || (split && Boolean(state.sidebar))
|
|
130
|
+
const active = [sidebarImage, sidebarImage,
|
|
131
|
+
conversationImage && preferences.conversationStyle === 'deck-chat',
|
|
132
|
+
conversationImage && preferences.conversationStyle === 'native']
|
|
133
|
+
controls.forEach((control,i)=>control.update(preferences,active[i]))
|
|
134
|
+
},
|
|
135
|
+
dispose() {for(const control of controls)control.dispose()},
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SCHEME_COMMIT_PROGRESS } from './theme-controller-v2.js'
|
|
1
2
|
import { createThemeSettingsOverlay, themeSettingsMarkup } from './theme-settings-workbench.js'
|
|
2
3
|
|
|
3
4
|
const PHONE_RAIL_BREAKPOINT = 640
|
|
@@ -45,7 +46,7 @@ export function createOperationsShell({
|
|
|
45
46
|
onSchemeToggle = () => {},
|
|
46
47
|
onSchemeDragStart = () => undefined,
|
|
47
48
|
onThemeDisable = () => {},
|
|
48
|
-
customEmblemStore, avatarStore,
|
|
49
|
+
customEmblemStore, avatarStore, backgroundStore,
|
|
49
50
|
onPreferenceChange = () => {},
|
|
50
51
|
onPreferencePreview = () => {},
|
|
51
52
|
onVisualReset = () => {},
|
|
@@ -83,9 +84,9 @@ export function createOperationsShell({
|
|
|
83
84
|
try { return window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches === true } catch { return false }
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
function phoneRailGesture() {
|
|
87
|
+
function phoneRailGesture(pointerType) {
|
|
87
88
|
if (railMode !== 'overlay' || Number(window.innerWidth) > PHONE_RAIL_BREAKPOINT) return false
|
|
88
|
-
return railPointerQuery?.matches === true
|
|
89
|
+
return pointerType === 'touch' || railPointerQuery?.matches === true
|
|
89
90
|
|| railHoverQuery?.matches === true
|
|
90
91
|
|| Number(window.navigator?.maxTouchPoints) > 0
|
|
91
92
|
}
|
|
@@ -105,13 +106,16 @@ export function createOperationsShell({
|
|
|
105
106
|
|
|
106
107
|
function armRailGesture() {
|
|
107
108
|
railHoldTimer = undefined
|
|
108
|
-
if (!railPointer || !phoneRailGesture()) return
|
|
109
|
+
if (!railPointer || !phoneRailGesture('touch')) return
|
|
109
110
|
railPointer.armed = true
|
|
110
111
|
railLauncher?.setAttribute('data-prts-rail-gesture-active', '')
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
let railInputType
|
|
113
115
|
function onRailPointerDown(event) {
|
|
114
|
-
|
|
116
|
+
railInputType = event.pointerType || 'mouse'
|
|
117
|
+
if (railInputType !== 'touch') return
|
|
118
|
+
if (!phoneRailGesture(event.pointerType) || railPointer || (event.button ?? 0) !== 0) return
|
|
115
119
|
const pointerId = event.pointerId ?? 1
|
|
116
120
|
railPointer = {
|
|
117
121
|
pointerId,
|
|
@@ -159,7 +163,9 @@ export function createOperationsShell({
|
|
|
159
163
|
}
|
|
160
164
|
|
|
161
165
|
function onRailClick(event) {
|
|
162
|
-
|
|
166
|
+
const inputType = event?.pointerType || railInputType
|
|
167
|
+
railInputType = undefined
|
|
168
|
+
if (phoneRailGesture(inputType) && inputType === 'touch' && Number(event?.detail) > 0) {
|
|
163
169
|
event.preventDefault?.()
|
|
164
170
|
return
|
|
165
171
|
}
|
|
@@ -262,9 +268,19 @@ export function createOperationsShell({
|
|
|
262
268
|
}, 1000)
|
|
263
269
|
}
|
|
264
270
|
|
|
271
|
+
function flushSchemeProgress(pointer) {
|
|
272
|
+
if (pointer.frame !== undefined) window.cancelAnimationFrame(pointer.frame)
|
|
273
|
+
pointer.frame = undefined
|
|
274
|
+
if (pointer.progress !== undefined) {
|
|
275
|
+
pointer.gesture?.update?.(pointer.progress)
|
|
276
|
+
pointer.progress = undefined
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
265
280
|
function settleSchemePointer({ commit = false } = {}) {
|
|
266
281
|
const pointer = schemePointer
|
|
267
282
|
if (!pointer) return
|
|
283
|
+
flushSchemeProgress(pointer)
|
|
268
284
|
schemePointer = undefined
|
|
269
285
|
try { schemeToggle?.releasePointerCapture?.(pointer.pointerId) } catch {}
|
|
270
286
|
if (!pointer.dragging) return
|
|
@@ -313,19 +329,25 @@ export function createOperationsShell({
|
|
|
313
329
|
}
|
|
314
330
|
}
|
|
315
331
|
event.preventDefault?.()
|
|
316
|
-
pointer.
|
|
332
|
+
pointer.progress = schemePointerProgress(event)
|
|
333
|
+
if (pointer.frame === undefined) pointer.frame = window.requestAnimationFrame(() => {
|
|
334
|
+
pointer.frame = undefined
|
|
335
|
+
if (schemePointer === pointer) flushSchemeProgress(pointer)
|
|
336
|
+
})
|
|
317
337
|
}
|
|
318
338
|
|
|
319
339
|
function onSchemePointerUp(event) {
|
|
320
340
|
const pointer = schemePointer
|
|
321
341
|
if (!pointer || (event.pointerId ?? 1) !== pointer.pointerId) return
|
|
322
|
-
|
|
342
|
+
if (pointer.dragging) pointer.progress = schemePointerProgress(event)
|
|
343
|
+
const commit = pointer.dragging && schemePointerProgress(event) >= SCHEME_COMMIT_PROGRESS
|
|
323
344
|
settleSchemePointer({ commit })
|
|
324
345
|
}
|
|
325
346
|
|
|
326
347
|
function cancelSchemePointer() {
|
|
327
348
|
const pointer = schemePointer
|
|
328
349
|
if (!pointer) return
|
|
350
|
+
if (pointer.frame !== undefined) window.cancelAnimationFrame(pointer.frame)
|
|
329
351
|
if (pointer.dragging) {
|
|
330
352
|
try { pointer.gesture?.cancel?.() } catch {}
|
|
331
353
|
}
|
|
@@ -353,7 +375,7 @@ export function createOperationsShell({
|
|
|
353
375
|
const overlay = railMode === 'overlay'
|
|
354
376
|
const open = overlay && root.hasAttribute('data-prts-rail-open')
|
|
355
377
|
railLauncher.setAttribute('aria-expanded', String(open))
|
|
356
|
-
const closedLabel = phoneRailGesture() ? '
|
|
378
|
+
const closedLabel = phoneRailGesture() ? '打开 P.R.T.S. 导航(触摸时长按并下滑)' : '打开 P.R.T.S. 导航'
|
|
357
379
|
railLauncher.setAttribute('aria-label', open ? '关闭 P.R.T.S. 导航' : closedLabel)
|
|
358
380
|
if (overlay && !open) {
|
|
359
381
|
navRail.setAttribute('aria-hidden', 'true')
|
|
@@ -418,12 +440,16 @@ export function createOperationsShell({
|
|
|
418
440
|
}
|
|
419
441
|
|
|
420
442
|
function onResponsiveChange() {
|
|
443
|
+
railInputType = undefined
|
|
421
444
|
clearRailGesture()
|
|
422
445
|
setRailViewport(window.innerWidth)
|
|
423
446
|
syncRailState()
|
|
424
447
|
}
|
|
425
448
|
|
|
449
|
+
let railPhoneQuery
|
|
426
450
|
function bindResponsiveQueries() {
|
|
451
|
+
railPhoneQuery = window.matchMedia?.('(max-width: 640px)')
|
|
452
|
+
railPhoneQuery?.addEventListener?.('change', onResponsiveChange)
|
|
427
453
|
railViewportQuery = window.matchMedia?.('(min-width: 1180px)')
|
|
428
454
|
railPointerQuery = window.matchMedia?.('(pointer: coarse)')
|
|
429
455
|
railHoverQuery = window.matchMedia?.('(hover: none)')
|
|
@@ -455,7 +481,7 @@ export function createOperationsShell({
|
|
|
455
481
|
trigger: shell.querySelector('[data-prts-rail-brand]'),
|
|
456
482
|
panel: shell.querySelector('[data-prts-theme-settings]'),
|
|
457
483
|
backdrop: shell.querySelector('[data-prts-settings-backdrop]'),
|
|
458
|
-
customEmblemStore, avatarStore,
|
|
484
|
+
customEmblemStore, avatarStore, backgroundStore,
|
|
459
485
|
onPreferenceChange,
|
|
460
486
|
onPreferencePreview,
|
|
461
487
|
onResetVisual: onVisualReset,
|
|
@@ -507,6 +533,9 @@ export function createOperationsShell({
|
|
|
507
533
|
schemeToggle?.removeEventListener('lostpointercapture', onSchemeLostPointerCapture)
|
|
508
534
|
window.removeEventListener?.('blur', cancelSchemePointer)
|
|
509
535
|
themeDisable?.removeEventListener('click', disableTheme)
|
|
536
|
+
railPhoneQuery?.removeEventListener?.('change', onResponsiveChange)
|
|
537
|
+
railPhoneQuery = undefined
|
|
538
|
+
railInputType = undefined
|
|
510
539
|
railViewportQuery?.removeEventListener?.('change', onResponsiveChange)
|
|
511
540
|
railPointerQuery?.removeEventListener?.('change', onResponsiveChange)
|
|
512
541
|
railHoverQuery?.removeEventListener?.('change', onResponsiveChange)
|
|
@@ -24,6 +24,17 @@ export const DEFAULT_PREFERENCES = Object.freeze({
|
|
|
24
24
|
glass: 'standard',
|
|
25
25
|
motion: 'system',
|
|
26
26
|
bootAnimation: true,
|
|
27
|
+
composerGlass: false,
|
|
28
|
+
backgroundMode: 'off',
|
|
29
|
+
backgroundDim: .45,
|
|
30
|
+
sidebarOpacity: .06,
|
|
31
|
+
railOpacity: .3,
|
|
32
|
+
bakerOpacity: .5,
|
|
33
|
+
classicUserOpacity: .5,
|
|
34
|
+
sidebarBlur: 8,
|
|
35
|
+
railBlur: 12,
|
|
36
|
+
bakerBlur: 8,
|
|
37
|
+
classicUserBlur: 8,
|
|
27
38
|
cursor: 'prts',
|
|
28
39
|
conversationParticleDensity: 'sparse',
|
|
29
40
|
heroParticleDensity: 'light',
|
|
@@ -48,6 +59,7 @@ export const DEFAULT_PREFERENCES = Object.freeze({
|
|
|
48
59
|
sessionFlowSpeed: SESSION_FLOW_SPEED_DEFAULT,
|
|
49
60
|
conversationStyle: 'native',
|
|
50
61
|
bakerGroupAvatars: true,
|
|
62
|
+
bakerLoading: false,
|
|
51
63
|
conversationEmblem: true,
|
|
52
64
|
})
|
|
53
65
|
|
|
@@ -85,12 +97,12 @@ export const PARTICLE_DETAIL_LEVELS = Object.freeze({
|
|
|
85
97
|
precise: Object.freeze({ conversationParticleDensity: 'dense', heroParticleDensity: 'ultra' }),
|
|
86
98
|
})
|
|
87
99
|
export const PREFERENCE_GROUPS = Object.freeze({
|
|
88
|
-
background: Object.freeze(['texture']),
|
|
100
|
+
background: Object.freeze(['texture', 'backgroundMode', 'backgroundDim', 'sidebarOpacity', 'railOpacity', 'bakerOpacity', 'classicUserOpacity', 'sidebarBlur', 'railBlur', 'bakerBlur', 'classicUserBlur']),
|
|
89
101
|
particles: Object.freeze(['conversationParticleDensity', 'heroParticleDensity', 'particleTraversalSpeed']),
|
|
90
|
-
material: Object.freeze(['glass']),
|
|
102
|
+
material: Object.freeze(['glass', 'composerGlass']),
|
|
91
103
|
accessibility: Object.freeze(['motion', 'bootAnimation', 'cursor']),
|
|
92
104
|
navigation: Object.freeze(['railDefaultHidden', 'sessionPreview', 'previewOngoing', 'previewWarning', 'previewDone', 'previewDoubleClick', 'previewFollowOngoing', 'previewFollowWarning', 'previewReplyLimit']),
|
|
93
|
-
conversation: Object.freeze(['sessionFlow', 'sessionGlow', 'sessionFlowPalette', 'sessionFlowColorBack', 'sessionFlowColorCore', 'sessionFlowColorFront', 'sessionFlowSpeed', 'conversationStyle', 'bakerGroupAvatars', 'conversationEmblem']),
|
|
105
|
+
conversation: Object.freeze(['sessionFlow', 'sessionGlow', 'sessionFlowPalette', 'sessionFlowColorBack', 'sessionFlowColorCore', 'sessionFlowColorFront', 'sessionFlowSpeed', 'conversationStyle', 'bakerGroupAvatars', 'bakerLoading', 'conversationEmblem']),
|
|
94
106
|
})
|
|
95
107
|
|
|
96
108
|
const TEXTURES = new Set(['off', 'restrained', 'full'])
|
|
@@ -134,6 +146,12 @@ function colorOr(value, fallback) {
|
|
|
134
146
|
return typeof value === 'string' && /^#[\da-f]{6}$/i.test(value) ? value.toLowerCase() : fallback
|
|
135
147
|
}
|
|
136
148
|
|
|
149
|
+
function materialBlurOr(value, fallback) {
|
|
150
|
+
return typeof value === 'number' && Number.isFinite(value)
|
|
151
|
+
? Math.min(32, Math.max(0, Math.round(value)))
|
|
152
|
+
: fallback
|
|
153
|
+
}
|
|
154
|
+
|
|
137
155
|
function particleTraversalSpeedOr(value, fallback) {
|
|
138
156
|
const numeric = Number(value)
|
|
139
157
|
if (!Number.isFinite(numeric)) return fallback
|
|
@@ -189,6 +207,18 @@ export function normalizePreferences(value) {
|
|
|
189
207
|
texture: enumOr(input.texture, TEXTURES, DEFAULT_PREFERENCES.texture),
|
|
190
208
|
glass: normalizeGlass(input),
|
|
191
209
|
motion: input.motion === 'full' ? 'system' : enumOr(input.motion, MOTIONS, DEFAULT_PREFERENCES.motion),
|
|
210
|
+
bakerLoading: input.bakerLoading === true,
|
|
211
|
+
backgroundMode: ['off', 'split', 'full'].includes(input.backgroundMode) ? input.backgroundMode : 'off',
|
|
212
|
+
sidebarOpacity: typeof input.sidebarOpacity === 'number' && Number.isFinite(input.sidebarOpacity) ? Math.min(1, Math.max(0, input.sidebarOpacity)) : .06,
|
|
213
|
+
railOpacity: typeof input.railOpacity === 'number' && Number.isFinite(input.railOpacity) ? Math.min(1, Math.max(0, input.railOpacity)) : .3,
|
|
214
|
+
bakerOpacity: typeof input.bakerOpacity === 'number' && Number.isFinite(input.bakerOpacity) ? Math.min(1, Math.max(0, input.bakerOpacity)) : .5,
|
|
215
|
+
classicUserOpacity: typeof input.classicUserOpacity === 'number' && Number.isFinite(input.classicUserOpacity) ? Math.min(1, Math.max(0, input.classicUserOpacity)) : .5,
|
|
216
|
+
sidebarBlur: materialBlurOr(input.sidebarBlur, DEFAULT_PREFERENCES.sidebarBlur),
|
|
217
|
+
railBlur: materialBlurOr(input.railBlur, DEFAULT_PREFERENCES.railBlur),
|
|
218
|
+
bakerBlur: materialBlurOr(input.bakerBlur, DEFAULT_PREFERENCES.bakerBlur),
|
|
219
|
+
classicUserBlur: materialBlurOr(input.classicUserBlur, DEFAULT_PREFERENCES.classicUserBlur),
|
|
220
|
+
backgroundDim: typeof input.backgroundDim === 'number' && Number.isFinite(input.backgroundDim) ? Math.min(.9, Math.max(0, input.backgroundDim)) : .45,
|
|
221
|
+
composerGlass: booleanOr(input.composerGlass, DEFAULT_PREFERENCES.composerGlass),
|
|
192
222
|
bootAnimation: booleanOr(input.bootAnimation, DEFAULT_PREFERENCES.bootAnimation),
|
|
193
223
|
cursor: input.cursor === 'system' ? 'system' : DEFAULT_PREFERENCES.cursor,
|
|
194
224
|
conversationParticleDensity: enumOr(legacyDensities.conversation, PARTICLE_DENSITIES, particlePreset.conversationParticleDensity),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export function createSettingsPage(React, meta = {}) {
|
|
2
2
|
const h = React.createElement
|
|
3
3
|
|
|
4
|
-
return function PrtsPluginSettingsPage({ useStore, updatePreference }) {
|
|
5
|
-
const { preferences } = useStore(state => ({ preferences: state.preferences }))
|
|
4
|
+
return function PrtsPluginSettingsPage({ useStore, updatePreference, retryPersistence }) {
|
|
5
|
+
const { preferences, persistence } = useStore(state => ({ preferences: state.preferences, persistence: state.status?.settingsPersistence }))
|
|
6
6
|
const safeMode = Boolean(meta.safeMode)
|
|
7
7
|
const enabled = Boolean(preferences.enabled) && !safeMode
|
|
8
8
|
const bootAnimation = preferences.bootAnimation !== false
|
|
@@ -40,6 +40,11 @@ export function createSettingsPage(React, meta = {}) {
|
|
|
40
40
|
safeMode && h('p', { className: 'prts-plugin-settings__safe', role: 'status' },
|
|
41
41
|
'安全模式已暂停主题。移除地址中的 ?prts-safe=1 后即可恢复。',
|
|
42
42
|
),
|
|
43
|
+
persistence?.phase === 'error' && h('div', { role: 'alert' },
|
|
44
|
+
h('p', null, '设置未保存到 DSH。请检查连接或持久化支持;重试会重新提交本次修改。'),
|
|
45
|
+
h('button', { type: 'button', onClick: retryPersistence }, '重试保存'),
|
|
46
|
+
),
|
|
47
|
+
persistence?.phase === 'saving' && h('p', { role: 'status' }, '正在保存…'),
|
|
43
48
|
h('div', { className: 'prts-plugin-settings__list' },
|
|
44
49
|
settingRow({
|
|
45
50
|
key: 'enabled',
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { createCursorAdapter } from './cursor-adapter.js'
|
|
2
2
|
import { normalizePreferences } from './preferences.js'
|
|
3
3
|
|
|
4
|
+
export const SCHEME_COMMIT_PROGRESS = .15
|
|
5
|
+
|
|
4
6
|
const ROOT_ATTRIBUTES = [
|
|
5
7
|
'data-dsh-prts',
|
|
6
8
|
'data-prts-scheme',
|
|
@@ -14,6 +16,7 @@ const ROOT_ATTRIBUTES = [
|
|
|
14
16
|
'data-prts-session-flow-palette',
|
|
15
17
|
'data-prts-session-flow-speed',
|
|
16
18
|
'data-prts-conversation-style',
|
|
19
|
+
'data-prts-background-mode',
|
|
17
20
|
]
|
|
18
21
|
const SESSION_FLOW_COLOR_PROPERTIES = {
|
|
19
22
|
'--prts-session-flow-back': 'sessionFlowColorBack',
|
|
@@ -112,6 +115,12 @@ export function createThemeController({ document, window, cssText, service, curs
|
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
function clearThemeState() {
|
|
118
|
+
root.removeAttribute('data-prts-baker-loading')
|
|
119
|
+
root.style.removeProperty('--prts-background-shade')
|
|
120
|
+
for (const name of ['sidebar', 'rail', 'baker']) root.style.removeProperty(`--prts-${name}-opacity`)
|
|
121
|
+
root.style.removeProperty('--prts-classic-user-opacity')
|
|
122
|
+
for (const name of ['sidebar', 'rail', 'baker']) root.style.removeProperty(`--prts-${name}-blur`)
|
|
123
|
+
root.style.removeProperty('--prts-classic-user-blur')
|
|
115
124
|
for (const attribute of ROOT_ATTRIBUTES) root.removeAttribute(attribute)
|
|
116
125
|
for (const property of Object.keys(SESSION_FLOW_COLOR_PROPERTIES)) root.style.removeProperty(property)
|
|
117
126
|
clearTransitionState()
|
|
@@ -237,11 +246,23 @@ export function createThemeController({ document, window, cssText, service, curs
|
|
|
237
246
|
function updateRevealHud(session) {
|
|
238
247
|
if (!session || session.settled) return
|
|
239
248
|
const percentage = Math.round(revealProgress(session.progress) * 100)
|
|
240
|
-
if (session.hudEdge) session.hudEdge.dataset.prtsSchemeRevealSide = 'left'
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
249
|
+
if (session.hudEdge && session.hudEdge.dataset.prtsSchemeRevealSide !== 'left') session.hudEdge.dataset.prtsSchemeRevealSide = 'left'
|
|
250
|
+
if (session.hudPercentage !== percentage) {
|
|
251
|
+
session.hudPercentage = percentage
|
|
252
|
+
const image = revealValueImageUrl(percentage)
|
|
253
|
+
if (session.hudAnimation?.effect?.setKeyframes) {
|
|
254
|
+
const frame = {
|
|
255
|
+
backgroundImage: image + ',linear-gradient(#ffd400,#ffd400)',
|
|
256
|
+
backgroundSize: '24px 11px,2px ' + (percentage * .48) + 'px',
|
|
257
|
+
}
|
|
258
|
+
session.hudAnimation.effect.setKeyframes([frame, frame])
|
|
259
|
+
} else {
|
|
260
|
+
root.style.setProperty('--prts-scheme-control-meter-height', `${percentage * .48}px`)
|
|
261
|
+
root.style.setProperty('--prts-scheme-control-value-image', image)
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const armed = session.interactive && session.progress >= SCHEME_COMMIT_PROGRESS
|
|
265
|
+
if (root.hasAttribute(TRANSITION_ARMED_ATTRIBUTE) !== armed) root.toggleAttribute(TRANSITION_ARMED_ATTRIBUTE, armed)
|
|
245
266
|
}
|
|
246
267
|
|
|
247
268
|
function releaseRevealHud(session) {
|
|
@@ -354,11 +375,16 @@ export function createThemeController({ document, window, cssText, service, curs
|
|
|
354
375
|
], options, '::view-transition-new(prts-scheme-grid)')
|
|
355
376
|
const mark = revealNamedAnimation([
|
|
356
377
|
{ offset: 0, translate: revealGroupTranslate(0), rotate: '45deg' },
|
|
357
|
-
{ offset: .
|
|
358
|
-
{ offset: .
|
|
378
|
+
{ offset: SCHEME_COMMIT_PROGRESS - .01, translate: revealGroupTranslate(session.geometry.width * (SCHEME_COMMIT_PROGRESS - .01)), rotate: '45deg' },
|
|
379
|
+
{ offset: SCHEME_COMMIT_PROGRESS + .01, translate: revealGroupTranslate(session.geometry.width * (SCHEME_COMMIT_PROGRESS + .01)), rotate: '225deg' },
|
|
359
380
|
{ offset: 1, translate: revealGroupTranslate(session.geometry.width), rotate: '225deg' },
|
|
360
381
|
], options, '::view-transition-group(prts-scheme-mark)')
|
|
361
382
|
session.scrubAnimations = [clip, edge, node, grid, gridPhase, mark]
|
|
383
|
+
session.hudAnimation = revealNamedAnimation([{}], options, '::view-transition-new(prts-scheme-node)')
|
|
384
|
+
session.hudAnimation.pause?.()
|
|
385
|
+
session.hudAnimation.currentTime = 0
|
|
386
|
+
session.hudPercentage = undefined
|
|
387
|
+
updateRevealHud(session)
|
|
362
388
|
for (const animation of session.scrubAnimations) {
|
|
363
389
|
animation.pause?.()
|
|
364
390
|
animation.currentTime = session.progress * session.duration
|
|
@@ -370,11 +396,10 @@ export function createThemeController({ document, window, cssText, service, curs
|
|
|
370
396
|
if (!session || session.settled) return
|
|
371
397
|
session.progress = revealProgress(value)
|
|
372
398
|
session.revealX = session.progress * session.geometry.width
|
|
373
|
-
root.style.setProperty('--prts-scheme-reveal-x', `${session.revealX}px`)
|
|
399
|
+
if (!session.scrubAnimations) root.style.setProperty('--prts-scheme-reveal-x', `${session.revealX}px`)
|
|
374
400
|
if (session.scrubAnimations) {
|
|
375
401
|
try {
|
|
376
402
|
for (const animation of session.scrubAnimations) {
|
|
377
|
-
animation.pause?.()
|
|
378
403
|
animation.currentTime = session.progress * session.duration
|
|
379
404
|
}
|
|
380
405
|
} catch {}
|
|
@@ -395,12 +420,19 @@ export function createThemeController({ document, window, cssText, service, curs
|
|
|
395
420
|
for (const animation of session?.scrubAnimations || []) {
|
|
396
421
|
try { animation?.cancel?.() } catch {}
|
|
397
422
|
}
|
|
398
|
-
|
|
423
|
+
session?.hudAnimation?.cancel?.()
|
|
424
|
+
if (session) {
|
|
425
|
+
session.scrubAnimations = undefined
|
|
426
|
+
session.hudAnimation = undefined
|
|
427
|
+
session.hudPercentage = undefined
|
|
428
|
+
}
|
|
399
429
|
}
|
|
400
430
|
|
|
401
431
|
function animateRevealTo(session, value, duration, easing) {
|
|
402
432
|
if (!session || session.settled) return Promise.resolve(false)
|
|
403
433
|
cancelRevealAnimation(session)
|
|
434
|
+
// Transfer the last HUD value once before the release animation takes over.
|
|
435
|
+
updateRevealHud(session)
|
|
404
436
|
const targetProgress = revealProgress(value)
|
|
405
437
|
const targetX = revealTargetX(session, targetProgress)
|
|
406
438
|
if (!(duration > 0)) {
|
|
@@ -442,8 +474,8 @@ export function createThemeController({ document, window, cssText, service, curs
|
|
|
442
474
|
{ backgroundSize: `2px ${targetProgress * 48}px` },
|
|
443
475
|
], options, '::view-transition-new(prts-scheme-node)')
|
|
444
476
|
const mark = revealNamedAnimation([
|
|
445
|
-
{ translate: revealGroupTranslate(session.revealX), rotate: session.progress >=
|
|
446
|
-
{ translate: revealGroupTranslate(targetX), rotate: targetProgress >=
|
|
477
|
+
{ translate: revealGroupTranslate(session.revealX), rotate: session.progress >= SCHEME_COMMIT_PROGRESS ? '225deg' : '45deg' },
|
|
478
|
+
{ translate: revealGroupTranslate(targetX), rotate: targetProgress >= SCHEME_COMMIT_PROGRESS ? '225deg' : '45deg' },
|
|
447
479
|
], options, '::view-transition-group(prts-scheme-mark)')
|
|
448
480
|
animations = [clip, edge, node, grid, gridPhase, nodeProgress, mark]
|
|
449
481
|
} catch {
|
|
@@ -601,7 +633,7 @@ export function createThemeController({ document, window, cssText, service, curs
|
|
|
601
633
|
let settled = false
|
|
602
634
|
return {
|
|
603
635
|
update(value) { if (!settled) progress = revealProgress(value) },
|
|
604
|
-
finish(commit = progress >=
|
|
636
|
+
finish(commit = progress >= SCHEME_COMMIT_PROGRESS) {
|
|
605
637
|
if (settled) return root.dataset.prtsScheme
|
|
606
638
|
settled = true
|
|
607
639
|
return commit ? setTheme(target, { animate: false }) : source
|
|
@@ -734,6 +766,13 @@ export function createThemeController({ document, window, cssText, service, curs
|
|
|
734
766
|
root.dataset.prtsSessionFlowSpeed = String(preferences.sessionFlowSpeed)
|
|
735
767
|
for (const [property, key] of Object.entries(SESSION_FLOW_COLOR_PROPERTIES)) root.style.setProperty(property, preferences[key])
|
|
736
768
|
root.dataset.prtsConversationStyle = preferences.conversationStyle
|
|
769
|
+
root.toggleAttribute('data-prts-baker-loading', preferences.bakerLoading === true)
|
|
770
|
+
root.dataset.prtsBackgroundMode = preferences.backgroundMode
|
|
771
|
+
root.style.setProperty('--prts-background-shade', `${preferences.backgroundDim * 100}%`)
|
|
772
|
+
for (const name of ['sidebar', 'rail', 'baker']) root.style.setProperty(`--prts-${name}-opacity`, `${preferences[`${name}Opacity`] * 100}%`)
|
|
773
|
+
root.style.setProperty('--prts-classic-user-opacity', `${preferences.classicUserOpacity * 100}%`)
|
|
774
|
+
for (const name of ['sidebar', 'rail', 'baker']) root.style.setProperty(`--prts-${name}-blur`, `${preferences[`${name}Blur`]}px`)
|
|
775
|
+
root.style.setProperty('--prts-classic-user-blur', `${preferences.classicUserBlur}px`)
|
|
737
776
|
refresh()
|
|
738
777
|
},
|
|
739
778
|
sync,
|