@deepseek-ai/dsh-client-ui-sidebar 0.1.3-alpha.2 → 0.1.5-alpha.2

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.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/client/ui-sidebar/README.md
5
- README.md: 1d7e66f792432ebf3c2093facab65cb2017ed10f
6
- README.zh.md: ca781bf3f8db578f0c238f3534871af147e29ec2
5
+ README.md: 1b520213809ca98a6819483a4ee5ea4f451841d2
6
+ README.zh.md: 8caae9f14eb4b7a70e674b9cef7245aaf077b378
package/README.md CHANGED
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- `dsh-client-ui-sidebar` is the sidebar shell of the dsh web client: users see the brand row, start new sessions, collapse into the layout-owned 56px rail, and reach Settings from the bottom-pinned seat, while the scroll-aware region seat hosts the Workspace and Session browser. The Workspace and Session browser rendered into `sidebar.workspaces` belongs to ui-workspace; this package neither derives its rows nor owns its view preferences. A deployment package can replace the brand mark or name without replacing the New Session control or the rail geometry, and New Session starts the runtime's page-local frontend Session Intent against the explicit, current, or most recently active Workspace. Collapse into the layout-owned 56px rail remains presentation-local.
12
+ The dsh web client sidebar lets users recognize the active build, start a new session, collapse navigation to a 56px rail, browse Workspaces and Sessions, and open Settings. It preserves a bottom-pinned Settings entry and hides idle scrollbars without moving browser rows. New Session uses an explicitly selected Workspace, then the current Session's Workspace, then the most recently active Workspace; if none exists, it opens a blank New Session page. Deployments can replace the brand mark or name while retaining the navigation controls and rail geometry.
13
13
 
14
14
  ## Table of Contents
15
15
 
@@ -31,6 +31,10 @@ The sidebar is the navigation shell: users see the brand, start new sessions, co
31
31
 
32
32
  The expanded brand row renders `sidebar.brand.mark` and `sidebar.brand.name` as independent single slots; the collapsed rail renders the same mark slot. Without occupants, the shell uses the fish mark and a localized local-build label. A complete build stacks a code badge below the label as `version[-commit][-dirty]`, using `DSH_CLIENT_VERSION`, the optional 7-character `DSH_CLIENT_COMMIT_HASH`, and `DSH_CLIENT_GIT_DIRTY=true`; missing version metadata omits the badge. New Session targets the explicit Workspace used by a scoped action, otherwise the current Session's Workspace, otherwise the most recently active Workspace; when none exists it clears into the blank New Session page.
33
33
 
34
+ ### Global panel entries
35
+
36
+ Plugins add an icon component to the root-scoped `sidebar.panellist` list with an `id`, optional `order`, and a string or locale-aware `label`. The same id addresses the component registered in the layout's root-scoped `main` keyed slot; selecting a missing main entry throws without changing the current selection. The label supplies plain visible text, the accessible name, and the collapsed tooltip. Each row reads its own selected state through `usePanelInfo`; moving DOM focus to search or a directory picker does not change the displayed panel or its selected row. With no registrations, neither the list nor spacing for it is rendered. The shipped composition registers no example panel.
37
+
34
38
  ### Collapse behavior
35
39
 
36
40
  During a live collapse, the expanded content fades out at its current width, the upper controls share one fade and leftward translation into the 56px rail, and the layout's column slide ends the motion. A page that starts collapsed renders the rail statically, and reduced-motion mode disables both transitions. The bottom-pinned `sidebar.settings` control shares the fade timing but has no horizontal translation.
@@ -47,7 +51,7 @@ Scrollbars in the column are a pointer affordance: the shell rebinds the scrollb
47
51
  <details>
48
52
  <summary>Implementation internals — click to expand</summary>
49
53
 
50
- The shell is pure composition: `SidebarRootComponentProps` composes the layout owner share, the global `useSessions` and `useWorkspaces` hooks, the declared brand, the `sidebar.workspaces` and `sidebar.settings` child slots, and injected `startSession` plus sidebar-toggle callbacks. There is no plugin store.
54
+ The shell is pure composition: `SidebarRootComponentProps` composes the layout owner share, the global `useSessions` and `useWorkspaces` hooks, the declared brand, the `sidebar.workspaces` and `sidebar.settings` child slots, and injected navigation callbacks. Panel entries and their optional titles use the same composition path. Panel metadata is derived from list registrations and locale changes; selection belongs to the layout store.
51
55
 
52
56
  ### Slot discipline
53
57
 
@@ -100,4 +104,4 @@ None.
100
104
 
101
105
  </details>
102
106
 
103
- **Runtime invariant:** No companion is published. A pure-consumer plugin deriving its rows in-component from the standard useSessions delivery it emits no cordis events and owns no cross-plugin mutable state; derivation and interaction behavior are asserted directly by this package's tree/component specs.
107
+ **Runtime invariant:** No companion is published. Panel metadata is a read-only presentation projection of the Slot registry and locale, with no independent write API. The registry owns entry identity and disposal; this package's assembly tests assert the projection after registration and locale notifications settle. The shell owns no separate navigation state to reconcile with those sources.
package/README.zh.md CHANGED
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- `dsh-client-ui-sidebar` 是 dsh Web 客户端的侧边栏外壳:用户看到品牌行、启动新会话、折叠进布局拥有的 56px 轨道,并从底部固定的席位进入 Settings;可感知滚动的区域席位承载 Workspace 与 Session 浏览器。渲染到 `sidebar.workspaces` Workspace 与 Session 浏览器归 ui-workspace 所有;本包既不派生其中的行,也不持有其视图偏好。部署包可以单独替换品牌标记或名称,而无须替换 New Session 控件或轨道几何;New Session 会针对显式指定、当前或最近活跃的 Workspace 启动运行时的页面局部前端 Session Intent。折叠到布局拥有的 56px 轨道仍属于本地呈现行为。
12
+ dsh Web 客户端的侧边栏让用户识别当前构建、启动新会话、将导航折叠为 56px 轨道、浏览 Workspace 与 Session,以及打开 Settings。它会将 Settings 入口固定在底部,并在隐藏空闲滚动条时避免浏览器行发生位移。New Session 优先使用显式选择的 Workspace,其次使用当前 Session 所属的 Workspace,再其次使用最近活跃的 Workspace;如果都不存在,则打开空白的 New Session 页面。部署可以替换品牌标记或名称,同时保留导航控件和轨道几何。
13
13
 
14
14
  ## 目录
15
15
 
@@ -31,6 +31,10 @@ kind: "package-reference"
31
31
 
32
32
  展开的品牌行把 `sidebar.brand.mark` 与 `sidebar.brand.name` 渲染为两个独立的 single slot;收起轨道则渲染同一个 mark slot。没有占位者时,外壳使用鱼形标记和本地化的本地构建标签。完整构建会在标签下方显示代码徽标;该徽标使用 `DSH_CLIENT_VERSION`、可选的 7 位 `DSH_CLIENT_COMMIT_HASH` 与 `DSH_CLIENT_GIT_DIRTY=true` 组装成 `version[-commit][-dirty]`;缺少版本元数据时不显示徽标。New Session 优先使用作用域操作明确指定的 Workspace,否则使用当前 Session 所属 Workspace,再否则使用最近活跃 Workspace;一个 Workspace 都没有时则清空选择,进入空白 New Session 页面。
33
33
 
34
+ ### 全局面板入口
35
+
36
+ 插件在 root 作用域的 `sidebar.panellist` list 中注册图标组件,提供 `id`、可选 `order`,以及字符串或随语言变化的 `label`。同一个 id 寻址布局中 root 作用域 `main` keyed slot 的组件;选择不存在的主面板条目会抛错,并保留当前选中态。标签提供普通可见文字、无障碍名称和折叠提示。每一行通过 `usePanelInfo` 读取自己的选中态;DOM 焦点移到搜索框或目录选择器时,显示的面板及其列表项选中态不变。没有注册项时,列表及其间距均不渲染。默认组合不注册示例面板。
37
+
34
38
  ### 折叠行为
35
39
 
36
40
  实时收起时,展开内容在当前宽度淡出,上方控件共用一次淡入并左移进入 56px 轨道,由布局的栏滑动结束整段动画。页面初始即为收起状态时会静态渲染轨道;减少动态效果模式会禁用两段过渡。固定在底部的 `sidebar.settings` 控件只共用淡入时序,不发生横向位移。
@@ -47,7 +51,7 @@ kind: "package-reference"
47
51
  <details>
48
52
  <summary>实现细节——点击展开</summary>
49
53
 
50
- 外壳是纯组合:`SidebarRootComponentProps` 组合布局 owner share、全局 `useSessions` 与 `useWorkspaces` 钩子、已声明的品牌、`sidebar.workspaces` 与 `sidebar.settings` 子 slot,以及注入的 `startSession` 与侧边栏切换回调。这里没有插件 store。
54
+ 外壳是纯组合:`SidebarRootComponentProps` 组合布局 owner share、全局 `useSessions` 与 `useWorkspaces` 钩子、已声明的品牌、`sidebar.workspaces` 与 `sidebar.settings` 子 slot,以及注入的导航回调。面板入口及其可选标题使用相同的组合方式。面板元数据由列表注册和语言变化派生;选中态属于布局存储。
51
55
 
52
56
  ### Slot 纪律
53
57
 
@@ -100,4 +104,4 @@ kind: "package-reference"
100
104
 
101
105
  </details>
102
106
 
103
- **运行时不变式:** 不发布伴生入口。这是从标准 `useSessions` delivery 直接派生行的纯消费插件,不发出 Cordis 事件,也不持有跨插件可变状态。
107
+ **运行时不变式:** 不发布伴生入口。面板元数据是 Slot 注册表与 locale 的只读呈现投影,没有独立写入 API。注册表拥有条目身份和释放;本包的装配测试在注册和语言通知完成后断言投影。外壳没有需要与这些来源协调的独立导航状态。
package/lib/client.js CHANGED
@@ -4,6 +4,8 @@ window.__ModuleLoader__.load({
4
4
  var module = { exports: {} };
5
5
  var exports = module.exports;
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let _deepseek_ai_dsh_client_store = require("@deepseek-ai/dsh-client-store");
8
+ let _deepseek_ai_dsh_client_ui_slots = require("@deepseek-ai/dsh-client-ui-slots");
7
9
  let react_jsx_runtime = require("react/jsx-runtime");
8
10
  let react = require("react");
9
11
  let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
@@ -23,7 +25,7 @@ window.__ModuleLoader__.load({
23
25
  }
24
26
  //#endregion
25
27
  //#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_regionArea{animation:hHd-Xa_rail-in .15s var(--ds-ease-in-out) backwards}.hHd-Xa_railIn .hHd-Xa_footArea{animation:hHd-Xa_rail-fade-in .15s var(--ds-ease-in-out) backwards}@keyframes hHd-Xa_rail-in{0%{opacity:0;transform:translate(49px)}}@keyframes hHd-Xa_rail-fade-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:8px;padding:8px 0 8px 4px;display:flex;overflow:hidden}.hHd-Xa_collapsed .hHd-Xa_logoRow{justify-content:flex-start;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_brandIdentity{align-items:center;gap:8px;min-width:0;height:24px;display:inline-flex}.hHd-Xa_brandMark{flex:none;justify-content:center;align-items:center;display:inline-flex}.hHd-Xa_brandName{letter-spacing:.04em;align-items:center;gap:6px;min-width:0;height:24px;font-size:18px;font-weight:600;line-height:24px;display:inline-flex}.hHd-Xa_fallbackBrandName{letter-spacing:0;white-space:nowrap;font-size:17px}.hHd-Xa_localBuildBrand{white-space:nowrap;flex-direction:column;flex:none;justify-content:center;align-items:flex-start;gap:1px;height:24px;display:inline-flex}.hHd-Xa_localBuildTitle{letter-spacing:0;font-size:12px;line-height:13px}.hHd-Xa_iconButton{corner-shape:round;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_railMark{display:none}.hHd-Xa_railMark{justify-content:center;align-items:center;display:inline-flex}.hHd-Xa_collapsed .hHd-Xa_iconButton{color:var(--dsw-alias-label-primary)}.hHd-Xa_buildVersion{height:10px;color:var(--dsw-alias-label-primary-inverted);background:var(--dsw-alias-label-primary);font-family:var(--ds-font-family-code);white-space:nowrap;border-radius:2px;flex:none;align-items:center;padding:0 3px;font-size:6px;font-weight:500;line-height:10px;display:inline-flex}.hHd-Xa_newSession{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);background:var(--dsw-alias-button-elevated-fill);height:38px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:12px;flex:none;justify-content:center;align-items:center;gap:6px;margin:0 2px 8px;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;align-self:flex-start;gap:0;width:36px;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-left:-4px;margin-right:calc(-1 * var(--dsh-sidebar-inline-padding));flex-direction:column;flex:1;padding-left:4px;display:flex;overflow:hidden}.hHd-Xa_collapsed .hHd-Xa_regionArea{margin-left:0;margin-right:0;padding-left:0}.hHd-Xa_footArea{flex-direction:column;flex:none;display:flex}.hHd-Xa_settingsArea,.hHd-Xa_footerActions{flex:none;width:100%;min-width:0}.hHd-Xa_footerActions{display:flex}.hHd-Xa_collapsed .hHd-Xa_footArea{align-items:center}.hHd-Xa_collapsed .hHd-Xa_settingsArea,.hHd-Xa_collapsed .hHd-Xa_footerActions{justify-content:center;width:auto;display:flex}@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,.hHd-Xa_railIn .hHd-Xa_regionArea{transition:none;animation:none}}";
28
+ 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_panelList,.hHd-Xa_railIn .hHd-Xa_regionArea{animation:hHd-Xa_rail-in .15s var(--ds-ease-in-out) backwards}.hHd-Xa_railIn .hHd-Xa_footArea{animation:hHd-Xa_rail-fade-in .15s var(--ds-ease-in-out) backwards}@keyframes hHd-Xa_rail-in{0%{opacity:0;transform:translate(49px)}}@keyframes hHd-Xa_rail-fade-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:8px;padding:8px 0 8px 4px;display:flex;overflow:hidden}.hHd-Xa_collapsed .hHd-Xa_logoRow{justify-content:flex-start;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_brandIdentity{align-items:center;gap:8px;min-width:0;height:24px;display:inline-flex}.hHd-Xa_brandMark{flex:none;justify-content:center;align-items:center;display:inline-flex}.hHd-Xa_brandName{letter-spacing:.04em;align-items:center;gap:6px;min-width:0;height:24px;font-size:18px;font-weight:600;line-height:24px;display:inline-flex}.hHd-Xa_fallbackBrandName{letter-spacing:0;white-space:nowrap;font-size:17px}.hHd-Xa_localBuildBrand{white-space:nowrap;flex-direction:column;flex:none;justify-content:center;align-items:flex-start;gap:1px;height:24px;display:inline-flex}.hHd-Xa_localBuildTitle{letter-spacing:0;font-size:12px;line-height:13px}.hHd-Xa_iconButton{corner-shape:round;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_railMark{display:none}.hHd-Xa_railMark{justify-content:center;align-items:center;display:inline-flex}.hHd-Xa_collapsed .hHd-Xa_iconButton{color:var(--dsw-alias-label-primary)}.hHd-Xa_buildVersion{height:10px;color:var(--dsw-alias-label-primary-inverted);background:var(--dsw-alias-label-primary);font-family:var(--ds-font-family-code);white-space:nowrap;border-radius:2px;flex:none;align-items:center;padding:0 3px;font-size:6px;font-weight:500;line-height:10px;display:inline-flex}.hHd-Xa_newSession{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l3);background:var(--dsw-alias-button-elevated-fill);height:38px;color:var(--dsw-alias-label-primary);cursor:pointer;border-radius:12px;flex:none;justify-content:center;align-items:center;gap:6px;margin:0 2px 8px;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;align-self:flex-start;gap:0;width:36px;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_panelList{flex-direction:column;flex:none;gap:4px;margin-bottom:8px;display:flex}.hHd-Xa_panelRow{box-sizing:border-box;width:100%;min-height:36px;color:var(--dsw-alias-label-secondary);font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;border-radius:8px;align-items:center;gap:8px;padding:7px 8px;line-height:22px;display:flex}.hHd-Xa_panelRow:hover{background:var(--dsw-alias-interactive-bg-hover)}.hHd-Xa_panelRow.hHd-Xa_panelActive{background:var(--dsw-alias-interactive-bg-active);color:var(--dsw-alias-label-primary);font-weight:500}.hHd-Xa_panelRow:focus-visible{outline:2px solid var(--dsw-alias-label-primary);outline-offset:-2px}.hHd-Xa_panelGlyph{flex:none;justify-content:center;align-items:center;display:inline-flex}.hHd-Xa_panelTitle{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.hHd-Xa_collapsed .hHd-Xa_panelList{gap:12px;margin-bottom:12px}.hHd-Xa_collapsed .hHd-Xa_panelRow{width:36px;height:36px;color:var(--dsw-alias-label-primary);justify-content:center;padding:0}.hHd-Xa_regionArea{min-height:0;margin-left:-4px;margin-right:calc(-1 * var(--dsh-sidebar-inline-padding));flex-direction:column;flex:1;padding-left:4px;display:flex;overflow:hidden}.hHd-Xa_collapsed .hHd-Xa_regionArea{margin-left:0;margin-right:0;padding-left:0}.hHd-Xa_footArea{flex-direction:column;flex:none;display:flex}.hHd-Xa_settingsArea,.hHd-Xa_footerActions{flex:none;width:100%;min-width:0}.hHd-Xa_footerActions{display:flex}.hHd-Xa_collapsed .hHd-Xa_footArea{align-items:center}.hHd-Xa_collapsed .hHd-Xa_settingsArea,.hHd-Xa_collapsed .hHd-Xa_footerActions{justify-content:center;width:auto;display:flex}@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_panelList,.hHd-Xa_railIn .hHd-Xa_footArea,.hHd-Xa_railIn .hHd-Xa_regionArea{transition:none;animation:none}}";
27
29
  const tagId = "@deepseek-ai/dsh-client-ui-sidebar/SidebarRoot.module.css";
28
30
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
29
31
  const tag = document.createElement("style");
@@ -49,7 +51,12 @@ window.__ModuleLoader__.load({
49
51
  "logoRow": "hHd-Xa_logoRow",
50
52
  "newSession": "hHd-Xa_newSession",
51
53
  "newSessionLabel": "hHd-Xa_newSessionLabel",
54
+ "panelActive": "hHd-Xa_panelActive",
55
+ "panelGlyph": "hHd-Xa_panelGlyph",
52
56
  "panelIcon": "hHd-Xa_panelIcon",
57
+ "panelList": "hHd-Xa_panelList",
58
+ "panelRow": "hHd-Xa_panelRow",
59
+ "panelTitle": "hHd-Xa_panelTitle",
53
60
  "quietBars": "hHd-Xa_quietBars",
54
61
  "rail-fade-in": "hHd-Xa_rail-fade-in",
55
62
  "rail-in": "hHd-Xa_rail-in",
@@ -65,14 +72,15 @@ window.__ModuleLoader__.load({
65
72
  //#endregion
66
73
  //#region lib/types/client/SidebarRoot.js
67
74
  /**
68
- * Sidebar shell: column geometry only. Collapse is a slide plus crossfade:
75
+ * Sidebar shell: column geometry and global panel navigation.
76
+ * Collapse is a slide plus crossfade:
69
77
  * content freezes at its expanded width (inline style) and fades out in place
70
78
  * while the sliding column (AppFrame grid tracks) clips it — nothing reflows
71
- * mid-slide. At settle the wide-only content unmounts and the four upper
79
+ * mid-slide. At settle the wide-only content unmounts and the upper
72
80
  * controls enter the 56px rail from the same horizontal offset (one icon each,
73
81
  * same top-down order) on one fade that ends with the slide. The bottom-pinned
74
82
  * settings control only fades. The workspace/session browsing region between
75
- * the New Session button and the foot is the `sidebar.workspaces` registrant's,
83
+ * global panel rows and the foot is the `sidebar.workspaces` registrant's,
76
84
  * and the foot holds `sidebar.settings` plus `sidebar.footer.action`; the shell
77
85
  * hands them the wide flag (plus an expand request callback for the browser).
78
86
  *
@@ -92,14 +100,44 @@ window.__ModuleLoader__.load({
92
100
  const SCROLLBAR_LINGER_MS = 2e3;
93
101
  /** Format complete-build metadata for the local brand badge. */
94
102
  function localBuildVersion() {
95
- return `0.1.3-alpha.2-41a2cdd` + ({}.DSH_CLIENT_GIT_DIRTY === "true" ? "-dirty" : "");
103
+ return `0.1.5-alpha.2-9ddbf04` + ({}.DSH_CLIENT_GIT_DIRTY === "true" ? "-dirty" : "");
104
+ }
105
+ /** Each panel row subscribes only to its own selection state. */
106
+ function PanelRow({ id, label, wide, usePanelInfo, selectPanel, renderSlot }) {
107
+ const active = usePanelInfo((info) => info.activePanelId === id);
108
+ return (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
109
+ label,
110
+ delayMs: 500,
111
+ disabled: wide,
112
+ children: (0, react_jsx_runtime.jsxs)("button", {
113
+ type: "button",
114
+ className: clsx(SidebarRoot_module_css_default.panelRow, active && SidebarRoot_module_css_default.panelActive),
115
+ "aria-label": label,
116
+ "aria-current": active ? "page" : void 0,
117
+ onClick: () => {
118
+ selectPanel(id);
119
+ },
120
+ children: [(0, react_jsx_runtime.jsx)("span", {
121
+ className: SidebarRoot_module_css_default.panelGlyph,
122
+ "aria-hidden": "true",
123
+ children: renderSlot("sidebar.panellist", {
124
+ size: wide ? 16 : 18,
125
+ active
126
+ }, { only: id })
127
+ }), wide && (0, react_jsx_runtime.jsx)("span", {
128
+ className: clsx(SidebarRoot_module_css_default.panelTitle, SidebarRoot_module_css_default.wide),
129
+ children: label
130
+ })]
131
+ })
132
+ });
96
133
  }
97
134
  /**
98
135
  * Render the sidebar column shell.
99
136
  * @param props - composed slot props (runtime share + injected callbacks, contract/slots.ts).
100
137
  * @returns the sidebar element tree.
101
138
  */
102
- function SidebarRoot({ collapsed, width, startSession, toggleSidebar, t, renderSlot }) {
139
+ function SidebarRoot({ collapsed, width, startSession, toggleSidebar, selectPanel, usePanels, usePanelInfo, t, renderSlot }) {
140
+ const panels = usePanels((snapshot) => snapshot);
103
141
  const [settled, setSettled] = (0, react.useState)(collapsed);
104
142
  (0, react.useEffect)(() => {
105
143
  if (!collapsed) {
@@ -230,6 +268,18 @@ window.__ModuleLoader__.load({
230
268
  })]
231
269
  })
232
270
  }),
271
+ panels.length > 0 && (0, react_jsx_runtime.jsx)("nav", {
272
+ className: SidebarRoot_module_css_default.panelList,
273
+ "aria-label": t("panels.label"),
274
+ children: panels.map(({ id, label }) => (0, react_jsx_runtime.jsx)(PanelRow, {
275
+ id,
276
+ label,
277
+ wide,
278
+ usePanelInfo,
279
+ selectPanel,
280
+ renderSlot
281
+ }, id))
282
+ }),
233
283
  (0, react_jsx_runtime.jsx)("div", {
234
284
  className: SidebarRoot_module_css_default.regionArea,
235
285
  children: renderSlot("sidebar.workspaces", {
@@ -254,24 +304,26 @@ window.__ModuleLoader__.load({
254
304
  }
255
305
  //#endregion
256
306
  //#region lib/types/client/locales.js
257
- /** `sidebar` namespace dictionaries: shell controls (brand row, New Session, fold toggle). */
307
+ /** `sidebar` namespace dictionaries for shell controls and global panels. */
258
308
  /** Simplified Chinese dictionary (the key-set source of truth). */
259
309
  const zh = {
260
310
  "session.new": "新会话",
261
311
  "session.new.label": "新建会话",
262
312
  "toggle.open": "打开侧边栏",
263
- "toggle.collapse": "收起侧边栏"
313
+ "toggle.collapse": "收起侧边栏",
314
+ "panels.label": "全局面板"
264
315
  };
265
316
  /** English dictionary, checked complete against the zh key set. */
266
317
  const en = {
267
318
  "session.new": "New Session",
268
319
  "session.new.label": "New session",
269
320
  "toggle.open": "Open sidebar",
270
- "toggle.collapse": "Collapse sidebar"
321
+ "toggle.collapse": "Collapse sidebar",
322
+ "panels.label": "Global panels"
271
323
  };
272
324
  //#endregion
273
325
  //#region lib/types/client/index.js
274
- /** Dictionary namespace owned by this plugin (shell controls copy). */
326
+ /** Dictionary namespace owned by this plugin. */
275
327
  const NS = "sidebar";
276
328
  /** Services required by the sidebar plugin. */
277
329
  const inject = [
@@ -289,15 +341,38 @@ window.__ModuleLoader__.load({
289
341
  zh,
290
342
  en
291
343
  }), "ui-sidebar: dictionaries");
344
+ const panels = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)([]);
345
+ const syncPanels = () => {
346
+ const next = ctx.slots.entriesOfSlot("sidebar.panellist").map(({ options }) => {
347
+ const id = options.id;
348
+ return {
349
+ id,
350
+ order: options.order ?? 0,
351
+ label: (0, _deepseek_ai_dsh_client_ui_slots.resolveSlotLabel)(options.label) ?? id
352
+ };
353
+ }).sort((a, b) => a.order - b.order);
354
+ const previous = panels.getSnapshot();
355
+ if (previous.length === next.length && previous.every((panel, index) => {
356
+ const candidate = next[index];
357
+ return panel.id === candidate.id && panel.order === candidate.order && panel.label === candidate.label;
358
+ })) return;
359
+ panels.set(next);
360
+ };
361
+ ctx.effect(() => ctx.slots.subscribe("sidebar.panellist", syncPanels), "ui-sidebar: panel entries");
362
+ ctx.effect(() => ctx.locale.subscribe(syncPanels), "ui-sidebar: panel labels");
292
363
  const injectProps = () => ({
293
364
  startSession: (workspaceId) => {
294
365
  workspaceNavigation.startSession(workspaceId);
295
366
  },
296
367
  toggleSidebar: () => {
297
368
  ctx.layout.toggleSidebar();
298
- }
369
+ },
370
+ selectPanel: (id) => {
371
+ ctx.layout.selectPanel(id);
372
+ },
373
+ hooks: { panels }
299
374
  });
300
- ctx.effect(() => ctx.slots.register({
375
+ ctx.slots.inject("sidebar", () => ctx.slots.register({
301
376
  name: "sidebar",
302
377
  locale: NS,
303
378
  children: {
@@ -309,6 +384,10 @@ window.__ModuleLoader__.load({
309
384
  kind: "single",
310
385
  scope: "root"
311
386
  },
387
+ "sidebar.panellist": {
388
+ kind: "list",
389
+ scope: "root"
390
+ },
312
391
  "sidebar.workspaces": {
313
392
  kind: "single",
314
393
  scope: "root"
@@ -323,7 +402,8 @@ window.__ModuleLoader__.load({
323
402
  }
324
403
  },
325
404
  inject: injectProps
326
- }, SidebarRoot), "ui-sidebar: slot registration");
405
+ }, SidebarRoot));
406
+ syncPanels();
327
407
  }
328
408
  //#endregion
329
409
  exports.apply = apply;
@@ -4,5 +4,5 @@ import type { SidebarRootComponentProps } from './contract/slots.ts';
4
4
  * @param props - composed slot props (runtime share + injected callbacks, contract/slots.ts).
5
5
  * @returns the sidebar element tree.
6
6
  */
7
- export declare function SidebarRoot({ collapsed, width, startSession, toggleSidebar, t, renderSlot, }: SidebarRootComponentProps): import("react").JSX.Element;
7
+ export declare function SidebarRoot({ collapsed, width, startSession, toggleSidebar, selectPanel, usePanels, usePanelInfo, t, renderSlot, }: SidebarRootComponentProps): import("react").JSX.Element;
8
8
  //# sourceMappingURL=SidebarRoot.d.ts.map
@@ -1,14 +1,16 @@
1
1
  /**
2
2
  * Sidebar slot contract: the registrant-side props composition for the
3
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
4
+ * owns column geometry, the brand row, New Session, and global panel rows;
5
+ * everything between the workspace section header and the list bottom is the
6
6
  * `sidebar.workspaces` registrant's (ui-workspace), and the foot is the
7
7
  * `sidebar.settings` registrant's (ui-settings), followed by optional footer
8
8
  * actions in `sidebar.footer.action`.
9
9
  */
10
- import type { PropsLocale, PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
10
+ import type { InjectFace, PropsLocale, PropsRenderSlots, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
11
+ import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
11
12
  import type { WorkspaceId } from '@deepseek-ai/dsh-api-workspace-controller/client';
13
+ import type { MainPanelId } from '@deepseek-ai/dsh-client-ui-layout/client';
12
14
  declare module '@deepseek-ai/dsh-client-ui-slots' {
13
15
  interface SlotMap {
14
16
  /**
@@ -30,6 +32,15 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
30
32
  scope: 'root';
31
33
  owner: SidebarBrandNameOwnerProps;
32
34
  };
35
+ /**
36
+ * Global panel icons. Each list id addresses the matching main panel;
37
+ * the sidebar owns the button and resolves its label from list metadata.
38
+ */
39
+ 'sidebar.panellist': {
40
+ kind: 'list';
41
+ scope: 'root';
42
+ owner: SidebarPanelIconOwnerProps;
43
+ };
33
44
  /**
34
45
  * The workspace/session browsing region: section header, search, the
35
46
  * grouped/flat session list, and every workspace dialog. Declared by this
@@ -72,6 +83,22 @@ export interface SidebarBrandNameOwnerProps {
72
83
  /** Marker field: the occupant owns its own content and width. */
73
84
  children?: never;
74
85
  }
86
+ /** Icon presentation supplied by the global panel row. */
87
+ export interface SidebarPanelIconOwnerProps {
88
+ /** Requested square edge in pixels. */
89
+ size: number;
90
+ /** Whether this panel is selected in the main column. */
91
+ active: boolean;
92
+ }
93
+ /** Serializable metadata for one active global panel list registration. */
94
+ export interface SidebarPanelMetadata {
95
+ /** List id and matching main panel key. */
96
+ id: MainPanelId;
97
+ /** Ascending row order; ties retain registration order. */
98
+ order: number;
99
+ /** Row title and accessible name: resolved label, or the id when omitted. */
100
+ label: string;
101
+ }
75
102
  /**
76
103
  * Owner share of the browser hole — the only facts crossing the shell/region
77
104
  * boundary. Business data and actions arrive through the region's own inject.
@@ -97,8 +124,7 @@ export interface SidebarFooterActionOwnerProps {
97
124
  }
98
125
  /**
99
126
  * Registrant-private injected share (arrives via the register inject
100
- * factory). The shell keeps only its own controls: starting a Session from
101
- * the New Session button and toggling the column.
127
+ * factory). The renderer binds the panel metadata source to usePanels.
102
128
  */
103
129
  export type SidebarRootInjected = {
104
130
  /**
@@ -109,11 +135,17 @@ export type SidebarRootInjected = {
109
135
  startSession: (workspaceId?: WorkspaceId) => void;
110
136
  /** Toggle the sidebar column through the layout service. */
111
137
  toggleSidebar: () => void;
138
+ /** Select the global panel addressed by a sidebar row. */
139
+ selectPanel: (id: MainPanelId) => void;
140
+ /** Private reactive sources bound to framework selector hooks. */
141
+ hooks: {
142
+ panels: ObservableSnapshot<readonly SidebarPanelMetadata[]>;
143
+ };
112
144
  };
113
145
  /**
114
146
  * Full component props: layout owner state/actions plus the declared holes'
115
147
  * render shares, this package's injected callbacks, and the standard locale
116
- * seat. No store is registered.
148
+ * seat. Panel metadata arrives through an injected observable.
117
149
  */
118
- export type SidebarRootComponentProps = PropsRuntime<'sidebar'> & PropsRenderSlots<'sidebar.brand.mark' | 'sidebar.brand.name' | 'sidebar.workspaces' | 'sidebar.settings' | 'sidebar.footer.action'> & SidebarRootInjected & PropsLocale<'sidebar'>;
150
+ export type SidebarRootComponentProps = PropsRuntime<'sidebar'> & PropsRenderSlots<'sidebar.brand.mark' | 'sidebar.brand.name' | 'sidebar.panellist' | 'sidebar.workspaces' | 'sidebar.settings' | 'sidebar.footer.action'> & InjectFace<SidebarRootInjected> & PropsLocale<'sidebar'>;
119
151
  //# sourceMappingURL=slots.d.ts.map
@@ -1,11 +1,11 @@
1
- /** Registers the sidebar shell into the layout-owned slot. */
1
+ /** Registers the sidebar shell and global panel navigation. */
2
2
  import type { Context as ClientContext } from '@deepseek-ai/cordis';
3
3
  import { type SidebarKey } from './locales.ts';
4
- export type { SidebarBrandMarkOwnerProps, SidebarBrandNameOwnerProps, SidebarFooterActionOwnerProps, SidebarRootComponentProps, SidebarRootInjected, SidebarSectionOwnerProps, SidebarSettingsOwnerProps, } from './contract/slots.ts';
4
+ export type { SidebarBrandMarkOwnerProps, SidebarBrandNameOwnerProps, SidebarFooterActionOwnerProps, SidebarPanelIconOwnerProps, SidebarPanelMetadata, SidebarRootComponentProps, SidebarRootInjected, SidebarSectionOwnerProps, SidebarSettingsOwnerProps, } from './contract/slots.ts';
5
5
  export type { SidebarKey } from './locales.ts';
6
6
  declare module '@deepseek-ai/dsh-client-ui-slots' {
7
7
  interface LocaleNamespaceMap {
8
- /** Sidebar shell controls copy. */
8
+ /** Sidebar controls and global panel copy. */
9
9
  sidebar: SidebarKey;
10
10
  }
11
11
  }
@@ -1,10 +1,11 @@
1
- /** `sidebar` namespace dictionaries: shell controls (brand row, New Session, fold toggle). */
1
+ /** `sidebar` namespace dictionaries for shell controls and global panels. */
2
2
  /** Simplified Chinese dictionary (the key-set source of truth). */
3
3
  export declare const zh: {
4
4
  'session.new': string;
5
5
  'session.new.label': string;
6
6
  'toggle.open': string;
7
7
  'toggle.collapse': string;
8
+ 'panels.label': string;
8
9
  };
9
10
  /** The sidebar namespace key union. */
10
11
  export type SidebarKey = keyof typeof zh;
@@ -14,5 +15,6 @@ export declare const en: {
14
15
  'session.new.label': string;
15
16
  'toggle.open': string;
16
17
  'toggle.collapse': string;
18
+ 'panels.label': string;
17
19
  };
18
20
  //# sourceMappingURL=locales.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-sidebar",
3
3
  "description": "Sidebar plugin: session multi-level tree, search, grouping, state dots",
4
- "version": "0.1.3-alpha.2",
4
+ "version": "0.1.5-alpha.2",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -39,25 +39,24 @@
39
39
  }
40
40
  },
41
41
  "license": "MIT",
42
- "dependencies": {
43
- "clsx": "^2.0.0"
44
- },
45
42
  "peerDependencies": {
46
43
  "@deepseek-ai/cordis": "^4.0.2"
47
44
  },
48
45
  "devDependencies": {
49
46
  "@types/react": "~18.3.1",
50
47
  "react": "^18.2.0",
51
- "@deepseek-ai/dsh-api-workspace-controller": "^0.1.3-alpha.2",
52
- "@deepseek-ai/dsh-client-locale": "^0.1.3-alpha.2",
53
- "@deepseek-ai/dsh-client-test-runtime": "^0.1.3-alpha.2",
54
- "@deepseek-ai/dsh-client-ui-layout": "^0.1.3-alpha.2",
55
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.3-alpha.2",
56
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.3-alpha.2",
57
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.3-alpha.2",
58
- "@deepseek-ai/dsh-client-ui-session": "^0.1.3-alpha.2",
59
- "@deepseek-ai/dsh-client-ui-workspace": "^0.1.3-alpha.2",
60
- "@deepseek-ai/cordis": "^4.0.2"
48
+ "clsx": "^2.0.0",
49
+ "@deepseek-ai/dsh-api-workspace-controller": "^0.1.5-alpha.2",
50
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-alpha.2",
51
+ "@deepseek-ai/dsh-client-store": "^0.1.5-alpha.2",
52
+ "@deepseek-ai/dsh-client-test-runtime": "^0.1.5-alpha.2",
53
+ "@deepseek-ai/dsh-client-ui-layout": "^0.1.5-alpha.2",
54
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-alpha.2",
55
+ "@deepseek-ai/dsh-client-ui-session": "^0.1.5-alpha.2",
56
+ "@deepseek-ai/cordis": "^4.0.2",
57
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-alpha.2",
58
+ "@deepseek-ai/dsh-client-ui-workspace": "^0.1.5-alpha.2",
59
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-alpha.2"
61
60
  },
62
61
  "files": [
63
62
  "lib/index.js",