@deepseek-ai/dsh-client-ui-sidebar-right 0.2.0-rc.1 → 0.2.1-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.i18n.yaml CHANGED
@@ -21,8 +21,8 @@
21
21
  en: 6d7f145ecb1fd140
22
22
  zh: 666d142ef249f6cb
23
23
  /deepseek-ai-dsh-client-ui-sidebar-right/the-expand-button:
24
- en: 28b0bc3d4050b0e0
25
- zh: 733b44ad9309f49a
24
+ en: 2614d3f179290606
25
+ zh: 88c08ee260ba57b7
26
26
  /deepseek-ai-dsh-client-ui-sidebar-right/state:
27
27
  en: 2b15cdf5f1383291
28
28
  zh: 57018036d77363ba
@@ -30,8 +30,8 @@
30
30
  en: ef2af36136c4a919
31
31
  zh: 48d992e2c923a130
32
32
  /deepseek-ai-dsh-client-ui-sidebar-right/ctx-sidebarright:
33
- en: 8cffef22360aa177
34
- zh: a09eef5bb7fdbd61
33
+ en: 4519c4633d160c2a
34
+ zh: e4019a62532f885a
35
35
  /deepseek-ai-dsh-client-ui-sidebar-right/the-tab-domain:
36
36
  en: 717b86e05b44ebdd
37
37
  zh: 0f4f694d0ea1d7f8
@@ -51,5 +51,5 @@
51
51
  en: 76a763b4dd71babd
52
52
  zh: fdc966dd427a82fb
53
53
  /deepseek-ai-dsh-client-ui-sidebar-right/known-limitations-and-deferred-work/dev-note:
54
- en: 33274d681fac9a21
55
- zh: e024c1c4766b289f
54
+ en: e67f633dc6fe7773
55
+ zh: e88aeb617ffb74ea
package/README.md CHANGED
@@ -58,7 +58,7 @@ While the panel is hidden, one button in the conversation header's corner seat (
58
58
 
59
59
  The panel takes the conversation's ground colour and content font sizes rather than a raised layer of its own: it is a column of the page, not a card over it.
60
60
 
61
- The root-scoped `rightbar` entry renders the selected Session and background Sessions with initialized `keepMounted` bodies through separate `rightbar.session` subtrees. Each View owns its Session reference. Only the foreground Conversation reports a frame track and binds public navigation; other subtrees stay hidden without deleting tab state.
61
+ The root-scoped `rightbar` entry renders the selected Session and background Sessions with initialized `keepMounted` bodies through separate `rightbar.session` subtrees. Each View owns its Session reference. Only the foreground subtree reports a frame track; other subtrees stay hidden without deleting tab state.
62
62
 
63
63
  <a id="state"></a>
64
64
  ## State
@@ -94,9 +94,9 @@ Two more seats extend what is already there: `sidebar.right.tab.guide` (chain) r
94
94
 
95
95
  `preferNewPane: true` first asks the target docked pane to split under the normal two-pane and room rules, then falls back to that pane when no split is available. `replaceTab` takes precedence and disables this preference.
96
96
 
97
- `close(tabId)` closes a tab; `active()` reads the active tab. `isExpanded()` and `toggleExpanded()` read and drive the column's expansion; `toggleFullscreen(target)` applies the panel control’s display-mode action to a captured dock pane. Layout operations, for callers that arrange the column programmatically, each recorded like the gesture it stands in for: `focus(tabId)` focuses a tab and its pane; `split(paneId?)` splits a docked pane (the active one by default) under the same pane budget and room rule as the strip's control and returns the new pane's id, or `undefined` — recording nothing — when it cannot; `float(tabId, rect?)` takes a docked tab out into a panel; `dock(paneId)` returns a floating panel to the active docked pane. A tab or pane that does not exist, or already is where the call would put it, is left alone. The face exposes operations only: no layout snapshot, no operation log, no lookup by address. `_undo()` / `_redo()` step the mounted surface's history; they are `@internal` — the sequence has no user-facing control, and these exist for tests. Commands need a mounted session surface; with none, they throw rather than write into a surface nobody draws. `mounted` is the observable of the session whose seat is on screen, `undefined` while a global panel replaces the Conversation or no session is selected. A component that opens content from its own mount effect reads it through a bound hook and acts once it is defined: the frame mounts the Conversation column ahead of this seat, and the seat publishes its binding from a passive effect of the same commit, so an effect that assumed a bound seat would run first and throw. The plan review's automatic open reads it this way.
97
+ `close(tabId)` closes a tab; `active()` reads the active tab. `isExpanded()` and `toggleExpanded()` read and drive the column's expansion; `toggleFullscreen(target)` applies the panel control’s display-mode action to a captured dock pane. Layout operations, for callers that arrange the column programmatically, each recorded like the gesture it stands in for: `focus(tabId)` focuses a tab and its pane; `split(paneId?)` splits a docked pane (the active one by default) under the same pane budget and room rule as the strip's control and returns the new pane's id, or `undefined` — recording nothing — when it cannot; `float(tabId, rect?)` takes a docked tab out into a panel; `dock(paneId)` returns a floating panel to the active docked pane. A tab or pane that does not exist, or already is where the call would put it, is left alone. The face exposes operations only: no layout snapshot, no operation log, no lookup by address. `_undo()` / `_redo()` step the on-screen surface's history; they are `@internal` — the sequence has no user-facing control, and these exist for tests. Commands act on the on-screen Session through its adopted store; with no Session on screen, or before the runtime mints its store, they throw rather than write into a surface nobody draws. `mounted` is the observable of the Session on screen: the selected Session while the Conversation fills the main column, `undefined` while a global panel replaces it or no Session is selected. The plugin derives it from the Session selection and `ctx.layout.panelInfo`, and it changes before React renders the change that causes it, so a component rendered in that commit reads the arriving Session and may act on it from its effects: the frame renders that Session's seat in the same pass, which mints its store. Seats never publish it; each reports only what it renders with: the room its docking kit measured, which `split` and `preferNewPane` apply to that Session's panes (unmeasured panes have room), and whether its frame width presents the panel fullscreen automatically, which the fullscreen command applies. The plan review's automatic open reads `mounted` through a bound hook.
98
98
 
99
- `focusedTarget(element?)` resolves the current DOM pane and tab, including an embedding iframe; outside focus returns no page. `commandTarget(element?)` additionally allows an outside open to use the mounted Session's active dock pane. Captures include the occurrence and navigation revision; `isTargetCurrent(target)` rejects changed Sessions, moved or reopened tabs, and intervening navigation. Focus and pointer activation remain separate from persisted layout selection.
99
+ `focusedTarget(element?)` resolves the current DOM pane and tab, including an embedding iframe; outside focus returns no page. `commandTarget(element?)` additionally allows an outside open to use the on-screen Session's active dock pane. Captures include the occurrence and navigation revision; `isTargetCurrent(target)` rejects changed Sessions, moved or reopened tabs, and intervening navigation. Focus and pointer activation remain separate from persisted layout selection.
100
100
 
101
101
  <a id="the-tab-domain"></a>
102
102
  ## The Tab domain
@@ -155,5 +155,3 @@ None; this package neither assembles nor sends a provider request.
155
155
  None.
156
156
 
157
157
  </details>
158
-
159
- **Runtime invariant:** No companion is published. The two services (`sidebarRight`, `sidebarRightTabs`) are provided through `ctx.reflect.provide` inside one effect and torn down with it; the seat's binding and the Tab domain's occurrence lifetimes are asserted directly by this package's specs, and no independent observation exists to diverge from them.
package/README.zh.md CHANGED
@@ -58,7 +58,7 @@ kind: "package-reference"
58
58
 
59
59
  面板取会话区的底色与正文字号,而不是自成一层浮起的表面:它是页面的一列,不是压在页面上的卡片。
60
60
 
61
- root 作用域的 `rightbar` 入口通过独立的 `rightbar.session` 子树渲染选中的 Session,以及拥有已初始化 `keepMounted` 正文的后台 Session。每个 View 拥有自身的 Session reference。只有前台 Conversation 上报框架列宽并绑定公共导航;其他子树保持隐藏,不删除 tab 状态。
61
+ root 作用域的 `rightbar` 入口通过独立的 `rightbar.session` 子树渲染选中的 Session,以及拥有已初始化 `keepMounted` 正文的后台 Session。每个 View 拥有自身的 Session reference。只有前台子树上报框架列宽;其他子树保持隐藏,不删除 tab 状态。
62
62
 
63
63
  <a id="state"></a>
64
64
  ## 状态
@@ -94,9 +94,9 @@ tab 类型分两阶段注册,随包发布的引导类型走的正是别的包
94
94
 
95
95
  `preferNewPane: true` 会先按普通的两格上限与空间规则尝试分割目标停靠格,无法分栏时回退到该格。`replaceTab` 优先并会禁用这项偏好。
96
96
 
97
- `close(tabId)` 关闭一个 tab;`active()` 读取活动 tab。`isExpanded()` 与 `toggleExpanded()` 读取并驱动该列的展开;`toggleFullscreen(target)` 对捕获的停靠分栏执行面板控件的显示模式操作。布局操作供以编程方式安排该列的调用方使用,每个都像它替代的手势一样被记录:`focus(tabId)` 聚焦一个 tab 及其格;`split(paneId?)` 在与 tab 条控件相同的格预算与空间规则下分栏一个停靠格(默认活跃格),返回新格的 id,做不到时返回 `undefined`——且不记录任何东西;`float(tabId, rect?)` 把停靠 tab 浮出为浮窗;`dock(paneId)` 把浮窗放回活跃停靠格。不存在的 tab 或格、或已处于调用目标状态的,都原样不动。该接口只暴露操作:没有布局快照、没有操作日志、没有按地址查找。`_undo()` / `_redo()` 步进已挂载停靠面的历史;它们是 `@internal`——序列没有面向用户的控件,这两个只为测试存在。命令需要一个已挂载的会话停靠面;没有时它们 throw,而不是写进一个没人绘制的面里。`mounted` 是「席位正在屏幕上的那个会话」的可观察值,全局面板替代 Conversation 或没有选中会话时为 `undefined`。要在自己的挂载 effect 里打开内容的组件通过绑定的 hook 读取它,并在它有值后再行动:框架把 Conversation 列挂载在这个席位之前,而席位在同一次提交的 passive effect 里才发布绑定,一个假定席位已绑定的 effect 会先运行并 throw。计划审阅的自动打开就是这样读取它的。
97
+ `close(tabId)` 关闭一个 tab;`active()` 读取活动 tab。`isExpanded()` 与 `toggleExpanded()` 读取并驱动该列的展开;`toggleFullscreen(target)` 对捕获的停靠分栏执行面板控件的显示模式操作。布局操作供以编程方式安排该列的调用方使用,每个都像它替代的手势一样被记录:`focus(tabId)` 聚焦一个 tab 及其格;`split(paneId?)` 在与 tab 条控件相同的格预算与空间规则下分栏一个停靠格(默认活跃格),返回新格的 id,做不到时返回 `undefined`——且不记录任何东西;`float(tabId, rect?)` 把停靠 tab 浮出为浮窗;`dock(paneId)` 把浮窗放回活跃停靠格。不存在的 tab 或格、或已处于调用目标状态的,都原样不动。该接口只暴露操作:没有布局快照、没有操作日志、没有按地址查找。`_undo()` / `_redo()` 步进屏幕上停靠面的历史;它们是 `@internal`——序列没有面向用户的控件,这两个只为测试存在。命令通过屏幕上会话已采用的 store 作用于该会话;没有会话在屏幕上、或运行时尚未创建它的 store 时,命令 throw,而不是写进一个没人绘制的面里。`mounted` 是「屏幕上的会话」的可观察值:Conversation 占据主栏时为选中的会话,全局面板替代它或没有选中会话时为 `undefined`。插件从会话选择与 `ctx.layout.panelInfo` 推导它,它在 React 渲染引起变化的那次更新之前就已改变,因此同一次提交里渲染的组件读到的是到来的会话,并可以在自己的 effect 里对它行动:框架在同一轮渲染里渲染该会话的席位,席位随之创建它的 store。席位从不发布它;每个席位只上报它渲染时依据的事实:停靠套件测得的空间,`split` 与 `preferNewPane` 把它用于该会话的分栏(未测量的分栏视为有空间);以及框架宽度是否让面板自动全屏,全屏命令据此执行。计划审阅的自动打开通过绑定的 hook 读取 `mounted`。
98
98
 
99
- `focusedTarget(element?)` 解析当前 DOM 分栏和标签页,也接受嵌入 iframe;焦点位于外部时不返回页面。`commandTarget(element?)` 另外允许外部打开动作使用已挂载会话的活动停靠分栏。捕获值包含 occurrence 和导航版本;`isTargetCurrent(target)` 拒绝会话变更、已移动或重新打开的标签页,以及期间发生的导航。焦点和指针激活独立于持久化的布局选择。
99
+ `focusedTarget(element?)` 解析当前 DOM 分栏和标签页,也接受嵌入 iframe;焦点位于外部时不返回页面。`commandTarget(element?)` 另外允许外部打开动作使用屏幕上会话的活动停靠分栏。捕获值包含 occurrence 和导航版本;`isTargetCurrent(target)` 拒绝会话变更、已移动或重新打开的标签页,以及期间发生的导航。焦点和指针激活独立于持久化的布局选择。
100
100
 
101
101
  <a id="the-tab-domain"></a>
102
102
  ## Tab 域
@@ -155,5 +155,3 @@ Tab 域按(Session,Tab id)保留品牌化 occurrence id、导航、中止
155
155
  无。
156
156
 
157
157
  </details>
158
-
159
- **运行时不变量:** 不发布 companion。两个服务(`sidebarRight`、`sidebarRightTabs`)在同一个 effect 内经 `ctx.reflect.provide` 提供并随之拆除;席位绑定与 Tab 域 occurrence 的生命周期由本包的 spec 直接断言,不存在会与之分歧的独立观察。
package/lib/client.js CHANGED
@@ -5426,38 +5426,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5426
5426
  "unavailable": "P3OORG_unavailable"
5427
5427
  };
5428
5428
  //#endregion
5429
- //#region lib/types/client/shell/close-focus.js
5430
- /** Focus continuity after page operations replace docked or floating pane elements. */
5431
- /**
5432
- * Focus the page selected by an open or split after its DOM has committed.
5433
- * @param document - product document owning the input focus.
5434
- * @param sessionId - Session whose page is opening.
5435
- * @param open - synchronous operation returning its selected pane, or undefined when unchanged.
5436
- */
5437
- function openWithPaneFocus(document, sessionId, open) {
5438
- let paneId;
5439
- (0, react_dom.flushSync)(() => {
5440
- paneId = open();
5441
- });
5442
- if (paneId !== void 0) visibleSidebarPane(document, sessionId, paneId)?.focus({ preventScroll: true });
5443
- }
5444
- /**
5445
- * Commit a focused page's removal before focusing a surviving visible pane.
5446
- * @param document - product document owning the input focus.
5447
- * @param sessionId - Session whose page is closing.
5448
- * @param paneId - pane whose page is closing; preferred if it survives.
5449
- * @param close - synchronous cleanup and layout removal; errors preserve focus.
5450
- */
5451
- function closeWithPaneFocus(document, sessionId, paneId, close) {
5452
- const before = document.activeElement;
5453
- const owner = before?.closest("[data-sidebar-right-session]");
5454
- const source = before?.closest("[data-dockkit-pane], [data-dockkit-float]");
5455
- const retain = owner?.dataset.sidebarRightSession === sessionId && (source?.dataset.dockkitPane ?? source?.dataset.dockkitFloat) === paneId;
5456
- (0, react_dom.flushSync)(close);
5457
- if (!retain || document.activeElement !== document.body && document.activeElement !== before) return;
5458
- visibleSidebarPane(document, sessionId, paneId)?.focus({ preventScroll: true });
5459
- }
5460
- //#endregion
5461
5429
  //#region lib/types/client/shell/SidebarRight.js
5462
5430
  /**
5463
5431
  * The Sidebar's seat in the frame, and the panel it draws.
@@ -5589,12 +5557,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5589
5557
  "data-sidebar-right-tab": tab.id,
5590
5558
  "data-sidebar-right-occurrence": id,
5591
5559
  children: content
5592
- }) : (0, react_jsx_runtime.jsx)("div", {
5560
+ }, id) : (0, react_jsx_runtime.jsx)("div", {
5593
5561
  className: SidebarRight_module_css_default.tabBody,
5594
5562
  "data-sidebar-right-tab": tab.id,
5595
5563
  "data-sidebar-right-occurrence": id,
5596
5564
  children: content
5597
- });
5565
+ }, id);
5598
5566
  }
5599
5567
  /**
5600
5568
  * Dispatch a tab's body to its registered type.
@@ -5712,7 +5680,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5712
5680
  "data-sidebar-right-session": sessionId,
5713
5681
  "data-sidebar-right-panel": fullscreen ? "fullscreen" : "push",
5714
5682
  "data-sidebar-right-open": expanded || void 0,
5715
- "aria-hidden": !expanded || void 0,
5683
+ "aria-hidden": !expanded && surface.layout.floats.length === 0 || void 0,
5716
5684
  children: (0, react_jsx_runtime.jsx)("div", {
5717
5685
  className: SidebarRight_module_css_default.panelBody,
5718
5686
  children: (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_dockkit.DockLayout, {
@@ -5747,22 +5715,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5747
5715
  }
5748
5716
  /**
5749
5717
  * The right column's occupant: stable tab containers, docked or floating.
5750
- * It is also where the
5751
- * frame learns the panel's presentation, and where `ctx.sidebarRight` learns
5752
- * which session it is acting on, because this is the seat that knows both.
5718
+ * It is also where the frame learns the panel's presentation, because this is
5719
+ * the seat that knows it. `ctx.sidebarRight` names the on-screen Session
5720
+ * itself; this seat reports only what it renders with: the room its kit
5721
+ * measured and the automatic fullscreen rule of its frame width.
5753
5722
  */
5754
- function RightbarSeat({ sessionId, width, viewportWidth, canShow, useStore, actions, t, renderSlot, syncPresentation, bindService, openTab, closeTab, useTabTypes, useTabNavigation, occurrence, retainTab, active, useShortcuts, splitPane, toggleFullscreen }) {
5723
+ function RightbarSeat({ sessionId, width, viewportWidth, canShow, useStore, actions, t, renderSlot, syncPresentation, measureRoom, reportAutoFullscreen, openTab, closeTab, useTabTypes, useTabNavigation, occurrence, retainTab, active, useShortcuts, splitPane, toggleFullscreen }) {
5755
5724
  const shortcuts = useShortcuts((entries) => entries);
5756
- const surfaces = useStore((state) => state.bySession);
5757
- const surface = surfaces[sessionId];
5725
+ const surface = useStore((state) => state.bySession[sessionId]);
5758
5726
  const shown = active && surface !== void 0 && surface.layout.expanded;
5759
5727
  const autoFullscreen = viewportWidth < 768;
5760
5728
  const fullscreen = autoFullscreen || surface?.layout.mode === "fullscreen";
5761
5729
  const panelRef = (0, react.useRef)(null);
5762
- const room = (0, react.useRef)(/* @__PURE__ */ new Map());
5763
5730
  const reportRoom = (0, react.useCallback)((fits) => {
5764
- room.current = fits;
5765
- }, []);
5731
+ measureRoom((paneId) => fits.get(paneId)?.row !== false);
5732
+ }, [measureRoom]);
5766
5733
  const track = shown && !autoFullscreen;
5767
5734
  (0, react.useEffect)(() => {
5768
5735
  if (active && surface === void 0) actions.open(sessionId);
@@ -5781,6 +5748,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5781
5748
  fullscreen,
5782
5749
  canShow
5783
5750
  ]);
5751
+ (0, react.useLayoutEffect)(() => {
5752
+ reportAutoFullscreen(autoFullscreen);
5753
+ }, [reportAutoFullscreen, autoFullscreen]);
5784
5754
  (0, react.useLayoutEffect)(() => {
5785
5755
  if (!active) return;
5786
5756
  let disposed = false;
@@ -5816,26 +5786,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5816
5786
  fullscreen: false
5817
5787
  });
5818
5788
  } : void 0, [syncPresentation, active]);
5819
- (0, react.useEffect)(() => active ? bindService({
5820
- sessionId,
5821
- actions,
5822
- surfaces,
5823
- autoFullscreen,
5824
- closeWithFocus: (paneId, close) => {
5825
- closeWithPaneFocus(document, sessionId, paneId, close);
5826
- },
5827
- openWithFocus: (open) => {
5828
- openWithPaneFocus(document, sessionId, open);
5829
- },
5830
- canSplitPane: (paneId) => room.current.get(paneId)?.row !== false
5831
- }) : void 0, [
5832
- bindService,
5833
- sessionId,
5834
- actions,
5835
- surfaces,
5836
- autoFullscreen,
5837
- active
5838
- ]);
5839
5789
  if (surface === void 0) return null;
5840
5790
  return (0, react_jsx_runtime.jsx)(SidebarPanel, {
5841
5791
  sessionId,
@@ -5862,6 +5812,38 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5862
5812
  });
5863
5813
  }
5864
5814
  //#endregion
5815
+ //#region lib/types/client/shell/close-focus.js
5816
+ /** Focus continuity after page operations replace docked or floating pane elements. */
5817
+ /**
5818
+ * Focus the page selected by an open or split after its DOM has committed.
5819
+ * @param document - product document owning the input focus.
5820
+ * @param sessionId - Session whose page is opening.
5821
+ * @param open - synchronous operation returning its selected pane, or undefined when unchanged.
5822
+ */
5823
+ function openWithPaneFocus(document, sessionId, open) {
5824
+ let paneId;
5825
+ (0, react_dom.flushSync)(() => {
5826
+ paneId = open();
5827
+ });
5828
+ if (paneId !== void 0) visibleSidebarPane(document, sessionId, paneId)?.focus({ preventScroll: true });
5829
+ }
5830
+ /**
5831
+ * Commit a focused page's removal before focusing a surviving visible pane.
5832
+ * @param document - product document owning the input focus.
5833
+ * @param sessionId - Session whose page is closing.
5834
+ * @param paneId - pane whose page is closing; preferred if it survives.
5835
+ * @param close - synchronous cleanup and layout removal; errors preserve focus.
5836
+ */
5837
+ function closeWithPaneFocus(document, sessionId, paneId, close) {
5838
+ const before = document.activeElement;
5839
+ const owner = before?.closest("[data-sidebar-right-session]");
5840
+ const source = before?.closest("[data-dockkit-pane], [data-dockkit-float]");
5841
+ const retain = owner?.dataset.sidebarRightSession === sessionId && (source?.dataset.dockkitPane ?? source?.dataset.dockkitFloat) === paneId;
5842
+ (0, react_dom.flushSync)(close);
5843
+ if (!retain || document.activeElement !== document.body && document.activeElement !== before) return;
5844
+ visibleSidebarPane(document, sessionId, paneId)?.focus({ preventScroll: true });
5845
+ }
5846
+ //#endregion
5865
5847
  //#region lib/types/client/shell/RightbarRoot.js
5866
5848
  /** Root-scoped controller for the right Sidebar's Session content. */
5867
5849
  function SessionView({ view, visible, SessionProvider, renderSlot, mountView, width, viewportWidth, canShow }) {
@@ -6260,19 +6242,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6260
6242
  *
6261
6243
  * The surface is per session and its state lives in that session's store
6262
6244
  * instance, which the slot runtime mints per session and a root service cannot
6263
- * reach on its own. Two paths lead in. The mounted seat publishes its binding —
6264
- * session id, bound actions, its surface — for exactly as long as it is mounted,
6265
- * and every command on the public face goes through that binding; a command
6266
- * arriving with no seat mounted has no session to act on and fails loudly rather
6267
- * than writing into a surface nobody is drawing. `mounted` publishes that
6268
- * binding's session as an observable, so a consumer that wants to open content
6269
- * as soon as a seat is on screen subscribes to it instead of assuming one is
6270
- * bound when its own effect runs. And the plugin adopts each
6271
- * session's store instance as the runtime mints it, so the controller reaches
6272
- * any session's store by id and syncs the Tab domain from that store's commits.
6245
+ * reach on its own. The plugin adopts each session's store instance as the
6246
+ * runtime mints it, so the controller reaches any session's store by id and
6247
+ * syncs the Tab domain from that store's commits, on screen or not.
6248
+ *
6249
+ * The plugin also names the Session on screen — the selected Session while the
6250
+ * Conversation fills the main column — from the selection and the main panel,
6251
+ * before React renders either change, and publishes it as `mounted`. Every
6252
+ * command on the public face acts on that Session through its adopted store; a
6253
+ * command with no Session on screen, or with one whose store the runtime has not
6254
+ * minted, has nothing to act on and fails loudly rather than writing into a
6255
+ * surface nobody is drawing. The seats never publish which Session they draw:
6256
+ * they report only what they render with, the room their docking kit measured
6257
+ * and the automatic fullscreen rule of their frame width.
6273
6258
  *
6274
6259
  * A tab's own actions (`tabActions`) aim at the session the tab is in, not at
6275
- * the mounted one: they run through that session's adopted store, so a callback
6260
+ * the on-screen one: they run through that session's adopted store, so a callback
6276
6261
  * fired after the user switched sessions still lands where its tab is, and they
6277
6262
  * do nothing for a session whose store was never minted.
6278
6263
  *
@@ -6284,24 +6269,39 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6284
6269
  * hand the store one settled intent and record the navigation in the Tab
6285
6270
  * domain. Placement is the caller's option, never a type's property.
6286
6271
  *
6287
- * The registration adopts Session stores and injects the mounted seat binding;
6288
- * callers use the service's navigation methods.
6272
+ * The registration adopts Session stores, names the on-screen Session, and
6273
+ * forwards the seats' reports; callers use the service's navigation methods.
6289
6274
  */
6290
6275
  /**
6291
- * Create the public controller and the plugin-private store adoption callback.
6276
+ * Create the public controller and the plugin-private Session callbacks.
6292
6277
  * Adoption reconciles restored records before any seat renders, then follows commits.
6293
6278
  * @param tabs - registered tab types.
6294
6279
  * @param pin - resource retention for an occurrence's lifetime.
6295
- * @returns the controller and plugin-owned adoption and scope-removal callbacks.
6280
+ * @param host - the viewport rule and focus continuity the commands use.
6281
+ * @returns the controller; store adoption and scope removal; naming the on-screen Session; and recording a seat's room rule.
6296
6282
  */
6297
- function createSidebarRightController(tabs, pin) {
6283
+ function createSidebarRightController(tabs, pin, host) {
6298
6284
  const adopted = /* @__PURE__ */ new Map();
6285
+ const rooms = /* @__PURE__ */ new Map();
6286
+ const onScreen = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(void 0);
6299
6287
  const inventory = new SidebarTabInventory();
6300
- const controller = new SidebarRightController(tabs, pin, adopted, inventory.source);
6288
+ const controller = new SidebarRightController(tabs, pin, host, {
6289
+ adopted,
6290
+ rooms,
6291
+ onScreen,
6292
+ openTabs: inventory.source
6293
+ });
6301
6294
  return {
6302
6295
  controller,
6303
6296
  forget: (sessionId) => {
6304
6297
  inventory.remove(sessionId);
6298
+ rooms.delete(sessionId);
6299
+ },
6300
+ show: (sessionId) => {
6301
+ if (onScreen.getSnapshot() !== sessionId) onScreen.set(sessionId);
6302
+ },
6303
+ measure: (sessionId, canSplitPane) => {
6304
+ rooms.set(sessionId, canSplitPane);
6305
6305
  },
6306
6306
  adopt(sessionId, store) {
6307
6307
  adopted.get(sessionId)?.unsubscribe();
@@ -6328,13 +6328,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6328
6328
  /** Cross-plugin right-Sidebar face (ctx.sidebarRight). */
6329
6329
  var SidebarRightController = class {
6330
6330
  tabs;
6331
- adopted;
6331
+ host;
6332
6332
  /** Open tab metadata across saved and adopted Sessions, independent of visible seats. */
6333
6333
  openTabs;
6334
- mountedSession = (0, _deepseek_ai_dsh_client_store.createSnapshotStore)(void 0);
6335
- /** The mounted seat's session; see {@link ISidebarRight.mounted}. */
6336
- mounted = this.mountedSession;
6337
- binding;
6334
+ /** The on-screen Session; see {@link ISidebarRight.mounted}. */
6335
+ mounted;
6336
+ adopted;
6337
+ rooms;
6338
6338
  closeHandlers = /* @__PURE__ */ new Map();
6339
6339
  /**
6340
6340
  * Register resource cleanup before explicit removal. Failure preserves the tab.
@@ -6357,13 +6357,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6357
6357
  /**
6358
6358
  * @param tabs - the tab-type registry consulted to claim an address.
6359
6359
  * @param pin - `ctx.resources.pin`, which the Tab domain holds addresses with.
6360
- * @param adopted - plugin-owned session stores used by occurrence actions.
6361
- * @param openTabs - plugin-owned metadata source across saved and adopted layouts.
6360
+ * @param host - the viewport rule and focus continuity the commands use.
6361
+ * @param sessions - plugin-owned adopted stores, room rules, on-screen Session, and open tab metadata.
6362
6362
  */
6363
- constructor(tabs, pin, adopted = /* @__PURE__ */ new Map(), openTabs = new SidebarTabInventory().source) {
6363
+ constructor(tabs, pin, host, sessions) {
6364
6364
  this.tabs = tabs;
6365
- this.adopted = adopted;
6366
- this.openTabs = openTabs;
6365
+ this.host = host;
6366
+ this.adopted = sessions.adopted;
6367
+ this.rooms = sessions.rooms;
6368
+ this.mounted = sessions.onScreen;
6369
+ this.openTabs = sessions.openTabs;
6367
6370
  this.tabDomain = new TabDomain(this, pin);
6368
6371
  }
6369
6372
  /**
@@ -6375,27 +6378,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6375
6378
  return Object.values(this.adopted.get(sessionId)?.store.getSnapshot().bySession[sessionId]?.layout.tabs ?? {});
6376
6379
  }
6377
6380
  /**
6378
- * Adopt the mounted seat's binding, replacing any previous one.
6379
- *
6380
- * Called from the seat while it is mounted, and released when it leaves.
6381
- * @param binding - the mounted seat's session, actions, and the store's surfaces.
6382
- * @returns a release callback that clears exactly this binding.
6383
- */
6384
- bind(binding) {
6385
- this.binding = binding;
6386
- this.publishMounted();
6387
- return () => {
6388
- if (this.binding !== binding) return;
6389
- this.binding = void 0;
6390
- this.publishMounted();
6391
- };
6392
- }
6393
- /** Publish the mounted session only when it changes; a republished binding for the same session is silent. */
6394
- publishMounted() {
6395
- const next = this.binding?.sessionId;
6396
- if (this.mountedSession.getSnapshot() !== next) this.mountedSession.set(next);
6397
- }
6398
- /**
6399
6381
  * Open a resource: claim it, place it, reveal the column, record the navigation.
6400
6382
  * @param address - a `dsh-resource://<type>/…` address.
6401
6383
  * @param options - placement, the opening type, and navigation parameters.
@@ -6476,10 +6458,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6476
6458
  }
6477
6459
  /** The steps both opens share: one store intent, and the navigation record for the tab it settles on. */
6478
6460
  place(sessionId, actions, claim, address, placement, params) {
6479
- const surface = this.adopted.get(sessionId)?.store.getSnapshot().bySession[sessionId] ?? (this.binding?.sessionId === sessionId ? this.binding.surfaces[sessionId] : void 0);
6461
+ const surface = this.adopted.get(sessionId)?.store.getSnapshot().bySession[sessionId];
6480
6462
  const targetPane = surface === void 0 ? void 0 : placement.paneId ?? (0, _deepseek_ai_dsh_client_ui_dockkit.activeDockPaneId)(surface.layout);
6481
6463
  const target = targetPane === void 0 ? void 0 : surface?.layout.nodes[targetPane];
6482
- const preferNewPane = placement.preferNewPane === true && placement.replaceTab === void 0 && surface !== void 0 && target?.kind === "pane" && target.host === "dock" && target.tabs.length > 0 && (0, _deepseek_ai_dsh_client_ui_dockkit.canSplit)(surface.layout) && (0, _deepseek_ai_dsh_client_ui_dockkit.dockPaneIds)(surface.layout).length < 2 && this.binding?.sessionId === sessionId && this.binding.canSplitPane(target.id);
6464
+ const preferNewPane = placement.preferNewPane === true && placement.replaceTab === void 0 && surface !== void 0 && target?.kind === "pane" && target.host === "dock" && target.tabs.length > 0 && (0, _deepseek_ai_dsh_client_ui_dockkit.canSplit)(surface.layout) && (0, _deepseek_ai_dsh_client_ui_dockkit.dockPaneIds)(surface.layout).length < 2 && this.mounted.getSnapshot() === sessionId && this.canSplitPane(sessionId, target.id);
6483
6465
  const commit = () => {
6484
6466
  actions.openContent(sessionId, {
6485
6467
  kind: claim.kind,
@@ -6498,7 +6480,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6498
6480
  };
6499
6481
  const layout = surface?.layout;
6500
6482
  const replaced = placement.replaceTab === void 0 ? void 0 : layout?.tabs[placement.replaceTab];
6501
- const revealed = layout === void 0 || placement.revealIfOpened === false ? void 0 : (0, _deepseek_ai_dsh_client_ui_dockkit.findContentTab)(layout, claim.contentId, claim.kind);
6483
+ const replacementPane = replaced === void 0 || layout === void 0 ? void 0 : (0, _deepseek_ai_dsh_client_ui_dockkit.findTabPane)(layout, replaced.id);
6484
+ const revealed = layout === void 0 ? void 0 : claim.contentId === pageAddress(claim.kind) ? (0, _deepseek_ai_dsh_client_ui_dockkit.findPaneContentTab)(layout, replacementPane?.host === "dock" ? replacementPane.id : placement.paneId ?? (0, _deepseek_ai_dsh_client_ui_dockkit.activeDockPaneId)(layout), claim.contentId, claim.kind) : placement.revealIfOpened === false ? void 0 : (0, _deepseek_ai_dsh_client_ui_dockkit.findContentTab)(layout, claim.contentId, claim.kind);
6502
6485
  if (replaced === void 0 || replaced.id === revealed) {
6503
6486
  commit();
6504
6487
  return;
@@ -6506,20 +6489,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6506
6489
  this.removeAfterCleanup(sessionId, replaced, commit);
6507
6490
  }
6508
6491
  /**
6509
- * Close one tab of the mounted session; the sole docked guide remains open.
6492
+ * Close one tab of the on-screen Session; the sole docked guide remains open.
6510
6493
  * @param tabId - the tab to close.
6511
6494
  */
6512
6495
  close(tabId) {
6513
- const { sessionId, actions } = this.require();
6514
- if (this.adopted.has(sessionId)) {
6515
- this.closeIn(sessionId, tabId);
6516
- return;
6517
- }
6518
- actions.closeTab(sessionId, tabId);
6496
+ this.closeIn(this.require().sessionId, tabId);
6519
6497
  }
6520
6498
  /**
6521
6499
  * The active tab of the active pane.
6522
- * @returns the record, or `undefined` with no mounted surface.
6500
+ * @returns the record, or `undefined` without an on-screen surface.
6523
6501
  */
6524
6502
  active() {
6525
6503
  const layout = this.mountedSurface()?.layout;
@@ -6529,15 +6507,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6529
6507
  }
6530
6508
  /**
6531
6509
  * Whether the column is currently showing its panel.
6532
- * @returns `true` while expanded; `false` while collapsed or with no mounted surface.
6510
+ * @returns `true` while expanded; `false` while collapsed or without an on-screen surface.
6533
6511
  */
6534
6512
  isExpanded() {
6535
6513
  return this.mountedSurface()?.layout.expanded ?? false;
6536
6514
  }
6537
6515
  /** Collapse the column, or expand it and focus its active dock pane after rendering. */
6538
6516
  toggleExpanded() {
6539
- const { sessionId, actions, openWithFocus } = this.require();
6540
- openWithFocus(() => {
6517
+ const { sessionId, actions } = this.require();
6518
+ this.host.openWithFocus(sessionId, () => {
6541
6519
  actions.toggleExpanded(sessionId);
6542
6520
  const layout = this.mountedSurface()?.layout;
6543
6521
  return layout?.expanded ? (0, _deepseek_ai_dsh_client_ui_dockkit.activeDockPaneId)(layout) : void 0;
@@ -6558,26 +6536,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6558
6536
  * @returns current focused page identity, or undefined outside a visible sidebar page.
6559
6537
  */
6560
6538
  focusedTarget(element = document.activeElement) {
6561
- const binding = this.binding;
6562
- const surface = this.mountedSurface();
6563
- return binding === void 0 || surface === void 0 ? void 0 : sidebarTargetFromElement(element, binding.sessionId, surface.layout, (tabId) => this.tabDomain.occurrence(binding.sessionId, { id: tabId }));
6539
+ const screen = this.screen();
6540
+ return screen === void 0 ? void 0 : sidebarTargetFromElement(element, screen.sessionId, screen.surface.layout, (tabId) => this.tabDomain.occurrence(screen.sessionId, { id: tabId }));
6564
6541
  }
6565
6542
  /**
6566
- * Choose a focused sidebar pane, or the mounted Session's active dock pane for an outside open.
6543
+ * Choose a focused sidebar pane, or the on-screen Session's active dock pane for an outside open.
6567
6544
  * @param element - live command input target; stale sidebar markup never falls back to another pane.
6568
- * @returns captured target, or undefined without a mounted Session.
6545
+ * @returns captured target, or undefined without an on-screen surface.
6569
6546
  */
6570
6547
  commandTarget(element = document.activeElement) {
6571
6548
  const focused = this.focusedTarget(element);
6572
6549
  if (focused !== void 0 || element?.closest("[data-sidebar-right-session]")) return focused;
6573
- const binding = this.binding;
6574
- const layout = this.mountedSurface()?.layout;
6575
- if (binding === void 0 || layout === void 0) return void 0;
6550
+ const screen = this.screen();
6551
+ if (screen === void 0) return void 0;
6552
+ const { sessionId, surface: { layout } } = screen;
6576
6553
  const pane = (0, _deepseek_ai_dsh_client_ui_dockkit.getPane)(layout, (0, _deepseek_ai_dsh_client_ui_dockkit.activeDockPaneId)(layout));
6577
6554
  const tabId = pane.activeTabId;
6578
- const held = tabId === void 0 ? void 0 : this.tabDomain.occurrence(binding.sessionId, { id: tabId });
6555
+ const held = tabId === void 0 ? void 0 : this.tabDomain.occurrence(sessionId, { id: tabId });
6579
6556
  return {
6580
- sessionId: binding.sessionId,
6557
+ sessionId,
6581
6558
  paneId: pane.id,
6582
6559
  host: pane.host,
6583
6560
  tabId,
@@ -6592,10 +6569,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6592
6569
  */
6593
6570
  openTabFromTarget(kind, target) {
6594
6571
  if (!this.isTargetCurrent(target)) return;
6595
- this.require().openWithFocus(() => {
6572
+ const { sessionId, actions } = this.require();
6573
+ this.host.openWithFocus(sessionId, () => {
6596
6574
  const tab = target.tabId === void 0 ? void 0 : this.mountedSurface()?.layout.tabs[target.tabId];
6597
6575
  if (target.host === "float" && tab?.kind === kind && this.tabs.get(kind)?.multiple !== true) {
6598
- this.require().actions.setExpanded(target.sessionId, true);
6576
+ actions.setExpanded(sessionId, true);
6599
6577
  this.focus(tab.id);
6600
6578
  } else this.openTab(kind, {
6601
6579
  ...target.host === "dock" ? { paneId: target.paneId } : {},
@@ -6623,20 +6601,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6623
6601
  const surface = this.mountedSurface();
6624
6602
  if (surface === void 0 || target.tabId === void 0) return "unavailable";
6625
6603
  const tabId = target.tabId;
6626
- const binding = this.require();
6627
- binding.closeWithFocus(target.paneId, () => {
6604
+ const { sessionId, actions } = this.require();
6605
+ this.host.closeWithFocus(sessionId, target.paneId, () => {
6628
6606
  if (canCloseTab(surface, tabId)) this.close(tabId);
6629
- else binding.actions.setExpanded(target.sessionId, false);
6607
+ else actions.setExpanded(sessionId, false);
6630
6608
  });
6631
6609
  return "closed";
6632
6610
  }
6633
6611
  /**
6634
6612
  * Check a captured target before acting, including reopened records and intervening navigation.
6635
6613
  * @param target - identity captured while resolving the input.
6636
- * @returns whether the mounted Session, pane, tab lifetime and navigation still match.
6614
+ * @returns whether the on-screen Session, pane, tab lifetime and navigation still match.
6637
6615
  */
6638
6616
  isTargetCurrent(target) {
6639
- if (this.binding?.sessionId !== target.sessionId) return false;
6617
+ if (this.mounted.getSnapshot() !== target.sessionId) return false;
6640
6618
  const layout = this.mountedSurface()?.layout;
6641
6619
  const pane = layout?.nodes[target.paneId];
6642
6620
  if (pane?.kind !== "pane" || pane.host !== target.host) return false;
@@ -6658,7 +6636,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6658
6636
  if (!layout.expanded) return "collapsed";
6659
6637
  if ((0, _deepseek_ai_dsh_client_ui_dockkit.getPane)(layout, target.paneId).tabs.length === 0) return "empty";
6660
6638
  if (!(0, _deepseek_ai_dsh_client_ui_dockkit.canSplit)(layout) || (0, _deepseek_ai_dsh_client_ui_dockkit.dockPaneIds)(layout).length >= 2) return "budget";
6661
- if (!this.require().canSplitPane(target.paneId)) return "width";
6639
+ if (!this.canSplitPane(target.sessionId, target.paneId)) return "width";
6662
6640
  }
6663
6641
  /**
6664
6642
  * Toggle the right panel's display mode through the same action as its chrome button.
@@ -6666,9 +6644,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6666
6644
  */
6667
6645
  toggleFullscreen(target) {
6668
6646
  if (!this.isTargetCurrent(target) || target.host === "float" || !this.isExpanded()) return;
6669
- const { sessionId, actions, autoFullscreen } = this.require();
6670
- const fullscreen = autoFullscreen === true || this.mountedSurface()?.layout.mode === "fullscreen";
6671
- if (fullscreen && autoFullscreen === true) actions.setExpanded(sessionId, false);
6647
+ const { sessionId, actions } = this.require();
6648
+ const autoFullscreen = this.host.autoFullscreen();
6649
+ const fullscreen = autoFullscreen || this.mountedSurface()?.layout.mode === "fullscreen";
6650
+ if (fullscreen && autoFullscreen) actions.setExpanded(sessionId, false);
6672
6651
  actions.setMode(sessionId, fullscreen ? "push" : "fullscreen");
6673
6652
  }
6674
6653
  /**
@@ -6677,15 +6656,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6677
6656
  * @returns the new pane's id, or `undefined` when nothing was split.
6678
6657
  */
6679
6658
  split(paneId) {
6680
- const { sessionId, actions, canSplitPane } = this.require();
6659
+ const { sessionId, actions } = this.require();
6681
6660
  const layout = this.mountedSurface()?.layout;
6682
6661
  if (layout === void 0) return void 0;
6683
6662
  const target = paneId ?? (0, _deepseek_ai_dsh_client_ui_dockkit.activeDockPaneId)(layout);
6684
6663
  const node = layout.nodes[target];
6685
6664
  if (node === void 0 || node.kind !== "pane" || node.host !== "dock") return void 0;
6686
- if (!(0, _deepseek_ai_dsh_client_ui_dockkit.canSplit)(layout) || (0, _deepseek_ai_dsh_client_ui_dockkit.dockPaneIds)(layout).length >= 2 || !canSplitPane(target)) return void 0;
6665
+ if (!(0, _deepseek_ai_dsh_client_ui_dockkit.canSplit)(layout) || (0, _deepseek_ai_dsh_client_ui_dockkit.dockPaneIds)(layout).length >= 2 || !this.canSplitPane(sessionId, target)) return void 0;
6687
6666
  let created;
6688
- this.require().openWithFocus(() => {
6667
+ this.host.openWithFocus(sessionId, () => {
6689
6668
  actions.splitPane(sessionId, target, (id) => {
6690
6669
  created = id;
6691
6670
  });
@@ -6716,7 +6695,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6716
6695
  actions.unfloatPane(sessionId, paneId);
6717
6696
  }
6718
6697
  /**
6719
- * Step the mounted session's surface back one intent.
6698
+ * Step the on-screen Session's surface back one intent.
6720
6699
  *
6721
6700
  * @internal Not part of the product: the sequence is an architectural fact
6722
6701
  * with no user-facing control yet. Kept reachable for tests.
@@ -6726,7 +6705,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6726
6705
  actions.undo(sessionId);
6727
6706
  }
6728
6707
  /**
6729
- * Step the mounted session's surface forward one intent.
6708
+ * Step the on-screen Session's surface forward one intent.
6730
6709
  *
6731
6710
  * @internal See `_undo`.
6732
6711
  */
@@ -6734,10 +6713,26 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6734
6713
  const { sessionId, actions } = this.require();
6735
6714
  actions.redo(sessionId);
6736
6715
  }
6737
- /** The mounted session's surface; `undefined` without a seat or before its first open. */
6716
+ /**
6717
+ * The on-screen Session and its committed surface; `undefined` with no Session
6718
+ * on screen, before the runtime mints its store, or before its first open.
6719
+ */
6720
+ screen() {
6721
+ const sessionId = this.mounted.getSnapshot();
6722
+ if (sessionId === void 0) return void 0;
6723
+ const surface = this.adopted.get(sessionId)?.store.getSnapshot().bySession[sessionId];
6724
+ return surface === void 0 ? void 0 : {
6725
+ sessionId,
6726
+ surface
6727
+ };
6728
+ }
6729
+ /** The on-screen Session's surface; see {@link SidebarRightController.screen}. */
6738
6730
  mountedSurface() {
6739
- const { binding } = this;
6740
- return binding === void 0 ? void 0 : this.adopted.get(binding.sessionId)?.store.getSnapshot().bySession[binding.sessionId] ?? binding.surfaces[binding.sessionId];
6731
+ return this.screen()?.surface;
6732
+ }
6733
+ /** Whether a Session's docked pane has room for two working halves, as its seat last measured; unmeasured panes do. */
6734
+ canSplitPane(sessionId, paneId) {
6735
+ return this.rooms.get(sessionId)?.(paneId) ?? true;
6741
6736
  }
6742
6737
  /**
6743
6738
  * The store actions a tab's own action on `sessionId` runs through: that
@@ -6748,8 +6743,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6748
6743
  return this.adopted.get(sessionId)?.store.actions;
6749
6744
  }
6750
6745
  require() {
6751
- if (this.binding === void 0) throw new Error("sidebarRight: no session surface is mounted");
6752
- return this.binding;
6746
+ const sessionId = this.mounted.getSnapshot();
6747
+ const actions = sessionId === void 0 ? void 0 : this.actionsFor(sessionId);
6748
+ if (sessionId === void 0 || actions === void 0) throw new Error("sidebarRight: no session surface is mounted");
6749
+ return {
6750
+ sessionId,
6751
+ actions
6752
+ };
6753
6753
  }
6754
6754
  };
6755
6755
  //#endregion
@@ -9044,9 +9044,33 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9044
9044
  views.dispose();
9045
9045
  };
9046
9046
  }, "ui-sidebar-right: retained Session views");
9047
- const { controller, adopt, forget } = createSidebarRightController(tabs, (address, signal) => {
9047
+ const layout = ctx.layout;
9048
+ let autoFullscreen = false;
9049
+ const { controller, adopt, forget, show, measure } = createSidebarRightController(tabs, (address, signal) => {
9048
9050
  ctx.resources.pin(address, signal);
9051
+ }, {
9052
+ autoFullscreen: () => autoFullscreen,
9053
+ openWithFocus: (sessionId, open) => {
9054
+ openWithPaneFocus(document, sessionId, open);
9055
+ },
9056
+ closeWithFocus: (sessionId, paneId, close) => {
9057
+ closeWithPaneFocus(document, sessionId, paneId, close);
9058
+ }
9049
9059
  });
9060
+ ctx.effect(() => {
9061
+ const sync = () => {
9062
+ const selected = views.source.getSnapshot().find((view) => view.selected);
9063
+ show(layout.panelInfo.getSnapshot().activePanelId === null ? selected?.sessionId : void 0);
9064
+ };
9065
+ const unsubscribeViews = views.source.subscribe(sync);
9066
+ const unsubscribePanel = layout.panelInfo.subscribe(sync);
9067
+ sync();
9068
+ return () => {
9069
+ unsubscribeViews();
9070
+ unsubscribePanel();
9071
+ show(void 0);
9072
+ };
9073
+ }, "ui-sidebar-right: on-screen Session");
9050
9074
  const disposeRegistry = ctx.reflect.provide("sidebarRightTabs", tabs);
9051
9075
  const disposeService = ctx.reflect.provide("sidebarRight", controller);
9052
9076
  ctx.effect(() => () => {
@@ -9085,13 +9109,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9085
9109
  };
9086
9110
  }
9087
9111
  };
9088
- const layout = ctx.layout;
9089
9112
  const injected = {
9090
9113
  syncPresentation({ shown, track, fullscreen }) {
9091
9114
  if (shown) layout.openRightbar(track, fullscreen);
9092
9115
  else layout.closeRightbar();
9093
9116
  },
9094
- bindService: (binding) => controller.bind(binding),
9117
+ reportAutoFullscreen: (value) => {
9118
+ autoFullscreen = value;
9119
+ },
9095
9120
  splitPane: (paneId) => {
9096
9121
  controller.split(paneId);
9097
9122
  },
@@ -9145,6 +9170,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9145
9170
  store,
9146
9171
  inject: (sessionId) => ({
9147
9172
  ...injected,
9173
+ measureRoom: (canSplitPane) => {
9174
+ measure(sessionId, canSplitPane);
9175
+ },
9148
9176
  closeTab: (tabId) => {
9149
9177
  try {
9150
9178
  controller.closeIn(sessionId, tabId);
@@ -6,7 +6,7 @@ export type { RightbarSeatProps, SidebarRightInjected, SidebarRightPresentation
6
6
  export type { GuideBodyProps, GuideInjected } from './tabs/guide/GuideBody.tsx';
7
7
  export type { ExpandButtonProps } from './shell/ExpandButton.tsx';
8
8
  export type { SidebarRightState, SurfaceState } from './stores.ts';
9
- export type { ISidebarRight, SidebarRightBinding, SidebarRightOpenResourceOptions, SidebarRightOpenTabOptions, SidebarRightPlacement, SidebarRightCloseHandler, SurfaceActions, } from './service.ts';
9
+ export type { ISidebarRight, SidebarRightOpenResourceOptions, SidebarRightOpenTabOptions, SidebarRightPlacement, SidebarRightCloseHandler, SurfaceActions, } from './service.ts';
10
10
  export type { SidebarRightGuideBox, SidebarRightGuideEntry, SidebarRightTabClaim, SidebarRightTabDefinition, SidebarRightTabPriority, } from './tab-registry.ts';
11
11
  export type { SidebarRightTabInfo, SidebarRightTabInjected, UseSidebarRightTabInfo, SidebarRightTabActions, SidebarRightTabMenuOwnerProps, SidebarRightTabNavigation, SidebarRightTabPlacement, SidebarRightGuideEntryOwnerProps, } from './contract/slots.ts';
12
12
  export type { SidebarRightNavigationParams, SidebarRightResourceParams, SidebarRightResourceParamsMap, SidebarRightTabParams, SidebarRightTabParamsFor, SidebarRightTabParamsMap, } from './contract/params.ts';
@@ -3,19 +3,22 @@
3
3
  *
4
4
  * The surface is per session and its state lives in that session's store
5
5
  * instance, which the slot runtime mints per session and a root service cannot
6
- * reach on its own. Two paths lead in. The mounted seat publishes its binding —
7
- * session id, bound actions, its surface — for exactly as long as it is mounted,
8
- * and every command on the public face goes through that binding; a command
9
- * arriving with no seat mounted has no session to act on and fails loudly rather
10
- * than writing into a surface nobody is drawing. `mounted` publishes that
11
- * binding's session as an observable, so a consumer that wants to open content
12
- * as soon as a seat is on screen subscribes to it instead of assuming one is
13
- * bound when its own effect runs. And the plugin adopts each
14
- * session's store instance as the runtime mints it, so the controller reaches
15
- * any session's store by id and syncs the Tab domain from that store's commits.
6
+ * reach on its own. The plugin adopts each session's store instance as the
7
+ * runtime mints it, so the controller reaches any session's store by id and
8
+ * syncs the Tab domain from that store's commits, on screen or not.
9
+ *
10
+ * The plugin also names the Session on screen — the selected Session while the
11
+ * Conversation fills the main column — from the selection and the main panel,
12
+ * before React renders either change, and publishes it as `mounted`. Every
13
+ * command on the public face acts on that Session through its adopted store; a
14
+ * command with no Session on screen, or with one whose store the runtime has not
15
+ * minted, has nothing to act on and fails loudly rather than writing into a
16
+ * surface nobody is drawing. The seats never publish which Session they draw:
17
+ * they report only what they render with, the room their docking kit measured
18
+ * and the automatic fullscreen rule of their frame width.
16
19
  *
17
20
  * A tab's own actions (`tabActions`) aim at the session the tab is in, not at
18
- * the mounted one: they run through that session's adopted store, so a callback
21
+ * the on-screen one: they run through that session's adopted store, so a callback
19
22
  * fired after the user switched sessions still lands where its tab is, and they
20
23
  * do nothing for a session whose store was never minted.
21
24
  *
@@ -27,8 +30,8 @@
27
30
  * hand the store one settled intent and record the navigation in the Tab
28
31
  * domain. Placement is the caller's option, never a type's property.
29
32
  *
30
- * The registration adopts Session stores and injects the mounted seat binding;
31
- * callers use the service's navigation methods.
33
+ * The registration adopts Session stores, names the on-screen Session, and
34
+ * forwards the seats' reports; callers use the service's navigation methods.
32
35
  */
33
36
  import { type SidebarRightTarget } from './focus.ts';
34
37
  import { type ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
@@ -37,7 +40,7 @@ import type { BoundActions } from '@deepseek-ai/dsh-client-ui-slots';
37
40
  import type { SessionId } from '@deepseek-ai/dsh-session/types';
38
41
  import type { SidebarRightResourceParams, SidebarRightTabParamsFor } from './contract/params.ts';
39
42
  import type { SidebarRightTabRegistry } from './tab-registry.ts';
40
- import { type SidebarRightState, type SurfaceState } from './stores.ts';
43
+ import { type SidebarRightState } from './stores.ts';
41
44
  import type { createSidebarRightStore } from './stores.ts';
42
45
  import { TabDomain, type PinResource } from './tab-domain.ts';
43
46
  import { SidebarTabInventory } from './tab-inventory.ts';
@@ -54,41 +57,54 @@ interface Adoption {
54
57
  readonly store: SidebarRightSurfaceStore;
55
58
  readonly unsubscribe: () => void;
56
59
  }
60
+ /** The room rule's verdict for a docked pane, as a seat's docking kit last measured it: whether two working halves would fit. */
61
+ type RoomRule = (paneId: PaneId) => boolean;
62
+ /** Plugin-owned Session state the controller reads; the plugin writes it through the factory's callbacks. */
63
+ interface SidebarRightSessions {
64
+ /** Each Session's latest adopted store. */
65
+ readonly adopted: ReadonlyMap<SessionId, Adoption>;
66
+ /** Each Session's room rule as its seat last reported it; a Session without one has room in every pane. */
67
+ readonly rooms: ReadonlyMap<SessionId, RoomRule>;
68
+ /** The Session the plugin names as on screen. */
69
+ readonly onScreen: ObservableSnapshot<SessionId | undefined>;
70
+ /** Open tab metadata across saved and adopted Sessions. */
71
+ readonly openTabs: SidebarTabInventory['source'];
72
+ }
57
73
  /**
58
- * Create the public controller and the plugin-private store adoption callback.
74
+ * Create the public controller and the plugin-private Session callbacks.
59
75
  * Adoption reconciles restored records before any seat renders, then follows commits.
60
76
  * @param tabs - registered tab types.
61
77
  * @param pin - resource retention for an occurrence's lifetime.
62
- * @returns the controller and plugin-owned adoption and scope-removal callbacks.
78
+ * @param host - the viewport rule and focus continuity the commands use.
79
+ * @returns the controller; store adoption and scope removal; naming the on-screen Session; and recording a seat's room rule.
63
80
  */
64
- export declare function createSidebarRightController(tabs: SidebarRightTabRegistry, pin: PinResource): {
81
+ export declare function createSidebarRightController(tabs: SidebarRightTabRegistry, pin: PinResource, host: SidebarRightHost): {
65
82
  controller: SidebarRightController;
66
83
  adopt: (sessionId: SessionId, store: SidebarRightSurfaceStore) => () => void;
67
84
  forget: (sessionId: SessionId) => void;
85
+ show: (sessionId: SessionId | undefined) => void;
86
+ measure: (sessionId: SessionId, canSplitPane: RoomRule) => void;
68
87
  };
69
- /** Everything a command needs, as the mounted seat sees it. */
70
- export interface SidebarRightBinding {
71
- /** The session the mounted seat is drawing. */
72
- readonly sessionId: SessionId;
73
- /** The seat's store's bound actions; every action names the session it acts on. */
74
- readonly actions: SurfaceActions;
88
+ /** What the controller needs from the page around it: the viewport rule and focus continuity. */
89
+ export interface SidebarRightHost {
75
90
  /**
76
- * The seat's store surfaces as last committed, keyed by session id; the
77
- * mounted session's is `surfaces[sessionId]`, absent before the seat's first
78
- * open. The runtime mints one store per session, so this holds that session.
91
+ * Whether the viewport is narrow enough that an expanded panel is presented fullscreen.
92
+ * @returns the rule's verdict for the frame width the seats last rendered at.
79
93
  */
80
- readonly surfaces: Readonly<Record<string, SurfaceState>>;
94
+ readonly autoFullscreen: () => boolean;
81
95
  /**
82
- * The room rule's verdict for a docked pane, as the kit last measured it:
83
- * whether two working halves would fit. Unmeasured panes fit.
96
+ * Commit a page operation and focus the pane it selects.
97
+ * @param sessionId - the Session whose page is opening.
98
+ * @param open - the synchronous operation; returns the selected pane, or `undefined` when unchanged.
84
99
  */
85
- readonly canSplitPane: (paneId: PaneId) => boolean;
86
- /** Commit a keyboard/menu close and retain focus on a surviving visible pane. */
87
- readonly closeWithFocus: (paneId: PaneId, close: () => void) => void;
88
- /** Commit a page operation and focus the pane it selects. */
89
- readonly openWithFocus: (open: () => PaneId | undefined) => void;
90
- /** Narrow viewports present an expanded panel fullscreen. */
91
- readonly autoFullscreen?: boolean;
100
+ readonly openWithFocus: (sessionId: SessionId, open: () => PaneId | undefined) => void;
101
+ /**
102
+ * Commit a keyboard/menu close and retain focus on a surviving visible pane.
103
+ * @param sessionId - the Session whose page is closing.
104
+ * @param paneId - the pane whose page is closing.
105
+ * @param close - the synchronous cleanup and removal.
106
+ */
107
+ readonly closeWithFocus: (sessionId: SessionId, paneId: PaneId, close: () => void) => void;
92
108
  }
93
109
  /** Where an open lands; every field is optional and the defaults are the common case. */
94
110
  export interface SidebarRightPlacement {
@@ -121,12 +137,13 @@ export type SidebarRightCloseHandler = (sessionId: SessionId, tab: TabRecord) =>
121
137
  /** The outward right-Sidebar face (`ctx.sidebarRight`). */
122
138
  export interface ISidebarRight {
123
139
  /**
124
- * The session whose seat is mounted, or `undefined` while no seat is on
125
- * screen (a global panel is active, or no session is selected). Moves when a
126
- * seat binds or releases, so a component that opens content from its own
127
- * mount effect reads it through a bound hook and acts once it is defined:
128
- * the frame mounts the Conversation column ahead of the seat, and the seat
129
- * publishes its binding from a passive effect of the same commit.
140
+ * The Session on screen, or `undefined` while none is (a global panel fills
141
+ * the main column, or no Session is selected). It changes before React
142
+ * renders the selection or panel change that causes it, so every component
143
+ * rendered in that commit reads the arriving Session, and the commands act on
144
+ * it from that commit's effects: its seat mints its store in the same render.
145
+ * Moves only when the on-screen Session changes; the Session's own store
146
+ * commits are silent.
130
147
  */
131
148
  readonly mounted: ObservableSnapshot<SessionId | undefined>;
132
149
  /**
@@ -150,13 +167,13 @@ export interface ISidebarRight {
150
167
  */
151
168
  openTab<K extends string>(kind: K, options?: SidebarRightOpenTabOptions<K>): void;
152
169
  /**
153
- * Close one tab of the mounted session; the sole docked guide remains open.
170
+ * Close one tab of the on-screen Session; the sole docked guide remains open.
154
171
  * @param tabId - the tab to close.
155
172
  */
156
173
  close(tabId: TabId): void;
157
174
  /**
158
175
  * The active tab of the active pane.
159
- * @returns the record, or `undefined` when no seat is mounted.
176
+ * @returns the record, or `undefined` with no Session on screen.
160
177
  */
161
178
  active(): TabRecord | undefined;
162
179
  /**
@@ -195,13 +212,13 @@ export interface ISidebarRight {
195
212
  /** Cross-plugin right-Sidebar face (ctx.sidebarRight). */
196
213
  export declare class SidebarRightController implements ISidebarRight {
197
214
  private readonly tabs;
198
- private readonly adopted;
215
+ private readonly host;
199
216
  /** Open tab metadata across saved and adopted Sessions, independent of visible seats. */
200
217
  readonly openTabs: SidebarTabInventory['source'];
201
- private readonly mountedSession;
202
- /** The mounted seat's session; see {@link ISidebarRight.mounted}. */
218
+ /** The on-screen Session; see {@link ISidebarRight.mounted}. */
203
219
  readonly mounted: ObservableSnapshot<SessionId | undefined>;
204
- private binding;
220
+ private readonly adopted;
221
+ private readonly rooms;
205
222
  private readonly closeHandlers;
206
223
  /**
207
224
  * Register resource cleanup before explicit removal. Failure preserves the tab.
@@ -218,26 +235,16 @@ export declare class SidebarRightController implements ISidebarRight {
218
235
  /**
219
236
  * @param tabs - the tab-type registry consulted to claim an address.
220
237
  * @param pin - `ctx.resources.pin`, which the Tab domain holds addresses with.
221
- * @param adopted - plugin-owned session stores used by occurrence actions.
222
- * @param openTabs - plugin-owned metadata source across saved and adopted layouts.
238
+ * @param host - the viewport rule and focus continuity the commands use.
239
+ * @param sessions - plugin-owned adopted stores, room rules, on-screen Session, and open tab metadata.
223
240
  */
224
- constructor(tabs: SidebarRightTabRegistry, pin: PinResource, adopted?: Map<SessionId, Adoption>, openTabs?: SidebarTabInventory['source']);
241
+ constructor(tabs: SidebarRightTabRegistry, pin: PinResource, host: SidebarRightHost, sessions: SidebarRightSessions);
225
242
  /**
226
243
  * Read the committed tabs of a Session so providers can restore their content.
227
244
  * @param sessionId - Session whose layout has been adopted.
228
245
  * @returns its open records, or an empty list before adoption.
229
246
  */
230
247
  tabsIn(sessionId: SessionId): readonly TabRecord[];
231
- /**
232
- * Adopt the mounted seat's binding, replacing any previous one.
233
- *
234
- * Called from the seat while it is mounted, and released when it leaves.
235
- * @param binding - the mounted seat's session, actions, and the store's surfaces.
236
- * @returns a release callback that clears exactly this binding.
237
- */
238
- bind(binding: SidebarRightBinding): () => void;
239
- /** Publish the mounted session only when it changes; a republished binding for the same session is silent. */
240
- private publishMounted;
241
248
  /**
242
249
  * Open a resource: claim it, place it, reveal the column, record the navigation.
243
250
  * @param address - a `dsh-resource://<type>/…` address.
@@ -284,18 +291,18 @@ export declare class SidebarRightController implements ISidebarRight {
284
291
  /** The steps both opens share: one store intent, and the navigation record for the tab it settles on. */
285
292
  private place;
286
293
  /**
287
- * Close one tab of the mounted session; the sole docked guide remains open.
294
+ * Close one tab of the on-screen Session; the sole docked guide remains open.
288
295
  * @param tabId - the tab to close.
289
296
  */
290
297
  close(tabId: TabId): void;
291
298
  /**
292
299
  * The active tab of the active pane.
293
- * @returns the record, or `undefined` with no mounted surface.
300
+ * @returns the record, or `undefined` without an on-screen surface.
294
301
  */
295
302
  active(): TabRecord | undefined;
296
303
  /**
297
304
  * Whether the column is currently showing its panel.
298
- * @returns `true` while expanded; `false` while collapsed or with no mounted surface.
305
+ * @returns `true` while expanded; `false` while collapsed or without an on-screen surface.
299
306
  */
300
307
  isExpanded(): boolean;
301
308
  /** Collapse the column, or expand it and focus its active dock pane after rendering. */
@@ -312,9 +319,9 @@ export declare class SidebarRightController implements ISidebarRight {
312
319
  */
313
320
  focusedTarget(element?: Element | null): SidebarRightTarget | undefined;
314
321
  /**
315
- * Choose a focused sidebar pane, or the mounted Session's active dock pane for an outside open.
322
+ * Choose a focused sidebar pane, or the on-screen Session's active dock pane for an outside open.
316
323
  * @param element - live command input target; stale sidebar markup never falls back to another pane.
317
- * @returns captured target, or undefined without a mounted Session.
324
+ * @returns captured target, or undefined without an on-screen surface.
318
325
  */
319
326
  commandTarget(element?: Element | null): SidebarRightTarget | undefined;
320
327
  /**
@@ -339,7 +346,7 @@ export declare class SidebarRightController implements ISidebarRight {
339
346
  /**
340
347
  * Check a captured target before acting, including reopened records and intervening navigation.
341
348
  * @param target - identity captured while resolving the input.
342
- * @returns whether the mounted Session, pane, tab lifetime and navigation still match.
349
+ * @returns whether the on-screen Session, pane, tab lifetime and navigation still match.
343
350
  */
344
351
  isTargetCurrent(target: SidebarRightTarget): boolean;
345
352
  /**
@@ -371,20 +378,27 @@ export declare class SidebarRightController implements ISidebarRight {
371
378
  */
372
379
  dock(paneId: PaneId): void;
373
380
  /**
374
- * Step the mounted session's surface back one intent.
381
+ * Step the on-screen Session's surface back one intent.
375
382
  *
376
383
  * @internal Not part of the product: the sequence is an architectural fact
377
384
  * with no user-facing control yet. Kept reachable for tests.
378
385
  */
379
386
  _undo(): void;
380
387
  /**
381
- * Step the mounted session's surface forward one intent.
388
+ * Step the on-screen Session's surface forward one intent.
382
389
  *
383
390
  * @internal See `_undo`.
384
391
  */
385
392
  _redo(): void;
386
- /** The mounted session's surface; `undefined` without a seat or before its first open. */
393
+ /**
394
+ * The on-screen Session and its committed surface; `undefined` with no Session
395
+ * on screen, before the runtime mints its store, or before its first open.
396
+ */
397
+ private screen;
398
+ /** The on-screen Session's surface; see {@link SidebarRightController.screen}. */
387
399
  private mountedSurface;
400
+ /** Whether a Session's docked pane has room for two working halves, as its seat last measured; unmeasured panes do. */
401
+ private canSplitPane;
388
402
  /**
389
403
  * The store actions a tab's own action on `sessionId` runs through: that
390
404
  * session's adopted store. `undefined` — nothing to act on — for a session
@@ -33,27 +33,21 @@ export interface SidebarRightInjected {
33
33
  */
34
34
  readonly syncPresentation: (presentation: SidebarRightPresentation) => void;
35
35
  /**
36
- * Publish this seat's session, actions, and the store's surfaces to `ctx.sidebarRight`.
36
+ * Record this seat's room rule for `ctx.sidebarRight`'s splits, replacing its previous one.
37
37
  *
38
- * The service is root-scoped and cannot read a per-entry store, so the only
39
- * honest source is the mounted seat. Held for as long as the seat is mounted;
40
- * the service publishes the bound session through `ctx.sidebarRight.mounted`.
41
- * @param binding - what a command needs to act on this session, and what a tab's own action needs to act on its.
42
- * @returns a release callback.
38
+ * The docking kit measures its panes after it renders; the seat forwards each
39
+ * reading, and the service applies it when it splits this seat's Session.
40
+ * @param canSplitPane - whether two working halves of a docked pane would fit; unmeasured panes fit.
43
41
  */
44
- readonly bindService: (binding: {
45
- sessionId: SessionId;
46
- actions: Store['actions'];
47
- /** Every session's surface as last committed; the mounted one is `surfaces[sessionId]`. */
48
- surfaces: Readonly<Record<string, SurfaceState>>;
49
- /** The room rule's verdict for a docked pane, as the kit last measured it. */
50
- canSplitPane: (paneId: PaneId) => boolean;
51
- /** Commit a keyboard/menu close and retain focus on a surviving visible pane. */
52
- closeWithFocus: (paneId: PaneId, close: () => void) => void;
53
- /** Commit a page operation and focus the pane it selects. */
54
- openWithFocus: (open: () => PaneId | undefined) => void;
55
- autoFullscreen?: boolean;
56
- }) => () => void;
42
+ readonly measureRoom: (canSplitPane: (paneId: PaneId) => boolean) => void;
43
+ /**
44
+ * Record whether the frame width this seat renders at presents an expanded panel fullscreen automatically.
45
+ *
46
+ * The fullscreen command reads the latest report: while it holds, the command
47
+ * closes the panel instead of switching its recorded mode.
48
+ * @param autoFullscreen - whether the frame is narrower than the automatic fullscreen width.
49
+ */
50
+ readonly reportAutoFullscreen: (autoFullscreen: boolean) => void;
57
51
  /**
58
52
  * The navigation face's `openTab`, for the strip's add control: a new tab is
59
53
  * the guide opened by kind, through the same path as every other open.
@@ -110,10 +104,11 @@ interface PanelProps {
110
104
  export declare function intentsFor(sessionId: SessionId, actions: Store['actions'], openTab: PanelProps['openTab'], closeTab?: PanelProps['closeTab'], splitPane?: PanelProps['splitPane']): DockIntents;
111
105
  /**
112
106
  * The right column's occupant: stable tab containers, docked or floating.
113
- * It is also where the
114
- * frame learns the panel's presentation, and where `ctx.sidebarRight` learns
115
- * which session it is acting on, because this is the seat that knows both.
107
+ * It is also where the frame learns the panel's presentation, because this is
108
+ * the seat that knows it. `ctx.sidebarRight` names the on-screen Session
109
+ * itself; this seat reports only what it renders with: the room its kit
110
+ * measured and the automatic fullscreen rule of its frame width.
116
111
  */
117
- export declare function RightbarSeat({ sessionId, width, viewportWidth, canShow, useStore, actions, t, renderSlot, syncPresentation, bindService, openTab, closeTab, useTabTypes, useTabNavigation, occurrence, retainTab, active, useShortcuts, splitPane, toggleFullscreen, }: RightbarSeatProps): ReactNode;
112
+ export declare function RightbarSeat({ sessionId, width, viewportWidth, canShow, useStore, actions, t, renderSlot, syncPresentation, measureRoom, reportAutoFullscreen, openTab, closeTab, useTabTypes, useTabNavigation, occurrence, retainTab, active, useShortcuts, splitPane, toggleFullscreen, }: RightbarSeatProps): ReactNode;
118
113
  export {};
119
114
  //# sourceMappingURL=SidebarRight.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-client-ui-sidebar-right",
3
3
  "description": "Right Sidebar: the docking surface's session-bound state, its panel and header expand control, and the navigation service over it",
4
- "version": "0.2.0-rc.1",
4
+ "version": "0.2.1-alpha.1",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "license": "MIT",
42
42
  "peerDependencies": {
43
- "@deepseek-ai/cordis": "~4.0.4"
43
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@testing-library/react": "^16.1.0",
@@ -52,23 +52,23 @@
52
52
  "picomatch": "^4.0.4",
53
53
  "clsx": "^2.0.0",
54
54
  "zod": "^4.4.3",
55
- "@deepseek-ai/cordis": "~4.0.4",
56
- "@deepseek-ai/dsh-api-session-controller": "0.2.0-rc.1",
57
- "@deepseek-ai/dsh-client-resources": "0.2.0-rc.1",
58
- "@deepseek-ai/dsh-client-store": "0.2.0-rc.1",
59
- "@deepseek-ai/dsh-client-locale": "0.2.0-rc.1",
60
- "@deepseek-ai/dsh-client-test-runtime": "0.2.0-rc.1",
61
- "@deepseek-ai/dsh-client-ui-conversation": "0.2.0-rc.1",
62
- "@deepseek-ai/dsh-client-ui-dockkit": "0.2.0-rc.1",
63
- "@deepseek-ai/dsh-client-ui-layout": "0.2.0-rc.1",
64
- "@deepseek-ai/dsh-client-ui-session": "0.2.0-rc.1",
65
- "@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.1",
66
- "@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.1",
67
- "@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.1",
68
- "@deepseek-ai/dsh-session": "0.2.0-rc.1",
69
- "@deepseek-ai/dsh-util-crypto": "0.2.0-rc.1",
70
- "@deepseek-ai/dsh-client-shortcuts": "0.2.0-rc.1",
71
- "@deepseek-ai/dsh-brand": "0.2.0-rc.1"
55
+ "@deepseek-ai/cordis": "~4.0.5-alpha.1",
56
+ "@deepseek-ai/dsh-api-session-controller": "0.2.1-alpha.1",
57
+ "@deepseek-ai/dsh-client-locale": "0.2.1-alpha.1",
58
+ "@deepseek-ai/dsh-client-resources": "0.2.1-alpha.1",
59
+ "@deepseek-ai/dsh-client-test-runtime": "0.2.1-alpha.1",
60
+ "@deepseek-ai/dsh-client-store": "0.2.1-alpha.1",
61
+ "@deepseek-ai/dsh-client-ui-conversation": "0.2.1-alpha.1",
62
+ "@deepseek-ai/dsh-client-ui-dockkit": "0.2.1-alpha.1",
63
+ "@deepseek-ai/dsh-client-ui-layout": "0.2.1-alpha.1",
64
+ "@deepseek-ai/dsh-client-ui-primitives": "0.2.1-alpha.1",
65
+ "@deepseek-ai/dsh-client-ui-renderer": "0.2.1-alpha.1",
66
+ "@deepseek-ai/dsh-client-ui-session": "0.2.1-alpha.1",
67
+ "@deepseek-ai/dsh-session": "0.2.1-alpha.1",
68
+ "@deepseek-ai/dsh-client-ui-slots": "0.2.1-alpha.1",
69
+ "@deepseek-ai/dsh-util-crypto": "0.2.1-alpha.1",
70
+ "@deepseek-ai/dsh-client-shortcuts": "0.2.1-alpha.1",
71
+ "@deepseek-ai/dsh-brand": "0.2.1-alpha.1"
72
72
  },
73
73
  "files": [
74
74
  "lib/index.js",