dsh-brief-sidebar 0.5.0 → 0.7.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/LICENSE +21 -0
- package/README.md +10 -8
- package/dsh.plugin.json +2 -2
- package/lib/client.js +174 -37
- package/lib/client.js.map +1 -1
- package/lib/types/client/index.d.ts +6 -0
- package/lib/types/client/native-sidebar.d.ts +50 -0
- package/package.json +12 -7
- package/src/client/index.tsx +145 -16
- package/src/client/native-sidebar.tsx +149 -0
- package/src/client/scope-face.ts +65 -65
- package/src/client/settings-card.tsx +199 -199
- package/src/client/summary/MemorySlotsSection.tsx +296 -296
- package/src/client/summary/memory-slots.ts +136 -136
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 drscrewdriver
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -15,14 +15,12 @@ DSH web 插件(`dsh-better-sidebar` 消费方),仓库/包名 **`dsh-brief-
|
|
|
15
15
|
|
|
16
16
|
**兼容性范围**:本线(分支 `main`,自 `compat/0.2.0` 升格)面向 **DSH 0.2.0 线**——`engines.dsh` 为 `>=0.2.0-rc.1 <0.2.1-0`,实测基线是 DSH 0.2.0-rc.1,发布走 npm dist-tag **`dsh-0.2.0`**。0.2.0 对本插件所用的全部宿主 API 是纯增量(消费面全部是 `ctx.get(...)` 纯 caller,零导出删除),所以支持线整体前移即可,没有运行时兼容分支。**按 DSH 版本选插件版本**(不要在旧宿主上裸用 `latest`——不满足旧宿主 `engines` 会被启动预检静默禁用;caret 范围跨宿主 minor 也不成立):
|
|
17
17
|
|
|
18
|
-
| DSH 宿主 | 本插件最新版本 | 安装 dist-tag |
|
|
19
|
-
|---|---|---|
|
|
20
|
-
| 0.2.0 | **0.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
(截至 2026-09-30;0.1.x 线由冻结保留分支 `compat/0.1.7` / `compat/0.1.5`(≤0.3.1)继续服务。)
|
|
18
|
+
| DSH 宿主 | 本插件最新版本 | 安装 dist-tag |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| 0.1.0-rc.2 ~ 0.2.0 全线(15rc) | **0.6.0**(单版本统一) | `latest`(或任一线 tag,全指 0.6.0) |
|
|
21
|
+
|
|
22
|
+
(2026-10-09 起单版本跨线:`compat/0.1.7` / `compat/0.1.5` 冻结退役。0.1.x 老格上 better-sidebar 未激活服务时本插件按设计 inert——console 干净、不拖死其它插件;「概要」tab 在 better-sidebar 正常激活的宿主(0.2.0 等)呈现。)
|
|
23
|
+
|
|
26
24
|
|
|
27
25
|

|
|
28
26
|
|
|
@@ -32,6 +30,10 @@ dock cell、`dsh-tool-todo` 属于 DSH 上游领域,沿用 `todo` 原词,不
|
|
|
32
30
|
|
|
33
31
|
**记忆系统**:简报的第三个分区(记忆召回)目前**仅预留布局插槽**,处于**规划中**,本期不渲染——见 R11 / K4。
|
|
34
32
|
|
|
33
|
+
<!-- host-compat:begin — generated by scripts/sync-hosts.mjs from scripts/hosts.mjs; DO NOT EDIT -->
|
|
34
|
+
- **Supported DSH hosts:** `0.1.0-rc.2` `0.1.0-rc.3` `0.1.0-rc.6` `0.1.0-rc.7` `0.1.0-rc.8` `0.1.1-rc.1` `0.1.1-rc.2` `0.1.2-rc.1` `0.1.5-rc.1` `0.1.5-rc.2` `0.1.5-rc.3` `0.1.7-rc.1` `0.1.7-rc.2` `0.2.0-rc.1` `0.2.0-rc.2` (15 rc releases across lines 0.1.0 / 0.1.1 / 0.1.2 / 0.1.5 / 0.1.7 / 0.2.0)
|
|
35
|
+
<!-- host-compat:end -->
|
|
36
|
+
|
|
35
37
|
## 需求映射
|
|
36
38
|
|
|
37
39
|
| 编号 | 需求 | 实现位置 |
|
package/dsh.plugin.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "dsh-external/dsh-brief-sidebar",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"main": "./lib/index.mjs",
|
|
5
5
|
"description": "会话简报侧栏页(brief):把会话的 todo 列表(`todos` 投影的进展看板)、产物列表(`dshSummaryDeliverables` 投影的实时产出文件)与记忆槽位(`memorySlots` 投影,来自 dsh-prime-memory,开关可关)一起渲染成右侧栏的一个「概要」tab,并遮蔽 DSH 官方那条 composer 上方的 todo 面板。只读展示,不做写回。",
|
|
6
6
|
"engines": {
|
|
7
|
-
"dsh": "
|
|
7
|
+
"dsh": "0.1.0-rc.2 || 0.1.0-rc.3 || 0.1.0-rc.6 || 0.1.0-rc.7 || 0.1.0-rc.8 || 0.1.1-rc.1 || 0.1.1-rc.2 || 0.1.2-rc.1 || 0.1.5-rc.1 || 0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.7-rc.1 || 0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2"
|
|
8
8
|
},
|
|
9
9
|
"contributes": {
|
|
10
10
|
"tools": [],
|
package/lib/client.js
CHANGED
|
@@ -1389,6 +1389,58 @@ window.__ModuleLoader__.load({
|
|
|
1389
1389
|
})));
|
|
1390
1390
|
}
|
|
1391
1391
|
//#endregion
|
|
1392
|
+
//#region src/client/native-sidebar.tsx
|
|
1393
|
+
/** The keyed seat a tab type registers its body under. */
|
|
1394
|
+
const PANE_TAB_SEAT = "sidebar.right.pane.tab";
|
|
1395
|
+
/**
|
|
1396
|
+
* Register the board as a native right-sidebar tab type.
|
|
1397
|
+
*
|
|
1398
|
+
* The registry arrives as a parameter, read from the deferred `ctx.inject`
|
|
1399
|
+
* scope in `apply` — the 0.1.7+ strict-proxy ctx refuses a root `ctx.get`
|
|
1400
|
+
* of an undeclared service, so reaching for `sidebarRightTabs` from the root
|
|
1401
|
+
* context is what made the native seat silently no-op on 0.1.7 (the "cannot
|
|
1402
|
+
* register directly" observation). better-sidebar's own native bridge reads
|
|
1403
|
+
* it the same way: from the inject callback's scope.
|
|
1404
|
+
*
|
|
1405
|
+
* @param ctx - the client root context (for the declared `slots` service).
|
|
1406
|
+
* @param registry - the host tab-type registry from the injected scope.
|
|
1407
|
+
* @param deps - the stable ids and the translator from `apply`.
|
|
1408
|
+
* @returns a disposer that unregisters the seat first, then the type (the
|
|
1409
|
+
* registry refuses opens for a type whose body is already gone).
|
|
1410
|
+
*/
|
|
1411
|
+
function registerNativeBoardTab(ctx, registry, deps) {
|
|
1412
|
+
const slots = ctx.get("slots");
|
|
1413
|
+
if (registry === void 0 || typeof registry.register !== "function" || slots === void 0) return () => {};
|
|
1414
|
+
const disposeType = registry.register({
|
|
1415
|
+
id: deps.id,
|
|
1416
|
+
kind: deps.kind,
|
|
1417
|
+
title: () => deps.t("tab.title"),
|
|
1418
|
+
guide: [{
|
|
1419
|
+
id: deps.id,
|
|
1420
|
+
order: deps.order,
|
|
1421
|
+
title: () => deps.t("tab.title"),
|
|
1422
|
+
description: () => deps.t("tab.desc")
|
|
1423
|
+
}]
|
|
1424
|
+
});
|
|
1425
|
+
const disposeSeat = slots.inject(PANE_TAB_SEAT, () => slots.register({
|
|
1426
|
+
name: PANE_TAB_SEAT,
|
|
1427
|
+
key: deps.id,
|
|
1428
|
+
inject: (sessionId) => ({
|
|
1429
|
+
t: deps.t,
|
|
1430
|
+
ctx,
|
|
1431
|
+
scope: { sessionId },
|
|
1432
|
+
visible: true
|
|
1433
|
+
})
|
|
1434
|
+
}, SummaryTab));
|
|
1435
|
+
return () => {
|
|
1436
|
+
try {
|
|
1437
|
+
disposeSeat();
|
|
1438
|
+
} finally {
|
|
1439
|
+
disposeType();
|
|
1440
|
+
}
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
//#endregion
|
|
1392
1444
|
//#region src/client/BriefIcon.tsx
|
|
1393
1445
|
/**
|
|
1394
1446
|
* A checklist mark: three rows with ticks and text rails — the same status
|
|
@@ -1636,10 +1688,14 @@ window.__ModuleLoader__.load({
|
|
|
1636
1688
|
* 1. **Dictionaries first.** Every later registration can render translated
|
|
1637
1689
|
* copy, and a dictionary that lands after the first paint would show raw
|
|
1638
1690
|
* keys for a frame.
|
|
1639
|
-
* 2. **
|
|
1640
|
-
*
|
|
1641
|
-
*
|
|
1642
|
-
*
|
|
1691
|
+
* 2. **Sidebar surface, deferred.** The board needs a sidebar to live in:
|
|
1692
|
+
* better-sidebar when it is installed (richer: badge + rail icon), else the
|
|
1693
|
+
* host's own right-sidebar tab system (0.1.5+ `sidebarRightTabs` +
|
|
1694
|
+
* `sidebar.right.pane.tab`). Both are acquired through the deferred
|
|
1695
|
+
* `ctx.inject` because neither service is ordered against this apply —
|
|
1696
|
+
* an immediate `ctx.get` was the 0.1.2 inert bug. With neither — ≤0.1.2
|
|
1697
|
+
* hosts without better-sidebar — the plugin stays sidebar-inert: the
|
|
1698
|
+
* official panel keeps rendering and the shadow never installs.
|
|
1643
1699
|
* 3. **Shadow the official dock.** Deliberately BEFORE the tab registration:
|
|
1644
1700
|
* the panel is the only other carrier of the list, so the replacement has to
|
|
1645
1701
|
* exist in the same activation.
|
|
@@ -1674,6 +1730,12 @@ window.__ModuleLoader__.load({
|
|
|
1674
1730
|
*/
|
|
1675
1731
|
const TAB_ID = "dsh-brief-sidebar:board";
|
|
1676
1732
|
/**
|
|
1733
|
+
* Tab kind on the native host-sidebar path (`openTab`/guide dispatch key).
|
|
1734
|
+
* Only the native registration uses it; better-sidebar keeps addressing the
|
|
1735
|
+
* tab by {@link TAB_ID}.
|
|
1736
|
+
*/
|
|
1737
|
+
const TAB_KIND = "brief-board";
|
|
1738
|
+
/**
|
|
1677
1739
|
* Position in the host's new-tab guide.
|
|
1678
1740
|
*
|
|
1679
1741
|
* Built-ins on this host generation sit at editor 10 / git 20 / subagent 30 /
|
|
@@ -1682,17 +1744,29 @@ window.__ModuleLoader__.load({
|
|
|
1682
1744
|
*/
|
|
1683
1745
|
const TAB_ORDER = 15;
|
|
1684
1746
|
/** Services required before `apply` runs. */
|
|
1685
|
-
const inject = [
|
|
1686
|
-
"betterSidebar",
|
|
1687
|
-
"locale",
|
|
1688
|
-
"slots"
|
|
1689
|
-
];
|
|
1747
|
+
const inject = ["slots", "locale"];
|
|
1690
1748
|
/**
|
|
1691
1749
|
* An effect body must return a disposer (cordis rejects `undefined` with a
|
|
1692
1750
|
* `TypeError`), so a path that could not register anything returns this no-op
|
|
1693
1751
|
* instead of nothing.
|
|
1694
1752
|
*/
|
|
1695
1753
|
const NOOP = () => {};
|
|
1754
|
+
/** Console prefix for the environment probe and registration attribution. */
|
|
1755
|
+
const LOG = "[dsh-brief-sidebar]";
|
|
1756
|
+
/**
|
|
1757
|
+
* Run a registration thunk, attributing a thrown failure to the console
|
|
1758
|
+
* instead of the cordis effect void (a bare throw inside `ctx.effect` leaves
|
|
1759
|
+
* the field reader with an empty sidebar and no clue which arm died).
|
|
1760
|
+
*/
|
|
1761
|
+
function guarded(label, run) {
|
|
1762
|
+
try {
|
|
1763
|
+
const dispose = run();
|
|
1764
|
+
return typeof dispose === "function" ? dispose : NOOP;
|
|
1765
|
+
} catch (error) {
|
|
1766
|
+
console.error(`${LOG} ${label} failed`, error);
|
|
1767
|
+
return NOOP;
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1696
1770
|
/**
|
|
1697
1771
|
* Browser-face apply.
|
|
1698
1772
|
*
|
|
@@ -1715,34 +1789,83 @@ window.__ModuleLoader__.load({
|
|
|
1715
1789
|
return key;
|
|
1716
1790
|
}
|
|
1717
1791
|
};
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1792
|
+
let surface = "none";
|
|
1793
|
+
let shadowDone = false;
|
|
1794
|
+
let nativeDisposer;
|
|
1795
|
+
/** Hide the official composer-band panel (reversible on unload), once. */
|
|
1796
|
+
const shadowDock = () => {
|
|
1797
|
+
if (shadowDone) return;
|
|
1798
|
+
shadowDone = true;
|
|
1799
|
+
ctx.effect(() => registerTodoDockShadow(ctx) ?? NOOP, "dsh-brief-sidebar: dock shadow");
|
|
1800
|
+
};
|
|
1801
|
+
const registerOnBar = (bar) => {
|
|
1802
|
+
if (surface === "bar") return;
|
|
1803
|
+
if (surface === "native") {
|
|
1804
|
+
const release = nativeDisposer;
|
|
1805
|
+
nativeDisposer = void 0;
|
|
1806
|
+
try {
|
|
1807
|
+
release?.();
|
|
1808
|
+
} catch {}
|
|
1809
|
+
console.info(`${LOG} surface=bar (handed over from native)`);
|
|
1810
|
+
} else console.info(`${LOG} surface=bar`);
|
|
1811
|
+
surface = "bar";
|
|
1812
|
+
shadowDock();
|
|
1813
|
+
ctx.effect(() => guarded("registerTab", () => bar.registerTab({
|
|
1814
|
+
id: TAB_ID,
|
|
1815
|
+
title: () => t("tab.title"),
|
|
1816
|
+
description: () => t("tab.desc"),
|
|
1817
|
+
icon: (size) => BriefIcon(size),
|
|
1818
|
+
order: 15,
|
|
1819
|
+
single: true,
|
|
1820
|
+
/**
|
|
1821
|
+
* Unfinished count on the tab strip. Called on every tab-bar render, so
|
|
1822
|
+
* it reads one snapshot and allocates no subscription; a completed list
|
|
1823
|
+
* shows no badge at all.
|
|
1824
|
+
*/
|
|
1825
|
+
badge: (badgeCtx, badgeScope) => {
|
|
1826
|
+
const todos = readTodos(projectionSnapshot(badgeCtx, badgeScope.sessionId, TODOS_KEY));
|
|
1827
|
+
if (todos === void 0) return null;
|
|
1828
|
+
const pending = unfinishedCount(todos);
|
|
1829
|
+
return pending > 0 ? pending : null;
|
|
1830
|
+
},
|
|
1831
|
+
component: (props) => (0, react.createElement)(SummaryTab, {
|
|
1832
|
+
t,
|
|
1833
|
+
ctx: props.ctx,
|
|
1834
|
+
scope: props.scope,
|
|
1835
|
+
visible: props.visible
|
|
1836
|
+
})
|
|
1837
|
+
})), "dsh-brief-sidebar: tab");
|
|
1838
|
+
};
|
|
1839
|
+
const registerOnNative = (registry) => {
|
|
1840
|
+
if (surface !== "none") return;
|
|
1841
|
+
surface = "native";
|
|
1842
|
+
console.info(`${LOG} surface=native`);
|
|
1843
|
+
shadowDock();
|
|
1844
|
+
ctx.effect(() => {
|
|
1845
|
+
nativeDisposer = guarded("native tab", () => registerNativeBoardTab(ctx, registry, {
|
|
1846
|
+
id: TAB_ID,
|
|
1847
|
+
kind: TAB_KIND,
|
|
1848
|
+
order: 15,
|
|
1849
|
+
t
|
|
1850
|
+
}));
|
|
1851
|
+
return () => {
|
|
1852
|
+
nativeDisposer = void 0;
|
|
1853
|
+
};
|
|
1854
|
+
}, "dsh-brief-sidebar: native tab");
|
|
1855
|
+
};
|
|
1856
|
+
let bar;
|
|
1857
|
+
try {
|
|
1858
|
+
bar = ctx.get("betterSidebar");
|
|
1859
|
+
} catch {
|
|
1860
|
+
bar = void 0;
|
|
1861
|
+
}
|
|
1862
|
+
if (bar !== void 0) registerOnBar(bar);
|
|
1863
|
+
else ctx.inject(["betterSidebar"], (scope) => {
|
|
1864
|
+
if (scope.betterSidebar !== void 0) registerOnBar(scope.betterSidebar);
|
|
1865
|
+
});
|
|
1866
|
+
ctx.inject(["sidebarRightTabs"], (scope) => {
|
|
1867
|
+
registerOnNative(scope.sidebarRightTabs);
|
|
1868
|
+
});
|
|
1746
1869
|
const slots = ctx.get("slots");
|
|
1747
1870
|
ctx.inject(["configForms"], (scope) => {
|
|
1748
1871
|
const configForms = scope.configForms;
|
|
@@ -1767,9 +1890,23 @@ window.__ModuleLoader__.load({
|
|
|
1767
1890
|
inject: injectFactory
|
|
1768
1891
|
}, component));
|
|
1769
1892
|
});
|
|
1893
|
+
try {
|
|
1894
|
+
let nativeTabsAtApply = false;
|
|
1895
|
+
try {
|
|
1896
|
+
nativeTabsAtApply = ctx.get("sidebarRightTabs") !== void 0;
|
|
1897
|
+
} catch {
|
|
1898
|
+
nativeTabsAtApply = false;
|
|
1899
|
+
}
|
|
1900
|
+
console.info(`${LOG} env ${JSON.stringify({
|
|
1901
|
+
locale: locale !== void 0,
|
|
1902
|
+
barAtApply: bar !== void 0,
|
|
1903
|
+
nativeTabsAtApply
|
|
1904
|
+
})}`);
|
|
1905
|
+
} catch {}
|
|
1770
1906
|
}
|
|
1771
1907
|
//#endregion
|
|
1772
1908
|
exports.TAB_ID = TAB_ID;
|
|
1909
|
+
exports.TAB_KIND = TAB_KIND;
|
|
1773
1910
|
exports.TAB_ORDER = TAB_ORDER;
|
|
1774
1911
|
exports.apply = apply;
|
|
1775
1912
|
exports.inject = inject;
|