@deepseek-ai/dsh-client-ui-sidebar 0.0.1-rc.1

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 ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, DeepSeek
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/client/ui-sidebar/README.md
5
+ README.md: 45ae267d98b17bbc612cf932f5b95b42ba6ff4bf
6
+ README.zh.md: a9fb927305d0bab5fb4d27adbfdbec90dfa1dd6d
package/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # @deepseek-ai/dsh-client-ui-sidebar
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ Sidebar plugin: real Host Workspaces in stable Host order, each containing its `sessionIds` in Workspace order with `parentId` nesting; Sessions outside every Workspace appear in a trailing `Ungrouped` section. Search, state dots, and collapse into the layout-owned 56px rail are presentation-local. Contract: the [slot system standard](../../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md).
6
+
7
+ New Session starts the runtime's page-local frontend Session Intent; a real Workspace's "+" starts one targeted to that Workspace. The Workspace header "+" opens ui-workspace's shared picker, whose selection also targets a frontend Session. A Workspace Intent does not appear in the sidebar.
8
+
9
+ `SidebarRootComponentProps` composes the layout owner share, the global `useSessions` and `useWorkspaces` hooks, the declared `sidebar.workspace` and `sidebar.settings` child slots, and injected `startSession`, `open`, and sidebar-toggle callbacks. There is no plugin store: `deriveGroups` consumes object-layer snapshots and component-local expansion/search state.
10
+
11
+ Scrollbars in the column are a pointer affordance: the shell rebinds ui-theme's [scrollbar indirection](../ui-theme/README.md) to `transparent` whenever the pointer is outside it, and keeps the thumb drawn for 2s after the pointer leaves, so a list nobody is pointing at carries no bar. The reservation that keeps rows from moving belongs to the scrolling region ([ui-workspace](../ui-workspace/README.md)), so revealing a thumb never reflows.
12
+
13
+ The foot is the `sidebar.settings` seat: the sidebar renders only the bottom-pinned layout slot and shares its column state (`wide`); ui-settings registers the trigger row and settings panel there.
14
+
15
+ The `/client` export surface is the plugin body (`apply`/`inject`) plus the contract types only; SidebarRoot, the row components, and the tree derivation remain package-internal behind the slot registration.
16
+
17
+ ## Model Experience
18
+
19
+ None, as the sidebar renders the browser session list; nothing here reaches a model request.
20
+
21
+ #### KV Cache effect
22
+
23
+ None; this package neither assembles nor sends a provider request.
24
+
25
+ ## Known Limitations and Deferred Work
26
+
27
+ - **Session state-dot rendering is owned by [ui-workspace](../ui-workspace/README.md)** — no done/error notification sources are available.
28
+ - **Group-by supports Workspace only** — Update and Status are not available strategies.
29
+ - **"New task completed" unread marking is local viewing state** — completion-time > last-seen never reaches the host.
package/README.zh.md ADDED
@@ -0,0 +1,29 @@
1
+ # @deepseek-ai/dsh-client-ui-sidebar
2
+
3
+ [English](README.md) | 中文
4
+
5
+ 侧边栏插件:真实 Host Workspace 按稳定的 Host 顺序排列;每个 Workspace 按自身顺序包含其 `sessionIds`,并以 `parentId` 嵌套;不属于任何 Workspace 的会话显示在末尾的 `Ungrouped` 分区。搜索、状态点以及折叠到布局拥有的 56px 轨道,都只属于呈现层。约定:[slot 系统标准](../../../.agents/notes/implemented/architecture/2026-07-22-slot-type-chain-implementation.md)。
6
+
7
+ New Session 会启动运行时的页面局部前端 Session Intent;真实 Workspace 的「+」会启动一项以该 Workspace 为目标的 Intent。Workspace 标题栏的「+」打开 ui-workspace 的共享选择器,选择结果同样以一个前端会话为目标。Workspace Intent 不会出现在侧边栏中。
8
+
9
+ `SidebarRootComponentProps` 组合布局 owner share、全局 `useSessions` 和 `useWorkspaces` 钩子、已声明的 `sidebar.workspace` 与 `sidebar.settings` 子 slot,以及注入的 `startSession`、`open` 和侧边栏切换回调。这里没有插件 store:`deriveGroups` 消费对象层快照与组件局部的展开/搜索状态。
10
+
11
+ 栏内的滚动条是一种指针可供性:只要指针不在栏内,外壳就把 ui-theme 的[滚动条间接层](../ui-theme/README.md)重新绑定为 `transparent`;指针离开后滑块再保留 2 秒,因此没人指向的列表不会带着滚动条。避免行位移的空间预留属于滚动区域本身([ui-workspace](../ui-workspace/README.md)),所以显示滑块不会引起重排。
12
+
13
+ 页脚承载 `sidebar.settings`:侧边栏只渲染固定在底部的布局 slot,并共享其栏状态(`wide`);ui-settings 在此注册触发行和设置面板。
14
+
15
+ `/client` 导出表层只包含插件主体(`apply`/`inject`)及约定类型;SidebarRoot、行组件和树派生仍由 slot 注册封装在包内。
16
+
17
+ ## 模型体验
18
+
19
+ 无。侧边栏渲染浏览器会话列表;这里没有任何内容进入模型请求。
20
+
21
+ #### KV Cache 影响
22
+
23
+ 无;该包(package)既不组装也不发送提供方请求。
24
+
25
+ ## 已知限制与暂缓事项
26
+
27
+ - **Session 状态点渲染由 [ui-workspace](../ui-workspace/README.md) 持有**:没有可用的 done/error 通知数据源。
28
+ - **分组只支持 Workspace**:Update 和 Status 不是可用策略。
29
+ - **「New task completed」未读标记是本地查看状态**:完成时间 > 上次查看时间这一事实永远不会到达宿主。
package/lib/client.js ADDED
@@ -0,0 +1,278 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@deepseek-ai/dsh-client-ui-sidebar",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react_jsx_runtime = require("react/jsx-runtime");
8
+ let react = require("react");
9
+ let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
10
+ //#region ../../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/dist/clsx.mjs
11
+ function r(e) {
12
+ var t, f, n = "";
13
+ if ("string" == typeof e || "number" == typeof e) n += e;
14
+ else if ("object" == typeof e) if (Array.isArray(e)) {
15
+ var o = e.length;
16
+ for (t = 0; t < o; t++) e[t] && (f = r(e[t])) && (n && (n += " "), n += f);
17
+ } else for (f in e) e[f] && (n && (n += " "), n += f);
18
+ return n;
19
+ }
20
+ function clsx() {
21
+ for (var e, t, f = 0, n = "", o = arguments.length; f < o; f++) (e = arguments[f]) && (t = r(e)) && (n && (n += " "), n += t);
22
+ return n;
23
+ }
24
+ //#endregion
25
+ //#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-sidebar/src/client/SidebarRoot.module.css.mjs
26
+ const css = ".hHd-Xa_root{--dsh-sidebar-inline-padding:12px;height:100%;padding:6px var(--dsh-sidebar-inline-padding);box-sizing:border-box;background:var(--dsw-specific-sidebar-fill);color:var(--dsw-alias-label-primary);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);flex-direction:column;font-size:14px;display:flex}.hHd-Xa_root.hHd-Xa_collapsed{padding:18px 10px 6px}.hHd-Xa_root.hHd-Xa_quietBars{--dsh-scrollbar-thumb:transparent;--dsh-scrollbar-thumb-hover:transparent}.hHd-Xa_fading>*{opacity:0;transition:opacity .15s var(--ds-ease-in-out)}.hHd-Xa_wide{animation:hHd-Xa_wide-in .2s var(--ds-ease-in-out)}@keyframes hHd-Xa_wide-in{0%{opacity:0}}.hHd-Xa_railIn .hHd-Xa_iconButton,.hHd-Xa_railIn .hHd-Xa_newSession,.hHd-Xa_railIn .hHd-Xa_footArea{animation:hHd-Xa_rail-in .15s var(--ds-ease-in-out) .1s backwards}@keyframes hHd-Xa_rail-in{0%{opacity:0}}.hHd-Xa_logoRow{box-sizing:border-box;flex:none;justify-content:flex-end;align-items:center;gap:8px;height:60px;margin-bottom:16px;padding:8px 0 8px 4px;display:flex;overflow:hidden}.hHd-Xa_collapsed .hHd-Xa_logoRow{height:36px;margin-bottom:12px;padding:0}.hHd-Xa_brand{min-width:0;color:inherit;cursor:pointer;background:0 0;border:none;flex:1;align-items:center;padding:0;display:inline-flex;overflow:hidden}.hHd-Xa_iconButton{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-secondary);background:0 0;border:none;border-radius:50%;flex:none;justify-content:center;align-items:center;padding:0;display:inline-flex}.hHd-Xa_iconButton:hover{background:var(--dsw-alias-interactive-bg-hover)}.hHd-Xa_collapsed .hHd-Xa_iconButton{width:36px;height:36px}.hHd-Xa_collapsed .hHd-Xa_toggle .hHd-Xa_panelIcon{display:none}.hHd-Xa_collapsed .hHd-Xa_toggle:hover .hHd-Xa_panelIcon{display:inline}.hHd-Xa_collapsed .hHd-Xa_toggle:hover .hHd-Xa_railFish{display:none}.hHd-Xa_collapsed .hHd-Xa_iconButton{color:var(--dsw-alias-label-primary)}.hHd-Xa_newSession{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-button-elevated-fill);height:38px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:24px;flex:none;justify-content:center;align-items:center;gap:6px;margin:0 2px 20px;padding:8px 16px;font-size:14px;font-weight:500;line-height:22px;display:flex;overflow:hidden}.hHd-Xa_newSession:hover{background:var(--dsw-alias-button-floating-hover)}.hHd-Xa_collapsed .hHd-Xa_newSession{background:0 0;border-color:#0000;gap:0;height:36px;margin:0 0 12px;padding:0}.hHd-Xa_collapsed .hHd-Xa_newSession:hover{background:var(--dsw-alias-interactive-bg-hover)}.hHd-Xa_newSessionLabel{white-space:nowrap;max-width:200px;overflow:hidden}.hHd-Xa_collapsed .hHd-Xa_newSessionLabel{max-width:0}.hHd-Xa_regionArea{min-height:0;margin-right:calc(-1 * var(--dsh-sidebar-inline-padding));flex-direction:column;flex:1;display:flex;overflow:hidden}.hHd-Xa_collapsed .hHd-Xa_regionArea{margin-right:0}.hHd-Xa_footArea{flex:none}@media (prefers-reduced-motion:reduce){.hHd-Xa_wide,.hHd-Xa_fading>*,.hHd-Xa_railIn .hHd-Xa_iconButton,.hHd-Xa_railIn .hHd-Xa_newSession,.hHd-Xa_railIn .hHd-Xa_footArea{transition:none;animation:none}}";
27
+ const tagId = "@deepseek-ai/dsh-client-ui-sidebar/SidebarRoot.module.css";
28
+ if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
29
+ const tag = document.createElement("style");
30
+ tag.dataset.plugin = "@deepseek-ai/dsh-client-ui-sidebar";
31
+ tag.dataset.pluginCss = tagId;
32
+ tag.textContent = css;
33
+ document.head.appendChild(tag);
34
+ }
35
+ var SidebarRoot_module_css_default = {
36
+ "collapsed": "hHd-Xa_collapsed",
37
+ "newSession": "hHd-Xa_newSession",
38
+ "toggle": "hHd-Xa_toggle",
39
+ "quietBars": "hHd-Xa_quietBars",
40
+ "iconButton": "hHd-Xa_iconButton",
41
+ "footArea": "hHd-Xa_footArea",
42
+ "brand": "hHd-Xa_brand",
43
+ "root": "hHd-Xa_root",
44
+ "wide-in": "hHd-Xa_wide-in",
45
+ "rail-in": "hHd-Xa_rail-in",
46
+ "railFish": "hHd-Xa_railFish",
47
+ "newSessionLabel": "hHd-Xa_newSessionLabel",
48
+ "railIn": "hHd-Xa_railIn",
49
+ "regionArea": "hHd-Xa_regionArea",
50
+ "panelIcon": "hHd-Xa_panelIcon",
51
+ "fading": "hHd-Xa_fading",
52
+ "logoRow": "hHd-Xa_logoRow",
53
+ "wide": "hHd-Xa_wide"
54
+ };
55
+ //#endregion
56
+ //#region lib/types/client/SidebarRoot.js
57
+ /**
58
+ * Sidebar shell: column geometry only. Collapse is a slide plus crossfade:
59
+ * content freezes at its expanded width (inline style) and fades out in place
60
+ * while the sliding column (AppFrame grid tracks) clips it — nothing reflows
61
+ * mid-slide. At settle the wide-only content unmounts and the control rows
62
+ * snap to the 56px rail (one icon each, same top-down order) fading in as the
63
+ * slide ends. The workspace/session browsing region between the New Session
64
+ * button and the foot is the `sidebar.workspaces` registrant's, and the foot
65
+ * is the `sidebar.settings` registrant's; the shell hands them the wide flag
66
+ * (plus an expand request callback for the browser).
67
+ *
68
+ * The column also owns whether the scroll regions nested in it draw a
69
+ * scrollbar at all: the shell tracks the pointer and rebinds ui-theme's
70
+ * scrollbar indirection away while it is elsewhere, so a list the user is not
71
+ * pointing at carries no bar.
72
+ */
73
+ /** Wide-content unmount delay; matches the 150ms wide-content fade-out. */
74
+ const COLLAPSE_SETTLE_MS = 150;
75
+ /**
76
+ * How long the column's scrollbars stay drawn after the pointer leaves it.
77
+ * The bar is a pointer affordance here, and hiding it on the leave event
78
+ * itself makes it blink out while the pointer is only crossing the column's
79
+ * edge — on the way to the conversation, or around a portalled menu.
80
+ */
81
+ const SCROLLBAR_LINGER_MS = 2e3;
82
+ /**
83
+ * Render the sidebar column shell.
84
+ * @param props - composed slot props (runtime share + injected callbacks, contract/slots.ts).
85
+ * @returns the sidebar element tree.
86
+ */
87
+ function SidebarRoot({ collapsed, width, startSession, toggleSidebar, t, renderSlot }) {
88
+ const [settled, setSettled] = (0, react.useState)(collapsed);
89
+ (0, react.useEffect)(() => {
90
+ if (!collapsed) {
91
+ setSettled(false);
92
+ return;
93
+ }
94
+ const timer = window.setTimeout(() => {
95
+ setSettled(true);
96
+ }, COLLAPSE_SETTLE_MS);
97
+ return () => {
98
+ window.clearTimeout(timer);
99
+ };
100
+ }, [collapsed]);
101
+ const wide = !collapsed || !settled;
102
+ const lastWideWidth = (0, react.useRef)(width);
103
+ if (!collapsed) lastWideWidth.current = width;
104
+ const everWide = (0, react.useRef)(!collapsed);
105
+ if (!collapsed) everWide.current = true;
106
+ const column = (0, react.useRef)(null);
107
+ const [pointerInside, setPointerInside] = (0, react.useState)(false);
108
+ const lingerTimer = (0, react.useRef)(void 0);
109
+ const armLinger = () => {
110
+ if (lingerTimer.current !== void 0) return;
111
+ lingerTimer.current = window.setTimeout(() => {
112
+ lingerTimer.current = void 0;
113
+ setPointerInside(false);
114
+ }, SCROLLBAR_LINGER_MS);
115
+ };
116
+ const cancelLinger = () => {
117
+ window.clearTimeout(lingerTimer.current);
118
+ lingerTimer.current = void 0;
119
+ };
120
+ (0, react.useEffect)(() => {
121
+ if (!pointerInside) return;
122
+ const onMove = (event) => {
123
+ const rect = column.current?.getBoundingClientRect();
124
+ /* v8 ignore next -- the listener only exists while the column is mounted and revealed. */
125
+ if (rect === void 0) return;
126
+ if (event.clientX >= rect.left && event.clientX < rect.right && event.clientY >= rect.top && event.clientY < rect.bottom) cancelLinger();
127
+ else armLinger();
128
+ };
129
+ document.addEventListener("pointermove", onMove);
130
+ return () => {
131
+ document.removeEventListener("pointermove", onMove);
132
+ cancelLinger();
133
+ };
134
+ }, [pointerInside]);
135
+ return (0, react_jsx_runtime.jsxs)("div", {
136
+ ref: column,
137
+ className: clsx(SidebarRoot_module_css_default.root, !wide && SidebarRoot_module_css_default.collapsed, !wide && everWide.current && SidebarRoot_module_css_default.railIn, collapsed && wide && SidebarRoot_module_css_default.fading, !pointerInside && SidebarRoot_module_css_default.quietBars),
138
+ style: wide ? { width: collapsed ? lastWideWidth.current : width } : void 0,
139
+ onPointerEnter: () => {
140
+ cancelLinger();
141
+ setPointerInside(true);
142
+ },
143
+ onPointerLeave: () => {
144
+ armLinger();
145
+ },
146
+ children: [
147
+ (0, react_jsx_runtime.jsxs)("div", {
148
+ className: SidebarRoot_module_css_default.logoRow,
149
+ children: [wide && (0, react_jsx_runtime.jsx)("button", {
150
+ type: "button",
151
+ className: clsx(SidebarRoot_module_css_default.brand, SidebarRoot_module_css_default.wide),
152
+ "aria-label": t("session.new.label"),
153
+ onClick: () => {
154
+ startSession();
155
+ },
156
+ children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.BrandWordmark, {})
157
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
158
+ label: collapsed ? t("toggle.open") : t("toggle.collapse"),
159
+ delayMs: 500,
160
+ children: (0, react_jsx_runtime.jsxs)("button", {
161
+ type: "button",
162
+ className: clsx(SidebarRoot_module_css_default.iconButton, SidebarRoot_module_css_default.toggle),
163
+ "aria-label": collapsed ? t("toggle.open") : t("toggle.collapse"),
164
+ onClick: () => {
165
+ toggleSidebar();
166
+ },
167
+ children: [!wide && (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.FishLogo, {
168
+ className: SidebarRoot_module_css_default.railFish,
169
+ size: 24
170
+ }), (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconPanelLeftOutline16, {
171
+ className: SidebarRoot_module_css_default.panelIcon,
172
+ size: wide ? 16 : 18
173
+ })]
174
+ })
175
+ })]
176
+ }),
177
+ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
178
+ label: t("session.new.label"),
179
+ delayMs: 500,
180
+ disabled: wide,
181
+ children: (0, react_jsx_runtime.jsxs)("button", {
182
+ type: "button",
183
+ className: SidebarRoot_module_css_default.newSession,
184
+ "aria-label": t("session.new.label"),
185
+ onClick: () => {
186
+ startSession();
187
+ },
188
+ children: [(0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconNewChatOutline16, { size: wide ? 14 : 18 }), wide && (0, react_jsx_runtime.jsx)("span", {
189
+ className: clsx(SidebarRoot_module_css_default.newSessionLabel, SidebarRoot_module_css_default.wide),
190
+ children: t("session.new")
191
+ })]
192
+ })
193
+ }),
194
+ (0, react_jsx_runtime.jsx)("div", {
195
+ className: SidebarRoot_module_css_default.regionArea,
196
+ children: renderSlot("sidebar.workspaces", {
197
+ wide,
198
+ expandSidebar: () => {
199
+ if (collapsed) toggleSidebar();
200
+ }
201
+ })
202
+ }),
203
+ (0, react_jsx_runtime.jsx)("div", {
204
+ className: SidebarRoot_module_css_default.footArea,
205
+ children: renderSlot("sidebar.settings", { wide })
206
+ })
207
+ ]
208
+ });
209
+ }
210
+ //#endregion
211
+ //#region lib/types/client/locales.js
212
+ /** `sidebar` namespace dictionaries: shell controls (brand row, New Session, fold toggle). */
213
+ /** Simplified Chinese dictionary (the key-set source of truth). */
214
+ const zh = {
215
+ "session.new": "新会话",
216
+ "session.new.label": "新建会话",
217
+ "toggle.open": "打开侧边栏",
218
+ "toggle.collapse": "收起侧边栏"
219
+ };
220
+ /** English dictionary, checked complete against the zh key set. */
221
+ const en = {
222
+ "session.new": "New Session",
223
+ "session.new.label": "New session",
224
+ "toggle.open": "Open sidebar",
225
+ "toggle.collapse": "Collapse sidebar"
226
+ };
227
+ //#endregion
228
+ //#region lib/types/client/index.js
229
+ /** Dictionary namespace owned by this plugin (shell controls copy). */
230
+ const NS = "sidebar";
231
+ /** Services required by the sidebar plugin. */
232
+ const inject = [
233
+ "slots",
234
+ "layout",
235
+ "sessions",
236
+ "workspaces",
237
+ "locale"
238
+ ];
239
+ /** Registers the sidebar shell and its service callbacks.
240
+ * @param ctx - Client root context.
241
+ */
242
+ function apply(ctx) {
243
+ ctx.effect(() => ctx.locale.register(NS, {
244
+ zh,
245
+ en
246
+ }), "ui-sidebar: dictionaries");
247
+ const injectProps = () => ({
248
+ startSession: (workspaceId) => {
249
+ ctx.workspaces.startSession(workspaceId);
250
+ },
251
+ toggleSidebar: () => {
252
+ ctx.layout.toggleSidebar();
253
+ }
254
+ });
255
+ ctx.effect(() => ctx.slots.register({
256
+ name: "sidebar",
257
+ locale: NS,
258
+ children: {
259
+ "sidebar.workspaces": {
260
+ kind: "single",
261
+ scope: "root"
262
+ },
263
+ "sidebar.settings": {
264
+ kind: "single",
265
+ scope: "root"
266
+ }
267
+ },
268
+ inject: injectProps
269
+ }, SidebarRoot), "ui-sidebar: slot registration");
270
+ }
271
+ //#endregion
272
+ exports.apply = apply;
273
+ exports.inject = inject;
274
+ return module.exports;
275
+ }
276
+ });
277
+
278
+ //# sourceMappingURL=client.js.map
package/lib/index.js ADDED
@@ -0,0 +1,6 @@
1
+ //#region lib/types/index.js
2
+ /** Host loader entry for the browser-only sidebar plugin. */
3
+ /** Provides no host-side behavior. */
4
+ function apply() {}
5
+ //#endregion
6
+ export { apply };
@@ -0,0 +1,25 @@
1
+ //#region lib/types/invariant.js
2
+ /**
3
+ * Package-owned invariant companion for `@deepseek-ai/dsh-client-ui-sidebar`.
4
+ * @module @deepseek-ai/dsh-client-ui-sidebar/invariant
5
+ */
6
+ const PACKAGE_NAME = "@deepseek-ai/dsh-client-ui-sidebar";
7
+ /** Cordis companion plugin name. */
8
+ const name = "client-ui-sidebar-invariant";
9
+ /** Service required before the companion can reserve package ownership. */
10
+ const inject = ["invariants"];
11
+ /**
12
+ * No runtime invariant: a pure-consumer plugin deriving its rows in-component
13
+ * from the standard useSessions delivery — it emits no cordis events and owns
14
+ * no cross-plugin mutable state; derivation and interaction behavior are
15
+ * asserted directly by this package's tree/component specs.
16
+ */
17
+ const install = () => {};
18
+ /**
19
+ * Register this package's invariant companion.
20
+ * @param ctx - Cordis context carrying the invariant service.
21
+ * @returns the installed registration's disposer after setup succeeds.
22
+ */
23
+ const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
24
+ //#endregion
25
+ export { apply, inject, name };
@@ -0,0 +1,8 @@
1
+ import type { SidebarRootComponentProps } from './contract/slots.ts';
2
+ /**
3
+ * Render the sidebar column shell.
4
+ * @param props - composed slot props (runtime share + injected callbacks, contract/slots.ts).
5
+ * @returns the sidebar element tree.
6
+ */
7
+ export declare function SidebarRoot({ collapsed, width, startSession, toggleSidebar, t, renderSlot, }: SidebarRootComponentProps): import("react").JSX.Element;
8
+ //# sourceMappingURL=SidebarRoot.d.ts.map
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Sidebar slot contract: the registrant-side props composition for the
3
+ * layout-owned `sidebar` slot, plus the holes this shell declares. The shell
4
+ * owns column geometry (fold state machine, brand row, New Session);
5
+ * everything between the section header and the list bottom is the
6
+ * `sidebar.workspaces` registrant's (ui-workspace), and the foot is the
7
+ * `sidebar.settings` registrant's (ui-settings).
8
+ */
9
+ import type { PropsLocale, PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
10
+ import type { WorkspaceId } from '@deepseek-ai/dsh-client-runtime/client';
11
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
12
+ interface SlotMap {
13
+ /**
14
+ * The workspace/session browsing region: section header, search, the
15
+ * grouped/flat session list, and every workspace dialog. Declared by this
16
+ * package's 'sidebar' entry (declaring is claiming); ui-workspace
17
+ * registers the browser.
18
+ */
19
+ 'sidebar.workspaces': {
20
+ kind: 'single';
21
+ scope: 'root';
22
+ owner: SidebarSectionOwnerProps;
23
+ };
24
+ /**
25
+ * The settings seat at the sidebar foot. Declared by this package's
26
+ * 'sidebar' entry; ui-settings registers its trigger row + modal panel.
27
+ * The sidebar passes only its column state — it holds no settings state.
28
+ */
29
+ 'sidebar.settings': {
30
+ kind: 'single';
31
+ scope: 'root';
32
+ owner: SidebarSettingsOwnerProps;
33
+ };
34
+ }
35
+ }
36
+ /**
37
+ * Owner share of the browser hole — the only facts crossing the shell/region
38
+ * boundary. Business data and actions arrive through the region's own inject.
39
+ */
40
+ export interface SidebarSectionOwnerProps {
41
+ /** Shell fold-state output: wide renders the full browser, rail the icon column. */
42
+ wide: boolean;
43
+ /** Rail icons request expansion; the browser rides the wide flip for focus. */
44
+ expandSidebar: () => void;
45
+ }
46
+ /**
47
+ * Owner share of the sidebar settings seat: the column display state the
48
+ * occupant's trigger row must render against (wide row vs rail icon).
49
+ */
50
+ export interface SidebarSettingsOwnerProps {
51
+ /** Whether the sidebar renders wide content (false = 56px rail). */
52
+ wide: boolean;
53
+ }
54
+ /**
55
+ * Registrant-private injected share (arrives via the register inject
56
+ * factory). The shell keeps only its own controls: starting a Session from
57
+ * the New Session button and toggling the column.
58
+ */
59
+ export type SidebarRootInjected = {
60
+ /**
61
+ * Start a New Session: with a workspace, reuse-or-create its blank session
62
+ * and open it; without one, clear the selection into the New Session pure
63
+ * view state (the conversation.empty seat).
64
+ */
65
+ startSession: (workspaceId?: WorkspaceId) => void;
66
+ /** Toggle the sidebar column through the layout service. */
67
+ toggleSidebar: () => void;
68
+ };
69
+ /**
70
+ * Full component props: layout owner state/actions plus the declared holes'
71
+ * render shares, this package's injected callbacks, and the standard locale
72
+ * seat. No store is registered.
73
+ */
74
+ export type SidebarRootComponentProps = PropsRuntime<'sidebar'> & PropsRenderSlots<'sidebar.workspaces' | 'sidebar.settings'> & SidebarRootInjected & PropsLocale<'sidebar'>;
75
+ //# sourceMappingURL=slots.d.ts.map
@@ -0,0 +1,18 @@
1
+ /** Registers the sidebar shell into the layout-owned slot. */
2
+ import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
3
+ import { type SidebarKey } from './locales.ts';
4
+ export type { SidebarRootComponentProps, SidebarRootInjected, SidebarSectionOwnerProps, SidebarSettingsOwnerProps } from './contract/slots.ts';
5
+ export type { SidebarKey } from './locales.ts';
6
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
7
+ interface LocaleNamespaceMap {
8
+ /** Sidebar shell controls copy. */
9
+ sidebar: SidebarKey;
10
+ }
11
+ }
12
+ /** Services required by the sidebar plugin. */
13
+ export declare const inject: string[];
14
+ /** Registers the sidebar shell and its service callbacks.
15
+ * @param ctx - Client root context.
16
+ */
17
+ export declare function apply(ctx: ClientContext): void;
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,18 @@
1
+ /** `sidebar` namespace dictionaries: shell controls (brand row, New Session, fold toggle). */
2
+ /** Simplified Chinese dictionary (the key-set source of truth). */
3
+ export declare const zh: {
4
+ 'session.new': string;
5
+ 'session.new.label': string;
6
+ 'toggle.open': string;
7
+ 'toggle.collapse': string;
8
+ };
9
+ /** The sidebar namespace key union. */
10
+ export type SidebarKey = keyof typeof zh;
11
+ /** English dictionary, checked complete against the zh key set. */
12
+ export declare const en: {
13
+ 'session.new': string;
14
+ 'session.new.label': string;
15
+ 'toggle.open': string;
16
+ 'toggle.collapse': string;
17
+ };
18
+ //# sourceMappingURL=locales.d.ts.map
@@ -0,0 +1,4 @@
1
+ /** Host loader entry for the browser-only sidebar plugin. */
2
+ /** Provides no host-side behavior. */
3
+ export declare function apply(): void;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Package-owned invariant companion for `@deepseek-ai/dsh-client-ui-sidebar`.
3
+ * @module @deepseek-ai/dsh-client-ui-sidebar/invariant
4
+ */
5
+ import type { Context } from '@deepseek-ai/cordis';
6
+ /** Cordis companion plugin name. */
7
+ export declare const name = "client-ui-sidebar-invariant";
8
+ /** Service required before the companion can reserve package ownership. */
9
+ export declare const inject: string[];
10
+ /**
11
+ * Register this package's invariant companion.
12
+ * @param ctx - Cordis context carrying the invariant service.
13
+ * @returns the installed registration's disposer after setup succeeds.
14
+ */
15
+ export declare const apply: (ctx: Context) => Promise<() => void>;
16
+ //# sourceMappingURL=invariant.d.ts.map
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@deepseek-ai/dsh-client-ui-sidebar",
3
+ "description": "Sidebar plugin: session multi-level tree, search, grouping, state dots",
4
+ "version": "0.0.1-rc.1",
5
+ "publishConfig": {
6
+ "access": "restricted"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
11
+ "directory": "packages/client/ui-sidebar"
12
+ },
13
+ "type": "module",
14
+ "main": "lib/index.js",
15
+ "types": "lib/types/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./lib/types/index.d.ts",
19
+ "default": "./lib/index.js"
20
+ },
21
+ "./invariant": {
22
+ "types": "./lib/types/invariant.d.ts",
23
+ "default": "./lib/invariant.js"
24
+ },
25
+ "./client": {
26
+ "types": "./lib/types/client/index.d.ts",
27
+ "default": "./lib/client.js"
28
+ },
29
+ "./src/*": "./src/*",
30
+ "./package.json": "./package.json"
31
+ },
32
+ "dsh": {
33
+ "client": {
34
+ "inject": [
35
+ "@deepseek-ai/dsh-client-runtime",
36
+ "@deepseek-ai/dsh-client-ui-layout",
37
+ "@deepseek-ai/dsh-client-locale"
38
+ ],
39
+ "platform": "web"
40
+ }
41
+ },
42
+ "license": "BSD-3-Clause",
43
+ "dependencies": {
44
+ "clsx": "^2.0.0"
45
+ },
46
+ "peerDependencies": {
47
+ "react": "^18.2.0",
48
+ "@deepseek-ai/dsh-client-locale": "^0.0.1-rc.1",
49
+ "@deepseek-ai/dsh-client-runtime": "^0.0.1-rc.1",
50
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.0.1-rc.1",
51
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
52
+ "@deepseek-ai/cordis": "^4.0.1-rc.1",
53
+ "@deepseek-ai/dsh-client-ui-slots": "^0.0.1-rc.1"
54
+ },
55
+ "devDependencies": {
56
+ "@types/react": "~18.3.1",
57
+ "react": "^18.2.0",
58
+ "@deepseek-ai/dsh-client-locale": "^0.0.1-rc.1",
59
+ "@deepseek-ai/dsh-client-runtime": "^0.0.1-rc.1",
60
+ "@deepseek-ai/dsh-client-test-runtime": "^0.0.1-rc.1",
61
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.0.1-rc.1",
62
+ "@deepseek-ai/dsh-client-ui-layout": "^0.0.1-rc.1",
63
+ "@deepseek-ai/dsh-client-ui-slots": "^0.0.1-rc.1",
64
+ "@deepseek-ai/cordis": "^4.0.1-rc.1",
65
+ "@deepseek-ai/dsh-invariants": "^0.0.1-rc.1"
66
+ },
67
+ "files": [
68
+ "lib/index.js",
69
+ "lib/invariant.js",
70
+ "lib/client.js",
71
+ "lib/types/**/*.d.ts"
72
+ ],
73
+ "scripts": {
74
+ "bundle": "tsdown",
75
+ "watch": "tsdown --watch"
76
+ }
77
+ }