diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f48e1c39e5..25f72f1f4f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -87,6 +87,7 @@ Docs: https://docs.openclaw.ai - UI: show reasoning choices as plain labels instead of leaking internal override wording in session and chat pickers. - Mac app: avoid repeating the Configuration heading inside channel quick settings. - Mac app: keep the Settings sidebar always visible and remove the redundant titlebar hide/show control. +- Mac app: normalize Settings pane content margins so pages share the same left and right rail. - Mac app: prefer explicit private/Tailscale/LAN Gateway endpoints over SSH tunnels, preserve legacy loopback tunnel configs, persist transport choices, and show captured SSH stderr when tunneling really fails. - Gateway/sessions: keep ACP/acpx and runtime child sessions visible in configured-only session lists when their owner or parent session belongs to a configured agent. - Mac app: keep app-level menu commands and Dashboard failure states reachable when the remote Gateway is disconnected. diff --git a/apps/macos/Sources/OpenClaw/AboutSettings.swift b/apps/macos/Sources/OpenClaw/AboutSettings.swift index b61cfee89a5..e3c8c9c2389 100644 --- a/apps/macos/Sources/OpenClaw/AboutSettings.swift +++ b/apps/macos/Sources/OpenClaw/AboutSettings.swift @@ -85,9 +85,7 @@ struct AboutSettings: View { Spacer() } .frame(maxWidth: .infinity, maxHeight: .infinity) - .padding(.top, 4) - .padding(.horizontal, 24) - .padding(.bottom, 24) + .settingsDetailContent() .onAppear { guard let updater, !self.didLoadUpdaterState else { return } // Keep Sparkle’s auto-check setting in sync with the persisted toggle. diff --git a/apps/macos/Sources/OpenClaw/ChannelsSettings+View.swift b/apps/macos/Sources/OpenClaw/ChannelsSettings+View.swift index 64493a59a7f..a9eeed95794 100644 --- a/apps/macos/Sources/OpenClaw/ChannelsSettings+View.swift +++ b/apps/macos/Sources/OpenClaw/ChannelsSettings+View.swift @@ -8,6 +8,7 @@ extension ChannelsSettings { self.detail } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .settingsDetailContent() .onAppear { self.updateActiveWork(active: self.isActive) self.ensureSelection(in: channels) @@ -72,8 +73,8 @@ extension ChannelsSettings { .font(.callout) .foregroundStyle(.secondary) } - .padding(.horizontal, 24) - .padding(.vertical, 18) + .padding(.horizontal, SettingsLayout.detailHorizontalPadding) + .padding(.vertical, SettingsLayout.detailVerticalPadding) } private func channelDetail(_ channel: ChannelItem) -> some View { @@ -85,8 +86,8 @@ extension ChannelsSettings { Spacer(minLength: 0) } .frame(maxWidth: .infinity, alignment: .leading) - .padding(.horizontal, 24) - .padding(.vertical, 18) + .padding(.horizontal, SettingsLayout.detailHorizontalPadding) + .padding(.vertical, SettingsLayout.detailVerticalPadding) } } diff --git a/apps/macos/Sources/OpenClaw/ConfigSettings.swift b/apps/macos/Sources/OpenClaw/ConfigSettings.swift index 256731bd550..b95ff6fdaa4 100644 --- a/apps/macos/Sources/OpenClaw/ConfigSettings.swift +++ b/apps/macos/Sources/OpenClaw/ConfigSettings.swift @@ -19,6 +19,7 @@ struct ConfigSettings: View { self.detail } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) + .settingsDetailContent() .task { guard !self.hasLoaded else { return } guard !self.isPreview else { return } @@ -117,8 +118,8 @@ extension ConfigSettings { .font(.callout) .foregroundStyle(.secondary) } - .padding(.horizontal, 24) - .padding(.vertical, 18) + .padding(.horizontal, SettingsLayout.detailHorizontalPadding) + .padding(.vertical, SettingsLayout.detailVerticalPadding) } private var schemaUnavailableDetail: some View { @@ -129,8 +130,8 @@ extension ConfigSettings { .foregroundStyle(.secondary) self.actionRow } - .padding(.horizontal, 24) - .padding(.vertical, 18) + .padding(.horizontal, SettingsLayout.detailHorizontalPadding) + .padding(.vertical, SettingsLayout.detailVerticalPadding) } private func sectionDetail(_ section: ConfigSection) -> some View { @@ -153,8 +154,8 @@ extension ConfigSettings { Spacer(minLength: 0) } .frame(maxWidth: .infinity, alignment: .leading) - .padding(.horizontal, 24) - .padding(.vertical, 18) + .padding(.horizontal, SettingsLayout.detailHorizontalPadding) + .padding(.vertical, SettingsLayout.detailVerticalPadding) .groupBoxStyle(PlainSettingsGroupBoxStyle()) } } diff --git a/apps/macos/Sources/OpenClaw/CronSettings+Layout.swift b/apps/macos/Sources/OpenClaw/CronSettings+Layout.swift index 8c8ef860d94..bb550b69843 100644 --- a/apps/macos/Sources/OpenClaw/CronSettings+Layout.swift +++ b/apps/macos/Sources/OpenClaw/CronSettings+Layout.swift @@ -9,8 +9,7 @@ extension CronSettings { Spacer(minLength: 0) } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) - .padding(.leading, 18) - .padding(.trailing, SettingsLayout.scrollbarGutter) + .settingsDetailContent() .onAppear { self.updateActiveWork(active: self.isActive) } diff --git a/apps/macos/Sources/OpenClaw/DebugSettings.swift b/apps/macos/Sources/OpenClaw/DebugSettings.swift index ee11a1a4c6e..f2252c01de7 100644 --- a/apps/macos/Sources/OpenClaw/DebugSettings.swift +++ b/apps/macos/Sources/OpenClaw/DebugSettings.swift @@ -62,9 +62,7 @@ struct DebugSettings: View { Spacer(minLength: 0) } - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.vertical, 4) - .padding(.trailing, SettingsLayout.scrollbarGutter) + .settingsDetailContent() .groupBoxStyle(PlainSettingsGroupBoxStyle()) } .task { diff --git a/apps/macos/Sources/OpenClaw/GeneralSettings.swift b/apps/macos/Sources/OpenClaw/GeneralSettings.swift index 2f0fa72b450..9ba2f1b2847 100644 --- a/apps/macos/Sources/OpenClaw/GeneralSettings.swift +++ b/apps/macos/Sources/OpenClaw/GeneralSettings.swift @@ -46,10 +46,7 @@ struct GeneralSettings: View { self.connectionPage } } - .frame(maxWidth: 760, alignment: .leading) - .padding(.bottom, 16) - .padding(.leading, 18) - .padding(.trailing, SettingsLayout.scrollbarGutter) + .settingsDetailContent() } .onAppear { self.updateActiveWork(active: self.isActive) diff --git a/apps/macos/Sources/OpenClaw/InstancesSettings.swift b/apps/macos/Sources/OpenClaw/InstancesSettings.swift index a9b9c7630ae..c54b26984e0 100644 --- a/apps/macos/Sources/OpenClaw/InstancesSettings.swift +++ b/apps/macos/Sources/OpenClaw/InstancesSettings.swift @@ -32,8 +32,7 @@ struct InstancesSettings: View { Spacer() } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) - .padding(.leading, 18) - .padding(.trailing, SettingsLayout.scrollbarGutter) + .settingsDetailContent() .onAppear { self.updateActiveWork(active: self.isActive) } .onChange(of: self.isActive) { _, active in self.updateActiveWork(active: active) diff --git a/apps/macos/Sources/OpenClaw/PermissionsSettings.swift b/apps/macos/Sources/OpenClaw/PermissionsSettings.swift index b99b9746f49..9addc5763b5 100644 --- a/apps/macos/Sources/OpenClaw/PermissionsSettings.swift +++ b/apps/macos/Sources/OpenClaw/PermissionsSettings.swift @@ -36,9 +36,7 @@ struct PermissionsSettings: View { } } } - .frame(maxWidth: 760, alignment: .leading) - .padding(.trailing, SettingsLayout.scrollbarGutter) - .padding(.vertical, 4) + .settingsDetailContent() } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) } diff --git a/apps/macos/Sources/OpenClaw/SessionsSettings.swift b/apps/macos/Sources/OpenClaw/SessionsSettings.swift index 53c267c7468..b1b5126566a 100644 --- a/apps/macos/Sources/OpenClaw/SessionsSettings.swift +++ b/apps/macos/Sources/OpenClaw/SessionsSettings.swift @@ -24,8 +24,7 @@ struct SessionsSettings: View { Spacer() } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) - .padding(.leading, 18) - .padding(.trailing, SettingsLayout.scrollbarGutter) + .settingsDetailContent() .task { guard !self.hasLoaded else { return } guard !self.isPreview else { return } diff --git a/apps/macos/Sources/OpenClaw/SettingsComponents.swift b/apps/macos/Sources/OpenClaw/SettingsComponents.swift index 6f376c3bca6..cf5538f65c4 100644 --- a/apps/macos/Sources/OpenClaw/SettingsComponents.swift +++ b/apps/macos/Sources/OpenClaw/SettingsComponents.swift @@ -3,8 +3,18 @@ import SwiftUI enum SettingsLayout { static let sidebarWidth: CGFloat = 250 static let detailHorizontalPadding: CGFloat = 22 + static let detailVerticalPadding: CGFloat = 18 static let nestedSidebarWidth: CGFloat = 260 - static let scrollbarGutter: CGFloat = 36 + static let detailBottomPadding: CGFloat = 16 +} + +extension View { + func settingsDetailContent() -> some View { + self + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.vertical, 4) + .padding(.bottom, SettingsLayout.detailBottomPadding) + } } struct SettingsPageHeader: View { diff --git a/apps/macos/Sources/OpenClaw/SettingsRootView.swift b/apps/macos/Sources/OpenClaw/SettingsRootView.swift index bfddeee9369..a4f83595fc3 100644 --- a/apps/macos/Sources/OpenClaw/SettingsRootView.swift +++ b/apps/macos/Sources/OpenClaw/SettingsRootView.swift @@ -84,7 +84,7 @@ struct SettingsRootView: View { } .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) .padding(.horizontal, SettingsLayout.detailHorizontalPadding) - .padding(.vertical, 18) + .padding(.vertical, SettingsLayout.detailVerticalPadding) } private var cachedDetailTabs: [SettingsTab] { diff --git a/apps/macos/Sources/OpenClaw/SkillsSettings.swift b/apps/macos/Sources/OpenClaw/SkillsSettings.swift index 09baa4c8f2a..2767a64cfb8 100644 --- a/apps/macos/Sources/OpenClaw/SkillsSettings.swift +++ b/apps/macos/Sources/OpenClaw/SkillsSettings.swift @@ -27,9 +27,7 @@ struct SkillsSettings: View { self.skillsList Spacer(minLength: 8) } - .frame(maxWidth: 860, alignment: .leading) - .padding(.trailing, SettingsLayout.scrollbarGutter) - .padding(.vertical, 4) + .settingsDetailContent() } .task { guard !self.didScheduleInitialRefresh else { return } diff --git a/apps/macos/Sources/OpenClaw/SystemRunSettingsView.swift b/apps/macos/Sources/OpenClaw/SystemRunSettingsView.swift index bc069b45c8f..927abf456a3 100644 --- a/apps/macos/Sources/OpenClaw/SystemRunSettingsView.swift +++ b/apps/macos/Sources/OpenClaw/SystemRunSettingsView.swift @@ -12,8 +12,7 @@ struct ExecApprovalsSettings: View { SystemRunSettingsView() } - .frame(maxWidth: .infinity, alignment: .leading) - .padding(.vertical, 4) + .settingsDetailContent() } } } diff --git a/apps/macos/Sources/OpenClaw/VoiceWakeSettings.swift b/apps/macos/Sources/OpenClaw/VoiceWakeSettings.swift index 81171b7ec94..df2bd43f9e5 100644 --- a/apps/macos/Sources/OpenClaw/VoiceWakeSettings.swift +++ b/apps/macos/Sources/OpenClaw/VoiceWakeSettings.swift @@ -186,9 +186,7 @@ struct VoiceWakeSettings: View { Spacer(minLength: 8) } - .frame(maxWidth: 760, alignment: .leading) - .padding(.trailing, SettingsLayout.scrollbarGutter) - .padding(.vertical, 4) + .settingsDetailContent() } .task { guard !self.isPreview else { return }