Files
ShadowsocksX-NG/docs/design/server-three-column-table.md

80 lines
6.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 服务器三栏视图
状态:实现与自动检查完成(2026-10-09);视觉及原生交互交给人工验证。
日期:2026-10-09。
本文记录本次任务的需求与设计决定,不代表当前实现。
## 确定要求
- 服务器页面使用三栏 `HSplitView`:左栏为来源边栏,中栏为服务器列表,右栏为原详情视图。用户后续要求用 `HSplitView` 替换最初批准的 `NavigationSplitView`。
- 不修改原服务器详情与配置组详情。
- 来源边栏只有「本地」和「订阅」。主窗口原「订阅」管理页面保留。
- 「本地」展示手动来源的根层节点与后代,不展示虚拟目录根。
- 「订阅」展示订阅组与后代。
- Content 用 SwiftUI `Table` 展示层级,类似 Finder 列表大纲。
- 三列为「名称」「创建时间」「修改时间」。名称列含配置组/服务器图标、左对齐的名称,以及右对齐的活动目标图标。
- 活动目标图标只标记目标节点本身,包含配置组目标,不级联标记后代。保留无效服务器与包含无效后代的配置组的警告图标。
- 单击选择节点,激活继续使用现有命令。列表保持单选。
- 切换来源清空节点选择,详情显示原有空状态。
- 默认展示目录顺序。列头排序只改变展示顺序,不改写目录;每组的直接子节点分别排序。
- 拖动仅改变父分组,不提供同级重排。移除右键菜单「移动到…」。其余现有操作保留,继续调用 `CatalogWorkflow`。
- 时间采用系统本地化日期和时间,精度到分钟;未知时间显示「—」。时间事实遵循 GLOSSARY「节点时间戳」与 ADR-0031。
- 拖放仅接受手动分组与「本地」列表空白;空白代表目录根。新父分组不同才执行移动,移动后追加到目标目录顺序末尾。服务器、订阅节点、自身及后代均不接受落点。
- 列头排序时,分组和服务器一起排序;未知时间始终排在最后,相等值保持目录顺序。
- 折叠包含选中节点的配置组时,选择切换到收起的配置组,详情同步切换。
- 创建、导入或订阅组复制产生手动节点后,切换到「本地」,展开其祖先并选择新节点。
- 支持列宽调整,固定显示三列。名称列占剩余宽度,时间列初始宽度按日期文本确定。
- 三栏保持显示;改用 `HSplitView` 后不提供 `NavigationSplitView` 的系统边栏显隐按钮。
- 来源、排序与展开状态在本次运行内跨主窗口页面切换保留,不落盘。重启恢复「本地」、目录顺序和全部展开。来源切换仍清空节点选择。
- 英文文案为 `Local`、`Subscriptions`、`Name`、`Date Created`、`Date Modified`。
- 增加列表排序菜单;「无」表示不施加展示排序、按目录顺序展示。英文为 `None`。列头继续切换对应列的升序与降序。
- 拖动移动成功后展开目标的祖先和目标分组,选择移动的节点,详情同步显示该节点。移到根层时无需展开目标分组。
## 实施前代码核实
- `ServersView` 使用 `HSplitView` 和扁平可见行 `List`。
- `CatalogTreeNode` 已包含来源、父节点与子树,但尚未包含创建时间与修改时间。
- `CatalogEntry` 已保存两个可空时间戳,无需重新定义时间语义。
- 现有目录移动命令支持同父重排;新列表必须拒绝同父拖放。
- 当前服务器管理的展开状态跨工作区页面切换保留,不落盘,重启全部展开。
## 最终确认
用户确认全部交互决定,并批准「无」的英文 `None`。用户要求使用 implement 流程,完成后提交当前分支;视觉验证交给人工,不由 Agent 执行。
## Module 方向
沿用 `CatalogWorkflow` 的 Interface 提供目录事实与变更命令。列表展示排序不进入目录持久化。树投影补充非敏感时间事实。视图持有来源、排序、展开和选择的呈现状态。具体 Interface 在交互规则确定后核实,不增加仅转发调用的 Module。
浏览状态需由跨工作区页面切换仍存续的持有者管理;不能只放入随页面卸载的 `ServersView` 局部状态。来源与排序属于服务器页面呈现,不加入工作区顶层路由或持久配置。新 Table 直接使用节点身份驱动选择与展开,替换原扁平 List 呈现。保留原详情 View 与现有变更工作流。
## 文档范围
本次沿用配置目录、服务器配置、配置组、手动组、订阅组、活动目标与节点时间戳的既有定义,不新增领域术语。本次布局与呈现决定可以局部修改,不新增 ADR。
## 实现验证范围
- 核实非敏感树投影中的时间戳与来源筛选。
- 验证每层排序、未知时间、相等值及目录顺序恢复。
- 验证同父拖放不提交目录变更,跨父移动仍使用现有工作流。
- 构建 macOS App,检查原详情与现有操作的编译集成。
- 视觉与原生交互由人工验证:三栏、层级 Table、列宽、选择、展开、排序菜单与拖放。Agent 不执行视觉验证,构建与 hostless 测试不替代该证据。
## 实现期间的补充决定
- 排序按钮直接展开字段列表;Picker 使用 inline 样式,不产生额外「排序」子菜单。
- 用户要求只给树展开图标设置最小宽度。名称列的展开按钮设置最小宽度 24 点、最小高度 28 点;图标不放大。
- Table 的行由树投影按来源、每层展示排序与展开状态生成,名称列按深度缩进。展开按钮显式绑定既有展开状态,以控制点击宽度和折叠选择规则。
- `TableRowContent.dropDestination` 不提供拒绝返回值,因此不使用该行级接口。统一 `DropDelegate` 借用原生 `NSTableView.row(at:)` 区分行与空白;无效行不得回退为根落点。AppKit Adapter 不替换 Table 或其 delegate。
- 目录节点拖放使用本进程专用载荷类型,释放时再次核对载荷身份与移动资格。
## 验证记录(2026-10-09)
- 定向验证树投影时间戳、来源筛选、每层排序、未知时间最后、相等值稳定、折叠选择和同父移动拒绝。
- Debug 构建通过;全量 hostless 摘要为 976 个测试通过、0 失败、0 跳过,包含 1062 次参数化执行。
- 两组 Python 离线检查分别通过 4 项与 17 项。
- 格式化和 diff 检查通过;SwiftLint 为 22 条非严重警告,严重违规为 0。ServersView 文件长度仍有警告。
- code-review 的 Standards 与 Spec 两轴最终均为 0 项发现;初次发现的无效行落点问题在复核中关闭。
- 原生拖放命中、菜单呈现、展开按钮点击区域与三栏视觉未验证,按用户要求由人工验证。