@philomelcc/dsh-workbench 0.1.4 → 0.1.5
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/README.md +11 -14
- package/client.js +51 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,26 +1,23 @@
|
|
|
1
1
|
# @philomelcc/dsh-workbench
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
small API modules register against.
|
|
3
|
+
外壳本体:顶部第二行的标签切换、每个模块一整页,以及模块注册用的那套小 API。
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
one module:
|
|
5
|
+
它单独装没什么用——至少配一个模块:
|
|
8
6
|
|
|
9
7
|
```powershell
|
|
10
8
|
dsh plugin --profile web add @philomelcc/dsh-workbench
|
|
11
9
|
dsh plugin --profile web add @philomelcc/dsh-status-monitor
|
|
12
10
|
```
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
契约:[`docs/SPEC.md`](../../docs/SPEC.md) · 模板:[`template/`](../../template) ·
|
|
13
|
+
现成模块:[状态监控](../status-monitor)、[下载中心](../download-center)。
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
外壳提供:
|
|
18
16
|
|
|
19
|
-
- `registerView({ id, title, icon, component, order, subtitle, badge, tab })`
|
|
20
|
-
|
|
21
|
-
- `
|
|
22
|
-
- `
|
|
23
|
-
- `
|
|
24
|
-
- `refresh()` — repaint the tab row after a badge changed.
|
|
17
|
+
- `registerView({ id, title, icon, component, order, subtitle, badge, tab })` —— 一个标签、一整页;
|
|
18
|
+
- `components`(StatusDot / Card / SectionHeader / EmptyState / Spinner / Button / Field);
|
|
19
|
+
- `tokens`(跟随明暗主题的颜色 token);
|
|
20
|
+
- `apiVersion` —— 目前是 `1`;
|
|
21
|
+
- `refresh()` —— 角标变化后重绘标签行。
|
|
25
22
|
|
|
26
|
-
MIT
|
|
23
|
+
MIT。
|
package/client.js
CHANGED
|
@@ -456,6 +456,10 @@ window.__ModuleLoader__.load({
|
|
|
456
456
|
'--dsw-alias-state-success-primary': '#4f7a5a',
|
|
457
457
|
'--dsw-alias-state-idle-primary': '#a49a89',
|
|
458
458
|
'--dsw-specific-sidebar-fill': '#efe9db',
|
|
459
|
+
// Chat bubbles: the light base uses a pale DeepSeek blue, which looks
|
|
460
|
+
// wrong on paper. These two are outside the alias layer.
|
|
461
|
+
'--dsw-specific-bubble': '#efe7d6',
|
|
462
|
+
'--dsw-specific-bubble-highlight': '#e4d8c0',
|
|
459
463
|
};
|
|
460
464
|
|
|
461
465
|
// Swatches double as both the current-state indicator and the toggle.
|
|
@@ -507,9 +511,47 @@ window.__ModuleLoader__.load({
|
|
|
507
511
|
}
|
|
508
512
|
|
|
509
513
|
function ThemeSwitcher() {
|
|
514
|
+
const boxRef = React.useRef(null);
|
|
510
515
|
const [paper, setPaperState] = React.useState(readPaper);
|
|
511
516
|
const [preference, setPreference] = React.useState(readPreference);
|
|
512
517
|
|
|
518
|
+
// The account block is fixed to the right and changes width (a reconnect
|
|
519
|
+
// banner widens it), so park the swatches against its measured left edge
|
|
520
|
+
// instead of a guessed margin.
|
|
521
|
+
React.useEffect(() => {
|
|
522
|
+
const box = boxRef.current;
|
|
523
|
+
if (!box) return undefined;
|
|
524
|
+
const place = () => {
|
|
525
|
+
const settings = document.querySelector('[class*="settingsArea"]');
|
|
526
|
+
if (!settings) {
|
|
527
|
+
box.style.right = '150px';
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const rect = settings.getBoundingClientRect();
|
|
531
|
+
if (rect.width > 0) box.style.right = (rect.width + 16 + 12) + 'px';
|
|
532
|
+
// Same box as the account block, so `align-items: center` lands on
|
|
533
|
+
// the account's optical center instead of a guessed offset.
|
|
534
|
+
if (rect.height > 0) {
|
|
535
|
+
box.style.top = rect.top + 'px';
|
|
536
|
+
box.style.height = rect.height + 'px';
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
place();
|
|
540
|
+
let observer = null;
|
|
541
|
+
const settings = document.querySelector('[class*="settingsArea"]');
|
|
542
|
+
if (settings && typeof ResizeObserver !== 'undefined') {
|
|
543
|
+
observer = new ResizeObserver(place);
|
|
544
|
+
// border-box: the account's width changes through its chrome, not
|
|
545
|
+
// just its text content.
|
|
546
|
+
observer.observe(settings, { box: 'border-box' });
|
|
547
|
+
}
|
|
548
|
+
window.addEventListener('resize', place);
|
|
549
|
+
return () => {
|
|
550
|
+
if (observer) observer.disconnect();
|
|
551
|
+
window.removeEventListener('resize', place);
|
|
552
|
+
};
|
|
553
|
+
}, []);
|
|
554
|
+
|
|
513
555
|
// Re-apply the saved paper layer after a reload / remount.
|
|
514
556
|
React.useEffect(() => {
|
|
515
557
|
if (readPaper()) setPaper(true);
|
|
@@ -555,7 +597,11 @@ window.__ModuleLoader__.load({
|
|
|
555
597
|
};
|
|
556
598
|
|
|
557
599
|
return h('div', {
|
|
558
|
-
|
|
600
|
+
ref: boxRef,
|
|
601
|
+
style: {
|
|
602
|
+
position: 'fixed', top: 2, height: 40, zIndex: 301,
|
|
603
|
+
display: 'inline-flex', alignItems: 'center', gap: 8,
|
|
604
|
+
},
|
|
559
605
|
title: '主题',
|
|
560
606
|
},
|
|
561
607
|
THEME_SWATCHES.map((swatch) => {
|
|
@@ -593,7 +639,9 @@ window.__ModuleLoader__.load({
|
|
|
593
639
|
const tone = snap.phase === 'ready' ? balanceTone(snap.amount) : 'muted';
|
|
594
640
|
const value = snap.phase === 'ready' && typeof snap.amount === 'number' ? snap.amount.toFixed(2) : (snap.phase === 'loading' ? '…' : '—');
|
|
595
641
|
const toneColor = tone === 'green' ? '#4ade80' : tone === 'amber' ? '#e3b341' : tone === 'red' ? '#f87171' : 'var(--dsw-alias-label-secondary)';
|
|
596
|
-
|
|
642
|
+
// The border follows the theme: hard-coded dark greens read as a black
|
|
643
|
+
// ring on light/paper backgrounds.
|
|
644
|
+
const borderBase = 'var(--dsw-alias-border-l2)';
|
|
597
645
|
const at = new Date(snap.updatedAt || Date.now()).toLocaleTimeString('zh-CN', { hour12: false });
|
|
598
646
|
return h('div', {
|
|
599
647
|
style: { position: 'relative', display: 'inline-flex' },
|
|
@@ -789,7 +837,7 @@ window.__ModuleLoader__.load({
|
|
|
789
837
|
iconBtn('plugins', '插件', IconPlugins),
|
|
790
838
|
iconBtn('schedules', '自动化任务', IconClock),
|
|
791
839
|
),
|
|
792
|
-
h(
|
|
840
|
+
h(ThemeSwitcher),
|
|
793
841
|
),
|
|
794
842
|
h('div', { style: { height: 45, display: 'flex', alignItems: 'center', gap: 8, padding: '0 14px', borderTop: '1px solid var(--dsw-alias-border-l1)' } },
|
|
795
843
|
h('div', { ref: tabsRef, style: { position: 'relative', display: 'flex', alignItems: 'center', gap: 4, flex: 1, overflowX: 'auto', overflowY: 'hidden' } },
|
package/package.json
CHANGED