承接 #34。那个 issue 只解决了 Settings 侧栏的选中态(已换成系统 List(selection:),d42322f)。#34 评论里那份 Pickroom 规范还剩四条没落地,各自的风险和收益差得很远,所以拆到这里逐条评估——不是一个"照着清单改完"的任务,每条都要先判断在本仓库成不成立。
前提回顾:#34 里已复验过,macOS 26.5.2 上 List 的 reentrant-layout 崩溃不再复现(1500 次内联 zoom 翻转 + 选中切换 + resize,零 assert)。但那是四行静态内容的结论,不能直接外推到主窗口侧栏(见第 3 条)。
1. .windowResizability(.contentMinSize)(主窗口)
规范说:不要只写 .frame(minWidth:) 却让 NSWindow 继续缩得更小。
本仓库现状是知道这件事的,但用 AppKit 解决的:TheGitApp.swift 的 WindowFloor(:512 起)是一个 NSViewRepresentable,专门把 window.contentMinSize 手动设上去。它的注释记录了原因——在 .windowStyle(.hiddenTitleBar) 下 .frame(minWidth:minHeight:) 没有变成窗口最小尺寸,窗口能拖得更小,内容居中后两侧同时被切掉。
要做的:验证 .windowResizability(.contentMinSize) 在 .hiddenTitleBar + macOS 26 下是否真的生效。如果生效,WindowFloor 里 contentMinSize 那部分可以删掉——但注意它同时还负责 setFrameAutosaveName(窗口位置恢复)和"从过小的已保存 frame 恢复时纠正一次",这两件事 windowResizability 管不了,不能整个删。
收益:删掉一段 AppKit hack。风险低,可独立完成。
2. Settings 窗口 chrome → NavigationSplitView
规范第 5 节那份"不要采用"清单,SettingsRootView.swift 目前踩了三条:
SettingsWindowChrome(:170 起)手动设 titlebarAppearsTransparent / titleVisibility / fullSizeContentView,并塞一个空的 unified NSToolbar 纯粹为了拿它的几何(把 traffic lights 挪到胶囊上);
- 根视图
.ignoresSafeArea(.container, edges: .top) 把内容推到标题栏下;
- 浮层胶囊的位置是写死的常数手算的:
titleBarHeight = 52、capsuleInset = 8、capsuleRadius = 18,玻璃是自建的 SidebarMaterial(NSVisualEffectView)+ clipShape + stroke。
换成标准 NavigationSplitView 后这些理论上全部由系统负责。但这是本 issue 里最大的一块,动手前要想清楚:
- 这套 chrome 现在是工作的,视觉上已经是 macOS 26 的浮层胶囊。换掉是把"手搓但正确"换成"系统画且正确",收益是维护性,不是修 bug。
SettingsWindowChrome 的注释记录了一个真实的坑:SwiftUI 的 Settings scene 在 app 失活/重新激活时会重设窗口属性,所以那些设置是挂在 key/occlusion 通知上反复施加的,不是设一次。换成 NavigationSplitView 后必须重测第一次 ⌘-tab 切出去再回来。
- 侧栏宽度现在是
168 * zoom(跟 UI zoom 走)。NavigationSplitView 下要用 navigationSplitViewColumnWidth,且要确认它接受随 zoom 变化的值而不会打架。
- 验收必须按规范第 6 节:
open -n 全新启动看,而不是 resize 之后"看起来正常"。
3. 主窗口侧栏的自绘选中态(风险最高,建议单独排期)
SidebarView.swift:1285 的 fill:选中 = Color.accentColor.opacity(0.15),hover = Color.primary.opacity(0.06),press 加深。就是规范第 3、5 节点名反对的模式,和 #34 修掉的是同一个反模式。
但这里正是 List 全局禁令当初写的那个视图,不能拿 #34 的复验结论直接换:
- 几千行(大仓库的分支/标签/stash),
LazyVStack + pinnedViews: [.sectionHeaders] + 可折叠分组 + 树形行 + 每行自带高度;
- 一次 zoom 变更要把这些全部重新布局——
SidebarView.swift:34 的注释说这里当初是 AppKit 布局异常的唯一来源。
要做的话,得先针对这个规模和结构重做一次压力复验(几千行 + 折叠展开 + zoom 翻转 + 过滤),而不是复用 #34 的探针结果。复验不过就维持现状,只把选中色调整得更贴近系统材质。
4. backgroundExtensionEffect()
规范里这条是给照片画布用的:把图片镜像模糊延展到浮动 sidebar 后面。
TheGit 没有照片画布。先判断本仓库有没有真实适用的地方(commit graph 背景?detail 面板?),没有就明确记下"不适用"并关掉这一条,不要为了对齐清单而加。真要用,因为 Package.swift 最低是 macOS 14,必须包 #available(macOS 26.0, *) 的 guard。
建议顺序
1 →(4 的判断,很快)→ 2 → 3。第 1 条独立且低风险;第 3 条最好等前面都稳定了再单独开分支做,而且随时可以判定"复验不过,维持现状"。
通用验收
open -n dist/TheGit.app 全新启动检查,不接受"resize 之后看起来正常";
- 五档 UI zoom(85/95/100/115/130)都要过,这是本仓库比 Pickroom 多出来的一维;
- 窗口拖到最小尺寸不裁切;
swift test 全绿(当前 359 个)。
注:#34 评论里的文件路径(PickroomApp.swift / SidebarView.swift / PhotoCanvas.swift)是 Pickroom 那边的,本仓库对应 TheGitApp.swift / SettingsRootView.swift / UI/SidebarView.swift。
承接 #34。那个 issue 只解决了 Settings 侧栏的选中态(已换成系统
List(selection:),d42322f)。#34 评论里那份 Pickroom 规范还剩四条没落地,各自的风险和收益差得很远,所以拆到这里逐条评估——不是一个"照着清单改完"的任务,每条都要先判断在本仓库成不成立。前提回顾:#34 里已复验过,macOS 26.5.2 上
List的 reentrant-layout 崩溃不再复现(1500 次内联 zoom 翻转 + 选中切换 + resize,零 assert)。但那是四行静态内容的结论,不能直接外推到主窗口侧栏(见第 3 条)。1.
.windowResizability(.contentMinSize)(主窗口)规范说:不要只写
.frame(minWidth:)却让NSWindow继续缩得更小。本仓库现状是知道这件事的,但用 AppKit 解决的:
TheGitApp.swift的WindowFloor(:512起)是一个NSViewRepresentable,专门把window.contentMinSize手动设上去。它的注释记录了原因——在.windowStyle(.hiddenTitleBar)下.frame(minWidth:minHeight:)没有变成窗口最小尺寸,窗口能拖得更小,内容居中后两侧同时被切掉。要做的:验证
.windowResizability(.contentMinSize)在.hiddenTitleBar+ macOS 26 下是否真的生效。如果生效,WindowFloor里contentMinSize那部分可以删掉——但注意它同时还负责setFrameAutosaveName(窗口位置恢复)和"从过小的已保存 frame 恢复时纠正一次",这两件事windowResizability管不了,不能整个删。收益:删掉一段 AppKit hack。风险低,可独立完成。
2. Settings 窗口 chrome →
NavigationSplitView规范第 5 节那份"不要采用"清单,
SettingsRootView.swift目前踩了三条:SettingsWindowChrome(:170起)手动设titlebarAppearsTransparent/titleVisibility/fullSizeContentView,并塞一个空的 unifiedNSToolbar纯粹为了拿它的几何(把 traffic lights 挪到胶囊上);.ignoresSafeArea(.container, edges: .top)把内容推到标题栏下;titleBarHeight = 52、capsuleInset = 8、capsuleRadius = 18,玻璃是自建的SidebarMaterial(NSVisualEffectView)+clipShape+stroke。换成标准
NavigationSplitView后这些理论上全部由系统负责。但这是本 issue 里最大的一块,动手前要想清楚:SettingsWindowChrome的注释记录了一个真实的坑:SwiftUI 的Settingsscene 在 app 失活/重新激活时会重设窗口属性,所以那些设置是挂在 key/occlusion 通知上反复施加的,不是设一次。换成NavigationSplitView后必须重测第一次 ⌘-tab 切出去再回来。168 * zoom(跟 UI zoom 走)。NavigationSplitView下要用navigationSplitViewColumnWidth,且要确认它接受随 zoom 变化的值而不会打架。open -n全新启动看,而不是 resize 之后"看起来正常"。3. 主窗口侧栏的自绘选中态(风险最高,建议单独排期)
SidebarView.swift:1285的fill:选中 =Color.accentColor.opacity(0.15),hover =Color.primary.opacity(0.06),press 加深。就是规范第 3、5 节点名反对的模式,和 #34 修掉的是同一个反模式。但这里正是 List 全局禁令当初写的那个视图,不能拿 #34 的复验结论直接换:
LazyVStack+pinnedViews: [.sectionHeaders]+ 可折叠分组 + 树形行 + 每行自带高度;SidebarView.swift:34的注释说这里当初是 AppKit 布局异常的唯一来源。要做的话,得先针对这个规模和结构重做一次压力复验(几千行 + 折叠展开 + zoom 翻转 + 过滤),而不是复用 #34 的探针结果。复验不过就维持现状,只把选中色调整得更贴近系统材质。
4.
backgroundExtensionEffect()规范里这条是给照片画布用的:把图片镜像模糊延展到浮动 sidebar 后面。
TheGit 没有照片画布。先判断本仓库有没有真实适用的地方(commit graph 背景?detail 面板?),没有就明确记下"不适用"并关掉这一条,不要为了对齐清单而加。真要用,因为
Package.swift最低是 macOS 14,必须包#available(macOS 26.0, *)的 guard。建议顺序
1 →(4 的判断,很快)→ 2 → 3。第 1 条独立且低风险;第 3 条最好等前面都稳定了再单独开分支做,而且随时可以判定"复验不过,维持现状"。
通用验收
open -n dist/TheGit.app全新启动检查,不接受"resize 之后看起来正常";swift test全绿(当前 359 个)。