import AppKitimport KeyboardShortcutsimport SwiftUI /// Colored rounded-square symbol used for app panes in the settings sidebar,/// mirroring the System Settings sidebar style.struct SettingsIconChip: View { static let side: CGFloat = 20 let systemImage: String let color: Color var body: some View { Image(systemName: self.systemImage) .font(.system(size: 11, weight: .semibold)) .foregroundStyle(.white) .frame(width: Self.side, height: Self.side) .background( RoundedRectangle(cornerRadius: 5, style: .continuous) .fill(LinearGradient( colors: [self.color.opacity(0.85), self.color], startPoint: .top, endPoint: .bottom))) .accessibilityHidden(true) }} /// Two-line label for grouped-form rows that genuinely need a supporting sentence.struct SettingsRowLabel: View { let title: String let subtitle: String? init(_ title: String, subtitle: String? = nil) { self.title = title self.subtitle = subtitle } var body: some View { VStack(alignment: .leading, spacing: 2) { Text(self.title) if let subtitle, !subtitle.isEmpty { Text(subtitle) .font(.caption) .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) } } }} /// Section footer for grouped forms. macOS renders bare footer text trailing-aligned/// at body size, which reads badly for long captions; this pins it leading at footnote/// size in secondary color, matching System Settings captions.struct SettingsSectionFooter<Content: View>: View { private let content: Content init(@ViewBuilder content: () -> Content) { self.content = content() } var body: some View { self.content .font(.footnote) .foregroundStyle(.secondary) .multilineTextAlignment(.leading) .fixedSize(horizontal: false, vertical: true) .frame(maxWidth: .infinity, alignment: .leading) }} extension SettingsSectionFooter where Content == Text { init(_ text: String) { self.init { Text(text) } }} @MainActorstruct OpenMenuShortcutRecorder: NSViewRepresentable { static let preferredWidth: CGFloat = 170 func makeNSView(context: Context) -> KeyboardShortcuts.RecorderCocoa { KeyboardShortcuts.RecorderCocoa(for: .openMenu) } func updateNSView(_ nsView: KeyboardShortcuts.RecorderCocoa, context: Context) { nsView.shortcutName = .openMenu } func sizeThatFits( _: ProposedViewSize, nsView: KeyboardShortcuts.RecorderCocoa, context: Context) -> CGSize? { Self.fittedSize(intrinsicHeight: nsView.intrinsicContentSize.height) } static func fittedSize(intrinsicHeight: CGFloat) -> CGSize { CGSize(width: self.preferredWidth, height: intrinsicHeight) }} // MARK: - Legacy building blocks (Debug pane) @MainActorstruct PreferenceToggleRow: View { let title: String let subtitle: String? @Binding var binding: Bool var body: some View { VStack(alignment: .leading, spacing: 5.4) { Toggle(isOn: self.$binding) { Text(self.title) .font(.body) } .toggleStyle(.checkbox) if let subtitle, !subtitle.isEmpty { Text(subtitle) .font(.footnote) .foregroundStyle(.tertiary) .fixedSize(horizontal: false, vertical: true) } } }} @MainActorstruct SettingsSection<Content: View>: View { let title: String? let caption: String? let contentSpacing: CGFloat private let content: () -> Content init( title: String? = nil, caption: String? = nil, contentSpacing: CGFloat = 14, @ViewBuilder content: @escaping () -> Content) { self.title = title self.caption = caption self.contentSpacing = contentSpacing self.content = content } var body: some View { VStack(alignment: .leading, spacing: 10) { if let title, !title.isEmpty { Text(title) .font(.subheadline.weight(.semibold)) } if let caption { Text(caption) .font(.footnote) .foregroundStyle(.tertiary) .fixedSize(horizontal: false, vertical: true) } VStack(alignment: .leading, spacing: self.contentSpacing) { self.content() } .frame(maxWidth: .infinity, alignment: .leading) } .frame(maxWidth: .infinity, alignment: .leading) }}
import AppKit
import KeyboardShortcuts
import SwiftUI
/// Colored rounded-square symbol used for app panes in the settings sidebar,
/// mirroring the System Settings sidebar style.
struct SettingsIconChip: View {
static let side: CGFloat = 20
let systemImage: String
let color: Color
var body: some View {
Image(systemName: self.systemImage)
.font(.system(size: 11, weight: .semibold))
.foregroundStyle(.white)
.frame(width: Self.side, height: Self.side)
.background(
RoundedRectangle(cornerRadius: 5, style: .continuous)
.fill(LinearGradient(
colors: [self.color.opacity(0.85), self.color],
startPoint: .top,
endPoint: .bottom)))
.accessibilityHidden(true)
}
}
/// Two-line label for grouped-form rows that genuinely need a supporting sentence.
struct SettingsRowLabel: View {
let title: String
let subtitle: String?
init(_ title: String, subtitle: String? = nil) {
self.title = title
self.subtitle = subtitle
}
var body: some View {
VStack(alignment: .leading, spacing: 2) {
Text(self.title)
if let subtitle, !subtitle.isEmpty {
Text(subtitle)
.font(.caption)
.foregroundStyle(.secondary)
.fixedSize(horizontal: false, vertical: true)
}
}
}
}
/// Section footer for grouped forms. macOS renders bare footer text trailing-aligned
/// at body size, which reads badly for long captions; this pins it leading at footnote
/// size in secondary color, matching System Settings captions.
struct SettingsSectionFooter<Content: View>: View {
private let content: Content
init(@ViewBuilder content: () -> Content) {
self.content = content()
}
var body: some View {
self.content
.font(.footnote)
.foregroundStyle(.secondary)
.multilineTextAlignment(.leading)
.fixedSize(horizontal: false, vertical: true)
.frame(maxWidth: .infinity, alignment: .leading)
}
}
extension SettingsSectionFooter where Content == Text {
init(_ text: String) {
self.init { Text(text) }
}
}
@MainActor
struct OpenMenuShortcutRecorder: NSViewRepresentable {
static let preferredWidth: CGFloat = 170
func makeNSView(context: Context) -> KeyboardShortcuts.RecorderCocoa {
KeyboardShortcuts.RecorderCocoa(for: .openMenu)
}
func updateNSView(_ nsView: KeyboardShortcuts.RecorderCocoa, context: Context) {
nsView.shortcutName = .openMenu
}
func sizeThatFits(
_: ProposedViewSize,
nsView: KeyboardShortcuts.RecorderCocoa,
context: Context)
-> CGSize?
{
Self.fittedSize(intrinsicHeight: nsView.intrinsicContentSize.height)
}
static func fittedSize(intrinsicHeight: CGFloat) -> CGSize {
CGSize(width: self.preferredWidth, height: intrinsicHeight)
}
}
// MARK: - Legacy building blocks (Debug pane)
@MainActor
struct PreferenceToggleRow: View {
let title: String
let subtitle: String?
@Binding var binding: Bool
var body: some View {
VStack(alignment: .leading, spacing: 5.4) {
Toggle(isOn: self.$binding) {
Text(self.title)
.font(.body)
}
.toggleStyle(.checkbox)
if let subtitle, !subtitle.isEmpty {
Text(subtitle)
.font(.footnote)
.foregroundStyle(.tertiary)
.fixedSize(horizontal: false, vertical: true)
}
}
}
}
@MainActor
struct SettingsSection<Content: View>: View {
let title: String?
let caption: String?
let contentSpacing: CGFloat
private let content: () -> Content
init(
title: String? = nil,
caption: String? = nil,
contentSpacing: CGFloat = 14,
@ViewBuilder content: @escaping () -> Content)
{
self.title = title
self.caption = caption
self.contentSpacing = contentSpacing
self.content = content
}
var body: some View {
VStack(alignment: .leading, spacing: 10) {
if let title, !title.isEmpty {
Text(title)
.font(.subheadline.weight(.semibold))
}
if let caption {
Text(caption)
.font(.footnote)
.foregroundStyle(.tertiary)
.fixedSize(horizontal: false, vertical: true)
}
VStack(alignment: .leading, spacing: self.contentSpacing) {
self.content()
}
.frame(maxWidth: .infinity, alignment: .leading)
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}