@deepseek-ai/dsh-client-ui-dockkit 0.1.6-alpha.2 → 0.1.7-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-dockkit/README.md
5
- README.md: f41668ad2eb6c16e13deb5fcc554c8717811a468
6
- README.zh.md: 6a4c70a1ddb46b75b614e9e63eaddd89f3641ac3
5
+ README.md: 613cdbcb105108eb848160f1cb6bc8ee28d39fac
6
+ README.zh.md: 77b256f01dd46aea5144e9f8da644fd39330f187
package/README.md CHANGED
@@ -50,13 +50,15 @@ Everything host-specific arrives through props:
50
50
  | `TabRenderer` | one tab's body (`renderTab`), drawn flush to the pane's edges and the unbordered strip's bottom edge with the insets it chooses, and optionally what its chip or panel header shows as a title (`renderTabTitle`, falling back to the record's `title`); the embedder dispatches on `tab.kind` |
51
51
  | `DockIntents` | the settled results of every gesture |
52
52
 
53
- `DockController` satisfies `DockIntents` as written, so the simplest embedding hands the controller straight to `DockSurface`. An embedder that routes through its own store implements the same method names instead. Three props carry control policy rather than gestures: `canSplit` (surface-wide, the pane budget; disables the split control with `splitPaneDisabled`), `canAddTab(paneId)` (per pane, omits the add control; leave it out to draw one in every pane), and `canCloseTab(tabId)` (per tab, withholds the chip's close control and the menu's close item together; leave it out to keep every tab closable). Hiding the add control moves nothing else in the strip, and a withheld close moves nothing in the chip — the close control paints over the title's end rather than beside it. A pane's lone chip whose close is withheld draws quiet — no capsule, no hover fill — since there is nothing to select against and nothing to do to it. The kit adds one policy of its own, the room rule below, which disables a pane's split control with `splitPaneNarrow`; `onRoom(fits)` reports its readings so an embedder splitting programmatically can honour the same rule.
53
+ `DockController` satisfies `DockIntents` as written, so an embedding can pass it to `DockLayout` for retained Sidebar layouts or `DockSurface` for recursive split trees. An embedder that routes through its own store implements the same method names instead. Three props carry control policy rather than gestures: `canSplit` (surface-wide, the pane budget; disables the split control with `splitPaneDisabled`), `canAddTab(paneId)` (per pane, omits the add control; leave it out to draw one in every pane), and `canCloseTab(tabId)` (per tab, withholds the chip's close control and the menu's close item together; leave it out to keep every tab closable). Hiding the add control moves nothing else in the strip, and a withheld close moves nothing in the chip — the close control paints over the title's end rather than beside it. A pane's lone chip whose close is withheld draws quiet — no capsule, no hover fill — since there is nothing to select against and nothing to do to it. The kit adds one policy of its own, the room rule below, which disables a pane's split control with `splitPaneNarrow`; `onRoom(fits)` reports its readings so an embedder splitting programmatically can honour the same rule.
54
54
 
55
55
  `dropZones="horizontal"` offers two half-pane hints; once budget or width forbids another split, the whole body accepts a move. A hint is a dashed card inset 8px inside its region, showing the zone's glyph and `labels.dropZone[zone]`; the preview layer covers all tab-body content, while the card under the pointer takes the accent and its neighbour stays a quiet outline. `minPaneFraction` sets the preview minimum, and `planResizeSplit` accepts the same minimum for the committed operation. The Sidebar uses 0.2 and enforces two panes in its own store. The generic engine retains its tree and other split directions. `hideSplitWhenBlocked` hides a blocked split control — pane budget spent or pane too narrow — instead of rendering it disabled; its default is false.
56
56
 
57
57
  A tab's `kind` is an opaque string. Seeded tabs are factories (`DockControllerOptions`), so what a fresh pane contains is the embedder's decision, not this package's. Content identity is the pair (`kind`, `contentId`): `findContentTab(state, contentId, kind?)` finds the tab showing it anywhere and `findPaneContentTab(state, paneId, contentId, kind?)` within one pane, and `planOpenContent` focuses that tab instead of opening another unless told `revealIfOpened: false`; an explicit `index` seats a new tab at a strip slot rather than at the end.
58
58
 
59
- `DockSurface` is the docked area. Chrome around it — a rail, a collapsed presentation, any history controls — belongs to the embedder, which reads `state.expanded` and decides; the kit ships no undo/redo control of its own. Surface-wide controls the embedder does want on the surface go through the `chrome` prop, which the kit places at the far end of the top-right pane's tab strip (the last child of every row split, the first of every column split), so a surface needs no header row of its own. `FloatLayer` owns its own gestures and positions panels in viewport coordinates, so it may be mounted anywhere, including a portal.
59
+ `DockLayout`, used by the Sidebar, keeps each tab in a stable DOM cell across selection, pane moves and floating. It accepts one docked pane or two horizontal panes; CSS Grid resolves their widths. `keepMounted(tab)` retains visited bodies and `active` controls Session visibility without reparenting content.
60
+
61
+ `DockSurface` is the docked area for the separate recursive renderer. Chrome around it — a rail, a collapsed presentation, any history controls — belongs to the embedder, which reads `state.expanded` and decides; the kit ships no undo/redo control of its own. Surface-wide controls the embedder does want on the surface go through the `chrome` prop, which the kit places at the far end of the top-right pane's tab strip (the last child of every row split, the first of every column split), so a surface needs no header row of its own. `FloatLayer` owns its own gestures and positions panels in viewport coordinates, so it may be mounted anywhere, including a portal.
60
62
 
61
63
  <a id="interaction-rules-worth-keeping"></a>
62
64
  ## Interaction rules worth keeping
package/README.zh.md CHANGED
@@ -50,13 +50,15 @@ kind: "package-reference"
50
50
  | `TabRenderer` | 一个 tab 的正文(`renderTab`),贴着格的边缘和(不带边线的)tab 条底边绘制、自己决定留白,以及可选的 chip 或浮窗头部显示的标题(`renderTabTitle`,回退到记录的 `title`);嵌入方按 `tab.kind` 分发 |
51
51
  | `DockIntents` | 每次手势落定的结果 |
52
52
 
53
- `DockController` 原样满足 `DockIntents`,所以最简单的嵌入就是把 controller 直接交给 `DockSurface`。经由自己 store 路由的嵌入方则实现同名方法。有三个 props 承载的是控制策略而非手势:`canSplit`(整面有效,即格预算;用 `splitPaneDisabled` 禁用分栏控件)、`canAddTab(paneId)`(按格,省略添加控件;不传则每格都画)与 `canCloseTab(tabId)`(按 tab,把 chip 的关闭控件和菜单的关闭项一并收起;不传则每个 tab 都可关闭)。隐藏添加控件不会移动 tab 条里的其它任何东西,收起关闭也不会移动 chip 里的任何东西——关闭控件压在标题末端之上而非并排。某格仅剩的一个 chip 在关闭被收起时画成安静样式——没有胶囊底色,没有悬停填充——因为既没有别的 tab 可供选择,也没有任何可对它做的事。套件自己再加一条策略,即下文的空间规则,它用 `splitPaneNarrow` 禁用某格的分栏控件;`onRoom(fits)` 上报其读数,让以编程方式分栏的嵌入方能遵守同一规则。
53
+ `DockController` 原样满足 `DockIntents`,因此嵌入方可将它交给 `DockLayout` 来保活 Sidebar 布局,或交给 `DockSurface` 来渲染递归分割树。经由自己 store 路由的嵌入方则实现同名方法。有三个 props 承载的是控制策略而非手势:`canSplit`(整面有效,即格预算;用 `splitPaneDisabled` 禁用分栏控件)、`canAddTab(paneId)`(按格,省略添加控件;不传则每格都画)与 `canCloseTab(tabId)`(按 tab,把 chip 的关闭控件和菜单的关闭项一并收起;不传则每个 tab 都可关闭)。隐藏添加控件不会移动 tab 条里的其它任何东西,收起关闭也不会移动 chip 里的任何东西——关闭控件压在标题末端之上而非并排。某格仅剩的一个 chip 在关闭被收起时画成安静样式——没有胶囊底色,没有悬停填充——因为既没有别的 tab 可供选择,也没有任何可对它做的事。套件自己再加一条策略,即下文的空间规则,它用 `splitPaneNarrow` 禁用某格的分栏控件;`onRoom(fits)` 上报其读数,让以编程方式分栏的嵌入方能遵守同一规则。
54
54
 
55
55
  `dropZones="horizontal"` 提供左右两个半区提示;预算或宽度不允许再拆时,正文整格接收移动。提示是一张内缩 8px 的虚线卡片,显示该落区的图形和 `labels.dropZone[zone]`;预览层覆盖全部 tab 正文,指针所在的卡片取强调色,另一张保持安静的轮廓。`minPaneFraction` 控制预览的最小比例,`planResizeSplit` 接受相同最小值以约束提交;Sidebar使用0.2并在自己的store限制两格。通用引擎仍保留原有树与其它分割方向。 `hideSplitWhenBlocked` 在分栏被阻止时(窗格预算已满或格太窄)直接隐藏分栏控件而不是渲染禁用态,默认值为 false。
56
56
 
57
57
  tab 的 `kind` 是不透明字符串。种子 tab 是工厂(`DockControllerOptions`),因此新格里放什么由嵌入方决定,与本包无关。内容身份是二元组(`kind`、`contentId`):`findContentTab(state, contentId, kind?)` 在任意位置找到展示它的 tab,`findPaneContentTab(state, paneId, contentId, kind?)` 在一个格内找;`planOpenContent` 会聚焦该 tab 而非再开一个,除非被告知 `revealIfOpened: false`;显式的 `index` 把新 tab 放到 tab 条的某个位置而非末尾。
58
58
 
59
- `DockSurface` 是停靠区。它周围的 chrome——轨道、折叠形态、任何历史控件——属于嵌入方,由嵌入方读取 `state.expanded` 后自行决定;套件不自带撤销/重做控件。嵌入方确实想放到面上的整面控件通过 `chrome` prop 传入,套件把它放在右上格 tab 条的最末端(每个横向分裂的最后一个子节点、每个纵向分裂的第一个子节点),因此停靠面不需要自己的标题行。`FloatLayer` 拥有自己的手势并以视口坐标定位浮窗,因此可以挂在任何位置,包括 portal 里。
59
+ Sidebar 使用的 `DockLayout` 在选中项变化、跨格移动与浮动期间,把每个 tab 保留在稳定的 DOM 容器中。它接受一个停靠格或左右两个格,由 CSS Grid 计算宽度。`keepMounted(tab)` 保留已访问正文,`active` 控制 Session 可见性,不改变内容的父子关系。
60
+
61
+ `DockSurface` 是独立递归渲染器的停靠区。它周围的 chrome——轨道、折叠形态、任何历史控件——属于嵌入方,由嵌入方读取 `state.expanded` 后自行决定;套件不自带撤销/重做控件。嵌入方确实想放到面上的整面控件通过 `chrome` prop 传入,套件把它放在右上格 tab 条的最末端(每个横向分裂的最后一个子节点、每个纵向分裂的第一个子节点),因此停靠面不需要自己的标题行。`FloatLayer` 拥有自己的手势并以视口坐标定位浮窗,因此可以挂在任何位置,包括 portal 里。
60
62
 
61
63
  <a id="interaction-rules-worth-keeping"></a>
62
64
  ## 值得保留的交互规则
@@ -129,6 +129,82 @@
129
129
  min-height: 0;
130
130
  }
131
131
 
132
+ /* Every tab remains a sibling. Explicit tracks determine placement; order only
133
+ changes painting, so raising a float never moves a connected DOM ancestor. */
134
+ .tabLayout {
135
+ display: grid;
136
+ flex: 1 1 auto;
137
+ grid-template-rows: minmax(0, 1fr);
138
+ min-width: 0;
139
+ min-height: 0;
140
+ }
141
+
142
+ .tabCell,
143
+ .emptyTabHost {
144
+ display: flex;
145
+ min-width: 0;
146
+ min-height: 0;
147
+ z-index: var(--dsh-dockkit-dock-layer, 10);
148
+ }
149
+
150
+ .tabCell[hidden] {
151
+ display: none;
152
+ }
153
+
154
+ /* A zero-sized Grid item supplies a stacking context, not a containing block
155
+ for its fixed child. Floats share one layer and use Grid paint order within it. */
156
+ .floatingCell {
157
+ width: 0;
158
+ height: 0;
159
+ z-index: var(--dsh-dockkit-float-layer, 60);
160
+ }
161
+
162
+ .tabHost {
163
+ min-width: 0;
164
+ min-height: 0;
165
+ pointer-events: auto;
166
+ }
167
+
168
+ .tabHost:not(.float),
169
+ .emptyTabHost {
170
+ background: var(--dsw-alias-bg-base);
171
+ }
172
+
173
+ .tabCell[data-dockkit-host='dock'][data-dockkit-column='0'] > .tabHost {
174
+ border-left: 0.5px solid var(--dsw-alias-border-l4);
175
+ }
176
+
177
+ .tabHostHeader {
178
+ flex: none;
179
+ }
180
+
181
+ .tabHostBody {
182
+ position: relative;
183
+ min-width: 0;
184
+ }
185
+
186
+ .tabLayoutDivider {
187
+ width: 0;
188
+ cursor: col-resize;
189
+ z-index: calc(var(--dsh-dockkit-dock-layer, 10) + 1);
190
+ pointer-events: auto;
191
+ }
192
+
193
+ .tabLayoutDivider::before {
194
+ top: 0;
195
+ bottom: 0;
196
+ left: -0.25px;
197
+ width: 0.5px;
198
+ }
199
+
200
+ .tabLayoutDivider::after {
201
+ top: 0;
202
+ bottom: 0;
203
+ left: -4px;
204
+ right: -4px;
205
+ background: linear-gradient(to bottom, transparent, var(--dsw-alias-label-caption) 50%, transparent) center / 1px 100% no-repeat;
206
+ }
207
+
132
208
  /* A body that overflows scrolls inside its pane; nothing escapes the pane. */
133
209
  .pane {
134
210
  display: flex;
@@ -221,6 +297,24 @@
221
297
  margin-left: 4px;
222
298
  }
223
299
 
300
+ /* macOS desktop: an embedder may pin this surface under the window's top drag
301
+ band (ui-layout .leadingBand — full-window geometry that Electron composes
302
+ in DOM order, not by stacking). The chip run, the strip-end chrome, the
303
+ pane body, and the split divider subtract their whole boxes so tabs,
304
+ controls, content, and the resize grab stay usable even where they overlap
305
+ the band; the strip's blank fill and top inset keep the band's drag, so the
306
+ row moves the window like a native tab bar. The divider must be named here:
307
+ it is a plain div, so ui-web base.css's interactive-element subtraction
308
+ never matches it. Hidden surfaces contribute nothing: Blink skips
309
+ non-visible boxes when it collects draggable regions. */
310
+ :global(html[data-platform='darwin']) .stripTabs,
311
+ :global(html[data-platform='darwin']) .stripChrome,
312
+ :global(html[data-platform='darwin']) .paneBody,
313
+ :global(html[data-platform='darwin']) .float,
314
+ :global(html[data-platform='darwin']) .divider {
315
+ -webkit-app-region: no-drag;
316
+ }
317
+
224
318
  /* The slot between two chips: a 10px box with a hairline down its centre.
225
319
  Targeted by a drag, the same box draws the caret instead, so the chips
226
320
  around it never move. */
@@ -450,10 +544,11 @@
450
544
  display: flex;
451
545
  flex-direction: column;
452
546
  min-width: 96px;
453
- padding: 4px;
454
- background: var(--dsw-alias-bg-layer-3);
547
+ padding: 3px;
548
+ background: var(--dsw-specific-menu);
549
+ backdrop-filter: var(--dsw-menu-backdrop-filter);
455
550
  border: 0.5px solid var(--dsw-alias-border-l2);
456
- border-radius: 6px;
551
+ border-radius: 8px;
457
552
  }
458
553
 
459
554
  .menu:not(:has([role='menuitem'], [role='menuitemcheckbox'], [role='menuitemradio'])) {
@@ -461,13 +556,13 @@
461
556
  }
462
557
 
463
558
  .menuItem {
464
- padding: 5px 8px;
559
+ padding: 4px 7px;
465
560
  color: var(--dsw-alias-label-primary);
466
- font-size: var(--dsh-content-font-size-secondary, 13px);
561
+ font-size: calc(var(--dsh-content-font-size-secondary, 13px) - 1px);
467
562
  text-align: left;
468
563
  background: transparent;
469
564
  border: none;
470
- border-radius: 4px;
565
+ border-radius: 5px;
471
566
  cursor: pointer;
472
567
  }
473
568