From 977ab9c04744cb4514975bed7b751832586c498d Mon Sep 17 00:00:00 2001 From: zhukang <274546966@qq.com> Date: Tue, 10 Mar 2026 21:54:09 +0800 Subject: [PATCH] feat(sidebar): add Settings and About as inline detail routes Move Settings and About from standalone windows to sidebar entries that display in the detail panel like other tabs. Remove redundant Settings scene and About window from AtlasApp. Skip searchable modifier for these routes and fix empty accessibility hint for About. Co-Authored-By: Claude Opus 4.6 --- .../Sources/AtlasApp/AppShellView.swift | 92 +++++++++---------- .../Sources/AtlasApp/AtlasAppCommands.swift | 16 ++-- 2 files changed, 52 insertions(+), 56 deletions(-) diff --git a/Apps/AtlasApp/Sources/AtlasApp/AppShellView.swift b/Apps/AtlasApp/Sources/AtlasApp/AppShellView.swift index 07367af..be14355 100644 --- a/Apps/AtlasApp/Sources/AtlasApp/AppShellView.swift +++ b/Apps/AtlasApp/Sources/AtlasApp/AppShellView.swift @@ -14,9 +14,22 @@ struct AppShellView: View { var body: some View { NavigationSplitView { - List(AtlasRoute.allCases, selection: $model.selection) { route in - SidebarRouteRow(route: route) - .tag(route) + List(selection: $model.selection) { + ForEach(AtlasRoute.SidebarSection.allCases) { section in + Section(section.title) { + ForEach(section.routes) { route in + SidebarRouteRow(route: route) + .tag(route) + } + } + } + + Section { + SidebarRouteRow(route: .settings) + .tag(AtlasRoute.settings) + SidebarRouteRow(route: .about) + .tag(AtlasRoute.about) + } } .navigationTitle(AtlasL10n.string("app.name")) .navigationSplitViewColumnWidth(min: AtlasLayout.sidebarMinWidth, ideal: AtlasLayout.sidebarIdealWidth) @@ -25,17 +38,7 @@ struct AppShellView: View { } detail: { let route = model.selection ?? .overview - detailView(for: route) - .id(route) - .transition(.opacity) - .searchable( - text: Binding( - get: { model.searchText(for: route) }, - set: { model.setSearchText($0, for: route) } - ), - prompt: AtlasL10n.string("app.search.prompt.route", route.searchPromptLabel) - ) - .accessibilityHint(AtlasL10n.string("app.search.hint.route", route.searchPromptLabel)) + detailContent(for: route) .toolbar { ToolbarItemGroup { Button { @@ -64,39 +67,6 @@ struct AppShellView: View { .accessibilityIdentifier("toolbar.taskCenter") .accessibilityLabel(AtlasL10n.string("toolbar.taskcenter.accessibilityLabel")) .accessibilityHint(AtlasL10n.string("toolbar.taskcenter.accessibilityHint")) - - Button { - model.navigate(to: .permissions) - Task { - await model.inspectPermissions() - } - } label: { - Label { - Text(AtlasL10n.string("toolbar.permissions")) - } icon: { - Image(systemName: AtlasIcon.permissions) - .symbolRenderingMode(.hierarchical) - } - } - .help(AtlasL10n.string("toolbar.permissions.help")) - .accessibilityIdentifier("toolbar.permissions") - .accessibilityLabel(AtlasL10n.string("toolbar.permissions.accessibilityLabel")) - .accessibilityHint(AtlasL10n.string("toolbar.permissions.accessibilityHint")) - - Button { - model.navigate(to: .settings) - } label: { - Label { - Text(AtlasL10n.string("toolbar.settings")) - } icon: { - Image(systemName: AtlasIcon.settings) - .symbolRenderingMode(.hierarchical) - } - } - .help(AtlasL10n.string("toolbar.settings.help")) - .accessibilityIdentifier("toolbar.settings") - .accessibilityLabel(AtlasL10n.string("toolbar.settings.accessibilityLabel")) - .accessibilityHint(AtlasL10n.string("toolbar.settings.accessibilityHint")) } } .animation(AtlasMotion.slow, value: model.selection) @@ -128,6 +98,28 @@ struct AppShellView: View { } } + @ViewBuilder + private func detailContent(for route: AtlasRoute) -> some View { + switch route { + case .settings, .about: + detailView(for: route) + .id(route) + .transition(.opacity) + default: + detailView(for: route) + .id(route) + .transition(.opacity) + .searchable( + text: Binding( + get: { model.searchText(for: route) }, + set: { model.setSearchText($0, for: route) } + ), + prompt: AtlasL10n.string("app.search.prompt.route", route.searchPromptLabel) + ) + .accessibilityHint(AtlasL10n.string("app.search.hint.route", route.searchPromptLabel)) + } + } + @ViewBuilder private func detailView(for route: AtlasRoute) -> some View { switch route { @@ -269,7 +261,7 @@ private struct SidebarRouteRow: View { .accessibilityElement(children: .combine) .accessibilityIdentifier("route.\(route.id)") .accessibilityLabel("\(route.title). \(route.subtitle)") - .accessibilityHint(AtlasL10n.string("sidebar.route.hint", route.shortcutNumber)) + .accessibilityHint(route.shortcutNumber.isEmpty ? "" : AtlasL10n.string("sidebar.route.hint", route.shortcutNumber)) } } @@ -291,9 +283,9 @@ private extension AtlasRoute { case .permissions: return "5" case .settings: - return "6" + return "," case .about: - return "7" + return "" } } } diff --git a/Apps/AtlasApp/Sources/AtlasApp/AtlasAppCommands.swift b/Apps/AtlasApp/Sources/AtlasApp/AtlasAppCommands.swift index 756e0aa..b95a510 100644 --- a/Apps/AtlasApp/Sources/AtlasApp/AtlasAppCommands.swift +++ b/Apps/AtlasApp/Sources/AtlasApp/AtlasAppCommands.swift @@ -5,8 +5,14 @@ struct AtlasAppCommands: Commands { @ObservedObject var model: AtlasAppModel var body: some Commands { + CommandGroup(replacing: .appInfo) { + Button(AtlasL10n.string("commands.about")) { + model.navigate(to: .about) + } + } + CommandMenu(AtlasL10n.string("commands.navigate.menu")) { - ForEach(AtlasRoute.allCases) { route in + ForEach(AtlasRoute.sidebarRoutes) { route in Button(route.title) { model.navigate(to: route) } @@ -18,7 +24,7 @@ struct AtlasAppCommands: Commands { Button(model.isTaskCenterPresented ? AtlasL10n.string("commands.taskcenter.close") : AtlasL10n.string("commands.taskcenter.open")) { model.toggleTaskCenter() } - .keyboardShortcut("7", modifiers: .command) + .keyboardShortcut("6", modifiers: .command) } CommandMenu(AtlasL10n.string("commands.actions.menu")) { @@ -80,10 +86,8 @@ private extension AtlasRoute { return "4" case .permissions: return "5" - case .settings: - return "6" - case .about: - return "7" + case .settings, .about: + preconditionFailure("Non-sidebar routes have no shortcut key") } } }