Skip to content

Commit c58b1a2

Browse files
Fix settings pane scrolling (#70)
* fix: make settings tabs scrollable Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: align settings window initial height Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d87f24e commit c58b1a2

2 files changed

Lines changed: 168 additions & 157 deletions

File tree

Sources/MacRunner/MacRunnerApp.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
9999
let window = NSWindow(contentViewController: hostingController)
100100
window.title = "Mac Runner Settings"
101101
window.styleMask = [.titled, .closable]
102-
window.setContentSize(NSSize(width: 400, height: 300))
102+
window.setContentSize(NSSize(width: 400, height: 420))
103103
window.center()
104104
window.isReleasedWhenClosed = false
105105
window.makeKeyAndOrderFront(nil)

Sources/Views/MenuBarView.swift

Lines changed: 167 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -417,207 +417,218 @@ struct SettingsView: View {
417417
var body: some View {
418418
TabView {
419419
// General Tab
420-
VStack(alignment: .leading, spacing: 16) {
421-
Text("General")
422-
.font(.headline)
423-
424-
Toggle("Launch at Login", isOn: Binding(
425-
get: { runnerManager.currentSettings.startOnLogin },
426-
set: { newValue in
427-
var settings = runnerManager.currentSettings
428-
settings.startOnLogin = newValue
429-
runnerManager.updateSettings(settings)
430-
}
431-
))
432-
433-
Text("When enabled, Mac Runner starts automatically when you log in and restarts any runners that were previously running.")
434-
.font(.caption)
435-
.foregroundColor(.secondary)
436-
437-
Toggle("Check for Updates Automatically", isOn: Binding(
438-
get: { runnerManager.currentSettings.autoCheckForUpdates },
439-
set: { newValue in
440-
var settings = runnerManager.currentSettings
441-
settings.autoCheckForUpdates = newValue
442-
runnerManager.updateSettings(settings)
443-
}
444-
))
445-
446-
Text("Checks the latest GitHub release on launch, then uses a 24-hour backoff with a 5-minute response cache for manual refreshes.")
447-
.font(.caption)
448-
.foregroundColor(.secondary)
449-
450-
Toggle("Auto-Restart on Crash", isOn: Binding(
451-
get: { runnerManager.currentSettings.autoRestartEnabled },
452-
set: { newValue in
453-
var settings = runnerManager.currentSettings
454-
settings.autoRestartEnabled = newValue
455-
runnerManager.updateSettings(settings)
456-
}
457-
))
458-
459-
HStack {
460-
Text("Max retries in 10 minutes")
461-
Spacer()
462-
Stepper(
463-
value: Binding(
464-
get: { runnerManager.currentSettings.autoRestartMaxRetries },
465-
set: { newValue in
466-
var settings = runnerManager.currentSettings
467-
settings.autoRestartMaxRetries = max(1, newValue)
468-
runnerManager.updateSettings(settings)
469-
}
470-
),
471-
in: 1...20
472-
) {
473-
Text("\(runnerManager.currentSettings.autoRestartMaxRetries)")
474-
.monospacedDigit()
475-
}
476-
.labelsHidden()
477-
}
478-
479-
Text("Crash recovery uses exponential backoff (5s, 10s, 20s, capped at 60s).")
480-
.font(.caption)
481-
.foregroundColor(.secondary)
482-
483-
Toggle("Job Notifications", isOn: Binding(
484-
get: { runnerManager.currentSettings.notificationsEnabled },
485-
set: { newValue in
486-
var settings = runnerManager.currentSettings
487-
settings.notificationsEnabled = newValue
488-
runnerManager.updateSettings(settings)
489-
}
490-
))
420+
ScrollView {
421+
VStack(alignment: .leading, spacing: 16) {
422+
Text("General")
423+
.font(.headline)
491424

492-
Text("Show native macOS notifications when a runner starts a job and when that job completes. Clicking a notification opens the GitHub Actions run.")
493-
.font(.caption)
494-
.foregroundColor(.secondary)
425+
Toggle("Launch at Login", isOn: Binding(
426+
get: { runnerManager.currentSettings.startOnLogin },
427+
set: { newValue in
428+
var settings = runnerManager.currentSettings
429+
settings.startOnLogin = newValue
430+
runnerManager.updateSettings(settings)
431+
}
432+
))
495433

496-
VStack(alignment: .leading, spacing: 6) {
497-
Text("Default Open Files Limit")
498-
.font(.subheadline)
434+
Text("When enabled, Mac Runner starts automatically when you log in and restarts any runners that were previously running.")
435+
.font(.caption)
499436
.foregroundColor(.secondary)
500437

501-
TextField("Open files limit", value: Binding(
502-
get: { runnerManager.currentSettings.openFileLimit },
438+
Toggle("Check for Updates Automatically", isOn: Binding(
439+
get: { runnerManager.currentSettings.autoCheckForUpdates },
503440
set: { newValue in
504441
var settings = runnerManager.currentSettings
505-
settings.openFileLimit = max(1, newValue)
442+
settings.autoCheckForUpdates = newValue
506443
runnerManager.updateSettings(settings)
507444
}
508-
), formatter: openFileLimitFormatter)
509-
.textFieldStyle(.roundedBorder)
445+
))
510446

511-
Text("Applied to runners by default across non-isolated, dedicated-user, and container modes unless a runner overrides it.")
447+
Text("Checks the latest GitHub release on launch, then uses a 24-hour backoff with a 5-minute response cache for manual refreshes.")
512448
.font(.caption)
513449
.foregroundColor(.secondary)
514-
}
515450

516-
VStack(alignment: .leading, spacing: 6) {
517-
Text("Extra CI Tools")
518-
.font(.subheadline)
451+
Toggle("Auto-Restart on Crash", isOn: Binding(
452+
get: { runnerManager.currentSettings.autoRestartEnabled },
453+
set: { newValue in
454+
var settings = runnerManager.currentSettings
455+
settings.autoRestartEnabled = newValue
456+
runnerManager.updateSettings(settings)
457+
}
458+
))
459+
460+
HStack {
461+
Text("Max retries in 10 minutes")
462+
Spacer()
463+
Stepper(
464+
value: Binding(
465+
get: { runnerManager.currentSettings.autoRestartMaxRetries },
466+
set: { newValue in
467+
var settings = runnerManager.currentSettings
468+
settings.autoRestartMaxRetries = max(1, newValue)
469+
runnerManager.updateSettings(settings)
470+
}
471+
),
472+
in: 1...20
473+
) {
474+
Text("\(runnerManager.currentSettings.autoRestartMaxRetries)")
475+
.monospacedDigit()
476+
}
477+
.labelsHidden()
478+
}
479+
480+
Text("Crash recovery uses exponential backoff (5s, 10s, 20s, capped at 60s).")
481+
.font(.caption)
519482
.foregroundColor(.secondary)
520483

521-
TextField("jq, pnpm, ...", text: Binding(
522-
get: { runnerManager.currentSettings.tools.extraPackages.joined(separator: ", ") },
484+
Toggle("Job Notifications", isOn: Binding(
485+
get: { runnerManager.currentSettings.notificationsEnabled },
523486
set: { newValue in
524487
var settings = runnerManager.currentSettings
525-
settings.tools = ToolProvisioningSettings(
526-
extraPackages: newValue
527-
.components(separatedBy: ",")
528-
.map {
529-
$0.trimmingCharacters(in: .whitespacesAndNewlines)
530-
}
531-
)
488+
settings.notificationsEnabled = newValue
532489
runnerManager.updateSettings(settings)
533490
}
534491
))
535-
.textFieldStyle(.roundedBorder)
536492

537-
Text("New runners always provision gh, detect common language toolchains from repo metadata, and install any extra Homebrew packages listed here.")
493+
Text("Show native macOS notifications when a runner starts a job and when that job completes. Clicking a notification opens the GitHub Actions run.")
538494
.font(.caption)
539495
.foregroundColor(.secondary)
496+
497+
VStack(alignment: .leading, spacing: 6) {
498+
Text("Default Open Files Limit")
499+
.font(.subheadline)
500+
.foregroundColor(.secondary)
501+
502+
TextField("Open files limit", value: Binding(
503+
get: { runnerManager.currentSettings.openFileLimit },
504+
set: { newValue in
505+
var settings = runnerManager.currentSettings
506+
settings.openFileLimit = max(1, newValue)
507+
runnerManager.updateSettings(settings)
508+
}
509+
), formatter: openFileLimitFormatter)
510+
.textFieldStyle(.roundedBorder)
511+
512+
Text("Applied to runners by default across non-isolated, dedicated-user, and container modes unless a runner overrides it.")
513+
.font(.caption)
514+
.foregroundColor(.secondary)
515+
}
516+
517+
VStack(alignment: .leading, spacing: 6) {
518+
Text("Extra CI Tools")
519+
.font(.subheadline)
520+
.foregroundColor(.secondary)
521+
522+
TextField("jq, pnpm, ...", text: Binding(
523+
get: { runnerManager.currentSettings.tools.extraPackages.joined(separator: ", ") },
524+
set: { newValue in
525+
var settings = runnerManager.currentSettings
526+
settings.tools = ToolProvisioningSettings(
527+
extraPackages: newValue
528+
.components(separatedBy: ",")
529+
.map {
530+
$0.trimmingCharacters(in: .whitespacesAndNewlines)
531+
}
532+
)
533+
runnerManager.updateSettings(settings)
534+
}
535+
))
536+
.textFieldStyle(.roundedBorder)
537+
538+
Text("New runners always provision gh, detect common language toolchains from repo metadata, and install any extra Homebrew packages listed here.")
539+
.font(.caption)
540+
.foregroundColor(.secondary)
541+
}
540542
}
541-
Spacer()
543+
.padding()
544+
.frame(maxWidth: .infinity, alignment: .leading)
542545
}
543-
.padding()
544546
.tabItem { Label("General", systemImage: "gear") }
545547

546548
// GitHub Tab
547-
VStack(alignment: .leading, spacing: 16) {
548-
Text("GitHub Authentication")
549-
.font(.headline)
549+
ScrollView {
550+
VStack(alignment: .leading, spacing: 16) {
551+
Text("GitHub Authentication")
552+
.font(.headline)
550553

551-
HStack {
552-
Circle()
553-
.fill(isAuthenticated ? Color.green : Color.red)
554-
.frame(width: 10, height: 10)
555-
Text(isAuthenticated ? "Authenticated" : "Not authenticated")
556-
}
554+
HStack {
555+
Circle()
556+
.fill(isAuthenticated ? Color.green : Color.red)
557+
.frame(width: 10, height: 10)
558+
Text(isAuthenticated ? "Authenticated" : "Not authenticated")
559+
}
557560

558-
Text(authStatusText)
559-
.font(.caption)
560-
.foregroundColor(.secondary)
561-
.textSelection(.enabled)
561+
Text(authStatusText)
562+
.font(.caption)
563+
.foregroundColor(.secondary)
564+
.textSelection(.enabled)
562565

563-
if !isAuthenticated {
564-
Button(action: {
565-
Task { await login() }
566-
}) {
567-
Label("Sign in with GitHub", systemImage: "person.badge.key")
566+
if !isAuthenticated {
567+
Button(action: {
568+
Task { await login() }
569+
}) {
570+
Label("Sign in with GitHub", systemImage: "person.badge.key")
571+
}
572+
.disabled(isLoggingIn)
568573
}
569-
.disabled(isLoggingIn)
570574
}
571-
572-
Spacer()
575+
.padding()
576+
.frame(maxWidth: .infinity, alignment: .leading)
573577
}
574-
.padding()
575578
.tabItem { Label("GitHub", systemImage: "lock.shield") }
576579

577580
// About Tab
578-
VStack(spacing: 12) {
579-
Image(systemName: "figure.run")
580-
.font(.system(size: 48))
581-
.foregroundColor(.accentColor)
581+
GeometryReader { proxy in
582+
ScrollView {
583+
VStack(spacing: 12) {
584+
Spacer(minLength: 0)
582585

583-
Text("Mac Runner")
584-
.font(.title2)
585-
.bold()
586+
Image(systemName: "figure.run")
587+
.font(.system(size: 48))
588+
.foregroundColor(.accentColor)
586589

587-
Text("Version \(CLIHandler.version)")
588-
.foregroundColor(.secondary)
590+
Text("Mac Runner")
591+
.font(.title2)
592+
.bold()
589593

590-
if let update = runnerManager.availableUpdate {
591-
Text("Update available: \(update.latestVersion)")
592-
.font(.caption)
593-
.foregroundColor(.accentColor)
594-
} else {
595-
Text(runnerManager.updateStatusMessage)
596-
.font(.caption)
597-
.foregroundColor(.secondary)
598-
.multilineTextAlignment(.center)
599-
}
594+
Text("Version \(CLIHandler.version)")
595+
.foregroundColor(.secondary)
600596

601-
Button(action: {
602-
Task {
603-
await runnerManager.checkForUpdates(force: true)
604-
}
605-
}) {
606-
Label("Check for Updates", systemImage: "arrow.clockwise")
607-
}
608-
.disabled(runnerManager.isCheckingForUpdates)
597+
if let update = runnerManager.availableUpdate {
598+
Text("Update available: \(update.latestVersion)")
599+
.font(.caption)
600+
.foregroundColor(.accentColor)
601+
} else {
602+
Text(runnerManager.updateStatusMessage)
603+
.font(.caption)
604+
.foregroundColor(.secondary)
605+
.multilineTextAlignment(.center)
606+
}
609607

610-
Text("GitHub Actions self-hosted runner manager for macOS")
611-
.font(.caption)
612-
.foregroundColor(.secondary)
613-
.multilineTextAlignment(.center)
608+
Button(action: {
609+
Task {
610+
await runnerManager.checkForUpdates(force: true)
611+
}
612+
}) {
613+
Label("Check for Updates", systemImage: "arrow.clockwise")
614+
}
615+
.disabled(runnerManager.isCheckingForUpdates)
614616

615-
Spacer()
617+
Text("GitHub Actions self-hosted runner manager for macOS")
618+
.font(.caption)
619+
.foregroundColor(.secondary)
620+
.multilineTextAlignment(.center)
621+
622+
Spacer(minLength: 0)
623+
}
624+
.padding()
625+
.frame(maxWidth: .infinity)
626+
.frame(minHeight: proxy.size.height)
627+
}
616628
}
617-
.padding()
618629
.tabItem { Label("About", systemImage: "info.circle") }
619630
}
620-
.frame(minWidth: 400, minHeight: 300)
631+
.frame(minWidth: 400, minHeight: 420)
621632
.task {
622633
await checkAuth()
623634
}

0 commit comments

Comments
 (0)