@philomelcc/dsh-workbench 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client.js +109 -56
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -435,84 +435,147 @@ window.__ModuleLoader__.load({
|
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
// ---------- theme switcher ----------
|
|
438
|
-
// `paper`
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
438
|
+
// `paper` cannot be a persisted preference: DSH only accepts light / dark /
|
|
439
|
+
// system there, and anything else is dropped when settings resync (that is
|
|
440
|
+
// why switching models used to fall back to dark). So the paper palette is
|
|
441
|
+
// an override layer on top of the legal `light` preference, remembered in
|
|
442
|
+
// localStorage by us.
|
|
443
|
+
const PAPER_KEY = 'dsh-workbench:paper';
|
|
444
|
+
const PAPER_TOKENS = {
|
|
445
|
+
'--dsw-alias-bg-base': '#f4efe3',
|
|
446
|
+
'--dsw-alias-bg-layer-1': '#efe9db',
|
|
447
|
+
'--dsw-alias-bg-layer-2': '#e7dfcd',
|
|
448
|
+
'--dsw-alias-bg-overlay': '#fbf7ee',
|
|
449
|
+
'--dsw-alias-border-l1': '#d8cfba',
|
|
450
|
+
'--dsw-alias-border-l2': '#c6bba2',
|
|
451
|
+
'--dsw-alias-brand-primary': '#8a6a45',
|
|
452
|
+
'--dsw-alias-label-primary': '#3b342a',
|
|
453
|
+
'--dsw-alias-label-secondary': '#6f6557',
|
|
454
|
+
'--dsw-alias-state-error-primary': '#a34a3c',
|
|
455
|
+
'--dsw-alias-state-warn-primary': '#a8792c',
|
|
456
|
+
'--dsw-alias-state-success-primary': '#4f7a5a',
|
|
457
|
+
'--dsw-alias-state-idle-primary': '#a49a89',
|
|
458
|
+
'--dsw-specific-sidebar-fill': '#efe9db',
|
|
458
459
|
};
|
|
459
460
|
|
|
460
461
|
// Swatches double as both the current-state indicator and the toggle.
|
|
461
462
|
const THEME_SWATCHES = [
|
|
462
|
-
{ id: 'dark', label: '深色', color: '#15171a' },
|
|
463
|
-
{ id: 'light', label: '浅色', color: '#f7f8fa' },
|
|
464
|
-
{ id: 'paper', label: '纸质', color: '#
|
|
463
|
+
{ id: 'dark', label: '深色', color: '#15171a', ring: 'rgba(255,255,255,0.28)' },
|
|
464
|
+
{ id: 'light', label: '浅色', color: '#f7f8fa', ring: 'rgba(0,0,0,0.35)' },
|
|
465
|
+
{ id: 'paper', label: '纸质', color: '#efe4cd', ring: 'rgba(0,0,0,0.28)' },
|
|
465
466
|
];
|
|
466
467
|
|
|
467
468
|
function createThemeSwitcher(ctx) {
|
|
468
|
-
|
|
469
|
+
const readPreference = () => {
|
|
470
|
+
try {
|
|
471
|
+
const theme = typeof ctx.get === 'function' ? ctx.get('theme') : ctx.theme;
|
|
472
|
+
const snapshot = theme && typeof theme.getTheme === 'function' ? theme.getTheme() : null;
|
|
473
|
+
return snapshot ? snapshot.preference : null;
|
|
474
|
+
} catch (_error) {
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
const readPaper = () => {
|
|
479
|
+
try { return window.localStorage.getItem(PAPER_KEY) === '1'; } catch (_error) { return false; }
|
|
480
|
+
};
|
|
481
|
+
const writePaper = (on) => {
|
|
482
|
+
try { window.localStorage.setItem(PAPER_KEY, on ? '1' : '0'); } catch (_error) { /* storage unavailable */ }
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
let paperDispose = null;
|
|
486
|
+
function setPaper(on) {
|
|
469
487
|
const theme = typeof ctx.get === 'function' ? ctx.get('theme') : ctx.theme;
|
|
470
|
-
|
|
488
|
+
if (!theme) return;
|
|
489
|
+
if (on) {
|
|
490
|
+
if (paperDispose) return;
|
|
491
|
+
// Legal persisted preference first, then paint the paper layer on top.
|
|
492
|
+
try { theme.setTheme('light'); } catch (error) { console.warn('[workbench] setTheme light failed', error); }
|
|
493
|
+
const modes = {};
|
|
494
|
+
Object.keys(PAPER_TOKENS).forEach((name) => {
|
|
495
|
+
modes[name] = { light: PAPER_TOKENS[name], dark: PAPER_TOKENS[name] };
|
|
496
|
+
});
|
|
471
497
|
try {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
498
|
+
paperDispose = typeof theme.overrideTokens === 'function' ? theme.overrideTokens('workbench:paper', modes) : null;
|
|
499
|
+
} catch (error) {
|
|
500
|
+
console.warn('[workbench] paper override failed', error);
|
|
501
|
+
paperDispose = null;
|
|
476
502
|
}
|
|
477
|
-
}
|
|
478
|
-
|
|
503
|
+
} else if (paperDispose) {
|
|
504
|
+
try { paperDispose(); } catch (_error) { /* already gone */ }
|
|
505
|
+
paperDispose = null;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
479
508
|
|
|
509
|
+
function ThemeSwitcher() {
|
|
510
|
+
const [paper, setPaperState] = React.useState(readPaper);
|
|
511
|
+
const [preference, setPreference] = React.useState(readPreference);
|
|
512
|
+
|
|
513
|
+
// Re-apply the saved paper layer after a reload / remount.
|
|
480
514
|
React.useEffect(() => {
|
|
481
|
-
if (
|
|
515
|
+
if (readPaper()) setPaper(true);
|
|
516
|
+
}, []);
|
|
517
|
+
|
|
518
|
+
// Swatches only re-render on real preference changes; the paper layer is
|
|
519
|
+
// local state because the theme service does not know about it.
|
|
520
|
+
React.useEffect(() => {
|
|
521
|
+
if (typeof ctx.on !== 'function') return undefined;
|
|
482
522
|
let off = null;
|
|
483
523
|
try {
|
|
484
|
-
off = ctx.on('theme/change', () =>
|
|
524
|
+
off = ctx.on('theme/change', () => {
|
|
525
|
+
const next = readPreference();
|
|
526
|
+
setPreference(next);
|
|
527
|
+
// Somebody picked a concrete palette in DSH's own settings: drop the layer.
|
|
528
|
+
if (next && next !== 'light' && paper) {
|
|
529
|
+
setPaper(false);
|
|
530
|
+
writePaper(false);
|
|
531
|
+
setPaperState(false);
|
|
532
|
+
}
|
|
533
|
+
});
|
|
485
534
|
} catch (_error) {
|
|
486
535
|
off = null;
|
|
487
536
|
}
|
|
488
537
|
return () => { if (typeof off === 'function') off(); };
|
|
489
|
-
}, []);
|
|
538
|
+
}, [paper]);
|
|
539
|
+
|
|
540
|
+
const activeId = paper ? 'paper' : (preference === 'dark' ? 'dark' : 'light');
|
|
541
|
+
|
|
542
|
+
const choose = (id) => {
|
|
543
|
+
const theme = typeof ctx.get === 'function' ? ctx.get('theme') : ctx.theme;
|
|
544
|
+
if (!theme) return;
|
|
545
|
+
if (id === 'paper') {
|
|
546
|
+
setPaper(true);
|
|
547
|
+
writePaper(true);
|
|
548
|
+
setPaperState(true);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
setPaper(false);
|
|
552
|
+
writePaper(false);
|
|
553
|
+
setPaperState(false);
|
|
554
|
+
try { theme.setTheme(id); } catch (error) { console.warn('[workbench] setTheme failed', error); }
|
|
555
|
+
};
|
|
490
556
|
|
|
491
557
|
return h('div', {
|
|
492
|
-
style: { display: 'inline-flex', alignItems: 'center', gap:
|
|
558
|
+
style: { display: 'inline-flex', alignItems: 'center', gap: 8, flex: '0 0 auto' },
|
|
493
559
|
title: '主题',
|
|
494
560
|
},
|
|
495
561
|
THEME_SWATCHES.map((swatch) => {
|
|
496
|
-
const active =
|
|
562
|
+
const active = activeId === swatch.id;
|
|
497
563
|
return h('button', {
|
|
498
564
|
key: swatch.id,
|
|
499
565
|
type: 'button',
|
|
500
566
|
title: swatch.label,
|
|
501
567
|
'aria-label': swatch.label,
|
|
502
568
|
'aria-pressed': active ? 'true' : 'false',
|
|
503
|
-
onClick: () =>
|
|
504
|
-
if (!theme || typeof theme.setTheme !== 'function') return;
|
|
505
|
-
try { theme.setTheme(swatch.id); } catch (error) { console.warn('[workbench] setTheme failed', error); }
|
|
506
|
-
},
|
|
569
|
+
onClick: () => choose(swatch.id),
|
|
507
570
|
style: {
|
|
508
|
-
width:
|
|
571
|
+
width: 17, height: 17, padding: 0, borderRadius: '50%', flex: '0 0 auto',
|
|
509
572
|
background: swatch.color,
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
573
|
+
// A hairline ring keeps the dark swatch readable on a dark bar;
|
|
574
|
+
// the selected one only thickens and takes the brand color.
|
|
575
|
+
border: (active ? '1.5px' : '1px') + ' solid ' + (active ? 'var(--dsw-alias-brand-primary)' : swatch.ring),
|
|
576
|
+
boxSizing: 'border-box',
|
|
514
577
|
cursor: 'pointer',
|
|
515
|
-
transition: '
|
|
578
|
+
transition: 'border-color .12s, border-width .12s',
|
|
516
579
|
},
|
|
517
580
|
});
|
|
518
581
|
}),
|
|
@@ -1329,16 +1392,6 @@ div[data-row-key^="session:"]:has(svg[data-state="ongoing"]) svg[data-state="ong
|
|
|
1329
1392
|
layoutService = ctx.get('layout') || null;
|
|
1330
1393
|
layoutGetter = () => ctx.get('layout') || null;
|
|
1331
1394
|
|
|
1332
|
-
// Extra palette on top of the built-in light/dark pair.
|
|
1333
|
-
try {
|
|
1334
|
-
const themeService = typeof ctx.get === 'function' ? ctx.get('theme') : null;
|
|
1335
|
-
if (themeService && typeof themeService.register === 'function') {
|
|
1336
|
-
ctx.effect(() => themeService.register(PAPER_THEME), 'workbench: paper theme');
|
|
1337
|
-
}
|
|
1338
|
-
} catch (error) {
|
|
1339
|
-
console.warn('[workbench] paper theme registration failed', error);
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
1395
|
// The shell's own overview lives above the tab row: it is reachable from
|
|
1343
1396
|
// the top-bar "工作台" icon, so it registers a page without a tab.
|
|
1344
1397
|
ctx.effect(() => api.registerView({
|
package/package.json
CHANGED