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 <noreply@anthropic.com>
This commit is contained in:
@@ -14,9 +14,22 @@ struct AppShellView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationSplitView {
|
NavigationSplitView {
|
||||||
List(AtlasRoute.allCases, selection: $model.selection) { route in
|
List(selection: $model.selection) {
|
||||||
SidebarRouteRow(route: route)
|
ForEach(AtlasRoute.SidebarSection.allCases) { section in
|
||||||
.tag(route)
|
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"))
|
.navigationTitle(AtlasL10n.string("app.name"))
|
||||||
.navigationSplitViewColumnWidth(min: AtlasLayout.sidebarMinWidth, ideal: AtlasLayout.sidebarIdealWidth)
|
.navigationSplitViewColumnWidth(min: AtlasLayout.sidebarMinWidth, ideal: AtlasLayout.sidebarIdealWidth)
|
||||||
@@ -25,17 +38,7 @@ struct AppShellView: View {
|
|||||||
} detail: {
|
} detail: {
|
||||||
let route = model.selection ?? .overview
|
let route = model.selection ?? .overview
|
||||||
|
|
||||||
detailView(for: route)
|
detailContent(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))
|
|
||||||
.toolbar {
|
.toolbar {
|
||||||
ToolbarItemGroup {
|
ToolbarItemGroup {
|
||||||
Button {
|
Button {
|
||||||
@@ -64,39 +67,6 @@ struct AppShellView: View {
|
|||||||
.accessibilityIdentifier("toolbar.taskCenter")
|
.accessibilityIdentifier("toolbar.taskCenter")
|
||||||
.accessibilityLabel(AtlasL10n.string("toolbar.taskcenter.accessibilityLabel"))
|
.accessibilityLabel(AtlasL10n.string("toolbar.taskcenter.accessibilityLabel"))
|
||||||
.accessibilityHint(AtlasL10n.string("toolbar.taskcenter.accessibilityHint"))
|
.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)
|
.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
|
@ViewBuilder
|
||||||
private func detailView(for route: AtlasRoute) -> some View {
|
private func detailView(for route: AtlasRoute) -> some View {
|
||||||
switch route {
|
switch route {
|
||||||
@@ -269,7 +261,7 @@ private struct SidebarRouteRow: View {
|
|||||||
.accessibilityElement(children: .combine)
|
.accessibilityElement(children: .combine)
|
||||||
.accessibilityIdentifier("route.\(route.id)")
|
.accessibilityIdentifier("route.\(route.id)")
|
||||||
.accessibilityLabel("\(route.title). \(route.subtitle)")
|
.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:
|
case .permissions:
|
||||||
return "5"
|
return "5"
|
||||||
case .settings:
|
case .settings:
|
||||||
return "6"
|
return ","
|
||||||
case .about:
|
case .about:
|
||||||
return "7"
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,14 @@ struct AtlasAppCommands: Commands {
|
|||||||
@ObservedObject var model: AtlasAppModel
|
@ObservedObject var model: AtlasAppModel
|
||||||
|
|
||||||
var body: some Commands {
|
var body: some Commands {
|
||||||
|
CommandGroup(replacing: .appInfo) {
|
||||||
|
Button(AtlasL10n.string("commands.about")) {
|
||||||
|
model.navigate(to: .about)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CommandMenu(AtlasL10n.string("commands.navigate.menu")) {
|
CommandMenu(AtlasL10n.string("commands.navigate.menu")) {
|
||||||
ForEach(AtlasRoute.allCases) { route in
|
ForEach(AtlasRoute.sidebarRoutes) { route in
|
||||||
Button(route.title) {
|
Button(route.title) {
|
||||||
model.navigate(to: route)
|
model.navigate(to: route)
|
||||||
}
|
}
|
||||||
@@ -18,7 +24,7 @@ struct AtlasAppCommands: Commands {
|
|||||||
Button(model.isTaskCenterPresented ? AtlasL10n.string("commands.taskcenter.close") : AtlasL10n.string("commands.taskcenter.open")) {
|
Button(model.isTaskCenterPresented ? AtlasL10n.string("commands.taskcenter.close") : AtlasL10n.string("commands.taskcenter.open")) {
|
||||||
model.toggleTaskCenter()
|
model.toggleTaskCenter()
|
||||||
}
|
}
|
||||||
.keyboardShortcut("7", modifiers: .command)
|
.keyboardShortcut("6", modifiers: .command)
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandMenu(AtlasL10n.string("commands.actions.menu")) {
|
CommandMenu(AtlasL10n.string("commands.actions.menu")) {
|
||||||
@@ -80,10 +86,8 @@ private extension AtlasRoute {
|
|||||||
return "4"
|
return "4"
|
||||||
case .permissions:
|
case .permissions:
|
||||||
return "5"
|
return "5"
|
||||||
case .settings:
|
case .settings, .about:
|
||||||
return "6"
|
preconditionFailure("Non-sidebar routes have no shortcut key")
|
||||||
case .about:
|
|
||||||
return "7"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user