@ash-qw/dsh-theme-prts 2.5.2 → 2.5.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.
@@ -171,6 +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
175
  </div>
175
176
  </section>
176
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>
@@ -531,6 +532,10 @@ export function createThemeSettingsOverlay({
531
532
 
532
533
  function renderState() {
533
534
  if (!panel || !currentPreferences) return
535
+ const cornerColor = panel.querySelector('[data-prts-corner-color]')
536
+ if (cornerColor) cornerColor.value = currentPreferences.cornerCustomColor
537
+ panel.querySelector('[data-prts-corner-preview]')?.style.setProperty('--swatch', currentPreferences.cornerCustomColor)
538
+ for (const preset of panel.querySelectorAll('[data-prts-corner-preset]')) preset.setAttribute('aria-pressed', String(preset.dataset.prtsCornerPreset === currentPreferences.cornerColor))
534
539
  for (const toggle of panel.querySelectorAll('[data-prts-setting-switch]')) {
535
540
  const key = toggle.dataset.prtsSettingSwitch
536
541
  const enabled = key === 'cursor' ? currentPreferences.cursor === 'prts'
@@ -625,6 +630,8 @@ export function createThemeSettingsOverlay({
625
630
  if (event.target === panel) { setOpen(false); return }
626
631
  const target = event.target?.closest?.('button')
627
632
  if (!target) return
633
+ if (target.hasAttribute('data-prts-corner-custom')) { panel.querySelector('[data-prts-corner-color]').click(); return }
634
+ if (target.hasAttribute('data-prts-corner-preset')) { onPreferenceChange('cornerColor', target.dataset.prtsCornerPreset); return }
628
635
  if (target.hasAttribute('data-prts-settings-jump')) {
629
636
  selectSection(target.dataset.prtsSettingsJump)
630
637
  return
@@ -663,6 +670,7 @@ export function createThemeSettingsOverlay({
663
670
 
664
671
  function onPanelInput(event) {
665
672
  const target = event.target
673
+ if (target?.matches?.('[data-prts-corner-color]')) { onPreferencePreview('cornerColor', target.value); return }
666
674
  if (target?.matches?.('[data-prts-preview-reply-limit]')) { onPreferenceChange('previewReplyLimit', Number(target.value)); return }
667
675
  if (target?.matches?.('input[data-prts-setting-color]')) {
668
676
  const key = target.dataset.prtsSettingKey
@@ -725,6 +733,7 @@ export function createThemeSettingsOverlay({
725
733
  const renderMediaState = () => renderState()
726
734
  const commitFromControl = event => {
727
735
  const target = event.target
736
+ if (target?.matches?.('[data-prts-corner-color]')) { if (event.type === 'change') onPreferenceChange('cornerCustomColor', target.value); return }
728
737
  if (target?.matches?.('input[data-prts-setting-color]')) {
729
738
  if (event.type === 'change' || event.type === 'focusout') commitSessionFlowColor(target.dataset.prtsSettingKey)
730
739
  return
@@ -13,9 +13,8 @@ export function themeTransferMarkup() {
13
13
  <button type="button" data-prts-transfer-export>导出主题文件 <span aria-hidden="true">↓</span></button>
14
14
  </section>
15
15
  <section data-prts-transfer-card="import" data-prts-transfer-drop>
16
- <h3>导入主题</h3><p>选择或拖入已导出的文件,确认内容后应用。</p>
17
- <div data-prts-transfer-file-hint><span aria-hidden="true">↑</span><small>.prts-theme.json · 最大 160 MB</small></div>
18
- <button type="button" data-prts-transfer-choose>选择主题文件 <span aria-hidden="true">↑</span></button>
16
+ <h3>导入主题</h3><p>导入已导出的主题文件,确认内容后应用。</p>
17
+ <button type="button" data-prts-transfer-file-hint data-prts-transfer-choose><span aria-hidden="true">↑</span><strong>点击选择或拖拽主题文件到此处</strong><small>.prts-theme.json · 最大 160 MB</small></button>
19
18
  <input type="file" accept=".prts-theme.json,application/json" aria-label="导入主题文件" data-prts-transfer-file hidden>
20
19
  </section>
21
20
  </div>
@@ -101,7 +101,7 @@ export function describeThemePackage(value) {
101
101
  return `${count} 项主题设置、${transferSlots.filter(k => a.backgrounds[k]).length} 张背景、${transferRoles.filter(k => a.avatars[k]).length} 个头像、${a.emblems.items.length} 个自定义徽记。将替换当前素材及徽记排列,空项恢复为默认。`
102
102
  }
103
103
 
104
- export function createThemeTransfer({ window, getPreferences, savePreferences, checkWritable, checkExport = checkWritable, themeVersion }) {
104
+ export function createThemeTransfer({ window, getPreferences, savePreferences, checkWritable, checkExport = checkWritable, themeVersion, onAvatarConfiguration = () => {} }) {
105
105
  let busy = false, backup
106
106
  const request = async (path, data, signal) => {
107
107
  checkTransferCancelled(signal)
@@ -198,7 +198,10 @@ export function createThemeTransfer({ window, getPreferences, savePreferences, c
198
198
  undo.push(async () => {
199
199
  const restored = await request(kind + '/configuration', { ...previous, revision: revisions[kind] })
200
200
  revisions[kind] = restored.revision
201
+ if (kind === 'avatars') onAvatarConfiguration(restored)
201
202
  })
203
+ // Apply the acknowledged images locally; SSE remains for other pages.
204
+ if (kind === 'avatars') onAvatarConfiguration(result)
202
205
  }
203
206
  try {
204
207
  if (value.assets) {
@@ -5,9 +5,10 @@ background: linear-gradient(var(--prts-background-wash), var(--prts-background-w
5
5
  }
6
6
  html[data-dsh-prts][data-prts-background-mode="full"][data-prts-background-full] :is([data-prts-region="sessions"], [data-prts-region="operation"]) { background: transparent; }
7
7
  html[data-dsh-prts][data-prts-background-mode="split"][data-prts-background-sidebar] {
8
+ & :is([data-prts-region="sessions"],[data-prts-nav-rail]) {
8
9
  --prts-wall-width:var(--prts-protected-sidebar-width,max(calc(var(--prts-sidebar-width,280px) + var(--prts-nav-width)),calc((100dvh - var(--prts-host-top-inset)) * var(--prts-sidebar-image-ratio,1))));
9
10
  --prts-wall-x:var(--prts-protected-sidebar-x,calc((var(--prts-sidebar-width,280px) + var(--prts-nav-width) - var(--prts-wall-width)) / 2));
10
- & :is([data-prts-region="sessions"],[data-prts-nav-rail]){background:linear-gradient(var(--prts-background-wash),var(--prts-background-wash)),var(--prts-background-sidebar) var(--prts-wall-x) var(--prts-protected-sidebar-y, 50%) / var(--prts-wall-width) auto no-repeat}
11
+ background:linear-gradient(var(--prts-background-wash),var(--prts-background-wash)),var(--prts-background-sidebar) var(--prts-wall-x) var(--prts-protected-sidebar-y, 50%) / var(--prts-wall-width) auto no-repeat}
11
12
  & [data-prts-region="sessions"]{background-position:center,calc(var(--prts-wall-x) - var(--prts-nav-width)) var(--prts-protected-sidebar-y, 50%)}
12
13
  }
13
14
  html[data-dsh-prts][data-prts-rail-mode="overlay"]:is([data-prts-background-mode="split"][data-prts-background-sidebar],[data-prts-background-mode="full"][data-prts-background-full]) [data-prts-nav-rail] { background: transparent; }
@@ -485,3 +486,13 @@ background:color-mix(in srgb,var(--prts-panel) 28%,transparent);
485
486
  -webkit-backdrop-filter:blur(12px);
486
487
  backdrop-filter:blur(12px);
487
488
  }
489
+
490
+ /* The task manager mounts directly in main, outside the conversation root. */
491
+ html[data-dsh-prts] [data-prts-region="operation"] [data-testid="task-manager-page"],
492
+ html[data-dsh-prts] [data-testid="task-manager-page"] :is([class$="_detail"],[class$="_detailNotice"]) {
493
+ background: var(--prts-canvas);
494
+ }
495
+ html[data-dsh-prts]:is([data-prts-background-mode="full"][data-prts-background-full],[data-prts-background-mode="split"][data-prts-background-conversation]) [data-prts-region="operation"] [data-testid="task-manager-page"],
496
+ html[data-dsh-prts]:is([data-prts-background-mode="full"][data-prts-background-full],[data-prts-background-mode="split"][data-prts-background-conversation]) [data-testid="task-manager-page"] :is([class$="_detail"],[class$="_detailNotice"]) {
497
+ background: transparent;
498
+ }
@@ -572,11 +572,11 @@ html[data-dsh-prts] [data-prts-session-pickup-bar]:nth-of-type(10) { --prts-pick
572
572
  html[data-dsh-prts] [data-prts-session-pickup-bar]:nth-of-type(11) { --prts-pickup-duration: 560ms; --prts-pickup-delay: -.3s; }
573
573
 
574
574
  @media (prefers-reduced-motion: no-preference) {
575
- html[data-dsh-prts]:not([data-prts-motion="reduced"]) [data-prts-session-row]:hover [data-prts-session-pickup-indicator] {
575
+ html[data-dsh-prts]:not([data-prts-motion="reduced"]) [data-prts-session-row]:hover[data-prts-pickup-ready] [data-prts-session-pickup-indicator] {
576
576
  animation: prts-session-record-indicator 1.1s ease-in-out infinite;
577
577
  }
578
578
 
579
- html[data-dsh-prts]:not([data-prts-motion="reduced"]) [data-prts-session-row]:hover [data-prts-session-pickup-bar] {
579
+ html[data-dsh-prts]:not([data-prts-motion="reduced"]) [data-prts-session-row]:hover[data-prts-pickup-ready] [data-prts-session-pickup-bar] {
580
580
  animation: prts-session-pickup-sample var(--prts-pickup-duration) ease-in-out var(--prts-pickup-delay) infinite;
581
581
  }
582
582
  }
@@ -886,3 +886,6 @@ html[data-dsh-prts] [data-prts-tool-record] > div { display:flex;align-items:bas
886
886
  html[data-dsh-prts] [data-prts-tool-record] b { flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--prts-cyan);font:11px/1.6 var(--prts-mono); }
887
887
  html[data-dsh-prts] [data-prts-tool-record] small { flex:none;color:var(--prts-muted);font-size:10px; }
888
888
  html[data-dsh-prts] [data-prts-tool-record] p { display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;margin:4px 0 0; }
889
+
890
+ /* Show the baseline immediately; only start the pickup after a deliberate hover. */
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; }
@@ -508,7 +508,7 @@ html[data-dsh-prts] [data-turn-trigger] {
508
508
  }
509
509
 
510
510
  /* Body interactions lock onto four corners; shell controls keep their own treatment. */
511
- html[data-dsh-prts] :is([data-chat-flow-kind],[data-message-role]) :is([data-prts-conversation-control],[data-presented-file],[data-changed-files],[data-produced-files-row] > button) {
511
+ html[data-dsh-prts] :is([data-chat-flow-kind],[data-message-role],[data-submission-echo],[data-pending-steering]) :is([data-prts-conversation-control],[data-presented-file],[data-changed-files],[data-produced-files-row] > button) {
512
512
  position:relative;
513
513
  --prts-control-outline:var(--prts-line);
514
514
  &:is(:hover,:focus-visible,:focus-within) {
@@ -967,7 +967,8 @@ html[data-dsh-prts] [data-prts-transfer] {
967
967
  & [data-prts-transfer-option] { align-items:flex-start; flex-wrap:nowrap; padding:12px 0 0; }
968
968
  & [data-prts-transfer-option] input { margin:5px 0 0; }
969
969
  & [data-prts-transfer-option] small { display:block; margin-top:4px; }
970
- & [data-prts-transfer-file-hint] { display:grid; place-items:center; flex:1; gap:6px; padding:12px; border:1px dashed var(--prts-line-strong); }
970
+ & [data-prts-transfer-card] > [data-prts-transfer-file-hint] { display:flex; flex-direction:column; justify-content:center; flex:1; gap:6px; min-height:130px; padding:16px 12px; border:1px dashed var(--prts-line-strong); text-align:center; white-space:normal; }
971
+ & [data-prts-transfer-file-hint] > strong { font-size:13px; font-weight:500; }
971
972
  & [data-prts-transfer-file-hint] > span { font-size:24px; color:var(--prts-muted); }
972
973
  & [data-prts-transfer-buttons] { display:flex; gap:8px; }
973
974
  & [data-prts-transfer-compatibility] { margin:0; padding:10px 12px; border:1px solid var(--prts-yellow); overflow-wrap:anywhere; }
@@ -1032,3 +1033,15 @@ html[data-dsh-prts] [data-prts-transfer]:not([aria-busy="true"]) [data-prts-tran
1032
1033
  }
1033
1034
  html[data-prts-motion="reduced"] [data-prts-transfer-card] { transition:none; }
1034
1035
  @media (prefers-reduced-motion:reduce) { [data-prts-transfer-card] { transition:none; } }
1036
+
1037
+ html[data-dsh-prts] [data-prts-setting-row="cornerColor"] { flex-wrap:wrap;gap:16px; }
1038
+ html[data-dsh-prts] [data-prts-corner-presets] { display:flex;flex-wrap:wrap;gap:6px;align-items:center;
1039
+ & > button { position:relative;display:grid;place-items:center;width:32px;height:32px;padding:5px;border:1px solid transparent;border-radius:4px;background:transparent; }
1040
+ & i { display:block;width:18px;height:18px;border-radius:3px;background:var(--swatch);box-shadow:inset 0 0 0 1px #8884; }
1041
+ & > button:hover { background:var(--prts-panel-raised); }
1042
+ & > button[aria-pressed="true"] { border-color:var(--prts-ink);background:var(--prts-panel-raised); }
1043
+ & > button[aria-pressed="true"]::after { content:"";position:absolute;width:8px;height:4px;border-left:2px solid #172025;border-bottom:2px solid #172025;transform:translateY(-1px) rotate(-45deg); }
1044
+ & > button:focus-visible { outline:2px solid var(--prts-ink);outline-offset:2px; }
1045
+ }
1046
+ html[data-dsh-prts] [data-prts-corner-presets] > [data-prts-corner-custom] { display:flex;align-items:center;gap:8px;width:auto;height:32px;margin-left:8px;padding:5px 10px;border:1px solid var(--prts-line-strong);border-radius:4px;background:var(--prts-panel-raised);color:var(--prts-ink);font:12px/1 var(--prts-body);white-space:nowrap; }
1047
+ html[data-dsh-prts] [data-prts-corner-presets] > [data-prts-corner-custom]:is(:hover,:focus-visible) { border-color:var(--prts-ink);background:color-mix(in srgb,var(--prts-ink) 8%,var(--prts-panel-raised)); }
@@ -407,14 +407,28 @@ const GlassSurface = ({
407
407
  useEffect(() => {
408
408
  if (!containerRef.current) return;
409
409
 
410
- const resizeObserver = new ResizeObserver(() => {
410
+ // PRTS: keep the existing map stretched with the live container while
411
+ // resizing. Rebuilding its distance field on every intermediate width
412
+ // defeats the outer composer's settled-shape update.
413
+ let timer;
414
+ let committed = '';
415
+ const commit = () => {
416
+ timer = undefined;
411
417
  const element = containerRef.current;
412
- if (element) setBoxSize(`${element.offsetWidth}x${element.offsetHeight}`);
418
+ if (!element || !element.offsetWidth || !element.offsetHeight) return;
419
+ const next = `${element.offsetWidth}x${element.offsetHeight}`;
420
+ if (next !== committed) { committed = next; setBoxSize(next); }
421
+ };
422
+ const resizeObserver = new ResizeObserver(() => {
423
+ clearTimeout(timer);
424
+ if (!committed) commit();
425
+ else timer = setTimeout(commit, 160);
413
426
  });
414
427
 
415
428
  resizeObserver.observe(containerRef.current);
416
429
 
417
430
  return () => {
431
+ clearTimeout(timer);
418
432
  resizeObserver.disconnect();
419
433
  };
420
434
  }, []);