Responsive Sidebar and injected tab information
响应式 Sidebar 与注入的标签信息
Tab extensions need consistent live information about their containing pane and Sidebar without a growing list of owner props. The workbench must also preserve content while adapting to limited viewport space, without reopening a Sidebar the user has closed.
English
Problem
Tab extensions need consistent live information about their containing pane and Sidebar without a growing list of owner props. The workbench must also preserve content while adapting to limited viewport space, without reopening a Sidebar the user has closed.
Decision
The slot framework injects one useTabInfo() returning nested sidebar, panel, and tab fields. It composes the framework-bound layout and navigation hooks; extensions neither subscribe themselves nor receive a service object. Body visibility requires an active tab in an expanded Sidebar; title visibility does not require an active tab. Hiding or switching Sessions leaves the tab lifetime intact. Closing the record aborts its signal. Tab actions stay bound to their owning Session. Store adoption is a private capability of the plugin assembly, not a public controller operation.
The frame protects 400px for the conversation by shrinking the right column, then closing it before shrinking the conversation. Its first-open preference is 45% of the viewport, retained thereafter in pixels, with a 300px floor and 70% viewport ceiling. The left column keeps its preference at widths of at least 1024px. Closing is recorded state: widening never opens it, while a user action or explicit Session API may. Layout persistence and provider recovery owns layout restoration after reload; frame width preferences still initialize from the viewport.
Fullscreen uses the same mounted content tree and covers the viewport while retaining the underlying column reservation. Opening below 768px selects automatic fullscreen; exiting it there closes the Sidebar. Widening can end automatic fullscreen but leaves manually selected fullscreen intact. The product permits two horizontal panes, a 50/50 initial split, and a 20–80% divider; narrow panes refuse new splits. The generic docking engine retains its independent capabilities. A fullscreen entry completes its slide before reporting the underlying track; that covered width change is instantaneous, so neither entry nor returning to normal reveals a background reflow. At the two-pane budget the split control is hidden; a one-pane width refusal remains disabled. On exit, the frame prepares the destination before the overlay retreats: no right track for close, a normal track for restore. Transition suppression survives clearing the fullscreen report and ends on the next geometry action.
This decision supersedes the flat owner-props choice in tab types and navigation and the no-concession, overlay presentation and product pane limit in docking infrastructure. Their registration, record-lifetime, state ownership and engine-selection rationale remain active.
Alternatives considered
Flat information props or three separate hooks. A single nested read groups the three ownership levels and allows additional fields without proliferating props or readers.
Automatic reopening after a viewport change. It makes opening depend on layout history rather than an explicit action. A closed Sidebar stays closed, with its content preserved.
A separate fullscreen content tree. Remounting would interrupt tab-local state. The same element changes presentation instead.
Consequences
Tab extensions use a framework-injected reader and keep their own store actions separate from tab.actions. Layout, seat and docking tests cover width concessions, explicit reopening, body/title visibility, tab lifetimes, horizontal drop zones and divider limits; browser tests exercise the assembled application. Compact mobile controls and layout persistence remain outside this decision.
中文
问题
标签扩展需要一致的所属窗格与 Sidebar 实时信息,而不依赖不断增长的 owner props。工作区也需要适应有限的视口空间,同时保留内容,并且不重新打开用户已经关闭的 Sidebar。
决策
Slot 框架注入一个 useTabInfo(),返回嵌套的 sidebar、panel 与 tab 字段。它组合框架绑定的布局与导航 hook;扩展既不自行订阅,也不接收服务对象。正文可见要求 Sidebar 展开且标签活跃;标题可见不要求标签活跃。隐藏或切换 Session 保留标签生命周期。关闭记录会中止其 signal。标签动作始终绑定到所属 Session。Store 收编是插件组装的私有能力,不是公共控制器操作。
框架先缩小右列,再关闭右列,最后才缩小会话区,以保护会话区的 400px 宽度。右列首次打开偏好为视口的 45%,此后按像素保留,下限为 300px,上限为视口的 70%。在宽度至少为 1024px 时,左列保持自身偏好。关闭是被记录的状态:变宽不会打开右栏,用户动作或显式 Session API 可以打开。布局持久化与 provider 恢复负责刷新后的布局还原;框架宽度偏好仍根据视口初始化。
全屏使用同一棵已挂载内容树,覆盖视口并保留底层列的占位。在 768px 以下打开会选择自动全屏;在此宽度下退出全屏会关闭 Sidebar。变宽可以结束自动全屏,但保留手动选择的全屏。产品允许两个水平窗格,初始按 50/50 分割,分割线范围为 20–80%;窄窗格拒绝新分栏。通用停靠引擎保留其独立能力。 全屏入场先完成滑入,再报告底层轨道;被覆盖的宽度变化瞬间完成,因此入场及返回普通模式都不暴露底层重排。达到两格预算时隐藏分栏控件;单格宽度不足时仍显示禁用控件。 退场时,框架先准备目标布局,再让覆盖层退出:关闭不留右轨道,恢复保留普通轨道。清除全屏报告时仍保留过渡抑制,直到下一次几何操作才结束。
本决策取代标签类型与导航的平铺 owner props 选择,以及停靠基础设施中的无让步、覆盖模式和产品窗格上限。它们的注册、记录生命周期、状态所有权与引擎选型理由继续有效。
考虑过的替代方案
平铺信息 props 或三个独立 hook。 一个嵌套读取接口按三个所有权层级分组,允许增加字段而不增加 props 或读取接口。
视口变化后自动重开。 这会让打开依赖布局历史,而不是显式动作。关闭的 Sidebar 保持关闭,同时保留内容。
独立的全屏内容树。 重新挂载会打断标签局部状态。因此由同一元素改变呈现方式。
后果
标签扩展使用框架注入的读取接口,自身 store actions 与 tab.actions 保持分离。布局、seat 与停靠测试覆盖列宽让步、显式重开、正文与标题可见性、标签生命周期、水平放置区与分割比例;浏览器测试覆盖组装后的应用。紧凑移动端控件与布局持久化不属于本决策。