diff --git a/packages/client/ui-entry-customization/README.i18n.yaml b/packages/client/ui-entry-customization/README.i18n.yaml
index 54af01f476..cb090d2ac6 100644
--- a/packages/client/ui-entry-customization/README.i18n.yaml
+++ b/packages/client/ui-entry-customization/README.i18n.yaml
@@ -27,8 +27,8 @@
en: dc50ebf94183efa2
zh: 617803c558367430
/deepseek-ai-dsh-client-ui-entry-customization/known-limitations-and-deferred-work:
- en: 2ef90af5f52401b7
- zh: d5c85d3fa7aeaf15
+ en: fe0eadab5528a411
+ zh: e5c1192378b4d26d
/deepseek-ai-dsh-client-ui-entry-customization/known-limitations-and-deferred-work/dev-note:
en: e67f633dc6fe7773
zh: 3bb0c20c93f16f98
diff --git a/packages/client/ui-entry-customization/README.md b/packages/client/ui-entry-customization/README.md
index 25f6da87b9..3f4225e69f 100644
--- a/packages/client/ui-entry-customization/README.md
+++ b/packages/client/ui-entry-customization/README.md
@@ -65,7 +65,7 @@ None; the package neither assembles nor sends model requests.
-- Only the sidebar More menu provides pin controls. Pin choices remain in memory in the region owner; plugin reload restores defaults.
+- Only the left sidebar provides pin controls: its rows unpin, and its More menu pins. Pin choices remain in memory in the region owner; plugin reload restores defaults.
- Legacy pin values in localStorage are neither read nor written.
- **Create your plugin** does not check whether the Creator preset exists. Without it, Session creation fails, the Workspace shows its creation-failure notice, and no draft is prepared. Draft preparation also requires a resolvable Workspace; without one, the existing flow reports that a Workspace is required.
- Conversation owns the estimated tab split; these menus provide no sorting.
diff --git a/packages/client/ui-entry-customization/README.zh.md b/packages/client/ui-entry-customization/README.zh.md
index 83b2c22abe..7578dcb1a5 100644
--- a/packages/client/ui-entry-customization/README.zh.md
+++ b/packages/client/ui-entry-customization/README.zh.md
@@ -65,7 +65,7 @@ kind: "package-reference"
-- 只有侧栏更多菜单提供 Pin 控件。固定选择仅保存在所属区域的内存中;插件重新加载后恢复默认值。
+- 只有左侧栏提供 Pin 控件:侧栏行取消固定,侧栏更多菜单重新固定。固定选择仅保存在所属区域的内存中;插件重新加载后恢复默认值。
- 不读取或写入 localStorage 中的旧 Pin 值。
- **创建你的插件**不检查 Creator 预设是否存在。预设缺失时 Session 创建失败,Workspace 显示创建失败提示,不准备草稿。草稿准备还要求可确定目标 Workspace;没有目标时,现有流程提示需要 Workspace。
- 会话区域负责按估算值分组;菜单不提供排序。
diff --git a/packages/client/ui-sidebar/README.i18n.yaml b/packages/client/ui-sidebar/README.i18n.yaml
index 1346cf4acb..6dc5eefe58 100644
--- a/packages/client/ui-sidebar/README.i18n.yaml
+++ b/packages/client/ui-sidebar/README.i18n.yaml
@@ -21,8 +21,8 @@
en: 6bc49e22f0f73f07
zh: 10883997ccb7d145
/deepseek-ai-dsh-client-ui-sidebar/use-this-package/global-panel-entries:
- en: 20099567e47e1252
- zh: 1418d3d3f36a2207
+ en: 44fe4c8e3c28c922
+ zh: b2805284cc27b8e5
/deepseek-ai-dsh-client-ui-sidebar/use-this-package/collapse-behavior:
en: 10381cbac96069a3
zh: c5849741a0be2f00
@@ -36,8 +36,8 @@
en: 3e84ffdf6c7c1731
zh: 3f7b19cac6d6674f
/deepseek-ai-dsh-client-ui-sidebar/understand-the-implementation:
- en: 8cc657bd9b66c93f
- zh: 47ad4bd9e906ef8e
+ en: 2ad084e81eabb4ae
+ zh: 938a6f96fd49dcfe
/deepseek-ai-dsh-client-ui-sidebar/understand-the-implementation/slot-discipline:
en: 6eefc5c45abe8a8f
zh: 20b651e48a0ea903
diff --git a/packages/client/ui-sidebar/README.md b/packages/client/ui-sidebar/README.md
index 7ccd3e0db1..018961ea3f 100644
--- a/packages/client/ui-sidebar/README.md
+++ b/packages/client/ui-sidebar/README.md
@@ -41,7 +41,7 @@ The expanded brand row renders `sidebar.brand.mark` and `sidebar.brand.name` as
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. Expanded navigation rows have a 2px gap. 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 panel registrations, neither the list nor spacing for it is rendered. The shipped composition registers no example panel.
-While the root-scoped `sidebar.panellist.actions` list has registrations, a **More** button styled as a panel row follows the rows inside the navigation landmark; the collapsed rail shows its label as a tooltip. It opens a menu whose rows are that list's registrations. Each receives `entries` (customizable panels in registration order with `id`, `label`, `pinned`, and `active`; Plugins is never listed), `onOpen(id)`, which selects the panel, `onPinnedChange(id, pinned)`, and `onDismiss()`, which closes the menu. The sidebar keeps one in-memory list of unpinned panel ids shared by every Session; Plugins always shows. Expanded rows other than Plugins show an unpin action on hover or keyboard focus; devices without hover show it continuously. Its resting color matches the Session pin marker, and its tooltip says “Unpin from left sidebar”. Both sidebar and More entries follow the same registration order rather than pin timing. The default More menu lists only unpinned panels before plugin browsing and creation; opening a panel does not pin it, while its separate pin icon returns the row to the sidebar and dismisses the menu. Choices reset when the plugin reloads and legacy localStorage values are ignored. Unpinning hides only the row; the main panel, its registration, and the current selection stay unchanged. Unregistered panel ids remain in the in-memory list until the plugin reloads.
+While the root-scoped `sidebar.panellist.actions` list has registrations, a **More** button styled as a panel row follows the rows inside the navigation landmark; the collapsed rail shows its label as a tooltip. It opens a menu whose rows are that list's registrations. Each receives `entries` (customizable panels in registration order with `id`, `label`, `pinned`, and `active`; Plugins is never listed), `onOpen(id)`, which selects the panel, `onPinnedChange(id, pinned)`, and `onDismiss()`, which closes the menu. The sidebar keeps one in-memory list of unpinned panel ids shared by every Session; Plugins always shows. While More shows, expanded rows other than Plugins show an unpin action on hover or keyboard focus; devices without hover show it continuously. Its resting color matches the Session pin marker, and its tooltip says “Unpin from left sidebar”. Both sidebar and More entries follow the same registration order rather than pin timing. The default More menu lists only unpinned panels before plugin browsing and creation; opening a panel does not pin it, while its separate pin icon returns the row to the sidebar and dismisses the menu. Choices reset when the plugin reloads and legacy localStorage values are ignored. Unpinning hides only the row, and only while More shows; without `sidebar.panellist.actions` registrations every registered row shows again. The main panel, its registration, and the current selection stay unchanged. Unregistered panel ids remain in the in-memory list until the plugin reloads.
### Collapse behavior
@@ -78,7 +78,7 @@ On Desktop, the official navigation controls report `sidebar_menu_click` with `m
Implementation internals — click to expand
-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. The sidebar owns `sidebar.panellist` and renders each panel's registered icon directly. Panel metadata is derived from list registrations and locale changes; selection belongs to the layout store. The injected `trackEntryClick(attributes)` forwards to `ctx.get('productAnalytics')?.track('sidebar_menu_click', attributes)`, and `selectPanel` only selects, so each control supplies its own entry and position.
+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. The sidebar declares the `sidebar.panel.icon` Component Factory, which owns the `sidebar.panellist` list; panel rows and the More menu render a panel's registered icon through that factory. Panel metadata is derived from list registrations and locale changes; selection belongs to the layout store. The injected `trackEntryClick(attributes)` forwards to `ctx.get('productAnalytics')?.track('sidebar_menu_click', attributes)`, and `selectPanel` only selects, so each control supplies its own entry and position.
Expanded navigation uses 8px outer gutters, hover and selected fills inset 3px on each side, and a 16px leading cell with a 6px label gap. New Session centers its content with 4px side margins and a 10px bottom margin. The logo row has no bottom margin; its content starts 16px from the column's left edge, with 5px between mark and name. Only the macOS non-fullscreen top toggle shifts 4px right while expanded, retaining that offset throughout its fade-out. Collapsed geometry stays independent.
diff --git a/packages/client/ui-sidebar/README.zh.md b/packages/client/ui-sidebar/README.zh.md
index ecbf9697a4..fbba95c489 100644
--- a/packages/client/ui-sidebar/README.zh.md
+++ b/packages/client/ui-sidebar/README.zh.md
@@ -41,7 +41,7 @@ dsh Web 客户端的侧边栏让用户识别当前构建、启动新会话、将
插件在 root 作用域的 `sidebar.panellist` list 中注册图标组件,提供 `id`、可选 `order`,以及字符串或 locale-aware 的 `label`。同一个 id 寻址布局中 root 作用域 `main` keyed slot 的组件;选择不存在的主面板条目会抛错,并保留当前选中态。标签提供普通可见文字、无障碍名称和折叠提示。展开态导航行的间距为 2px。每一行通过 `usePanelInfo` 读取自己的选中态;DOM 焦点移到搜索框或目录选择器时,显示的面板及其列表项选中态不变。没有面板注册项时,列表及其间距均不渲染。产品随附的组合不注册示例面板。
-root 作用域的 `sidebar.panellist.actions` list 有注册项时,导航区域的面板行之后显示面板行样式的**更多**按钮;收起的窄栏以提示框显示其名称。它打开的菜单以该 list 的注册项为菜单行。每个注册项接收 `entries`(按注册顺序的可自定义面板,含 `id`、`label`、`pinned` 与 `active`;不含插件管理)、选中面板的 `onOpen(id)`、`onPinnedChange(id, pinned)`,以及关闭菜单的 `onDismiss()`。侧边栏在内存中保存所有会话共用的已收起面板 id 列表;插件管理入口始终显示。展开态中,除插件管理外的面板行在悬停或键盘聚焦时显示取消固定操作;不支持悬停的设备始终显示该操作。图标默认颜色与会话置顶标记一致,提示为“从左侧栏取消固定”。侧栏和更多菜单入口遵循相同的注册排序,不按固定时间重排。默认更多菜单在插件浏览和创建之前仅列出未固定的面板;打开面板不会将其固定,独立的固定图标则将该行放回侧边栏并关闭菜单。插件重新加载时选择重置,不读取旧 localStorage 值。收起只隐藏导航行,主面板、其注册和当前选中态不变。未注册面板的 id 保留在内存列表中,直到插件重新加载。
+root 作用域的 `sidebar.panellist.actions` list 有注册项时,导航区域的面板行之后显示面板行样式的**更多**按钮;收起的窄栏以提示框显示其名称。它打开的菜单以该 list 的注册项为菜单行。每个注册项接收 `entries`(按注册顺序的可自定义面板,含 `id`、`label`、`pinned` 与 `active`;不含插件管理)、选中面板的 `onOpen(id)`、`onPinnedChange(id, pinned)`,以及关闭菜单的 `onDismiss()`。侧边栏在内存中保存所有会话共用的已收起面板 id 列表;插件管理入口始终显示。更多按钮显示时,展开态中除插件管理外的面板行在悬停或键盘聚焦时显示取消固定操作;不支持悬停的设备始终显示该操作。图标默认颜色与会话置顶标记一致,提示为“从左侧栏取消固定”。侧栏和更多菜单入口遵循相同的注册排序,不按固定时间重排。默认更多菜单在插件浏览和创建之前仅列出未固定的面板;打开面板不会将其固定,独立的固定图标则将该行放回侧边栏并关闭菜单。插件重新加载时选择重置,不读取旧 localStorage 值。收起只隐藏导航行,且仅在更多按钮显示时生效;`sidebar.panellist.actions` 没有注册项时,所有已注册的面板行重新显示。主面板、其注册和当前选中态不变。未注册面板的 id 保留在内存列表中,直到插件重新加载。
### 折叠行为
@@ -78,7 +78,7 @@ root 作用域的 `sidebar.panellist.actions` list 有注册项时,导航区
实现细节——点击展开
-外壳是纯组合:`SidebarRootComponentProps` 组合布局 owner share、全局 `useSessions` 与 `useWorkspaces` 钩子、已声明的品牌、`sidebar.workspaces` 与 `sidebar.settings` 子 slot,以及注入的导航回调。侧边栏声明 `sidebar.panellist`,并直接渲染各面板注册的图标。面板元数据由列表注册和 locale 变化派生;选中态属于布局存储。注入的 `trackEntryClick(attributes)` 转发到 `ctx.get('productAnalytics')?.track('sidebar_menu_click', attributes)`;`selectPanel` 只负责选中,因此由各控件自行提供入口与位置。
+外壳是纯组合:`SidebarRootComponentProps` 组合布局 owner share、全局 `useSessions` 与 `useWorkspaces` 钩子、已声明的品牌、`sidebar.workspaces` 与 `sidebar.settings` 子 slot,以及注入的导航回调。侧边栏声明 `sidebar.panel.icon` Component Factory,由它拥有 `sidebar.panellist` list;面板行和更多菜单都通过该 factory 渲染面板注册的图标。面板元数据由列表注册和 locale 变化派生;选中态属于布局存储。注入的 `trackEntryClick(attributes)` 转发到 `ctx.get('productAnalytics')?.track('sidebar_menu_click', attributes)`;`selectPanel` 只负责选中,因此由各控件自行提供入口与位置。
展开态导航的左右外距为 8px,悬停与选中底色在两侧各内缩 3px,前置单元宽 16px,与标签间隔 6px。新会话内容居中,左右外边距为 4px,底部外边距为 10px。logo 行无底部外边距;内容从侧栏左边缘向内 16px 开始,标记与名称间隔 5px。仅 macOS 非全屏、侧栏展开时,顶部开关向右偏移 4px,并在整个淡出期间保持该偏移。折叠态尺寸独立保留。
diff --git a/packages/client/ui-sidebar/src/client/SidebarRoot.tsx b/packages/client/ui-sidebar/src/client/SidebarRoot.tsx
index 196f1e5958..37f871a11d 100644
--- a/packages/client/ui-sidebar/src/client/SidebarRoot.tsx
+++ b/packages/client/ui-sidebar/src/client/SidebarRoot.tsx
@@ -395,7 +395,7 @@ export function SidebarRoot({
{panels.length > 0 && (