dsh-dock 0.9.8 → 0.9.9
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 +25 -18
- package/features/animation/view.jsx +4 -1
- package/package.json +5 -1
- package/src/client.jsx +13 -10
- package/src/shared.js +11 -3
package/client.js
CHANGED
|
@@ -2138,13 +2138,17 @@ var FeatureBoundary = class extends import_react.default.Component {
|
|
|
2138
2138
|
static getDerivedStateFromError(error) {
|
|
2139
2139
|
return { error };
|
|
2140
2140
|
}
|
|
2141
|
+
componentDidCatch(error) {
|
|
2142
|
+
console.error("[dsh-dock] \u529F\u80FD\u89C6\u56FE\u6E32\u67D3\u51FA\u9519\uFF1A", error);
|
|
2143
|
+
}
|
|
2141
2144
|
render() {
|
|
2142
2145
|
if (this.state.error) {
|
|
2143
2146
|
const msg = this.state.error && this.state.error.message ? this.state.error.message : String(this.state.error);
|
|
2147
|
+
const who = this.props && this.props.label ? "\u300C" + this.props.label + "\u300D" : "\u529F\u80FD\u89C6\u56FE";
|
|
2144
2148
|
return import_react.default.createElement(
|
|
2145
2149
|
"div",
|
|
2146
2150
|
{ className: "dockm-note dockm-err" },
|
|
2147
|
-
"\
|
|
2151
|
+
who + "\u6E32\u67D3\u51FA\u9519\uFF1A" + msg + "\uFF08\u5DF2\u9694\u79BB\uFF0C\u4E0D\u5F71\u54CD\u9762\u677F\u5176\u4ED6\u529F\u80FD\uFF1B\u8BE6\u60C5\u89C1\u6D4F\u89C8\u5668\u63A7\u5236\u53F0\uFF09"
|
|
2148
2152
|
);
|
|
2149
2153
|
}
|
|
2150
2154
|
return this.props.children;
|
|
@@ -5378,12 +5382,12 @@ function AnimationView(props) {
|
|
|
5378
5382
|
const st = snap.status;
|
|
5379
5383
|
const active = st && st.active ? st.active : [];
|
|
5380
5384
|
const recent = st && st.recent ? st.recent.slice(0, 6) : [];
|
|
5385
|
+
const waitingCount = active.reduce((n, t) => n + (Array.isArray(t.approvals) ? t.approvals.length : 0), 0);
|
|
5381
5386
|
const permNote = typeof Notification === "undefined" ? "\u5F53\u524D\u6D4F\u89C8\u5668\u4E0D\u652F\u6301\u7CFB\u7EDF\u901A\u77E5" : Notification.permission === "granted" ? "\u5DF2\u6388\u6743 \xB7 \u4EC5\u9875\u9762\u540E\u53F0\u65F6\u63A8\u9001" : Notification.permission === "denied" ? "\u5DF2\u88AB\u6D4F\u89C8\u5668\u62D2\u7EDD\uFF08\u9700\u5728\u6D4F\u89C8\u5668\u6743\u9650\u8BBE\u7F6E\u91CC\u91CD\u65B0\u5141\u8BB8\uFF09" : "\u672A\u6388\u6743 \xB7 \u5F00\u542F\u65F6\u4F1A\u8BF7\u6C42\u6388\u6743\uFF0C\u4EC5\u9875\u9762\u540E\u53F0\u65F6\u63A8\u9001";
|
|
5382
5387
|
const rows = [];
|
|
5383
5388
|
if (!cfg) {
|
|
5384
5389
|
rows.push(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "dkan-note", children: snap.error ? "\u72B6\u6001\u4E0D\u53EF\u7528\uFF1A" + snap.error : snap.loading ? "\u6B63\u5728\u62C9\u53D6\u4EFB\u52A1\u72B6\u6001\u2026" : "\u7B49\u5F85\u4EFB\u52A1\u72B6\u6001" }, "load"));
|
|
5385
5390
|
} else {
|
|
5386
|
-
const waitingCount2 = active.reduce((n, t) => n + (Array.isArray(t.approvals) ? t.approvals.length : 0), 0);
|
|
5387
5391
|
rows.push(
|
|
5388
5392
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-sec", children: [
|
|
5389
5393
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "dkan-sec-head", children: [
|
|
@@ -10141,7 +10145,7 @@ var feature8 = {
|
|
|
10141
10145
|
};
|
|
10142
10146
|
|
|
10143
10147
|
// src/client.jsx
|
|
10144
|
-
var DOCK_VERSION = "0.9.
|
|
10148
|
+
var DOCK_VERSION = "0.9.9";
|
|
10145
10149
|
var BUILTIN_FEATURES = [feature, feature2, feature3, feature4, feature5, feature6, feature7, feature8];
|
|
10146
10150
|
var PLANNED_FEATURES = [];
|
|
10147
10151
|
var PLANNED_NOTES = {};
|
|
@@ -10447,8 +10451,8 @@ function DockModal() {
|
|
|
10447
10451
|
"div",
|
|
10448
10452
|
{ className: "dockm-view" },
|
|
10449
10453
|
import_react20.default.createElement(
|
|
10450
|
-
|
|
10451
|
-
|
|
10454
|
+
FeatureBoundary,
|
|
10455
|
+
{ key: mod.id, label: mod.name },
|
|
10452
10456
|
import_react20.default.createElement(View, { ctx: ctxRef.current, feature: mod, params: navParams })
|
|
10453
10457
|
)
|
|
10454
10458
|
) : null;
|
|
@@ -10614,8 +10618,8 @@ function HomeView(props) {
|
|
|
10614
10618
|
const enabled = !!(st && st.enabled);
|
|
10615
10619
|
const Stat = m.HomeStat;
|
|
10616
10620
|
const statNode = m.planned ? import_react20.default.createElement("span", null, PLANNED_NOTES[m.id] || "\u5F85\u63A5\u5165\uFF1A\u89C1 README \u8DEF\u7EBF\u56FE") : enabled && Stat ? import_react20.default.createElement(
|
|
10617
|
-
|
|
10618
|
-
|
|
10621
|
+
FeatureBoundary,
|
|
10622
|
+
{ key: m.id, label: m.name },
|
|
10619
10623
|
import_react20.default.createElement(Stat, { ctx })
|
|
10620
10624
|
) : import_react20.default.createElement("span", null, "\u5DF2\u505C\u7528\uFF0C\u542F\u7528\u540E\u5728\u6B64\u5C55\u793A\u8FD0\u884C\u6982\u8981");
|
|
10621
10625
|
return import_react20.default.createElement(
|
|
@@ -10697,8 +10701,8 @@ function DockPanel() {
|
|
|
10697
10701
|
"div",
|
|
10698
10702
|
{ className: "dock-body" },
|
|
10699
10703
|
import_react20.default.createElement(
|
|
10700
|
-
|
|
10701
|
-
|
|
10704
|
+
FeatureBoundary,
|
|
10705
|
+
{ key: f.id, label: f.name },
|
|
10702
10706
|
import_react20.default.createElement(View, { ctx, feature: f })
|
|
10703
10707
|
)
|
|
10704
10708
|
) : null;
|
|
@@ -10750,14 +10754,17 @@ function DockChips(props) {
|
|
|
10750
10754
|
const items = [];
|
|
10751
10755
|
for (const f of allModules()) {
|
|
10752
10756
|
if (f.planned || !stateOf(f.id).enabled || !chipShown(f.id) || typeof f.Chip !== "function") continue;
|
|
10753
|
-
items.push(import_react20.default.createElement(
|
|
10754
|
-
|
|
10755
|
-
|
|
10756
|
-
|
|
10757
|
-
|
|
10758
|
-
|
|
10759
|
-
|
|
10760
|
-
|
|
10757
|
+
items.push(import_react20.default.createElement(
|
|
10758
|
+
FeatureBoundary,
|
|
10759
|
+
{ key: f.id, label: f.name },
|
|
10760
|
+
import_react20.default.createElement(f.Chip, {
|
|
10761
|
+
ctx: props.ctx,
|
|
10762
|
+
feature: f,
|
|
10763
|
+
session: props.session,
|
|
10764
|
+
sessionId: props.sessionId,
|
|
10765
|
+
input: props.input
|
|
10766
|
+
})
|
|
10767
|
+
));
|
|
10761
10768
|
}
|
|
10762
10769
|
if (items.length === 0) return null;
|
|
10763
10770
|
return import_react20.default.createElement("div", { className: "dockchip-row" }, items);
|
|
@@ -10771,7 +10778,7 @@ function FeatureOverlays() {
|
|
|
10771
10778
|
if (f.planned || !stateOf(f.id).enabled || typeof f.Overlay !== "function") continue;
|
|
10772
10779
|
items.push(import_react20.default.createElement(
|
|
10773
10780
|
FeatureBoundary,
|
|
10774
|
-
{ key: f.id },
|
|
10781
|
+
{ key: f.id, label: f.name },
|
|
10775
10782
|
import_react20.default.createElement(f.Overlay, { ctx: ctxRef.current, feature: f })
|
|
10776
10783
|
));
|
|
10777
10784
|
}
|
|
@@ -1553,6 +1553,10 @@ function AnimationView(props) {
|
|
|
1553
1553
|
const st = snap.status;
|
|
1554
1554
|
const active = st && st.active ? st.active : [];
|
|
1555
1555
|
const recent = st && st.recent ? st.recent.slice(0, 6) : [];
|
|
1556
|
+
// 等待确认的审批项总数:运行状态标题与页脚都用,必须在 if/else 之外声明。
|
|
1557
|
+
// ⚠️ 曾误写在 if(!cfg) 的 else 块内、却在块外引用 → ReferenceError: waitingCount is not defined,
|
|
1558
|
+
// 整个视图渲染抛错、宿主把面板插槽换成空 div(点「任务动画」界面直接消失)。
|
|
1559
|
+
const waitingCount = active.reduce((n, t) => n + (Array.isArray(t.approvals) ? t.approvals.length : 0), 0);
|
|
1556
1560
|
const permNote = typeof Notification === "undefined"
|
|
1557
1561
|
? "当前浏览器不支持系统通知"
|
|
1558
1562
|
: Notification.permission === "granted" ? "已授权 · 仅页面后台时推送"
|
|
@@ -1562,7 +1566,6 @@ function AnimationView(props) {
|
|
|
1562
1566
|
if (!cfg) {
|
|
1563
1567
|
rows.push(<div key="load" className="dkan-note">{snap.error ? "状态不可用:" + snap.error : (snap.loading ? "正在拉取任务状态…" : "等待任务状态")}</div>);
|
|
1564
1568
|
} else {
|
|
1565
|
-
const waitingCount = active.reduce((n, t) => n + (Array.isArray(t.approvals) ? t.approvals.length : 0), 0);
|
|
1566
1569
|
rows.push(
|
|
1567
1570
|
<div key="anim" className="dkan-sec">
|
|
1568
1571
|
<div className="dkan-sec-head">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-dock",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.9",
|
|
4
4
|
"description": "DeepSeek Harness 功能坞插件(dsh-dock):模块化功能中枢——每个功能一个 features/ 模块(可单独提取成包发布,又可经 dockBridge 装回面板)。含用量记录(LLM 调用记账/费用统计)、模型设置、图片理解代理、模型余额、任务动画(19 种运行动画 + 桌面伙伴 3D 场景、完成/需确认通知与提示音、钉钉/飞书推送,开关独立、配置持久化);侧栏入口 + 可拖拽弹窗(最大化/最小化/缩放)。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -40,6 +40,10 @@
|
|
|
40
40
|
],
|
|
41
41
|
"author": "wycto",
|
|
42
42
|
"license": "MIT",
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build:client": "node scripts/build-client.mjs",
|
|
45
|
+
"test:client": "node scripts/test-client-views.mjs"
|
|
46
|
+
},
|
|
43
47
|
"repository": {
|
|
44
48
|
"type": "git",
|
|
45
49
|
"url": "git+https://github.com/wycto/dsh-dock.git"
|
package/src/client.jsx
CHANGED
|
@@ -28,7 +28,7 @@ import { feature as fGames } from "../features/games/view.jsx";
|
|
|
28
28
|
import { feature as fMobileRelay } from "../features/mobile-relay/view.jsx";
|
|
29
29
|
|
|
30
30
|
const name = "dsh-dock";
|
|
31
|
-
const DOCK_VERSION = "0.9.
|
|
31
|
+
const DOCK_VERSION = "0.9.9";
|
|
32
32
|
|
|
33
33
|
// ---- 内置功能注册表:新功能 = features/<id>/ 加模块 + 这里 import 一行 ----
|
|
34
34
|
const BUILTIN_FEATURES = [fTokenlog, fModelconfig, fHeartbeat, fTheme, fBalance, fAnimation, fGames, fMobileRelay];
|
|
@@ -357,12 +357,14 @@ function DockModal() {
|
|
|
357
357
|
const isHome = active === "home";
|
|
358
358
|
const mod = isHome ? null : (MODULES.find((m) => m.id === active) || MODULES[0]);
|
|
359
359
|
const st = mod ? stateOf(mod.id) : null;
|
|
360
|
-
//
|
|
360
|
+
// 视图统一收到 { ctx, feature, params } props
|
|
361
361
|
//(params 来自导航总线:如 { provider } 高亮余额行、{ sessionId } 按会话筛选用量)
|
|
362
362
|
const View = mod ? mod.View : null;
|
|
363
|
+
// 所有视图都包错误边界(含内置):视图抛错只降级为一行提示,不能把整块面板打没。
|
|
364
|
+
// key 用 mod.id:切换功能时重建边界,避免上一个功能的错误态留在新页面上。
|
|
363
365
|
const viewNode = mod && View
|
|
364
366
|
? react.createElement("div", { className: "dockm-view" },
|
|
365
|
-
react.createElement(
|
|
367
|
+
react.createElement(FeatureBoundary, { key: mod.id, label: mod.name },
|
|
366
368
|
react.createElement(View, { ctx: ctxRef.current, feature: mod, params: navParams })))
|
|
367
369
|
: null;
|
|
368
370
|
const enabledCount = MODULES.filter((m) => { const s = stateOf(m.id); return !!(s && s.enabled); }).length;
|
|
@@ -500,7 +502,7 @@ function HomeView(props) {
|
|
|
500
502
|
const statNode = m.planned
|
|
501
503
|
? react.createElement("span", null, PLANNED_NOTES[m.id] || "待接入:见 README 路线图")
|
|
502
504
|
: enabled && Stat
|
|
503
|
-
? react.createElement(
|
|
505
|
+
? react.createElement(FeatureBoundary, { key: m.id, label: m.name },
|
|
504
506
|
react.createElement(Stat, { ctx: ctx }))
|
|
505
507
|
: react.createElement("span", null, "已停用,启用后在此展示运行概要");
|
|
506
508
|
return react.createElement("div", {
|
|
@@ -560,7 +562,7 @@ function DockPanel() {
|
|
|
560
562
|
const View = f.View;
|
|
561
563
|
const viewNode = (!f.planned && st.enabled && View)
|
|
562
564
|
? react.createElement("div", { className: "dock-body" },
|
|
563
|
-
react.createElement(
|
|
565
|
+
react.createElement(FeatureBoundary, { key: f.id, label: f.name },
|
|
564
566
|
react.createElement(View, { ctx: ctx, feature: f })))
|
|
565
567
|
: null;
|
|
566
568
|
return react.createElement("div", { className: "dock-card", key: f.id },
|
|
@@ -608,10 +610,11 @@ function DockChips(props) {
|
|
|
608
610
|
const items = [];
|
|
609
611
|
for (const f of allModules()) {
|
|
610
612
|
if (f.planned || !stateOf(f.id).enabled || !chipShown(f.id) || typeof f.Chip !== "function") continue;
|
|
611
|
-
items.push(react.createElement(f.
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
613
|
+
items.push(react.createElement(FeatureBoundary, { key: f.id, label: f.name },
|
|
614
|
+
react.createElement(f.Chip, {
|
|
615
|
+
ctx: props.ctx, feature: f,
|
|
616
|
+
session: props.session, sessionId: props.sessionId, input: props.input,
|
|
617
|
+
})));
|
|
615
618
|
}
|
|
616
619
|
if (items.length === 0) return null;
|
|
617
620
|
return react.createElement("div", { className: "dockchip-row" }, items);
|
|
@@ -627,7 +630,7 @@ function FeatureOverlays() {
|
|
|
627
630
|
const items = [];
|
|
628
631
|
for (const f of allModules()) {
|
|
629
632
|
if (f.planned || !stateOf(f.id).enabled || typeof f.Overlay !== "function") continue;
|
|
630
|
-
items.push(react.createElement(FeatureBoundary, { key: f.id },
|
|
633
|
+
items.push(react.createElement(FeatureBoundary, { key: f.id, label: f.name },
|
|
631
634
|
react.createElement(f.Overlay, { ctx: ctxRef.current, feature: f })));
|
|
632
635
|
}
|
|
633
636
|
if (items.length === 0) return null;
|
package/src/shared.js
CHANGED
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
import react from "react";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* 功能视图错误边界:视图渲染抛错时降级为一行错误提示,不拖垮整块面板 UI。
|
|
6
|
+
*
|
|
7
|
+
* ⚠️ 内置视图也必须包(v0.9.9 起):宿主把每个插槽条目包在 SlotErrorBoundary 里,
|
|
8
|
+
* 条目内任何渲染抛错都会被宿主换成空 div——表现为「点开功能坞界面整个没了」而侧栏
|
|
9
|
+
* 入口按钮仍是选中态。v0.9.3~0.9.8 的 animation 视图就是这样被一个 ReferenceError 打没的。
|
|
10
|
+
* label 用于提示里点明是哪个功能;调用处应带 key(切换功能时重建,否则错误态会跟着串页)。
|
|
7
11
|
*/
|
|
8
12
|
export class FeatureBoundary extends react.Component {
|
|
9
13
|
constructor(props) {
|
|
@@ -13,11 +17,15 @@ export class FeatureBoundary extends react.Component {
|
|
|
13
17
|
static getDerivedStateFromError(error) {
|
|
14
18
|
return { error: error };
|
|
15
19
|
}
|
|
20
|
+
componentDidCatch(error) {
|
|
21
|
+
console.error("[dsh-dock] 功能视图渲染出错:", error);
|
|
22
|
+
}
|
|
16
23
|
render() {
|
|
17
24
|
if (this.state.error) {
|
|
18
25
|
const msg = this.state.error && this.state.error.message ? this.state.error.message : String(this.state.error);
|
|
26
|
+
const who = this.props && this.props.label ? "「" + this.props.label + "」" : "功能视图";
|
|
19
27
|
return react.createElement("div", { className: "dockm-note dockm-err" },
|
|
20
|
-
"
|
|
28
|
+
who + "渲染出错:" + msg + "(已隔离,不影响面板其他功能;详情见浏览器控制台)");
|
|
21
29
|
}
|
|
22
30
|
return this.props.children;
|
|
23
31
|
}
|