fix: polish Mac settings layout

This commit is contained in:
Peter Steinberger
2026-05-18 06:10:40 +01:00
parent 198f20fd20
commit 55ca2df62a
4 changed files with 30 additions and 17 deletions

View File

@@ -40,6 +40,9 @@ Docs: https://docs.openclaw.ai
- 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, and keep the Settings sidebar toggle in the leading titlebar area.
- Mac app: allow longer Gateway and Context errors to wrap in the menu instead of truncating the useful failure detail.
- Mac app: tighten remote Gateway fields in Settings so the Connection pane keeps readable labels and full action button text.
- Mac app: keep custom Settings card rows left-aligned and full-width so Discovery and status sections no longer appear centered or detached.
- Mac app: align Location permission controls to the same trailing column as the rest of Settings.
- Gateway/webchat: hide internal runtime-context and other `display: false` transcript messages from Chat history and live message events. Fixes #83216. Thanks @EmpireCreator.
- CLI/help: keep `gateway`, `doctor`, `status`, and `health` help registration out of action/runtime imports so subcommand `--help` stays lightweight in constrained terminals. Fixes #83228. Thanks @dfguerrerom.
- Cron/Discord: keep explicit announce runs in message-tool-only source-reply mode so scheduled agent turns post once instead of also echoing through automatic visible replies. Fixes #83261. Thanks @Theralley.

View File

@@ -11,6 +11,9 @@ struct GeneralSettings: View {
case connection
}
private static let remoteFieldWidth: CGFloat = 320
private static let remoteSecretFieldWidth: CGFloat = 300
@Bindable var state: AppState
@AppStorage(cameraEnabledKey) private var cameraEnabled: Bool = false
let page: Page
@@ -384,6 +387,7 @@ struct GeneralSettings: View {
self.applyDiscoveredGateway(gateway)
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 14)
.padding(.vertical, 11)
.overlay(alignment: .bottom) {
@@ -500,7 +504,7 @@ struct GeneralSettings: View {
SettingsCardRow(title: "SSH target", subtitle: "User and host for the remote Gateway machine.") {
TextField("user@host[:22]", text: self.$state.remoteTarget)
.textFieldStyle(.roundedBorder)
.frame(width: 420)
.frame(width: Self.remoteFieldWidth)
self.remoteTestButton(disabled: !canTest)
}
if let validationMessage {
@@ -514,20 +518,20 @@ struct GeneralSettings: View {
}
private var remoteDirectRow: some View {
VStack(alignment: .leading, spacing: 0) {
SettingsCardRow(title: "Gateway URL", subtitle: "The WebSocket URL exposed by the remote Gateway.") {
TextField("wss://gateway.example.ts.net", text: self.$state.remoteUrl)
.textFieldStyle(.roundedBorder)
.frame(width: 420)
self.remoteTestButton(
disabled: self.state.remoteUrl.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
SettingsCardRow(title: "Gateway URL", subtitle: "The WebSocket URL exposed by the remote Gateway.") {
VStack(alignment: .leading, spacing: 6) {
HStack(spacing: 8) {
TextField("wss://gateway.example.ts.net", text: self.$state.remoteUrl)
.textFieldStyle(.roundedBorder)
.frame(width: Self.remoteFieldWidth)
self.remoteTestButton(
disabled: self.state.remoteUrl.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)
}
Text("Use wss:// for public hosts. ws:// is allowed for localhost, LAN, .local, and Tailnet hosts.")
.font(.caption)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
Text(
"Use wss:// for public hosts. ws:// is allowed for localhost, LAN, .local, and Tailnet hosts.")
.font(.caption)
.foregroundStyle(.secondary)
.padding(.horizontal, 14)
.padding(.bottom, 10)
}
}
@@ -540,7 +544,7 @@ struct GeneralSettings: View {
{
SecureField("remote gateway auth token (gateway.remote.token)", text: self.$state.remoteToken)
.textFieldStyle(.roundedBorder)
.frame(width: 360)
.frame(width: Self.remoteSecretFieldWidth)
}
if self.state.remoteTokenUnsupported {
Text(
@@ -566,6 +570,7 @@ struct GeneralSettings: View {
}
}
.buttonStyle(.borderedProminent)
.frame(minWidth: 116)
.disabled(self.remoteStatus == .checking || disabled)
}

View File

@@ -80,6 +80,8 @@ struct PermissionsSettings: View {
}
private struct LocationAccessSettings: View {
private static let controlWidth: CGFloat = 180
@AppStorage(locationModeKey) private var locationModeRaw: String = OpenClawLocationMode.off.rawValue
@AppStorage(locationPreciseKey) private var locationPreciseEnabled: Bool = true
@State private var lastLocationModeRaw: String = OpenClawLocationMode.off.rawValue
@@ -97,7 +99,7 @@ private struct LocationAccessSettings: View {
}
.labelsHidden()
.pickerStyle(.menu)
.frame(width: 190)
.frame(width: Self.controlWidth, alignment: .trailing)
}
SettingsCardRow(
@@ -108,6 +110,7 @@ private struct LocationAccessSettings: View {
Toggle("Precise Location", isOn: self.$locationPreciseEnabled)
.labelsHidden()
.toggleStyle(.switch)
.frame(width: Self.controlWidth, alignment: .trailing)
.disabled(self.locationMode == .off)
}
}

View File

@@ -63,9 +63,10 @@ struct SettingsCardGroup<Content: View>: View {
.font(.subheadline.weight(.semibold))
.foregroundStyle(.secondary)
VStack(spacing: 0) {
VStack(alignment: .leading, spacing: 0) {
self.content
}
.frame(maxWidth: .infinity, alignment: .leading)
.background(.quaternary.opacity(0.38), in: RoundedRectangle(cornerRadius: 12, style: .continuous))
.overlay {
RoundedRectangle(cornerRadius: 12, style: .continuous)
@@ -110,6 +111,7 @@ struct SettingsCardRow<Content: View>: View {
self.content
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 14)
.padding(.vertical, 11)
.overlay(alignment: .bottom) {