OpenProgram · Web UI · implementation design
Settings 可折叠导航列
问题:主侧栏、设置分类栏、Provider 列表栏同时展开时占用 864px 左右,Provider 详情在窄窗口中被压缩并出现横向溢出。
Audience
Web / Desktop UI 实现
Scope
Settings 三层左侧导航
Expanded
每列约 288px
Collapsed
每列 49px
当前状态与目标状态
当前:三列固定展开
Provider 详情只能使用剩余宽度。
‹
Programs
LLM Providers
General
OpenAI Codex
DeepSeek
Provider detail
目标:三个导航列独立收起
窄轨保留展开按钮和可识别图标。
›
›
›
Provider detail
列级契约
| 列 | 展开态 | 收起态 | 持久化 | 必须保留 |
|---|---|---|---|---|
| 应用主侧栏 | --sidebar-width | 49px | sidebarOpen(现有) | 展开按钮、导航图标 |
| Settings 分类栏 | --sidebar-width - 1px | 49px | settingsNavOpen | 展开按钮、分类图标、当前页状态 |
| Provider 列表栏 | --sidebar-width - 1px | 49px | providerListOpen | 展开按钮、Provider 图标、当前 Provider 状态 |
交互与状态边界
独立控制收起任意一列不修改另外两列的状态。
选择不展开在窄轨点击分类或 Provider,只切换内容,不强制展开导航列。
完整键盘入口折叠按钮使用原生
button、aria-expanded、明确的中英文名称。
宽度过渡只过渡 grid column width;不改变详情表单数据、Provider 选择或滚动根。
持久化刷新和重新进入 Settings 后恢复用户最后一次选择。
窄窗固定轨主侧栏进入 fixed 模式后,Settings 保留 49px 左侧空间,窄轨不覆盖页面标题或设置内容。
方案比较
| 方案 | 结论 | 原因 |
|---|---|---|
| React 本地 disclosure 状态 + CSS Grid 列宽 | 采用 | 沿用主侧栏模式,不增加 store 或依赖;每列状态边界明确。 |
| 只在固定 breakpoint 自动隐藏 | 不采用 | 窗口和字体缩放组合不同,固定阈值会替用户改变布局且无法手动恢复。 |
| Provider 详情整体横向滚动 | 不采用 | 不释放导航占用,表单操作仍需横向滚动。 |
| 拖拽调整三列宽度 | 不采用 | 本次只解决显示/隐藏;增加三个 resize handle 会扩大状态和指针交互范围。 |
规范基础:CSS Grid 列宽负责布局;原生 disclosure 语义由 button 与 aria-expanded 表达;不引入新组件库。
验收与排除项
验收
- 两处新增折叠按钮均可用鼠标和键盘触发。
- 三列收起后各为 49px,详情区得到释放的全部宽度。
- 收起态没有标签、搜索框和分组标题溢出。
- 分类与 Provider 仍可通过图标选择。
- 默认安装版在窄窗口中验证。
排除
- 不改 Provider 账号行的字段和动作。
- 不新增可拖拽列宽。
- 不改变主侧栏已有持久化语义。
- 不把导航改成弹窗或 overlay。
实现证据
状态:已实现并安装到默认 App。
settings-tabs-layout.tsx:Settings 分类图标窄轨、独立按钮与settingsNavOpen。providers/index.tsx:Provider 图标窄轨、独立按钮与providerListOpen。check:settings-columns、TypeScript、生产构建、文档链接和补丁格式检查通过。- 默认安装版 800px 视口实测:三列均为 49px;Provider 详情
clientWidth = scrollWidth = 652px;Settings 标题起点 x=73px,没有被 fixed 主侧栏覆盖。