@ash-qw/dsh-theme-prts 2.5.4 → 2.6.0
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 +25 -0
- package/README.en.md +3 -2
- package/README.md +3 -2
- package/lib/client.js +222 -34
- package/package.json +2 -2
- package/src/client/conversation-control-adapter.js +16 -2
- package/src/client/cursor-adapter.js +37 -6
- package/src/client/index.js +5 -1
- package/src/client/native-preferences.js +8 -4
- package/src/client/preferences.js +24 -4
- package/src/client/preview-markdown.js +5 -2
- package/src/client/session-summary-preview.js +49 -5
- package/src/client/theme-controller-v2.js +3 -2
- package/src/client/theme-controller.js +3 -2
- package/src/client/theme-settings-workbench.js +82 -9
- package/src/index.js +2 -1
- package/src/styles/facility-vector.css +11 -2
- package/src/styles/prts.css +1 -1
- package/src/styles/theme-settings-workbench.css +52 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ash-qw/dsh-theme-prts",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Unofficial Arknights P.R.T.S.-inspired UI theme for DeepSeek Harness Web and the official desktop client",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
},
|
|
70
70
|
"engines": {
|
|
71
71
|
"node": ">=20.9.0",
|
|
72
|
-
"dsh": "0.2.1-rc.2 || 0.2.1-rc.3"
|
|
72
|
+
"dsh": "0.2.1-rc.2 || 0.2.1-rc.3 || 0.2.1-rc.5"
|
|
73
73
|
},
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"@playwright/test": "1.55.1",
|
|
@@ -23,6 +23,8 @@ function isPendingInteractionAction(button) {
|
|
|
23
23
|
function classify(button, operation) {
|
|
24
24
|
// The host's process disclosure owns its transparent, full-width hit area.
|
|
25
25
|
if (button.hasAttribute('data-turn-process')) return null
|
|
26
|
+
// Schedule cards use an absolute transparent button as the whole-card hit area.
|
|
27
|
+
if (button.parentElement?.matches('[data-tool="schedule_create"]') && hasClassSuffix(button, '_cardOpen')) return null
|
|
26
28
|
if (button.querySelector('[data-compaction-disclosure]')) return null
|
|
27
29
|
// Notification headers belong to the card surface, not standalone buttons.
|
|
28
30
|
if (button.parentElement?.matches('[data-turn-trigger]')) return null
|
|
@@ -183,10 +185,22 @@ function mountBodyTargetFrame(document, window, operation) {
|
|
|
183
185
|
const find=node=>{
|
|
184
186
|
if(activeModal && !activeModal.contains(node))return null
|
|
185
187
|
if (!node?.closest || node.closest('[data-prts-workspace-actions],[data-prts-session-actions]')) return null
|
|
186
|
-
const
|
|
188
|
+
const container=node.closest('[data-presented-file],[data-changed-files],[data-tool="schedule_create"]')
|
|
187
189
|
const button=node.closest('button')
|
|
190
|
+
let cardTarget=null
|
|
191
|
+
if(container){
|
|
192
|
+
const action=node.closest('button,a[href],[role="button"],[role="link"],summary,input:not([type="hidden"]),select,textarea')
|
|
193
|
+
const nested=action && action!==container && container.contains(action)
|
|
194
|
+
// Disabled controls must not fall back to their enclosing card.
|
|
195
|
+
if(node.closest(':disabled,[aria-disabled="true"],[inert]'))return null
|
|
196
|
+
const hitLayer=nested && action.tagName==='BUTTON' && (
|
|
197
|
+
container.matches('[data-presented-file]') && hasClassSuffix(action,'_cardPreview') ||
|
|
198
|
+
action.parentElement===container && container.matches('[data-tool="schedule_create"]') && hasClassSuffix(action,'_cardOpen'))
|
|
199
|
+
const openable=!container.matches('[data-tool="schedule_create"]') || [...container.children].some(child=>child.tagName==='BUTTON' && hasClassSuffix(child,'_cardOpen') && !child.matches(':disabled,[aria-disabled="true"]'))
|
|
200
|
+
cardTarget=nested && !hitLayer ? action : openable ? container : null
|
|
201
|
+
}
|
|
188
202
|
const pendingAction=operation.contains(button) && isPendingInteractionAction(button) ? button : null
|
|
189
|
-
const candidate=
|
|
203
|
+
const candidate=cardTarget || pendingAction || node.closest(selector)
|
|
190
204
|
return candidate && !candidate.matches(':disabled,[aria-disabled="true"]') && !candidate.closest('[inert]') ? candidate : null
|
|
191
205
|
}
|
|
192
206
|
const measure=(box)=>{bounds=box || target.getBoundingClientRect();targetArm=7+Math.max(0,Math.min(1,(Math.min(bounds.width,bounds.height)-36)/36))*5;boundsDirty=false}
|
|
@@ -7,6 +7,7 @@ export function createCursorAdapter({ document, window, assets }) {
|
|
|
7
7
|
let handPhase
|
|
8
8
|
let gestureAssets = assets
|
|
9
9
|
let textTarget
|
|
10
|
+
let moveFrame, pendingMove
|
|
10
11
|
let lastPoint
|
|
11
12
|
const heroTextSelector = '[data-phase="hero"] [class$="_titleGroup"]'
|
|
12
13
|
const contentSelector = '[data-markdown], [data-slot*="markdown" i], [class*="markdown" i], :is([data-chat-flow-kind="user"], [data-chat-flow-kind="steering"], [data-submission-echo], [data-pending-steering]) [class$="_bubble"]'
|
|
@@ -90,6 +91,7 @@ export function createCursorAdapter({ document, window, assets }) {
|
|
|
90
91
|
let originalPriority
|
|
91
92
|
let appliedCursor
|
|
92
93
|
let appliedPriority
|
|
94
|
+
let lastHandFrame
|
|
93
95
|
function ownsHandCursor() {
|
|
94
96
|
return handTarget?.style.cursor === appliedCursor
|
|
95
97
|
&& handTarget.style.getPropertyPriority('cursor') === appliedPriority
|
|
@@ -106,6 +108,7 @@ export function createCursorAdapter({ document, window, assets }) {
|
|
|
106
108
|
}
|
|
107
109
|
handTarget?.removeAttribute('data-prts-hand-active')
|
|
108
110
|
handTarget = undefined
|
|
111
|
+
lastHandFrame = undefined
|
|
109
112
|
}
|
|
110
113
|
// Cursor is inherited. Animating a large container still restyles its subtree.
|
|
111
114
|
// Keep those background hits static, while controls retain native animation.
|
|
@@ -130,7 +133,9 @@ export function createCursorAdapter({ document, window, assets }) {
|
|
|
130
133
|
}
|
|
131
134
|
function applyHandFrame() {
|
|
132
135
|
if (stopIfHandChanged() || !handTarget) return
|
|
136
|
+
if(lastHandFrame === handCursor && ownsHandCursor())return
|
|
133
137
|
handTarget.style.setProperty('cursor', handCursor)
|
|
138
|
+
lastHandFrame = handCursor
|
|
134
139
|
appliedCursor = handTarget.style.cursor
|
|
135
140
|
appliedPriority = handTarget.style.getPropertyPriority('cursor')
|
|
136
141
|
}
|
|
@@ -156,20 +161,27 @@ export function createCursorAdapter({ document, window, assets }) {
|
|
|
156
161
|
}
|
|
157
162
|
function play(frames, done) {
|
|
158
163
|
clearTimer()
|
|
159
|
-
|
|
164
|
+
const started = window.performance.now()
|
|
165
|
+
let index = 0, deadline = frames[0]?.duration ?? 0
|
|
160
166
|
function next() {
|
|
161
167
|
timer = undefined
|
|
162
168
|
if (stopIfHandChanged()) return
|
|
163
|
-
|
|
169
|
+
const elapsed = window.performance.now() - started
|
|
170
|
+
// Skip expired frames after a busy main thread; never replay a backlog.
|
|
171
|
+
while (index < frames.length - 1 && elapsed >= deadline) {
|
|
172
|
+
index += 1
|
|
173
|
+
deadline += frames[index]?.duration ?? 0
|
|
174
|
+
}
|
|
175
|
+
// The source ANI ends with a 60-second hold frame, which is not animated.
|
|
164
176
|
if (index >= frames.length - 1) { done(); return }
|
|
165
|
-
|
|
166
|
-
show(frame.url)
|
|
177
|
+
show(frames[index].url)
|
|
167
178
|
if (!handCursor) return
|
|
168
|
-
timer = window.setTimeout(next,
|
|
179
|
+
timer = window.setTimeout(next, Math.max(1, deadline - (window.performance.now() - started)))
|
|
169
180
|
}
|
|
170
181
|
next()
|
|
171
182
|
}
|
|
172
183
|
function down(event) {
|
|
184
|
+
clearPendingMove()
|
|
173
185
|
if (event.pointerType !== 'mouse' || event.button !== 0 || event.isPrimary === false) return
|
|
174
186
|
updateResize(event)
|
|
175
187
|
updateTextHit(event)
|
|
@@ -203,13 +215,32 @@ export function createCursorAdapter({ document, window, assets }) {
|
|
|
203
215
|
if (handPhase === 'press') return
|
|
204
216
|
releaseHand()
|
|
205
217
|
}
|
|
206
|
-
function
|
|
218
|
+
function applyMove(event) {
|
|
207
219
|
updateResize(event)
|
|
208
220
|
updateTextHit(event)
|
|
209
221
|
if (handCursor) followHand(event.target?.nodeType === 1 ? event.target : event.target?.parentElement)
|
|
210
222
|
if (pointerId !== undefined && event.pointerId === pointerId && !(event.buttons & 1)) reset()
|
|
211
223
|
}
|
|
224
|
+
function clearPendingMove() {
|
|
225
|
+
if (moveFrame !== undefined) window.cancelAnimationFrame?.(moveFrame)
|
|
226
|
+
moveFrame = undefined
|
|
227
|
+
pendingMove = undefined
|
|
228
|
+
}
|
|
229
|
+
function move(event) {
|
|
230
|
+
if (event.pointerType !== 'mouse') return
|
|
231
|
+
// Lost releases must clear synchronously even when the next paint is delayed.
|
|
232
|
+
if (pointerId !== undefined && event.pointerId === pointerId && !(event.buttons & 1)) reset()
|
|
233
|
+
if (!window.requestAnimationFrame) { applyMove(event); return }
|
|
234
|
+
pendingMove = { target:event.target, pointerType:event.pointerType, pointerId:event.pointerId,
|
|
235
|
+
buttons:event.buttons, clientX:event.clientX, clientY:event.clientY }
|
|
236
|
+
if (moveFrame === undefined) moveFrame = window.requestAnimationFrame(() => {
|
|
237
|
+
const latest = pendingMove
|
|
238
|
+
clearPendingMove()
|
|
239
|
+
if (latest?.target?.isConnected) applyMove(latest)
|
|
240
|
+
})
|
|
241
|
+
}
|
|
212
242
|
function resetInteraction() {
|
|
243
|
+
clearPendingMove()
|
|
213
244
|
reset()
|
|
214
245
|
clearResize()
|
|
215
246
|
clearTextHit()
|
package/src/client/index.js
CHANGED
|
@@ -223,7 +223,11 @@ export function applyPrtsPlugin(ctx, environment) {
|
|
|
223
223
|
persistAndApply(updatePreferenceValue(preferences, key, value))
|
|
224
224
|
},
|
|
225
225
|
onPreferencePreview(key, value) {
|
|
226
|
-
if (key === 'cornerColor'
|
|
226
|
+
if (key === 'cornerColor' || key === 'cornerColorLight') {
|
|
227
|
+
const properties = preferences.cornerColorLinked ? ['--prts-corner-color-light', '--prts-corner-color-dark'] : [key === 'cornerColorLight' ? '--prts-corner-color-light' : '--prts-corner-color-dark']
|
|
228
|
+
for (const property of properties) document.documentElement.style.setProperty(property, String(value))
|
|
229
|
+
return
|
|
230
|
+
}
|
|
227
231
|
if (key === 'particleTraversalSpeed') {
|
|
228
232
|
particleField.update(updatePreferenceValue(preferences, key, value))
|
|
229
233
|
return
|
|
@@ -28,11 +28,15 @@ export function createNativePreferences({ storage, onChange }) {
|
|
|
28
28
|
migrationAttempted = true
|
|
29
29
|
pending = { ...legacy, ...pending }
|
|
30
30
|
}
|
|
31
|
-
// Materialize
|
|
32
|
-
// cannot change a right-side preference that was never written by the Host.
|
|
31
|
+
// Materialize legacy shared values before either side can be edited independently.
|
|
33
32
|
if (s.value?.preferencesInitialized && writable(s)) {
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
if (s.value.cornerColorSource == null && s.value.cornerColorLinked === true) {
|
|
34
|
+
for (const key of ['cornerColor', 'cornerCustomColor']) {
|
|
35
|
+
if (s.value[key] !== host[key] && !Object.hasOwn(pending, key) && !Object.hasOwn(inFlight, key) && !Object.hasOwn(failed, key)) pending[key] = host[key]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
for (const key of ['rightSidebarOpacity', 'rightSidebarBlur', 'cornerColorLight', 'cornerCustomColorLight', 'cornerColorSource']) {
|
|
39
|
+
if (s.value[key] == null && !Object.hasOwn(pending, key) && !Object.hasOwn(inFlight, key) && !Object.hasOwn(failed, key)) pending[key] = host[key]
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
42
|
current = normalizePreferences({ ...host, ...inFlight, ...pending })
|
|
@@ -39,8 +39,12 @@ export const DEFAULT_PREFERENCES = Object.freeze({
|
|
|
39
39
|
bakerBlur: 8,
|
|
40
40
|
classicUserBlur: 8,
|
|
41
41
|
cursor: 'prts',
|
|
42
|
+
cornerColorLinked: false,
|
|
43
|
+
cornerColorSource: 'dark',
|
|
42
44
|
cornerColor: '#f0c800',
|
|
43
45
|
cornerCustomColor: '#f0c800',
|
|
46
|
+
cornerColorLight: '#f0c800',
|
|
47
|
+
cornerCustomColorLight: '#f0c800',
|
|
44
48
|
conversationParticleDensity: 'sparse',
|
|
45
49
|
heroParticleDensity: 'light',
|
|
46
50
|
particleTraversalSpeed: PARTICLE_TRAVERSAL_SPEED_DEFAULT,
|
|
@@ -107,7 +111,7 @@ export const PREFERENCE_GROUPS = Object.freeze({
|
|
|
107
111
|
background: Object.freeze(['texture', 'backgroundMode', 'backgroundDim', 'heroContrast', 'sidebarOpacity', 'rightSidebarOpacity', 'railOpacity', 'bakerOpacity', 'classicUserOpacity', 'sidebarBlur', 'rightSidebarBlur', 'railBlur', 'bakerBlur', 'classicUserBlur']),
|
|
108
112
|
particles: Object.freeze(['conversationParticleDensity', 'heroParticleDensity', 'particleTraversalSpeed']),
|
|
109
113
|
material: Object.freeze(['glass', 'composerGlass']),
|
|
110
|
-
accessibility: Object.freeze(['motion', 'bootAnimation', 'cursor', 'cornerColor', 'cornerCustomColor']),
|
|
114
|
+
accessibility: Object.freeze(['motion', 'bootAnimation', 'cursor', 'cornerColorLinked', 'cornerColorSource', 'cornerColor', 'cornerCustomColor', 'cornerColorLight', 'cornerCustomColorLight']),
|
|
111
115
|
aiTools: Object.freeze(['aiToolsEnabled']),
|
|
112
116
|
navigation: Object.freeze(['railDefaultHidden', 'sessionPreview', 'previewOngoing', 'previewTools', 'previewWarning', 'previewDone', 'previewDoubleClick', 'previewFollowOngoing', 'previewFollowWarning', 'previewReplyLimit']),
|
|
113
117
|
conversation: Object.freeze(['sessionFlow', 'sessionGlow', 'sessionFlowPalette', 'sessionFlowColorBack', 'sessionFlowColorCore', 'sessionFlowColorFront', 'sessionFlowSpeed', 'conversationStyle', 'bakerGroupAvatars', 'bakerLoading', 'conversationEmblem']),
|
|
@@ -231,8 +235,12 @@ export function normalizePreferences(value) {
|
|
|
231
235
|
backgroundDim: typeof input.backgroundDim === 'number' && Number.isFinite(input.backgroundDim) ? Math.min(.9, Math.max(0, input.backgroundDim)) : .45,
|
|
232
236
|
composerGlass: booleanOr(input.composerGlass, DEFAULT_PREFERENCES.composerGlass),
|
|
233
237
|
bootAnimation: booleanOr(input.bootAnimation, DEFAULT_PREFERENCES.bootAnimation),
|
|
238
|
+
cornerColorLinked: booleanOr(input.cornerColorLinked, false),
|
|
239
|
+
cornerColorSource: input.cornerColorSource === 'light' ? 'light' : 'dark',
|
|
234
240
|
cornerCustomColor: colorOr(input.cornerCustomColor, DEFAULT_PREFERENCES.cornerCustomColor),
|
|
235
241
|
cornerColor: colorOr(input.cornerColor, DEFAULT_PREFERENCES.cornerColor),
|
|
242
|
+
cornerColorLight: colorOr(input.cornerColorLight, colorOr(input.cornerColor, DEFAULT_PREFERENCES.cornerColor)),
|
|
243
|
+
cornerCustomColorLight: colorOr(input.cornerCustomColorLight, colorOr(input.cornerCustomColor, DEFAULT_PREFERENCES.cornerCustomColor)),
|
|
236
244
|
cursor: input.cursor === 'system' ? 'system' : DEFAULT_PREFERENCES.cursor,
|
|
237
245
|
conversationParticleDensity: enumOr(legacyDensities.conversation, PARTICLE_DENSITIES, particlePreset.conversationParticleDensity),
|
|
238
246
|
heroParticleDensity: enumOr(legacyDensities.hero, PARTICLE_DENSITIES, particlePreset.heroParticleDensity),
|
|
@@ -263,6 +271,11 @@ export function normalizePreferences(value) {
|
|
|
263
271
|
bakerGroupAvatars: booleanOr(input.bakerGroupAvatars, DEFAULT_PREFERENCES.bakerGroupAvatars),
|
|
264
272
|
conversationStyle: enumOr(input.conversationStyle, CONVERSATION_STYLES, DEFAULT_PREFERENCES.conversationStyle),
|
|
265
273
|
}
|
|
274
|
+
// Preserve the active color from the interim shared-palette format on upgrade.
|
|
275
|
+
if (input.cornerColorSource == null && input.cornerColorLinked === true) {
|
|
276
|
+
normalized.cornerColor = colorOr(input.cornerColorShared, normalized.cornerColor)
|
|
277
|
+
normalized.cornerCustomColor = colorOr(input.cornerCustomColorShared, normalized.cornerCustomColor)
|
|
278
|
+
}
|
|
266
279
|
normalized.preset = matchedVisualPreset(normalized)
|
|
267
280
|
return normalized
|
|
268
281
|
}
|
|
@@ -287,9 +300,16 @@ export function updatePreferenceValue(value, key, nextValue) {
|
|
|
287
300
|
if (key === 'preset') return applyVisualPreset(value, nextValue)
|
|
288
301
|
if (key === 'particleDetail') return applyParticleDetail(value, nextValue)
|
|
289
302
|
const current = normalizePreferences(value)
|
|
290
|
-
if (key === '
|
|
291
|
-
const
|
|
292
|
-
return normalizePreferences({ ...current,
|
|
303
|
+
if (key === 'cornerColorLink') {
|
|
304
|
+
const enabled = nextValue === 'light' || nextValue === 'dark'
|
|
305
|
+
return normalizePreferences({ ...current, cornerColorLinked: enabled,
|
|
306
|
+
...(enabled ? { cornerColorSource: nextValue } : {}) })
|
|
307
|
+
}
|
|
308
|
+
if (['cornerColor', 'cornerColorLight', 'cornerCustomColor', 'cornerCustomColorLight'].includes(key)) {
|
|
309
|
+
const color = colorOr(nextValue, current[key])
|
|
310
|
+
const custom = key.startsWith('cornerCustom')
|
|
311
|
+
const colorKey = custom ? key.replace('cornerCustomColor', 'cornerColor') : key
|
|
312
|
+
return normalizePreferences({ ...current, [colorKey]: color, ...(custom ? { [key]: color } : {}) })
|
|
293
313
|
}
|
|
294
314
|
if (key === 'sessionFlowPalette') {
|
|
295
315
|
const palette = enumOr(nextValue, SESSION_FLOW_PALETTE_IDS, current.sessionFlowPalette)
|
|
@@ -27,9 +27,12 @@ export function createPreviewContentCache(document, render = renderPreviewMarkdo
|
|
|
27
27
|
const html = render(text)
|
|
28
28
|
const plain = document.createElement('div')
|
|
29
29
|
plain.innerHTML = html
|
|
30
|
-
const
|
|
30
|
+
const fullSummary = plain.textContent.replace(/\s+/g, ' ').trim()
|
|
31
|
+
const summary = fullSummary.slice(0, 100)
|
|
32
|
+
const truncated = fullSummary.length > summary.length
|
|
33
|
+
const needsDetail = truncated || Boolean(plain.querySelector('a,pre,ul,ol,table,blockquote,h1,h2,h3,h4,h5,h6,br') || plain.children.length > 1)
|
|
31
34
|
const cost = text.length + html.length + summary.length
|
|
32
|
-
const value = {html, summary, cost}
|
|
35
|
+
const value = {html, summary, cost, needsDetail, truncated}
|
|
33
36
|
if (cost <= 262144) {
|
|
34
37
|
while (entries.size && (entries.size >= 32 || size + cost > 262144)) {
|
|
35
38
|
const key = entries.keys().next().value
|
|
@@ -293,12 +293,29 @@ export function createSessionSummaryPreview({ document, window, sessions, getSes
|
|
|
293
293
|
const height = Math.max(1, (viewport?.height ?? window.innerHeight) - 16)
|
|
294
294
|
const place = (surface, reference, preferredTop, noticeOwner, anchorBottom = preferredTop) => {
|
|
295
295
|
surface.style.maxWidth = 'none'
|
|
296
|
-
|
|
296
|
+
let fixedEdge
|
|
297
|
+
if (surface === panel) {
|
|
298
|
+
// Pick a direction once, before async replies arrive; keep the same edge
|
|
299
|
+
// while content grows and let the existing scroll surface absorb overflow.
|
|
300
|
+
if (!current.placement) {
|
|
301
|
+
const below = top + height - preferredTop
|
|
302
|
+
const above = anchorBottom - top
|
|
303
|
+
current.placement = below < 160 && above > below
|
|
304
|
+
? { direction: 'above', edge: anchorBottom }
|
|
305
|
+
: { direction: 'below', edge: preferredTop }
|
|
306
|
+
}
|
|
307
|
+
fixedEdge = Math.max(top, Math.min(current.placement.edge, top + height))
|
|
308
|
+
const available = current.placement.direction === 'above' ? fixedEdge - top : top + height - fixedEdge
|
|
309
|
+
surface.style.maxHeight = Math.max(1, available) + 'px'
|
|
310
|
+
surface.setAttribute('data-prts-preview-direction', current.placement.direction)
|
|
311
|
+
} else surface.style.maxHeight = 'none'
|
|
297
312
|
const bounds = surface.getBoundingClientRect()
|
|
298
313
|
const x = reference ? reference.right + 10 : left
|
|
299
|
-
const fitsVertically = y => y >= top && y + bounds.height <= top + height
|
|
314
|
+
const fitsVertically = y => y >= top - 1 && y + bounds.height <= top + height + 1
|
|
300
315
|
const above = anchorBottom - bounds.height
|
|
301
|
-
const y =
|
|
316
|
+
const y = surface === panel
|
|
317
|
+
? current.placement.direction === 'above' ? fixedEdge - bounds.height : fixedEdge
|
|
318
|
+
: !fitsVertically(preferredTop) && fitsVertically(above) ? above : preferredTop
|
|
302
319
|
const insufficient = Boolean(reference) && (x < left || x + bounds.width > left + width || !fitsVertically(y))
|
|
303
320
|
surface.toggleAttribute('data-prts-preview-no-space', insufficient)
|
|
304
321
|
const noticeKey = surface === panel ? 'spaceNotice' : 'detailSpaceNotice'
|
|
@@ -319,6 +336,7 @@ export function createSessionSummaryPreview({ document, window, sessions, getSes
|
|
|
319
336
|
surface.style.top = y + 'px'
|
|
320
337
|
return !insufficient
|
|
321
338
|
}
|
|
339
|
+
updateReplyAvailability()
|
|
322
340
|
const menu = current.anchor?.closest('[data-prts-session-summary-popover]')
|
|
323
341
|
const reference = menu?.getBoundingClientRect()
|
|
324
342
|
const row = current.anchor?.getBoundingClientRect()
|
|
@@ -343,6 +361,8 @@ export function createSessionSummaryPreview({ document, window, sessions, getSes
|
|
|
343
361
|
current.spaceNotice?.remove()
|
|
344
362
|
current.detailSpaceNotice?.remove()
|
|
345
363
|
panel.removeAttribute('data-prts-preview-no-space')
|
|
364
|
+
panel.removeAttribute('data-prts-preview-direction')
|
|
365
|
+
panel.style.removeProperty('max-height')
|
|
346
366
|
positionObserver?.disconnect()
|
|
347
367
|
window.cancelAnimationFrame(positionFrame)
|
|
348
368
|
window.removeEventListener('resize', schedulePosition)
|
|
@@ -537,8 +557,24 @@ export function createSessionSummaryPreview({ document, window, sessions, getSes
|
|
|
537
557
|
detailEntry?.removeAttribute('data-prts-reply-active')
|
|
538
558
|
detailEntry = undefined
|
|
539
559
|
}
|
|
560
|
+
const replyIsTruncated = item => {
|
|
561
|
+
const summary = item.querySelector('span')
|
|
562
|
+
return item.dataset.prtsSummaryTruncated === 'true' || summary.scrollHeight > summary.clientHeight + 1 || summary.scrollWidth > summary.clientWidth + 1
|
|
563
|
+
}
|
|
564
|
+
const updateReplyAvailability = () => {
|
|
565
|
+
const entries = [...body.querySelectorAll('[data-prts-reply-entry]')].map(item => [item, replyIsTruncated(item)])
|
|
566
|
+
for (const [item, truncated] of entries) {
|
|
567
|
+
const needed = truncated || item.dataset.prtsReplyTruncated === 'true'
|
|
568
|
+
item.toggleAttribute('data-prts-reply-overflow', truncated)
|
|
569
|
+
item.toggleAttribute('data-prts-reply-complete', !needed)
|
|
570
|
+
item.tabIndex = needed ? 0 : -1
|
|
571
|
+
item.setAttribute('aria-disabled', String(!needed))
|
|
572
|
+
if (!needed && detailEntry === item) hideDetail()
|
|
573
|
+
}
|
|
574
|
+
}
|
|
540
575
|
const showDetail = (reply, index, item, refresh = false) => {
|
|
541
576
|
keep()
|
|
577
|
+
if (!reply.tool && item.hasAttribute('data-prts-reply-complete')) { hideDetail(); return }
|
|
542
578
|
if (!refresh && detailKey === reply.key && current.detail && !current.detail.hidden) return
|
|
543
579
|
if (!current.detail) {
|
|
544
580
|
const detail = panel.ownerDocument.createElement('section')
|
|
@@ -708,8 +744,15 @@ export function createSessionSummaryPreview({ document, window, sessions, getSes
|
|
|
708
744
|
icon.append(outline)
|
|
709
745
|
heading.prepend(icon)
|
|
710
746
|
const summary = panel.ownerDocument.createElement('span')
|
|
711
|
-
|
|
712
|
-
|
|
747
|
+
const content = current.contentCache(reply.text)
|
|
748
|
+
summary.textContent = content.summary
|
|
749
|
+
item.dataset.prtsReplyTruncated = String(content.needsDetail)
|
|
750
|
+
item.dataset.prtsSummaryTruncated = String(content.truncated)
|
|
751
|
+
const ellipsis = panel.ownerDocument.createElement('i')
|
|
752
|
+
ellipsis.setAttribute('data-prts-reply-ellipsis', '')
|
|
753
|
+
ellipsis.setAttribute('aria-hidden', 'true')
|
|
754
|
+
ellipsis.textContent = '…'
|
|
755
|
+
item.append(heading, summary, ellipsis)
|
|
713
756
|
item.addEventListener('pointermove', () => showDetail(reply, index, item))
|
|
714
757
|
item.addEventListener('focus', () => showDetail(reply, index, item))
|
|
715
758
|
item.addEventListener('click', () => showDetail(reply, index, item))
|
|
@@ -725,6 +768,7 @@ export function createSessionSummaryPreview({ document, window, sessions, getSes
|
|
|
725
768
|
if (scrollAnchor?.isConnected) body.scrollTop += scrollAnchor.getBoundingClientRect().top - anchorTop
|
|
726
769
|
else if (previousTop === 0) body.scrollTop = 0
|
|
727
770
|
historyKeys = replies
|
|
771
|
+
schedulePosition()
|
|
728
772
|
previousText = undefined
|
|
729
773
|
previousQuestion = undefined
|
|
730
774
|
return
|
|
@@ -19,7 +19,8 @@ const ROOT_ATTRIBUTES = [
|
|
|
19
19
|
'data-prts-background-mode',
|
|
20
20
|
]
|
|
21
21
|
const SESSION_FLOW_COLOR_PROPERTIES = {
|
|
22
|
-
'--prts-corner-color': 'cornerColor',
|
|
22
|
+
'--prts-corner-color-dark': 'cornerColor',
|
|
23
|
+
'--prts-corner-color-light': 'cornerColorLight',
|
|
23
24
|
'--prts-session-flow-back': 'sessionFlowColorBack',
|
|
24
25
|
'--prts-session-flow-core': 'sessionFlowColorCore',
|
|
25
26
|
'--prts-session-flow-front': 'sessionFlowColorFront',
|
|
@@ -766,7 +767,7 @@ export function createThemeController({ document, window, cssText, service, curs
|
|
|
766
767
|
root.dataset.prtsSessionGlow = preferences.sessionGlow ? 'on' : 'off'
|
|
767
768
|
root.dataset.prtsSessionFlowPalette = preferences.sessionFlowPalette
|
|
768
769
|
root.dataset.prtsSessionFlowSpeed = String(preferences.sessionFlowSpeed)
|
|
769
|
-
for (const [property, key] of Object.entries(SESSION_FLOW_COLOR_PROPERTIES)) root.style.setProperty(property, preferences[key])
|
|
770
|
+
for (const [property, key] of Object.entries(SESSION_FLOW_COLOR_PROPERTIES)) root.style.setProperty(property, preferences.cornerColorLinked && (key === 'cornerColor' || key === 'cornerColorLight') ? preferences[preferences.cornerColorSource === 'light' ? 'cornerColorLight' : 'cornerColor'] : preferences[key])
|
|
770
771
|
root.dataset.prtsConversationStyle = preferences.conversationStyle
|
|
771
772
|
root.toggleAttribute('data-prts-baker-loading', preferences.bakerLoading === true)
|
|
772
773
|
root.dataset.prtsBackgroundMode = preferences.backgroundMode
|
|
@@ -14,7 +14,8 @@ const ROOT_ATTRIBUTES = [
|
|
|
14
14
|
'data-prts-conversation-style',
|
|
15
15
|
]
|
|
16
16
|
const SESSION_FLOW_COLOR_PROPERTIES = {
|
|
17
|
-
'--prts-corner-color': 'cornerColor',
|
|
17
|
+
'--prts-corner-color-dark': 'cornerColor',
|
|
18
|
+
'--prts-corner-color-light': 'cornerColorLight',
|
|
18
19
|
'--prts-session-flow-back': 'sessionFlowColorBack',
|
|
19
20
|
'--prts-session-flow-core': 'sessionFlowColorCore',
|
|
20
21
|
'--prts-session-flow-front': 'sessionFlowColorFront',
|
|
@@ -124,7 +125,7 @@ export function createThemeController({ document, window, cssText, service }) {
|
|
|
124
125
|
root.dataset.prtsSessionGlow = preferences.sessionGlow ? 'on' : 'off'
|
|
125
126
|
root.dataset.prtsSessionFlowPalette = preferences.sessionFlowPalette
|
|
126
127
|
root.dataset.prtsSessionFlowSpeed = String(preferences.sessionFlowSpeed)
|
|
127
|
-
for (const [property, key] of Object.entries(SESSION_FLOW_COLOR_PROPERTIES)) root.style.setProperty(property, preferences[key])
|
|
128
|
+
for (const [property, key] of Object.entries(SESSION_FLOW_COLOR_PROPERTIES)) root.style.setProperty(property, preferences.cornerColorLinked && (key === 'cornerColor' || key === 'cornerColorLight') ? preferences[preferences.cornerColorSource === 'light' ? 'cornerColorLight' : 'cornerColor'] : preferences[key])
|
|
128
129
|
root.dataset.prtsConversationStyle = preferences.conversationStyle
|
|
129
130
|
refresh()
|
|
130
131
|
},
|
|
@@ -171,7 +171,7 @@ export function themeSettingsMarkup() {
|
|
|
171
171
|
<section data-prts-settings-common data-prts-settings-section="interaction" role="tabpanel" id="prts-page-interaction" aria-labelledby="prts-tab-interaction" tabindex="0" hidden>
|
|
172
172
|
<div data-prts-settings-common-grid>
|
|
173
173
|
${COMMON_CONTROLS.filter(([key]) => ['cursor', 'railDefaultHidden', 'motion'].includes(key)).map(([key, label, options]) => settingButtons(key, label, options)).join('')}
|
|
174
|
-
<div data-prts-setting-row="cornerColor"><span><strong>交互提示框颜色</strong><small data-prts-setting-note>部分按钮和卡片的四角提示颜色,危险操作保持红色。</small></span><div data-prts-corner-presets role="group" aria-label="交互提示框预设颜色">${[['黄色','#f0c800'],['白色','#ffffff'],['青色','#65ddb0'],['蓝色','#6eb7f3'],['紫色','#b487ef']].map(([name,color]) => `<button type="button" data-prts-corner-preset="${color}" aria-label="${name}" title="${name}" aria-pressed="false" style="--swatch:${color}"><i aria-hidden="true"></i></button>`).join('')}<button type="button" data-prts-corner-custom title="选择自定义颜色"><i data-prts-corner-preview aria-hidden="true"></i><span>自定义</span></button><input type="color" data-prts-corner-color aria-label="交互提示框颜色" value="#f0c800" hidden></div></div>
|
|
174
|
+
<div data-prts-setting-row="cornerColor"><span><span data-prts-corner-heading><strong>交互提示框颜色</strong><span data-prts-corner-modes role="group" aria-label="编辑配色模式"><button type="button" data-prts-corner-mode="light" aria-pressed="false">☀ 日间</button><button type="button" data-prts-corner-mode="dark" aria-pressed="false">☾ 夜间</button></span><button type="button" data-prts-corner-sync role="switch" aria-checked="false" title="开启后,日间和夜间共用颜色"><span>日夜共用</span><i aria-hidden="true"></i></button></span><small data-prts-setting-note>部分按钮和卡片的四角提示颜色,危险操作保持红色。</small><small data-prts-setting-note>开启后,日夜都使用当前选定模式的配色,修改也保存到该模式;关闭后各用各的,另一套配色不变。</small></span><div data-prts-corner-presets role="group" aria-label="交互提示框预设颜色">${[['黄色','#f0c800'],['白色','#ffffff'],['青色','#65ddb0'],['蓝色','#6eb7f3'],['紫色','#b487ef']].map(([name,color]) => `<button type="button" data-prts-corner-preset="${color}" aria-label="${name}" title="${name}" aria-pressed="false" style="--swatch:${color}"><i aria-hidden="true"></i></button>`).join('')}<span data-prts-corner-picker><button type="button" data-prts-corner-custom aria-expanded="false" title="选择自定义颜色"><i data-prts-corner-preview aria-hidden="true"></i><span>自定义</span></button><input type="color" data-prts-corner-color aria-label="交互提示框颜色" value="#f0c800" tabindex="-1"><span data-prts-color-editor hidden role="group" aria-label="自定义提示框颜色"><span data-prts-color-editor-title>自定义颜色</span><span data-prts-color-sample></span><label data-prts-color-hex-label>HEX<input data-prts-color-hex aria-label="HEX 颜色" maxlength="7" spellcheck="false"></label><label data-prts-color-channel><span>R</span><input type="range" min="0" max="255" step="1" data-prts-color-channel-index="0" aria-label="R 通道"><output>0</output></label><label data-prts-color-channel><span>G</span><input type="range" min="0" max="255" step="1" data-prts-color-channel-index="1" aria-label="G 通道"><output>0</output></label><label data-prts-color-channel><span>B</span><input type="range" min="0" max="255" step="1" data-prts-color-channel-index="2" aria-label="B 通道"><output>0</output></label><span data-prts-color-actions><button type="button" data-prts-color-cancel>取消</button><button type="button" data-prts-color-apply>应用颜色</button></span></span></span></div></div>
|
|
175
175
|
</div>
|
|
176
176
|
</section>
|
|
177
177
|
<section data-prts-settings-section="ai-tools" data-prts-ai-tools role="tabpanel" id="prts-page-ai-tools" aria-labelledby="prts-tab-ai-tools" tabindex="0" hidden>
|
|
@@ -285,6 +285,35 @@ export function createThemeSettingsOverlay({
|
|
|
285
285
|
const nativeModal = typeof panel?.showModal === 'function' && typeof panel?.close === 'function'
|
|
286
286
|
let isOpen = false
|
|
287
287
|
let previousFocus
|
|
288
|
+
let colorDraft = '#f0c800'
|
|
289
|
+
function closeColorEditor(restoreFocus = false) {
|
|
290
|
+
const editor = panel?.querySelector('[data-prts-color-editor]')
|
|
291
|
+
if (!editor || editor.hidden) return
|
|
292
|
+
editor.hidden = true
|
|
293
|
+
panel.querySelector('[data-prts-corner-custom]').setAttribute('aria-expanded', 'false')
|
|
294
|
+
onPreferencePreview(cornerKey(), currentPreferences[cornerKey()])
|
|
295
|
+
if (restoreFocus) panel.querySelector('[data-prts-corner-custom]').focus()
|
|
296
|
+
}
|
|
297
|
+
function renderColorDraft() {
|
|
298
|
+
const editor = panel.querySelector('[data-prts-color-editor]')
|
|
299
|
+
editor.querySelector('[data-prts-color-sample]').style.background = colorDraft
|
|
300
|
+
const hex = editor.querySelector('[data-prts-color-hex]')
|
|
301
|
+
hex.value = colorDraft.toUpperCase()
|
|
302
|
+
hex.removeAttribute('aria-invalid')
|
|
303
|
+
const channels = [1, 3, 5].map(start => parseInt(colorDraft.slice(start, start + 2), 16))
|
|
304
|
+
for (const slider of editor.querySelectorAll('[data-prts-color-channel-index]')) {
|
|
305
|
+
const index = Number(slider.dataset.prtsColorChannelIndex)
|
|
306
|
+
slider.value = channels[index]
|
|
307
|
+
slider.nextElementSibling.value = channels[index]
|
|
308
|
+
const end = [...channels], start = [...channels]
|
|
309
|
+
start[index] = 0; end[index] = 255
|
|
310
|
+
slider.style.background = 'linear-gradient(to right,rgb(' + start.join(',') + '),rgb(' + end.join(',') + '))'
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
let cornerEditMode = 'dark'
|
|
314
|
+
const cornerMode = () => currentPreferences?.cornerColorLinked ? currentPreferences.cornerColorSource : cornerEditMode
|
|
315
|
+
const cornerKey = () => cornerMode() === 'light' ? 'cornerColorLight' : 'cornerColor'
|
|
316
|
+
const cornerCustomKey = () => cornerMode() === 'light' ? 'cornerCustomColorLight' : 'cornerCustomColor'
|
|
288
317
|
let currentPreferences
|
|
289
318
|
let currentStatus
|
|
290
319
|
let currentPersistence = { phase: 'idle', revision: 0 }
|
|
@@ -342,6 +371,8 @@ export function createThemeSettingsOverlay({
|
|
|
342
371
|
const value = Boolean(next)
|
|
343
372
|
if (value === isOpen) return isOpen
|
|
344
373
|
if (!value) {
|
|
374
|
+
closeColorEditor()
|
|
375
|
+
if (currentPreferences) onPreferencePreview(cornerKey(), currentPreferences[cornerKey()])
|
|
345
376
|
commitParticleTraversalSpeed()
|
|
346
377
|
commitSessionFlowSpeed()
|
|
347
378
|
commitSessionFlowColors()
|
|
@@ -351,6 +382,8 @@ export function createThemeSettingsOverlay({
|
|
|
351
382
|
document.documentElement.toggleAttribute('data-prts-settings-open', isOpen)
|
|
352
383
|
panel.setAttribute('aria-hidden', String(!isOpen))
|
|
353
384
|
if (isOpen) {
|
|
385
|
+
cornerEditMode = document.documentElement.getAttribute('data-prts-scheme') === 'light' ? 'light' : 'dark'
|
|
386
|
+
renderState()
|
|
354
387
|
previousFocus = document.activeElement
|
|
355
388
|
onOpen()
|
|
356
389
|
showPanel()
|
|
@@ -532,10 +565,18 @@ export function createThemeSettingsOverlay({
|
|
|
532
565
|
|
|
533
566
|
function renderState() {
|
|
534
567
|
if (!panel || !currentPreferences) return
|
|
568
|
+
panel.querySelector('[data-prts-corner-modes]').hidden = currentPreferences.cornerColorLinked
|
|
569
|
+
panel.querySelector('[data-prts-corner-sync]').setAttribute('aria-checked', String(currentPreferences.cornerColorLinked))
|
|
570
|
+
for (const mode of panel.querySelectorAll('[data-prts-corner-mode]')) mode.setAttribute('aria-pressed', String(mode.dataset.prtsCornerMode === cornerEditMode))
|
|
535
571
|
const cornerColor = panel.querySelector('[data-prts-corner-color]')
|
|
536
|
-
if (cornerColor) cornerColor.value = currentPreferences
|
|
537
|
-
panel.querySelector('[data-prts-corner-preview]')?.style.setProperty('--swatch', currentPreferences
|
|
538
|
-
|
|
572
|
+
if (cornerColor) cornerColor.value = currentPreferences[cornerCustomKey()]
|
|
573
|
+
panel.querySelector('[data-prts-corner-preview]')?.style.setProperty('--swatch', currentPreferences[cornerCustomKey()])
|
|
574
|
+
const customButton = panel.querySelector('[data-prts-corner-custom]')
|
|
575
|
+
const customApplied = currentPreferences[cornerKey()] === currentPreferences[cornerCustomKey()]
|
|
576
|
+
customButton.setAttribute('aria-pressed', String(customApplied))
|
|
577
|
+
customButton.title = customApplied ? '自定义颜色已应用,点击编辑' : '选择自定义颜色'
|
|
578
|
+
customButton.querySelector('span').textContent = '自定义'
|
|
579
|
+
for (const preset of panel.querySelectorAll('[data-prts-corner-preset]')) preset.setAttribute('aria-pressed', String(preset.dataset.prtsCornerPreset === currentPreferences[cornerKey()]))
|
|
539
580
|
for (const toggle of panel.querySelectorAll('[data-prts-setting-switch]')) {
|
|
540
581
|
const key = toggle.dataset.prtsSettingSwitch
|
|
541
582
|
const enabled = key === 'cursor' ? currentPreferences.cursor === 'prts'
|
|
@@ -628,10 +669,29 @@ export function createThemeSettingsOverlay({
|
|
|
628
669
|
|
|
629
670
|
function onPanelClick(event) {
|
|
630
671
|
if (event.target === panel) { setOpen(false); return }
|
|
672
|
+
if (!event.target?.closest?.('[data-prts-corner-picker]')) closeColorEditor()
|
|
631
673
|
const target = event.target?.closest?.('button')
|
|
632
674
|
if (!target) return
|
|
633
|
-
if (target.hasAttribute('data-prts-corner-
|
|
634
|
-
if (target.hasAttribute('data-prts-corner-
|
|
675
|
+
if (target.hasAttribute('data-prts-corner-mode')) { onPreferencePreview(cornerKey(), currentPreferences[cornerKey()]); cornerEditMode = target.dataset.prtsCornerMode; renderState(); return }
|
|
676
|
+
if (target.hasAttribute('data-prts-corner-sync')) { if (currentPreferences.cornerColorLinked) cornerEditMode = currentPreferences.cornerColorSource; onPreferenceChange('cornerColorLink', currentPreferences.cornerColorLinked ? false : cornerEditMode); return }
|
|
677
|
+
if (target.hasAttribute('data-prts-color-cancel')) { closeColorEditor(true); return }
|
|
678
|
+
if (target.hasAttribute('data-prts-color-apply')) {
|
|
679
|
+
if (panel.querySelector('[data-prts-color-hex]').getAttribute('aria-invalid') === 'true') return
|
|
680
|
+
onPreferenceChange(cornerCustomKey(), colorDraft); closeColorEditor(true); return
|
|
681
|
+
}
|
|
682
|
+
if (target.hasAttribute('data-prts-corner-custom')) {
|
|
683
|
+
const editor = panel.querySelector('[data-prts-color-editor]')
|
|
684
|
+
if (!editor.hidden) { closeColorEditor(); return }
|
|
685
|
+
colorDraft = currentPreferences[cornerCustomKey()]
|
|
686
|
+
renderColorDraft(); editor.hidden = false
|
|
687
|
+
editor.removeAttribute('data-open-below')
|
|
688
|
+
const scroller = panel.querySelector('[data-prts-theme-settings-scroll]')
|
|
689
|
+
if (editor.getBoundingClientRect().top < (scroller?.getBoundingClientRect().top ?? 0)) editor.setAttribute('data-open-below', '')
|
|
690
|
+
target.setAttribute('aria-expanded', 'true')
|
|
691
|
+
editor.querySelector('[data-prts-color-hex]').focus()
|
|
692
|
+
return
|
|
693
|
+
}
|
|
694
|
+
if (target.hasAttribute('data-prts-corner-preset')) { onPreferenceChange(cornerKey(), target.dataset.prtsCornerPreset); return }
|
|
635
695
|
if (target.hasAttribute('data-prts-settings-jump')) {
|
|
636
696
|
selectSection(target.dataset.prtsSettingsJump)
|
|
637
697
|
return
|
|
@@ -670,7 +730,20 @@ export function createThemeSettingsOverlay({
|
|
|
670
730
|
|
|
671
731
|
function onPanelInput(event) {
|
|
672
732
|
const target = event.target
|
|
673
|
-
if (target?.matches?.('[data-prts-
|
|
733
|
+
if (target?.matches?.('[data-prts-color-channel-index], [data-prts-color-hex]')) {
|
|
734
|
+
if (target.hasAttribute('data-prts-color-hex')) {
|
|
735
|
+
const value = target.value.trim()
|
|
736
|
+
if (!/^#?[0-9a-f]{6}$/i.test(value)) { target.setAttribute('aria-invalid', 'true'); return }
|
|
737
|
+
colorDraft = (value.startsWith('#') ? value : '#' + value).toLowerCase()
|
|
738
|
+
} else {
|
|
739
|
+
const channels = [...panel.querySelectorAll('[data-prts-color-channel-index]')].map(input => Number(input.value).toString(16).padStart(2, '0'))
|
|
740
|
+
colorDraft = '#' + channels.join('')
|
|
741
|
+
}
|
|
742
|
+
renderColorDraft()
|
|
743
|
+
onPreferencePreview(cornerKey(), colorDraft)
|
|
744
|
+
return
|
|
745
|
+
}
|
|
746
|
+
if (target?.matches?.('[data-prts-corner-color]')) { onPreferencePreview(cornerKey(), target.value); return }
|
|
674
747
|
if (target?.matches?.('[data-prts-preview-reply-limit]')) { onPreferenceChange('previewReplyLimit', Number(target.value)); return }
|
|
675
748
|
if (target?.matches?.('input[data-prts-setting-color]')) {
|
|
676
749
|
const key = target.dataset.prtsSettingKey
|
|
@@ -713,7 +786,7 @@ export function createThemeSettingsOverlay({
|
|
|
713
786
|
selectSection(tabs[next].dataset.prtsSettingsJump, true)
|
|
714
787
|
return
|
|
715
788
|
}
|
|
716
|
-
if (event.key === 'Escape') { event.preventDefault(); setOpen(false); return }
|
|
789
|
+
if (event.key === 'Escape') { event.preventDefault(); if (!panel.querySelector('[data-prts-color-editor]').hidden) { event.stopPropagation(); closeColorEditor(true) } else setOpen(false); return }
|
|
717
790
|
if (nativeModal || event.key !== 'Tab') return
|
|
718
791
|
const nodes = focusable()
|
|
719
792
|
if (!nodes.length) return
|
|
@@ -733,7 +806,7 @@ export function createThemeSettingsOverlay({
|
|
|
733
806
|
const renderMediaState = () => renderState()
|
|
734
807
|
const commitFromControl = event => {
|
|
735
808
|
const target = event.target
|
|
736
|
-
if (target?.matches?.('[data-prts-corner-color]')) { if (event.type === 'change') onPreferenceChange(
|
|
809
|
+
if (target?.matches?.('[data-prts-corner-color]')) { if (event.type === 'change') onPreferenceChange(cornerCustomKey(), target.value); return }
|
|
737
810
|
if (target?.matches?.('input[data-prts-setting-color]')) {
|
|
738
811
|
if (event.type === 'change' || event.type === 'focusout') commitSessionFlowColor(target.dataset.prtsSettingKey)
|
|
739
812
|
return
|
package/src/index.js
CHANGED
|
@@ -12,9 +12,10 @@ export const PACKAGE_ID = '@ash-qw/dsh-theme-prts'
|
|
|
12
12
|
export const THEME_PLUGIN_ID = 'dsh-theme-prts'
|
|
13
13
|
export const inject = ['tools', 'attachments', 'connection', 'webServer']
|
|
14
14
|
|
|
15
|
+
// Leave new palette fields absent until normalization can inherit the legacy color.
|
|
15
16
|
export const Config = Schema.object(Object.fromEntries([
|
|
16
17
|
...Object.entries(DEFAULT_PREFERENCES).map(([key, value]) => [key,
|
|
17
|
-
(typeof value === 'boolean' ? Schema.boolean() : typeof value === 'number' ? Schema.number() : Schema.string()).default(value).volatile(),
|
|
18
|
+
(typeof value === 'boolean' ? Schema.boolean() : typeof value === 'number' ? Schema.number() : Schema.string()).default(['cornerColorLight', 'cornerCustomColorLight', 'cornerColorSource'].includes(key) ? undefined : value).volatile(),
|
|
18
19
|
]),
|
|
19
20
|
['preferencesInitialized', Schema.boolean().default(false).volatile()],
|
|
20
21
|
]))
|
|
@@ -48,7 +48,7 @@ html[data-dsh-prts] [data-prts-session-idle-indicator] {
|
|
|
48
48
|
fill: var(--prts-dim);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
html[data-dsh-prts] [data-prts-session-state] [data-prts-session-idle-indicator] {
|
|
51
|
+
html[data-dsh-prts] :is([data-prts-session-state], [data-prts-session-row]:has([data-session-schedule-mark])) [data-prts-session-idle-indicator] {
|
|
52
52
|
opacity: 0;
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -824,7 +824,7 @@ html[data-dsh-prts] [data-prts-session-summary-popover]:has(> [data-prts-preview
|
|
|
824
824
|
}
|
|
825
825
|
|
|
826
826
|
/* Keep different-sized state markers centered on the same axis. */
|
|
827
|
-
html[data-dsh-prts] [data-prts-session-row]
|
|
827
|
+
html[data-dsh-prts] [data-prts-session-row] :is([data-state="ongoing"], [data-state="done"], [data-state="warning"], [data-state="error"], [data-session-schedule-mark]) {
|
|
828
828
|
flex-shrink: 0;
|
|
829
829
|
margin-left: 20px;
|
|
830
830
|
}
|
|
@@ -889,3 +889,12 @@ html[data-dsh-prts] [data-prts-tool-record] p { display:-webkit-box;-webkit-box-
|
|
|
889
889
|
|
|
890
890
|
/* Show the baseline immediately; only start the pickup after a deliberate hover. */
|
|
891
891
|
html[data-dsh-prts] [data-prts-session-row]:not([data-prts-pickup-ready]) :is([data-prts-session-pickup-bar],[data-prts-session-pickup-indicator]) { visibility:hidden; }
|
|
892
|
+
|
|
893
|
+
html[data-dsh-prts] [data-prts-reply-entry][data-prts-reply-complete] { cursor:text; }
|
|
894
|
+
|
|
895
|
+
/* Reserve the cue's width so showing it never changes the truncation measurement. */
|
|
896
|
+
html[data-dsh-prts] [data-prts-reply-entry] > span { display:block;-webkit-line-clamp:unset;max-height:3.2em;color:#b8bec6; }
|
|
897
|
+
html[data-dsh-prts] [data-prts-reply-ellipsis] { flex:0 0 12px;align-self:flex-end;color:#f0e6d6;font:600 14px/1.4 var(--prts-body);visibility:hidden; }
|
|
898
|
+
html[data-dsh-prts] [data-prts-reply-overflow] > [data-prts-reply-ellipsis] { visibility:visible; }
|
|
899
|
+
|
|
900
|
+
html[data-dsh-prts] [data-prts-reply-overflow] > span { color:#f0e6d6; }
|
package/src/styles/prts.css
CHANGED
|
@@ -963,7 +963,7 @@ html[data-dsh-prts] [data-prts-row-title] {
|
|
|
963
963
|
white-space: nowrap;
|
|
964
964
|
}
|
|
965
965
|
|
|
966
|
-
html[data-dsh-prts] [data-prts-session-row] > span:has(> [data-state]) {
|
|
966
|
+
html[data-dsh-prts] [data-prts-session-row] > span:has(> :is([data-state], [data-session-schedule-mark])) {
|
|
967
967
|
padding-left: 0 !important;
|
|
968
968
|
}
|
|
969
969
|
|