@deepseek-ai/dsh-client-ui-layout 0.1.7-alpha.2 → 0.1.7-rc.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 +42 -5
- package/README.md +4 -2
- package/README.zh.md +4 -2
- package/lib/client.js +57 -32
- package/lib/types/client/index.d.ts +5 -0
- package/lib/types/client/service.d.ts +6 -2
- package/lib/types/client/shortcut-locales.d.ts +7 -0
- package/package.json +12 -10
package/README.i18n.yaml
CHANGED
|
@@ -1,6 +1,43 @@
|
|
|
1
|
-
# Bilingual-pair consistency record (docs/i18n/README.md):
|
|
2
|
-
#
|
|
3
|
-
#
|
|
1
|
+
# Bilingual-pair consistency record for README.md (docs/i18n/README.md): per heading
|
|
2
|
+
# section, a hash of its English and Chinese blocks outside code blocks and generated regions.
|
|
3
|
+
# After editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/client/ui-layout/README.md
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/:
|
|
6
|
+
en: d96ec500357f8268
|
|
7
|
+
zh: 57610f377112cbde
|
|
8
|
+
/deepseek-ai-dsh-client-ui-layout:
|
|
9
|
+
en: a2f7bed6f7fd12d9
|
|
10
|
+
zh: 9d7981efe76c734d
|
|
11
|
+
/deepseek-ai-dsh-client-ui-layout/summary:
|
|
12
|
+
en: 0315d385c20948ac
|
|
13
|
+
zh: b4d31027504adc25
|
|
14
|
+
/deepseek-ai-dsh-client-ui-layout/table-of-contents:
|
|
15
|
+
en: d152484eb41ac6b4
|
|
16
|
+
zh: 09388d293f9be9cb
|
|
17
|
+
/deepseek-ai-dsh-client-ui-layout/use-this-package:
|
|
18
|
+
en: 7d135dd3dbd7ff96
|
|
19
|
+
zh: 43ac4d9c5a12f741
|
|
20
|
+
/deepseek-ai-dsh-client-ui-layout/use-this-package/window-chrome-seat:
|
|
21
|
+
en: e55c831b460b40e4
|
|
22
|
+
zh: 8670e583bec8c50b
|
|
23
|
+
/deepseek-ai-dsh-client-ui-layout/use-this-package/theme-presentation:
|
|
24
|
+
en: 1e1d3361dac7d8b0
|
|
25
|
+
zh: b6b3d210d65738f0
|
|
26
|
+
/deepseek-ai-dsh-client-ui-layout/understand-the-implementation:
|
|
27
|
+
en: 8af800fa55786c71
|
|
28
|
+
zh: 7421c12eb3585c21
|
|
29
|
+
/deepseek-ai-dsh-client-ui-layout/further-exploration:
|
|
30
|
+
en: b6c728b6ed70f2a7
|
|
31
|
+
zh: 8ee2003b98695323
|
|
32
|
+
/deepseek-ai-dsh-client-ui-layout/model-experience:
|
|
33
|
+
en: 9a94a7197da60052
|
|
34
|
+
zh: 369a59ab63d050f1
|
|
35
|
+
/deepseek-ai-dsh-client-ui-layout/model-experience/kv-cache-effect:
|
|
36
|
+
en: ca75c51c89c2c9b0
|
|
37
|
+
zh: de54f3467ef3b9be
|
|
38
|
+
/deepseek-ai-dsh-client-ui-layout/known-limitations-and-deferred-work:
|
|
39
|
+
en: da92b97ac60d989f
|
|
40
|
+
zh: 5816b861728f1a04
|
|
41
|
+
/deepseek-ai-dsh-client-ui-layout/known-limitations-and-deferred-work/dev-note:
|
|
42
|
+
en: 4671d1561021e834
|
|
43
|
+
zh: cf6dba523a442cb5
|
package/README.md
CHANGED
|
@@ -25,13 +25,15 @@ This package provides the Web GUI's three-column AppFrame, edge-column widths, a
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## Use this package
|
|
27
27
|
|
|
28
|
+
Desktop Mod+B toggles the left sidebar through the same layout action used by its controls. The shortcut command is unavailable behind modal dialogs and yields to terminal input.
|
|
29
|
+
|
|
28
30
|
The root slot composes the sidebar, main content, and right column. The sidebar spans 264–420px, defaults to 280px, and retains a 56px rail when collapsed; below 1024px it collapses automatically, and opening the right panel collapses a manually expanded sidebar. The right panel first opens at 45% of the viewport, then retains the user's pixel preference, capped at 70%. To protect 400px for the center, the frame first reduces the right panel to 300px, then reports insufficient room so its occupant closes it, and only then compresses the center further. Dragging has no transition delay; the right handle is absent while closed or fullscreen.
|
|
29
31
|
|
|
30
32
|
Global panels occupy the root-scoped `main` keyed slot; `conversation` is the reserved key for the Conversation. `ctx.layout.selectPanel(id)` selects a registered panel, and `null` selects the Conversation without changing the current Session. No global panel is registered by the shipped composition.
|
|
31
33
|
|
|
32
34
|
### Window-chrome seat
|
|
33
35
|
|
|
34
|
-
On macOS desktop (`html[data-platform='darwin']`, set only by the desktop preload) a collapsed sidebar hides its column entirely instead of keeping the rail, and the frame mounts the single root-scoped `shell.leading` seat at its top-left — beside the hiddenInset traffic lights, over every main panel; ui-sidebar occupies it with the reopen and New Session controls. While the seat is mounted the frame publishes `--dsh-frame-leading-clearance`, the inline band the window chrome occupies measured from the frame's left edge; a main panel whose content reaches the top-left corner pads by it so nothing lands under the lights or the controls. The frame also always publishes `--dsh-frame-top-clearance` (48px) on the root element, the constant step below the window's top strip; entry pages in the main panel (plugin manager and similar, not the conversation) pad their top by it, and overlay primitives (portal menus, bottom-anchored overlays, the settings panel) keep it as their top viewport margin — the root-element home lets overlays portalled to `document.body` read it too. The
|
|
36
|
+
On macOS desktop (`html[data-platform='darwin']`, set only by the desktop preload) a collapsed sidebar hides its column entirely instead of keeping the rail, and the frame mounts the single root-scoped `shell.leading` seat at its top-left — beside the hiddenInset traffic lights, over every main panel; ui-sidebar occupies it with the reopen and New Session controls. While the seat is mounted the frame publishes `--dsh-frame-leading-clearance`, the inline band the window chrome occupies measured from the frame's left edge; a main panel whose content reaches the top-left corner pads by it so nothing lands under the lights or the controls. The frame also always publishes `--dsh-frame-top-clearance` (48px) on the root element, the constant step below the window's top strip; entry pages in the main panel (plugin manager and similar, not the conversation) pad their top by it, and overlay primitives (portal menus, bottom-anchored overlays, the settings panel) keep it as their top viewport margin — the root-element home lets overlays portalled to `document.body` read it too. The frame declares no darwin drag of its own: every chrome row marks itself `data-window-drag`, ui-web base.css turns that mark into the one darwin drag rule, and the row's own box is therefore the window's draggable geometry — a row's blank runs drag while its controls stay clickable. The frame's remaining drag rule is the Windows caption row, which belongs to that platform.
|
|
35
37
|
|
|
36
38
|
Windows Electron's `data-windows-titlebar` marker reserves the caption height above all columns and removes the collapsed sidebar rail. Only the content area's top-left corner has a 16px radius; the other corners and the internal divider remain square. The frame publishes `--dsh-windows-content-radius` and `--dsh-windows-sidebar-width` for ui-sidebar-right's fullscreen corner and sidebar clearance. Ordinary Web documents do not receive the marker; macOS retains its separate layout.
|
|
37
39
|
|
|
@@ -49,7 +51,7 @@ The presenter consumes resolved theme snapshots and projects them onto the docum
|
|
|
49
51
|
|
|
50
52
|
`selectPanel(id)` checks the live `main` registry before changing selection; an absent key throws and leaves the current panel intact. `beginNavigation()` returns an abort signal for an asynchronous UI navigation. A later call, a valid panel selection (including repeated selection), or layout disposal aborts that signal without cancelling underlying Session creation. Consumers check the signal before committing navigation or moving drafts.
|
|
51
53
|
|
|
52
|
-
One registration declares five child slots and binds `ctx.layout` methods `selectPanel`, `toggleSidebar`, `openRightbar(track, fullscreen)`, and `closeRightbar`. One root store separates `panelInfo` selection from `layoutInfo` measurements, width preferences, and presentation reports. `usePanelInfo`
|
|
54
|
+
One registration declares five child slots and binds `ctx.layout` methods `selectPanel`, `toggleSidebar`, `openRightbar(track, fullscreen)`, and `closeRightbar`. One root store separates `panelInfo` selection from `layoutInfo` measurements, width preferences, and presentation reports. `ctx.layout.panelInfo` and the standard `usePanelInfo` hook share the same selection source; AppFrame subscribes to the stable layout object. The `rightbar` owner supplies actual `width`, `viewportWidth`, and normal-presentation eligibility `canShow`; insufficient room causes a deterministic close, never automatic reopening on widening. Fullscreen hides the width handle without releasing a track the occupant retains. AppFrame keeps the column containers mounted. The right column's root controller renders `rightbar.session` through `SessionProvider` only while the Conversation is selected; its unmount report releases the track. The independent title component uses the selected Session title only while the Conversation is visible, with the build-configured product title or localized `common.brand.localBuild` as its fallback; locale revisions update that fallback. The theme presenter is a second effect: pure DOM writes from resolved snapshots — initial state through the getter once, then event-driven only, with no React path. It applies palette, font-size, and token variables before measuring the rendered background as the single color authority. Fullscreen presentation suppresses grid and handle transitions; its occupant reports the new columns only after covering the frame. Fullscreen exit keeps transitions suppressed while the frame installs its destination geometry: close removes the right track, and restore retains it. Subsequent normal geometry actions restore ordinary transitions.
|
|
53
55
|
|
|
54
56
|
</details>
|
|
55
57
|
|
package/README.zh.md
CHANGED
|
@@ -25,13 +25,15 @@ kind: "package-reference"
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## 使用本包
|
|
27
27
|
|
|
28
|
+
桌面 Mod+B 通过与控件共用的布局操作切换左侧栏。模态弹窗打开时此快捷键不可用;终端输入优先。
|
|
29
|
+
|
|
28
30
|
本插件在 root slot 中组合侧边栏、主内容和右栏。侧边栏宽度为 264~420px,默认为 280px,收起后保留 56px 控制栏;窗口宽度低于 1024px 时自动收起,打开右侧面板也会收起手动展开的侧边栏。右侧面板首次打开时使用视口宽度的 45%,之后保留用户的像素宽度偏好,上限为 70%。为给中栏保留 400px,框架先将右侧面板缩减至 300px,再报告空间不足,使占用方将其关闭,最后才进一步压缩中栏。拖动没有过渡延迟;右侧手柄在关闭或全屏时不显示。
|
|
29
31
|
|
|
30
32
|
全局面板占据 root 作用域的 `main` keyed slot;`conversation` 是为会话界面保留的 key。`ctx.layout.selectPanel(id)` 选中已注册面板,`null` 则选中会话界面,但不改变当前会话。默认组合不注册任何全局面板。
|
|
31
33
|
|
|
32
34
|
### 窗口 chrome 座
|
|
33
35
|
|
|
34
|
-
在 macOS 桌面(`html[data-platform='darwin']`,仅由桌面 preload 设置)下,收起的侧边栏整列隐藏而非保留控制栏,框架在左上角挂载 root 作用域的单一 `shell.leading` 座——位于 hiddenInset 红绿灯旁,覆盖所有主面板;ui-sidebar 以重新打开与 New Session 控件占据该座。座挂载期间框架发布 `--dsh-frame-leading-clearance`:窗口 chrome 占据的行内带宽,自框架左边缘起量;内容抵达左上角的主面板以它做内边距,避免落在红绿灯或控件之下。框架还始终在根元素上发布 `--dsh-frame-top-clearance`(48px):窗口顶带之下的固定下沉量;主面板中的入口型页面(插件管理器等类似页面,非对话)以它做顶部内边距,浮层原语(portal 菜单、底部锚定浮层、设置面板)以它做视口顶部安全边距——放在根元素上让 portal 到 document.body
|
|
36
|
+
在 macOS 桌面(`html[data-platform='darwin']`,仅由桌面 preload 设置)下,收起的侧边栏整列隐藏而非保留控制栏,框架在左上角挂载 root 作用域的单一 `shell.leading` 座——位于 hiddenInset 红绿灯旁,覆盖所有主面板;ui-sidebar 以重新打开与 New Session 控件占据该座。座挂载期间框架发布 `--dsh-frame-leading-clearance`:窗口 chrome 占据的行内带宽,自框架左边缘起量;内容抵达左上角的主面板以它做内边距,避免落在红绿灯或控件之下。框架还始终在根元素上发布 `--dsh-frame-top-clearance`(48px):窗口顶带之下的固定下沉量;主面板中的入口型页面(插件管理器等类似页面,非对话)以它做顶部内边距,浮层原语(portal 菜单、底部锚定浮层、设置面板)以它做视口顶部安全边距——放在根元素上让 portal 到 document.body 的浮层也能读到。框架自己不声明任何 darwin drag:每个 chrome 行给自身打上 `data-window-drag`,ui-web base.css 把该标记变成唯一那条 darwin drag 规则,于是行自己的盒子就是窗口的可拖几何——行的空白段可拖,控件保持可点。框架仅剩的那条 drag 规则属于 Windows 标题栏行。
|
|
35
37
|
|
|
36
38
|
Windows Electron 的 `data-windows-titlebar` 标记在所有列上方预留顶栏高度,并移除收起后的侧栏轨道。内容区仅左上角保留 16px 圆角,其余角和内部交界处保持直角。框架发布 `--dsh-windows-content-radius` 和 `--dsh-windows-sidebar-width`,供 ui-sidebar-right 的全屏圆角及侧栏避让使用。普通 Web 文档不会获得该标记;macOS 保留其独立布局。
|
|
37
39
|
|
|
@@ -49,7 +51,7 @@ Windows Electron 的 `data-windows-titlebar` 标记在所有列上方预留顶
|
|
|
49
51
|
|
|
50
52
|
`selectPanel(id)` 在改变选中态前检查实时 `main` 注册表;缺失的 key 会抛错并保留当前面板。`beginNavigation()` 为异步 UI 导航返回 abort signal。后续调用、有效面板选择(包括重复选择)或布局释放会中止该 signal,但不取消底层会话创建。消费方在提交导航或搬移草稿前检查 signal。
|
|
51
53
|
|
|
52
|
-
一次注册声明五个子 slot,并绑定 `ctx.layout` 的 `selectPanel`、`toggleSidebar`、`openRightbar(track, fullscreen)` 与 `closeRightbar`。同一个 root 存储把 `panelInfo` 选中态与 `layoutInfo` 测量、宽度偏好、呈现报告分开。`usePanelInfo`
|
|
54
|
+
一次注册声明五个子 slot,并绑定 `ctx.layout` 的 `selectPanel`、`toggleSidebar`、`openRightbar(track, fullscreen)` 与 `closeRightbar`。同一个 root 存储把 `panelInfo` 选中态与 `layoutInfo` 测量、宽度偏好、呈现报告分开。`ctx.layout.panelInfo` 与标准 `usePanelInfo` hook 共用同一个选中态来源;AppFrame 订阅引用稳定的布局对象。`rightbar` owner 提供实际 `width`、`viewportWidth`,以及表示能否以普通模式呈现的 `canShow`;占用方在空间不足时执行确定性的收起,变宽不自行重新展开。全屏隐藏宽度手柄,但不自行释放占用方要求保留的轨道。AppFrame 保持各列容器挂载。右栏的 root 控制器仅在选中会话界面时,经 `SessionProvider` 渲染 `rightbar.session`;内容卸载时的报告释放轨道。独立的标题组件仅在会话界面可见时使用所选会话标题,以构建配置的产品标题或本地化 `common.brand.localBuild` 为回退值;语言变化会更新该回退值。主题呈现器是第二个 effect:从解析后的快照做纯 DOM 写入——初始状态经 getter 读取一次,此后仅事件驱动,不经过 React。它先应用调色板、字号与 token 变量,再把渲染出的背景测量为唯一的颜色依据。全屏呈现禁用网格和手柄过渡;占用方完全覆盖框架后才报告新的列布局。退出全屏时,框架先保持无过渡并安装目标布局:关闭移除右轨道,恢复保留右轨道。后续普通几何操作恢复正常过渡。
|
|
53
55
|
|
|
54
56
|
</details>
|
|
55
57
|
|
package/lib/client.js
CHANGED
|
@@ -70,7 +70,7 @@ window.__ModuleLoader__.load({
|
|
|
70
70
|
}
|
|
71
71
|
//#endregion
|
|
72
72
|
//#region \0dsh-css:/home/runner/work/deepseek-harness/deepseek-harness/packages/client/ui-layout/src/client/AppFrame.module.css.mjs
|
|
73
|
-
const css = ".pI_x6G_frame{background:var(--dsw-alias-bg-base);grid-template-rows:100%;height:100%;display:grid;position:relative;overflow:hidden}.pI_x6G_frame[data-animating]{transition:grid-template-columns var(--ds-transition-duration-slow) var(--ds-ease-in-out)}.pI_x6G_frame[data-dragging]{transition:none}[data-windows-titlebar] .pI_x6G_frame{--dsh-windows-content-radius:16px;box-sizing:border-box;padding-top:var(--dsh-windows-titlebar-height);background:var(--dsw-specific-sidebar-fill);grid-template-rows:minmax(0,1fr)}[data-windows-titlebar] .pI_x6G_centerCol{background:var(--dsw-alias-bg-base);border-radius:var(--dsh-windows-content-radius) 0 0 0;corner-shape:round}[data-windows-titlebar] .pI_x6G_frame:before{content:\"\";height:var(--dsh-windows-titlebar-height);background:var(--dsw-specific-sidebar-fill);-webkit-app-region:drag;position:absolute;inset:0 0 auto}[data-windows-titlebar] .pI_x6G_sidebarCol{border-right:none}[data-windows-titlebar] .pI_x6G_handle{top:var(--dsh-windows-titlebar-height)}@media (prefers-reduced-motion:reduce){.pI_x6G_frame[data-animating]{transition:none}}.pI_x6G_sidebarCol{background:var(--dsw-specific-sidebar-fill);border-right:.5px solid var(--dsw-alias-border-l3);min-width:0;overflow:hidden}.pI_x6G_centerCol{flex-direction:column;min-width:0;display:flex;overflow:hidden}[data-platform=darwin] .pI_x6G_frame{background:0 0}html[data-platform=darwin]{--dsh-frame-top-clearance:48px}[data-platform=darwin] .pI_x6G_sidebarCol{background:linear-gradient(to bottom, #7a9bf01a, #7a9bf000 35%, #8f89b800 68%, #8f89b817), color-mix(in srgb, color-mix(in srgb, var(--dsw-specific-sidebar-fill) 97%, #7a9bf0) 40%, transparent);border-right:none}[data-platform=darwin] [data-ds-dark-theme] .pI_x6G_sidebarCol{background:linear-gradient(to bottom, #7a9bf014, #7a9bf000 35%, #8f89b800 68%, #8f89b812), color-mix(in srgb, var(--dsw-specific-sidebar-fill) 50%, transparent)}@media (prefers-reduced-transparency:reduce){[data-platform=darwin] .pI_x6G_sidebarCol,[data-platform=darwin] [data-ds-dark-theme] .pI_x6G_sidebarCol{background:color-mix(in srgb, var(--dsw-specific-sidebar-fill) 90%, transparent)}}[data-platform=darwin] .pI_x6G_centerCol{background:var(--dsw-alias-bg-base);border-left:.5px solid var(--dsw-alias-border-l3)}[data-platform=darwin] .pI_x6G_rightbarCol{background:var(--dsw-alias-bg-base)}[data-platform=darwin] [data-sidebar-collapsed] .pI_x6G_centerCol{border-left:none}[data-platform=darwin] .pI_x6G_frame[data-sidebar-collapsed]{--dsh-frame-leading-clearance:160px}[data-platform=darwin][data-fullscreen] .pI_x6G_frame[data-sidebar-collapsed]{--dsh-frame-leading-clearance:84px}[data-platform=darwin][data-fullscreen] .pI_x6G_leadingSeat{left:12px}.
|
|
73
|
+
const css = ".pI_x6G_frame{background:var(--dsw-alias-bg-base);grid-template-rows:100%;height:100%;display:grid;position:relative;overflow:hidden}.pI_x6G_frame[data-animating]{transition:grid-template-columns var(--ds-transition-duration-slow) var(--ds-ease-in-out)}.pI_x6G_frame[data-dragging]{transition:none}[data-windows-titlebar] .pI_x6G_frame{--dsh-windows-content-radius:16px;box-sizing:border-box;padding-top:var(--dsh-windows-titlebar-height);background:var(--dsw-specific-sidebar-fill);grid-template-rows:minmax(0,1fr)}[data-windows-titlebar] .pI_x6G_centerCol{background:var(--dsw-alias-bg-base);border-radius:var(--dsh-windows-content-radius) 0 0 0;corner-shape:round}[data-windows-titlebar] .pI_x6G_frame:before{content:\"\";height:var(--dsh-windows-titlebar-height);background:var(--dsw-specific-sidebar-fill);-webkit-app-region:drag;position:absolute;inset:0 0 auto}[data-windows-titlebar] .pI_x6G_sidebarCol{border-right:none}[data-windows-titlebar] .pI_x6G_handle{top:var(--dsh-windows-titlebar-height)}@media (prefers-reduced-motion:reduce){.pI_x6G_frame[data-animating]{transition:none}}.pI_x6G_sidebarCol{background:var(--dsw-specific-sidebar-fill);border-right:.5px solid var(--dsw-alias-border-l3);min-width:0;overflow:hidden}.pI_x6G_centerCol{flex-direction:column;min-width:0;display:flex;overflow:hidden}[data-platform=darwin] .pI_x6G_frame{background:0 0}html[data-platform=darwin]{--dsh-frame-top-clearance:48px}[data-platform=darwin] .pI_x6G_sidebarCol{background:linear-gradient(to bottom, #7a9bf01a, #7a9bf000 35%, #8f89b800 68%, #8f89b817), color-mix(in srgb, color-mix(in srgb, var(--dsw-specific-sidebar-fill) 97%, #7a9bf0) 40%, transparent);border-right:none}[data-platform=darwin] [data-ds-dark-theme] .pI_x6G_sidebarCol{background:linear-gradient(to bottom, #7a9bf014, #7a9bf000 35%, #8f89b800 68%, #8f89b812), color-mix(in srgb, var(--dsw-specific-sidebar-fill) 50%, transparent)}@media (prefers-reduced-transparency:reduce){[data-platform=darwin] .pI_x6G_sidebarCol,[data-platform=darwin] [data-ds-dark-theme] .pI_x6G_sidebarCol{background:color-mix(in srgb, var(--dsw-specific-sidebar-fill) 90%, transparent)}}[data-platform=darwin] .pI_x6G_centerCol{background:var(--dsw-alias-bg-base);border-left:.5px solid var(--dsw-alias-border-l3)}[data-platform=darwin] .pI_x6G_rightbarCol{background:var(--dsw-alias-bg-base)}[data-platform=darwin] [data-sidebar-collapsed] .pI_x6G_centerCol{border-left:none}[data-platform=darwin] .pI_x6G_frame[data-sidebar-collapsed]{--dsh-frame-leading-clearance:160px}[data-platform=darwin][data-fullscreen] .pI_x6G_frame[data-sidebar-collapsed]{--dsh-frame-leading-clearance:84px}[data-platform=darwin][data-fullscreen] .pI_x6G_leadingSeat{left:12px}.pI_x6G_leadingSeat{z-index:15;-webkit-app-region:no-drag;align-items:center;display:flex;position:absolute;top:11px;left:88px}.pI_x6G_handle{cursor:col-resize;z-index:11;touch-action:none;width:8px;margin-left:-4px;position:absolute;top:0;bottom:0}.pI_x6G_frame[data-animating] .pI_x6G_handle{transition:left var(--ds-transition-duration-slow) var(--ds-ease-in-out)}.pI_x6G_frame[data-dragging] .pI_x6G_handle,.pI_x6G_frame[data-rightbar-fullscreen],.pI_x6G_frame[data-rightbar-fullscreen] .pI_x6G_handle,.pI_x6G_frame[data-rightbar-instant],.pI_x6G_frame[data-rightbar-instant] .pI_x6G_handle{transition:none}@media (prefers-reduced-motion:reduce){.pI_x6G_frame[data-animating] .pI_x6G_handle{transition:none}}.pI_x6G_rightbarCol{min-width:0;position:relative;overflow:visible}.pI_x6G_overlayLayer{z-index:20;pointer-events:none;position:absolute;inset:0}.pI_x6G_overlayLayer>*{pointer-events:auto}";
|
|
74
74
|
const tagId = "@deepseek-ai/dsh-client-ui-layout/AppFrame.module.css";
|
|
75
75
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
76
76
|
const tag = document.createElement("style");
|
|
@@ -83,7 +83,6 @@ window.__ModuleLoader__.load({
|
|
|
83
83
|
"centerCol": "pI_x6G_centerCol",
|
|
84
84
|
"frame": "pI_x6G_frame",
|
|
85
85
|
"handle": "pI_x6G_handle",
|
|
86
|
-
"leadingBand": "pI_x6G_leadingBand",
|
|
87
86
|
"leadingSeat": "pI_x6G_leadingSeat",
|
|
88
87
|
"overlayLayer": "pI_x6G_overlayLayer",
|
|
89
88
|
"rightbarCol": "pI_x6G_rightbarCol",
|
|
@@ -119,23 +118,6 @@ window.__ModuleLoader__.load({
|
|
|
119
118
|
return renderSlot("main", {}, { entryKey: usePanelInfo((info) => info.activePanelId) ?? "conversation" });
|
|
120
119
|
}
|
|
121
120
|
/**
|
|
122
|
-
* Marks the frame while the Conversation is selected — the deepened drag band
|
|
123
|
-
* (AppFrame.module.css) keys off the attribute. A DOM write from a child keeps
|
|
124
|
-
* the frame itself out of the panel subscription: selecting a panel must not
|
|
125
|
-
* re-render the columns.
|
|
126
|
-
*/
|
|
127
|
-
function ConversationMarker({ usePanelInfo, frameRef }) {
|
|
128
|
-
const conversationActive = usePanelInfo((info) => info.activePanelId === null);
|
|
129
|
-
(0, react.useLayoutEffect)(() => {
|
|
130
|
-
const frame = frameRef.current;
|
|
131
|
-
/* v8 ignore next -- the ref is attached by effect time: the marker renders inside the frame div. */
|
|
132
|
-
if (frame === null) return;
|
|
133
|
-
if (conversationActive) frame.setAttribute("data-panel-conversation", "");
|
|
134
|
-
else frame.removeAttribute("data-panel-conversation");
|
|
135
|
-
}, [conversationActive, frameRef]);
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
121
|
* Right column grid item. Zero-width unless the occupant asked for a track; the
|
|
140
122
|
* occupant's panel is positioned against the column's right edge, which never
|
|
141
123
|
* moves, so it can hang over the centre when there is no track.
|
|
@@ -344,14 +326,6 @@ window.__ModuleLoader__.load({
|
|
|
344
326
|
"data-dragging": dragging || void 0,
|
|
345
327
|
"data-animating": animating > 0 || void 0,
|
|
346
328
|
children: [
|
|
347
|
-
darwin && (0, react_jsx_runtime.jsx)("div", {
|
|
348
|
-
className: AppFrame_module_css_default.leadingBand,
|
|
349
|
-
"data-shell-leading-band": true
|
|
350
|
-
}),
|
|
351
|
-
(0, react_jsx_runtime.jsx)(ConversationMarker, {
|
|
352
|
-
usePanelInfo,
|
|
353
|
-
frameRef
|
|
354
|
-
}),
|
|
355
329
|
(0, react_jsx_runtime.jsx)(DocumentTitle, {
|
|
356
330
|
productTitle,
|
|
357
331
|
useSessions,
|
|
@@ -472,14 +446,17 @@ window.__ModuleLoader__.load({
|
|
|
472
446
|
var LayoutController = class {
|
|
473
447
|
panels;
|
|
474
448
|
hasMainPanel;
|
|
449
|
+
panelInfo;
|
|
475
450
|
navigation = new AbortController();
|
|
476
451
|
/**
|
|
477
452
|
* @param panels - actions of the instance shared with the root entry.
|
|
478
453
|
* @param hasMainPanel - checks the live main-slot registry for a panel id.
|
|
454
|
+
* @param panelInfo - root store's shared central-panel selection source.
|
|
479
455
|
*/
|
|
480
|
-
constructor(panels, hasMainPanel) {
|
|
456
|
+
constructor(panels, hasMainPanel, panelInfo) {
|
|
481
457
|
this.panels = panels;
|
|
482
458
|
this.hasMainPanel = hasMainPanel;
|
|
459
|
+
this.panelInfo = panelInfo;
|
|
483
460
|
}
|
|
484
461
|
/** Select a global panel or return to the Conversation. */
|
|
485
462
|
selectPanel(panelId) {
|
|
@@ -511,6 +488,12 @@ window.__ModuleLoader__.load({
|
|
|
511
488
|
}
|
|
512
489
|
};
|
|
513
490
|
//#endregion
|
|
491
|
+
//#region lib/types/client/shortcut-locales.js
|
|
492
|
+
/** Layout command labels. */
|
|
493
|
+
const zh = { toggle: "展开/收起左侧栏" };
|
|
494
|
+
/** English labels for the same layout commands. */
|
|
495
|
+
const en = { toggle: "Toggle left sidebar" };
|
|
496
|
+
//#endregion
|
|
514
497
|
//#region lib/types/client/theme-presenter.js
|
|
515
498
|
/** Body attribute selecting the dark base palette in the token stylesheets. */
|
|
516
499
|
const DARK_ATTRIBUTE = "data-ds-dark-theme";
|
|
@@ -584,7 +567,8 @@ window.__ModuleLoader__.load({
|
|
|
584
567
|
const inject = [
|
|
585
568
|
"slots",
|
|
586
569
|
"theme",
|
|
587
|
-
"locale"
|
|
570
|
+
"locale",
|
|
571
|
+
"shortcuts"
|
|
588
572
|
];
|
|
589
573
|
/**
|
|
590
574
|
* Client plugin body: provide ctx.layout, then one register() call — AppFrame
|
|
@@ -593,6 +577,11 @@ window.__ModuleLoader__.load({
|
|
|
593
577
|
* @param ctx - client root context.
|
|
594
578
|
*/
|
|
595
579
|
function apply(ctx) {
|
|
580
|
+
ctx.effect(() => ctx.locale.register("shortcuts.layout", {
|
|
581
|
+
zh,
|
|
582
|
+
en
|
|
583
|
+
}), "layout: command labels");
|
|
584
|
+
const t = ctx.locale.bind("shortcuts.layout");
|
|
596
585
|
ctx.effect(() => {
|
|
597
586
|
const handle = createLayoutStore();
|
|
598
587
|
const instance = handle.create();
|
|
@@ -600,14 +589,14 @@ window.__ModuleLoader__.load({
|
|
|
600
589
|
...handle,
|
|
601
590
|
create: () => instance
|
|
602
591
|
};
|
|
603
|
-
const layout = new LayoutController(instance.actions, (id) => ctx.slots.entries("main").some((entry) => entry.options.key === id));
|
|
604
592
|
const retainMainPanels = () => {
|
|
605
593
|
instance.actions.retainMainPanels(ctx.slots.entries("main").flatMap((entry) => entry.options.key === void 0 ? [] : [entry.options.key]));
|
|
606
594
|
};
|
|
607
|
-
const
|
|
595
|
+
const layout = new LayoutController(instance.actions, (id) => ctx.slots.entries("main").some((entry) => entry.options.key === id), {
|
|
608
596
|
getSnapshot: () => instance.getSnapshot().panelInfo,
|
|
609
597
|
subscribe: (listener) => instance.subscribe(listener)
|
|
610
|
-
}
|
|
598
|
+
});
|
|
599
|
+
const disposePanelInfo = ctx.slots.provideRoot({ hooks: { panelInfo: layout.panelInfo } });
|
|
611
600
|
const disposeService = ctx.reflect.provide("layout", layout);
|
|
612
601
|
const disposeRegistration = ctx.slots.register({
|
|
613
602
|
name: "root",
|
|
@@ -636,9 +625,45 @@ window.__ModuleLoader__.load({
|
|
|
636
625
|
},
|
|
637
626
|
store
|
|
638
627
|
}, AppFrame);
|
|
628
|
+
const disposeShortcut = ctx.shortcuts.register({
|
|
629
|
+
id: "sidebar.left.toggle",
|
|
630
|
+
label: () => t("toggle"),
|
|
631
|
+
aliases: ["sidebar", "toggle left sidebar"],
|
|
632
|
+
defaults: {
|
|
633
|
+
"desktop:macos": {
|
|
634
|
+
code: "KeyB",
|
|
635
|
+
modifiers: ["primary"]
|
|
636
|
+
},
|
|
637
|
+
"desktop:windows": {
|
|
638
|
+
code: "KeyB",
|
|
639
|
+
modifiers: ["primary"]
|
|
640
|
+
},
|
|
641
|
+
"desktop:linux": {
|
|
642
|
+
code: "KeyB",
|
|
643
|
+
modifiers: ["primary"]
|
|
644
|
+
},
|
|
645
|
+
"web:macos": {
|
|
646
|
+
code: "KeyB",
|
|
647
|
+
modifiers: ["primary", "alt"]
|
|
648
|
+
},
|
|
649
|
+
"web:windows": {
|
|
650
|
+
code: "KeyB",
|
|
651
|
+
modifiers: ["primary", "alt"]
|
|
652
|
+
}
|
|
653
|
+
},
|
|
654
|
+
regions: ["page", "editable"],
|
|
655
|
+
modals: [],
|
|
656
|
+
resolve: () => ({
|
|
657
|
+
status: "handled",
|
|
658
|
+
run: () => {
|
|
659
|
+
layout.toggleSidebar();
|
|
660
|
+
}
|
|
661
|
+
})
|
|
662
|
+
});
|
|
639
663
|
const disposePanels = ctx.slots.subscribe("main", retainMainPanels);
|
|
640
664
|
retainMainPanels();
|
|
641
665
|
return () => {
|
|
666
|
+
disposeShortcut();
|
|
642
667
|
layout.dispose();
|
|
643
668
|
disposePanels();
|
|
644
669
|
disposeRegistration();
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
11
11
|
import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots';
|
|
12
12
|
import type { PanelInfo } from './service.ts';
|
|
13
|
+
import { zh } from './shortcut-locales.ts';
|
|
13
14
|
export { LayoutController } from './service.ts';
|
|
14
15
|
export type { ILayout, MainPanelId, PanelInfo } from './service.ts';
|
|
15
16
|
/** Selector hook over root-scoped panel selection. */
|
|
@@ -21,6 +22,10 @@ declare module '@deepseek-ai/cordis' {
|
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
25
|
+
interface LocaleNamespaceMap {
|
|
26
|
+
/** Layout keyboard command labels. */
|
|
27
|
+
'shortcuts.layout': keyof typeof zh;
|
|
28
|
+
}
|
|
24
29
|
interface GlobalStandardProps {
|
|
25
30
|
/** Subscribe to the selected main panel independently of parent renders. */
|
|
26
31
|
usePanelInfo: UsePanelInfo;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* right-panel show/hide from ui-sidebar-right) — writes stay inside the
|
|
9
9
|
* store's declared action set, shared with the root registration.
|
|
10
10
|
*/
|
|
11
|
-
import type { BoundActions } from '@deepseek-ai/dsh-client-ui-slots';
|
|
11
|
+
import type { BoundActions, HostObservable } from '@deepseek-ai/dsh-client-ui-slots';
|
|
12
12
|
import type { Branded } from '@deepseek-ai/dsh-brand';
|
|
13
13
|
import type { createLayoutStore } from './stores.ts';
|
|
14
14
|
/** Identity shared by a sidebar panel entry and its main-slot occupant. */
|
|
@@ -22,6 +22,8 @@ export interface PanelInfo {
|
|
|
22
22
|
export type PanelActions = BoundActions<ReturnType<typeof createLayoutStore>>;
|
|
23
23
|
/** Panel navigation and geometry actions exposed through ctx.layout. */
|
|
24
24
|
export interface ILayout {
|
|
25
|
+
/** Selected central panel from the same root store used by `usePanelInfo`. */
|
|
26
|
+
readonly panelInfo: HostObservable<PanelInfo>;
|
|
25
27
|
/**
|
|
26
28
|
* Select a global central panel without changing the current Session.
|
|
27
29
|
* @param panelId - registered main key, or null to show the Conversation.
|
|
@@ -50,12 +52,14 @@ export interface ILayout {
|
|
|
50
52
|
export declare class LayoutController implements ILayout {
|
|
51
53
|
private readonly panels;
|
|
52
54
|
private readonly hasMainPanel;
|
|
55
|
+
readonly panelInfo: HostObservable<PanelInfo>;
|
|
53
56
|
private navigation;
|
|
54
57
|
/**
|
|
55
58
|
* @param panels - actions of the instance shared with the root entry.
|
|
56
59
|
* @param hasMainPanel - checks the live main-slot registry for a panel id.
|
|
60
|
+
* @param panelInfo - root store's shared central-panel selection source.
|
|
57
61
|
*/
|
|
58
|
-
constructor(panels: PanelActions, hasMainPanel: (id: MainPanelId) => boolean);
|
|
62
|
+
constructor(panels: PanelActions, hasMainPanel: (id: MainPanelId) => boolean, panelInfo: HostObservable<PanelInfo>);
|
|
59
63
|
/** Select a global panel or return to the Conversation. */
|
|
60
64
|
selectPanel(panelId: MainPanelId | null): void;
|
|
61
65
|
/** @returns the new pending navigation's cancellation signal. */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-ui-layout",
|
|
3
3
|
"description": "Shell plugin: three-column AppFrame with drag handles, ctx.layout viewing-state service (navigation + panels)",
|
|
4
|
-
"version": "0.1.7-
|
|
4
|
+
"version": "0.1.7-rc.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"@deepseek-ai/dsh-client-locale",
|
|
32
32
|
"@deepseek-ai/dsh-client-ui-renderer",
|
|
33
33
|
"@deepseek-ai/dsh-client-ui-session",
|
|
34
|
-
"@deepseek-ai/dsh-client-ui-theme"
|
|
34
|
+
"@deepseek-ai/dsh-client-ui-theme",
|
|
35
|
+
"@deepseek-ai/dsh-client-shortcuts"
|
|
35
36
|
],
|
|
36
37
|
"platform": "web"
|
|
37
38
|
}
|
|
@@ -43,14 +44,15 @@
|
|
|
43
44
|
"devDependencies": {
|
|
44
45
|
"@types/react": "~18.3.1",
|
|
45
46
|
"react": "^18.2.0",
|
|
46
|
-
"@deepseek-ai/dsh-client-locale": "0.1.7-
|
|
47
|
-
"@deepseek-ai/dsh-brand": "0.1.7-
|
|
48
|
-
"@deepseek-ai/dsh-client-store": "0.1.7-
|
|
49
|
-
"@deepseek-ai/dsh-client-ui-renderer": "0.1.7-
|
|
50
|
-
"@deepseek-ai/dsh-client-ui-session": "0.1.7-
|
|
51
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.1.7-
|
|
52
|
-
"@deepseek-ai/dsh-client-ui-theme": "0.1.7-
|
|
53
|
-
"@deepseek-ai/cordis": "~4.0.4"
|
|
47
|
+
"@deepseek-ai/dsh-client-locale": "0.1.7-rc.2",
|
|
48
|
+
"@deepseek-ai/dsh-brand": "0.1.7-rc.2",
|
|
49
|
+
"@deepseek-ai/dsh-client-store": "0.1.7-rc.2",
|
|
50
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.2",
|
|
51
|
+
"@deepseek-ai/dsh-client-ui-session": "0.1.7-rc.2",
|
|
52
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.2",
|
|
53
|
+
"@deepseek-ai/dsh-client-ui-theme": "0.1.7-rc.2",
|
|
54
|
+
"@deepseek-ai/cordis": "~4.0.4",
|
|
55
|
+
"@deepseek-ai/dsh-client-shortcuts": "0.1.7-rc.2"
|
|
54
56
|
},
|
|
55
57
|
"files": [
|
|
56
58
|
"lib/index.js",
|