Neon-Vision-Editor/Neon Vision Editor/UI/ContentView+Toolbar.swift

1461 lines
57 KiB
Swift
Raw Normal View History

import SwiftUI
#if os(macOS)
import AppKit
#elseif os(iOS)
import UIKit
#endif
/// MARK: - Types
extension ContentView {
2026-02-12 09:15:40 +00:00
private var compactActiveProviderName: String {
activeProviderName.components(separatedBy: " (").first ?? activeProviderName
}
private var providerBadgeLabelText: String {
#if os(macOS)
if compactActiveProviderName == "Apple" {
return "AI Provider \(compactActiveProviderName)"
}
#endif
return compactActiveProviderName
}
private var providerBadgeIsAppleCompletionActive: Bool {
compactActiveProviderName == "Apple" && isAutoCompletionEnabled
}
private var providerBadgeForegroundColor: Color {
providerBadgeIsAppleCompletionActive ? .green : .secondary
}
private var providerBadgeBackgroundColor: Color {
providerBadgeIsAppleCompletionActive ? Color.green.opacity(0.16) : Color.secondary.opacity(0.12)
}
private var providerBadgeTooltip: String {
"AI Provider for Code Completion"
}
#if os(macOS)
private var macToolbarSymbolColor: Color {
let isDarkMode = colorScheme == .dark
switch toolbarSymbolsColorMacRaw {
case "black":
return isDarkMode
? Color(.sRGB, white: 0.94, opacity: 1.0)
: .black
case "darkGray":
return isDarkMode
? Color(.sRGB, white: 0.84, opacity: 1.0)
: Color(.sRGB, white: 0.40, opacity: 1.0)
default:
return NeonUIStyle.accentBlue
}
}
2026-04-16 10:37:03 +00:00
@ViewBuilder
private var markdownPreviewExportToolbarMenuContent: some View {
Button(action: { exportMarkdownPreviewPDF() }) {
Label("Export PDF", systemImage: "square.and.arrow.down")
}
Divider()
Menu {
Button(action: { markdownPDFExportModeRaw = MarkdownPDFExportMode.paginatedFit.rawValue }) {
if markdownPDFExportModeRaw == MarkdownPDFExportMode.paginatedFit.rawValue {
Label("Paginated Fit", systemImage: "checkmark")
} else {
Text("Paginated Fit")
}
}
Button(action: { markdownPDFExportModeRaw = MarkdownPDFExportMode.onePageFit.rawValue }) {
if markdownPDFExportModeRaw == MarkdownPDFExportMode.onePageFit.rawValue {
Label("One Page Fit", systemImage: "checkmark")
} else {
Text("One Page Fit")
}
}
} label: {
Label("PDF Mode", systemImage: "doc.text")
}
Menu {
Button("Default") { markdownPreviewTemplateRaw = "default" }
Button("Docs") { markdownPreviewTemplateRaw = "docs" }
Button("Article") { markdownPreviewTemplateRaw = "article" }
Button("Compact") { markdownPreviewTemplateRaw = "compact" }
Divider()
Button("GitHub Docs") { markdownPreviewTemplateRaw = "github-docs" }
Button("Academic Paper") { markdownPreviewTemplateRaw = "academic-paper" }
Button("Terminal Notes") { markdownPreviewTemplateRaw = "terminal-notes" }
Button("Magazine") { markdownPreviewTemplateRaw = "magazine" }
Button("Minimal Reader") { markdownPreviewTemplateRaw = "minimal-reader" }
Button("Presentation") { markdownPreviewTemplateRaw = "presentation" }
Button("Night Contrast") { markdownPreviewTemplateRaw = "night-contrast" }
Button("Warm Sepia") { markdownPreviewTemplateRaw = "warm-sepia" }
Button("Dense Compact") { markdownPreviewTemplateRaw = "dense-compact" }
Button("Developer Spec") { markdownPreviewTemplateRaw = "developer-spec" }
} label: {
Label(NSLocalizedString("Preview Style", comment: "Markdown preview style menu label"), systemImage: "paintbrush")
}
Divider()
Button(action: { copyMarkdownPreviewHTML() }) {
Label("Copy HTML", systemImage: "doc.on.doc")
}
Button(action: { copyMarkdownPreviewMarkdown() }) {
Label("Copy Markdown", systemImage: "doc.on.clipboard")
}
}
#endif
#if os(iOS)
private var iOSToolbarChromeStyle: GlassChromeStyle { .single }
2026-02-20 00:31:14 +00:00
private var iOSToolbarTintColor: Color {
if toolbarIconsBlueIOS {
return NeonUIStyle.accentBlue
}
2026-02-20 02:41:08 +00:00
return colorScheme == .dark ? Color.white.opacity(0.95) : Color.primary.opacity(0.92)
2026-02-20 00:31:14 +00:00
}
private var isIPadToolbarLayout: Bool {
guard UIDevice.current.userInterfaceIdiom == .pad else { return false }
// During first render on iOS, horizontalSizeClass can transiently be nil.
// Treat nil as regular so the full iPad toolbar appears immediately.
if horizontalSizeClass == .compact { return false }
return true
}
private var iPhoneToolbarWidth: CGFloat {
UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.first(where: { $0.activationState == .foregroundActive })?
.screen.bounds.width ?? 390
}
private var activeWindowWidth: CGFloat {
let scenes = UIApplication.shared.connectedScenes
.compactMap { $0 as? UIWindowScene }
.filter { $0.activationState == .foregroundActive }
let normalWindowWidths = scenes
.flatMap(\.windows)
.filter { window in
!window.isHidden &&
window.alpha > 0.01 &&
window.windowLevel == .normal &&
window.bounds.width > 0
}
.map { $0.bounds.width }
if let width = normalWindowWidths.max() {
return width
}
return scenes.first?.screen.bounds.width ?? 1024
}
private var iPhonePromotedActionsCount: Int {
switch iPhoneToolbarWidth {
case 430...: return 5
case 395...: return 4
default: return 1
}
}
private var iPhoneLanguagePickerWidth: CGFloat {
switch iPhoneToolbarWidth {
case 430...: return 108
case 395...: return 100
default: return 94
}
}
private var iPadToolbarMaxWidth: CGFloat {
// Use live window width (not full screen width) so Stage Manager/split sizes
// immediately rebalance promoted vs overflow actions.
let target = activeWindowWidth - 28
return min(max(target, 560), 1320)
}
2026-02-20 00:31:14 +00:00
2026-02-19 14:29:53 +00:00
private enum IPadToolbarAction: String, CaseIterable, Hashable {
case openFile
case undo
2026-02-19 14:29:53 +00:00
case newTab
2026-03-08 14:31:01 +00:00
case closeAllTabs
2026-02-19 14:29:53 +00:00
case saveFile
case codeSnapshot
case markdownPreview
case fontDecrease
case fontIncrease
2026-02-19 14:29:53 +00:00
case toggleSidebar
case toggleProjectSidebar
case findReplace
case findInFiles
2026-02-19 14:29:53 +00:00
case settings
case codeCompletion
case performanceMode
case lineWrap
case keyboardAccessory
case clearEditor
case insertTemplate
case brainDump
case welcomeTour
case translucentWindow
}
private var iPadActionPriority: [IPadToolbarAction] {
[
.openFile,
.undo,
2026-02-19 14:29:53 +00:00
.newTab,
2026-03-08 14:31:01 +00:00
.closeAllTabs,
2026-02-19 14:29:53 +00:00
.saveFile,
.codeSnapshot,
.markdownPreview,
.fontDecrease,
.fontIncrease,
2026-02-19 14:29:53 +00:00
.toggleSidebar,
.toggleProjectSidebar,
2026-02-20 00:31:14 +00:00
.findReplace,
.findInFiles,
2026-02-19 14:29:53 +00:00
.settings,
.codeCompletion,
.lineWrap,
2026-02-20 00:31:14 +00:00
.keyboardAccessory,
2026-02-19 14:29:53 +00:00
.clearEditor,
.insertTemplate,
.performanceMode,
.brainDump,
.welcomeTour,
.translucentWindow
]
}
private func toggleKeyboardAccessoryBar() {
showKeyboardAccessoryBarIOS.toggle()
NotificationCenter.default.post(
name: .keyboardAccessoryBarVisibilityChanged,
object: showKeyboardAccessoryBarIOS
)
}
private func toggleBrainDumpModeIOSAware() {
#if os(iOS)
viewModel.isBrainDumpMode = false
UserDefaults.standard.set(false, forKey: "BrainDumpModeEnabled")
#else
viewModel.isBrainDumpMode.toggle()
UserDefaults.standard.set(viewModel.isBrainDumpMode, forKey: "BrainDumpModeEnabled")
#endif
}
private var iPadPinnedOverflowActions: Set<IPadToolbarAction> {
[
.closeAllTabs,
.performanceMode,
2026-02-19 14:29:53 +00:00
.brainDump,
.welcomeTour,
.translucentWindow
]
}
private var iPadAlwaysVisibleActions: [IPadToolbarAction] {
[.openFile, .newTab, .saveFile, .findReplace, .findInFiles, .settings]
}
2026-02-19 14:29:53 +00:00
private var iPadPromotedActionSlotCount: Int {
2026-02-20 00:31:14 +00:00
switch iPadToolbarMaxWidth {
case 1200...: return 11
case 1080...: return 10
case 980...: return 9
2026-02-20 00:31:14 +00:00
case 900...: return 9
case 820...: return 8
case 740...: return 7
case 660...: return 6
default: return 5
2026-02-20 00:31:14 +00:00
}
}
2026-02-19 14:29:53 +00:00
private var iPadPromotedActions: [IPadToolbarAction] {
let eligible = iPadActionPriority.filter {
!iPadPinnedOverflowActions.contains($0) &&
!iPadAlwaysVisibleActions.contains($0)
}
return Array(eligible.prefix(iPadPromotedActionSlotCount))
2026-02-19 14:29:53 +00:00
}
private var iPadOverflowActions: [IPadToolbarAction] {
iPadActionPriority.filter {
!iPadAlwaysVisibleActions.contains($0) &&
(iPadPinnedOverflowActions.contains($0) || !iPadPromotedActions.contains($0))
}
2026-02-19 14:29:53 +00:00
}
@ViewBuilder
private var newTabControl: some View {
Button(action: { viewModel.addNewTab() }) {
Image(systemName: "plus.square.on.square")
}
.help("New Tab (Cmd+T)")
.accessibilityLabel("New tab")
.accessibilityHint("Creates a new editor tab")
2026-02-16 19:02:43 +00:00
.keyboardShortcut("t", modifiers: .command)
}
@ViewBuilder
private var settingsControl: some View {
2026-02-27 17:13:12 +00:00
Button(action: { openSettings() }) {
Image(systemName: "gearshape")
}
2026-02-27 17:13:12 +00:00
.help("Settings (Cmd+,)")
.accessibilityLabel("Settings")
.accessibilityHint("Opens app settings")
2026-02-27 17:13:12 +00:00
.keyboardShortcut(",", modifiers: .command)
}
@ViewBuilder
private var languagePickerControl: some View {
Menu {
2026-03-26 18:19:45 +00:00
let selectedLanguage = currentLanguagePickerBinding.wrappedValue
Button {
currentLanguagePickerBinding.wrappedValue = selectedLanguage
} label: {
Label(languageLabel(for: selectedLanguage), systemImage: "checkmark")
}
Button(action: { presentLanguageSearchSheet() }) {
Label("Language…", systemImage: "magnifyingglass")
}
.keyboardShortcut("l", modifiers: [.command, .shift])
2026-03-26 18:19:45 +00:00
Divider()
ForEach(languageOptions.filter { $0 != selectedLanguage }, id: \.self) { lang in
Button {
currentLanguagePickerBinding.wrappedValue = lang
} label: {
Text(languageLabel(for: lang))
}
}
} label: {
Text(toolbarCompactLanguageLabel(currentLanguagePickerBinding.wrappedValue))
.lineLimit(1)
.truncationMode(.tail)
#if os(iOS)
.padding(.horizontal, 10)
.padding(.vertical, 5)
.background(.ultraThinMaterial, in: Capsule())
.overlay(
Capsule()
.stroke(iOSToolbarTintColor.opacity(0.35), lineWidth: 1)
)
.frame(width: isIPadToolbarLayout ? 112 : iPhoneLanguagePickerWidth)
#endif
}
.labelsHidden()
.help("Language")
.accessibilityLabel("Language picker")
.accessibilityHint("Choose syntax language for the current tab")
.layoutPriority(2)
#if os(iOS)
2026-03-26 18:19:45 +00:00
.tint(iOSToolbarTintColor)
.menuStyle(.button)
#endif
2026-02-19 14:29:53 +00:00
}
@ViewBuilder
private var activeProviderBadgeControl: some View {
Text(providerBadgeLabelText)
.font(.caption)
.foregroundColor(providerBadgeForegroundColor)
2026-02-12 09:15:40 +00:00
.lineLimit(1)
.truncationMode(.tail)
.minimumScaleFactor(0.9)
.fixedSize(horizontal: true, vertical: false)
.padding(.horizontal, 6)
.padding(.vertical, 2)
.background(providerBadgeBackgroundColor, in: Capsule())
.help(providerBadgeTooltip)
}
@ViewBuilder
private var clearEditorControl: some View {
Button(action: {
requestClearEditorContent()
}) {
Image(systemName: "eraser")
}
.help("Clear Editor")
}
@ViewBuilder
private var insertTemplateControl: some View {
Button(action: { insertTemplateForCurrentLanguage() }) {
Image(systemName: "doc.badge.plus")
}
.help("Insert Template for Current Language")
}
2026-02-16 19:02:43 +00:00
@ViewBuilder
private var lineWrapControl: some View {
Button(action: {
viewModel.isLineWrapEnabled.toggle()
}) {
Image(systemName: "text.justify")
}
.help("Enable Wrap / Disable Wrap (Cmd+Opt+L)")
.keyboardShortcut("l", modifiers: [.command, .option])
}
@ViewBuilder
private var openFileControl: some View {
Button(action: { openFileFromToolbar() }) {
Image(systemName: "folder")
}
.help("Open File… (Cmd+O)")
.accessibilityLabel("Open file")
.accessibilityHint("Opens a file picker")
2026-02-16 19:02:43 +00:00
.keyboardShortcut("o", modifiers: .command)
}
@ViewBuilder
private var undoControl: some View {
Button(action: { undoFromToolbar() }) {
Image(systemName: "arrow.uturn.backward")
}
.help("Undo (Cmd+Z)")
.keyboardShortcut("z", modifiers: .command)
}
@ViewBuilder
private var saveFileControl: some View {
Button(action: { saveCurrentTabFromToolbar() }) {
Image(systemName: "square.and.arrow.down")
}
.disabled(viewModel.selectedTab == nil || viewModel.selectedTab?.isReadOnlyPreview == true)
.help("Save File (Cmd+S)")
.accessibilityLabel("Save file")
.accessibilityHint("Saves the current tab")
2026-02-16 19:02:43 +00:00
.keyboardShortcut("s", modifiers: .command)
}
2026-03-08 14:31:01 +00:00
@ViewBuilder
private var closeAllTabsControl: some View {
Button(action: { requestCloseAllTabsFromToolbar() }) {
Image(systemName: "xmark.square")
}
.disabled(viewModel.tabs.isEmpty)
.help("Close All Tabs")
.accessibilityLabel("Close all tabs")
.accessibilityHint("Closes every open tab")
}
@ViewBuilder
private var fontDecreaseControl: some View {
Button(action: { adjustEditorFontSize(-1) }) {
Image(systemName: "textformat.size.smaller")
}
.help("Decrease Font Size")
}
@ViewBuilder
private var fontIncreaseControl: some View {
Button(action: { adjustEditorFontSize(1) }) {
Image(systemName: "textformat.size.larger")
}
.help("Increase Font Size")
}
@ViewBuilder
private var toggleSidebarControl: some View {
Button(action: { toggleSidebarFromToolbar() }) {
Image(systemName: "sidebar.left")
}
.help("Toggle Sidebar (Cmd+Opt+S)")
2026-02-16 19:02:43 +00:00
.keyboardShortcut("s", modifiers: [.command, .option])
}
@ViewBuilder
private var toggleProjectSidebarControl: some View {
Button(action: { toggleProjectSidebarFromToolbar() }) {
Image(systemName: "sidebar.right")
}
.help("Toggle Project Structure Sidebar")
}
@ViewBuilder
private var findReplaceControl: some View {
Button(action: { showFindReplace = true }) {
Image(systemName: "magnifyingglass")
}
.help("Find & Replace (Cmd+F)")
2026-02-16 19:02:43 +00:00
.keyboardShortcut("f", modifiers: .command)
}
@ViewBuilder
private var findInFilesControl: some View {
Button(action: { showFindInFiles = true }) {
Image(systemName: "text.magnifyingglass")
}
.help("Find in Files (Cmd+Shift+F)")
.accessibilityLabel("Find in Files")
.accessibilityHint("Searches across files in the current project")
.keyboardShortcut("f", modifiers: [.command, .shift])
}
@ViewBuilder
private var brainDumpControl: some View {
Button(action: {
toggleBrainDumpModeIOSAware()
}) {
Image(systemName: "note.text")
.symbolVariant(viewModel.isBrainDumpMode ? .fill : .none)
}
.help("Brain Dump Mode")
.accessibilityLabel("Brain Dump Mode")
}
@ViewBuilder
private var codeCompletionControl: some View {
Button(action: {
toggleAutoCompletion()
}) {
Image(systemName: "bolt.horizontal.circle")
.symbolVariant(isAutoCompletionEnabled ? .fill : .none)
}
.help(isAutoCompletionEnabled ? "Disable Code Completion" : "Enable Code Completion")
.accessibilityLabel("Code Completion")
}
@ViewBuilder
private var performanceModeControl: some View {
Button(action: {
forceLargeFileMode.toggle()
updateLargeFileMode(for: currentContentBinding.wrappedValue)
recordDiagnostic("Toolbar toggled performance mode: \(forceLargeFileMode ? "on" : "off")")
}) {
Image(systemName: forceLargeFileMode ? "speedometer" : "speedometer")
.symbolVariant(forceLargeFileMode ? .fill : .none)
}
.help("Performance Mode")
.accessibilityLabel("Performance Mode")
}
@ViewBuilder
private var markdownPreviewControl: some View {
Button(action: {
toggleMarkdownPreviewFromToolbar()
}) {
Image(systemName: showMarkdownPreviewPane ? "doc.richtext.fill" : "doc.richtext")
}
.disabled(currentLanguage != "markdown")
.help("Toggle Markdown Preview")
.accessibilityLabel("Markdown Preview")
}
2026-04-16 10:37:03 +00:00
@ViewBuilder
private var markdownPreviewExportControl: some View {
if showMarkdownPreviewPane && currentLanguage == "markdown" {
Menu {
markdownPreviewExportToolbarMenuContent
} label: {
Image(systemName: "square.and.arrow.down")
}
.help(NSLocalizedString("Markdown Preview Export Options", comment: "Toolbar help for markdown preview export options"))
.accessibilityLabel(NSLocalizedString("Export Markdown preview as PDF", comment: "Accessibility label for markdown preview export button"))
}
}
@ViewBuilder
private var markdownPreviewStyleControl: some View {
if showMarkdownPreviewPane && currentLanguage == "markdown" {
Menu {
Button("Default") { markdownPreviewTemplateRaw = "default" }
Button("Docs") { markdownPreviewTemplateRaw = "docs" }
Button("Article") { markdownPreviewTemplateRaw = "article" }
Button("Compact") { markdownPreviewTemplateRaw = "compact" }
Divider()
Button("GitHub Docs") { markdownPreviewTemplateRaw = "github-docs" }
Button("Academic Paper") { markdownPreviewTemplateRaw = "academic-paper" }
Button("Terminal Notes") { markdownPreviewTemplateRaw = "terminal-notes" }
Button("Magazine") { markdownPreviewTemplateRaw = "magazine" }
Button("Minimal Reader") { markdownPreviewTemplateRaw = "minimal-reader" }
Button("Presentation") { markdownPreviewTemplateRaw = "presentation" }
Button("Night Contrast") { markdownPreviewTemplateRaw = "night-contrast" }
Button("Warm Sepia") { markdownPreviewTemplateRaw = "warm-sepia" }
Button("Dense Compact") { markdownPreviewTemplateRaw = "dense-compact" }
Button("Developer Spec") { markdownPreviewTemplateRaw = "developer-spec" }
} label: {
Image(systemName: "paintbrush")
}
.help(NSLocalizedString("Markdown Preview Template", comment: "Toolbar help for markdown preview style menu"))
.accessibilityLabel(NSLocalizedString("Markdown Preview Template", comment: "Accessibility label for markdown preview style menu"))
}
}
@ViewBuilder
private var markdownPreviewExportToolbarMenuContent: some View {
Button(action: { exportMarkdownPreviewPDF() }) {
Label("Export PDF", systemImage: "square.and.arrow.down")
}
Divider()
Menu {
Button(action: { markdownPDFExportModeRaw = MarkdownPDFExportMode.paginatedFit.rawValue }) {
if markdownPDFExportModeRaw == MarkdownPDFExportMode.paginatedFit.rawValue {
Label("Paginated Fit", systemImage: "checkmark")
} else {
Text("Paginated Fit")
}
}
Button(action: { markdownPDFExportModeRaw = MarkdownPDFExportMode.onePageFit.rawValue }) {
if markdownPDFExportModeRaw == MarkdownPDFExportMode.onePageFit.rawValue {
Label("One Page Fit", systemImage: "checkmark")
} else {
Text("One Page Fit")
}
}
} label: {
Label("PDF Mode", systemImage: "doc.text")
}
Menu {
Button("Default") { markdownPreviewTemplateRaw = "default" }
Button("Docs") { markdownPreviewTemplateRaw = "docs" }
Button("Article") { markdownPreviewTemplateRaw = "article" }
Button("Compact") { markdownPreviewTemplateRaw = "compact" }
Divider()
Button("GitHub Docs") { markdownPreviewTemplateRaw = "github-docs" }
Button("Academic Paper") { markdownPreviewTemplateRaw = "academic-paper" }
Button("Terminal Notes") { markdownPreviewTemplateRaw = "terminal-notes" }
Button("Magazine") { markdownPreviewTemplateRaw = "magazine" }
Button("Minimal Reader") { markdownPreviewTemplateRaw = "minimal-reader" }
Button("Presentation") { markdownPreviewTemplateRaw = "presentation" }
Button("Night Contrast") { markdownPreviewTemplateRaw = "night-contrast" }
Button("Warm Sepia") { markdownPreviewTemplateRaw = "warm-sepia" }
Button("Dense Compact") { markdownPreviewTemplateRaw = "dense-compact" }
Button("Developer Spec") { markdownPreviewTemplateRaw = "developer-spec" }
} label: {
Label(NSLocalizedString("Preview Style", comment: "Markdown preview style menu label"), systemImage: "paintbrush")
}
Divider()
Button(action: { copyMarkdownPreviewHTML() }) {
Label("Copy HTML", systemImage: "doc.on.doc")
}
Button(action: { copyMarkdownPreviewMarkdown() }) {
Label("Copy Markdown", systemImage: "doc.on.clipboard")
}
}
@ViewBuilder
private var keyboardAccessoryControl: some View {
Button(action: {
toggleKeyboardAccessoryBar()
}) {
Image(systemName: showKeyboardAccessoryBarIOS ? "keyboard.chevron.compact.down.fill" : "keyboard.chevron.compact.down")
}
.help(showKeyboardAccessoryBarIOS ? "Hide Keyboard Snippet Bar" : "Show Keyboard Snippet Bar")
.accessibilityLabel("Keyboard Snippet Bar")
}
@ViewBuilder
private var welcomeTourControl: some View {
Button(action: {
showWelcomeTour = true
}) {
Image(systemName: "sparkles.rectangle.stack")
}
.help("Welcome Tour")
}
@ViewBuilder
private var translucentWindowControl: some View {
Button(action: {
enableTranslucentWindow.toggle()
UserDefaults.standard.set(enableTranslucentWindow, forKey: "EnableTranslucentWindow")
NotificationCenter.default.post(name: .toggleTranslucencyRequested, object: enableTranslucentWindow)
}) {
Image(systemName: enableTranslucentWindow ? "rectangle.fill" : "rectangle")
}
.help("Toggle Translucent Window Background")
.accessibilityLabel("Translucent Window Background")
}
@ViewBuilder
private var codeSnapshotControl: some View {
Button(action: { presentCodeSnapshotComposer() }) {
Image(systemName: "camera.viewfinder")
}
.disabled(!canCreateCodeSnapshot)
.help("Create Code Snapshot from Selection")
.accessibilityLabel("Create Code Snapshot")
}
@ViewBuilder
2026-02-19 14:29:53 +00:00
private func iPadToolbarActionControl(_ action: IPadToolbarAction) -> some View {
switch action {
case .openFile: openFileControl
case .undo: undoControl
2026-02-19 14:29:53 +00:00
case .newTab: newTabControl
2026-03-08 14:31:01 +00:00
case .closeAllTabs: closeAllTabsControl
2026-02-19 14:29:53 +00:00
case .saveFile: saveFileControl
case .codeSnapshot: codeSnapshotControl
case .markdownPreview: markdownPreviewControl
case .fontDecrease: fontDecreaseControl
case .fontIncrease: fontIncreaseControl
2026-02-19 14:29:53 +00:00
case .toggleSidebar: toggleSidebarControl
case .toggleProjectSidebar: toggleProjectSidebarControl
case .findReplace: findReplaceControl
case .findInFiles: findInFilesControl
2026-02-19 14:29:53 +00:00
case .settings: settingsControl
case .codeCompletion: codeCompletionControl
case .performanceMode: performanceModeControl
case .lineWrap: lineWrapControl
case .keyboardAccessory: keyboardAccessoryControl
case .clearEditor: clearEditorControl
case .insertTemplate: insertTemplateControl
case .brainDump: brainDumpControl
case .welcomeTour: welcomeTourControl
case .translucentWindow: translucentWindowControl
}
}
@ViewBuilder
private var iPadOverflowMenuControl: some View {
if !iPadOverflowActions.isEmpty {
Menu {
ForEach(iPadOverflowActions, id: \.self) { action in
switch action {
case .openFile:
Button(action: { openFileFromToolbar() }) {
Label("Open File…", systemImage: "folder")
}
case .undo:
Button(action: { undoFromToolbar() }) {
Label("Undo", systemImage: "arrow.uturn.backward")
}
.keyboardShortcut("z", modifiers: .command)
2026-02-19 14:29:53 +00:00
case .newTab:
Button(action: { viewModel.addNewTab() }) {
Label("New Tab", systemImage: "plus.square.on.square")
}
2026-03-08 14:31:01 +00:00
case .closeAllTabs:
Button(action: { requestCloseAllTabsFromToolbar() }) {
Label("Close All Tabs", systemImage: "xmark.square")
}
.disabled(viewModel.tabs.isEmpty)
2026-02-19 14:29:53 +00:00
case .saveFile:
Button(action: { saveCurrentTabFromToolbar() }) {
Label("Save File", systemImage: "square.and.arrow.down")
}
.disabled(viewModel.selectedTab == nil)
case .codeSnapshot:
Button(action: { presentCodeSnapshotComposer() }) {
Label("Create Code Snapshot", systemImage: "camera.viewfinder")
}
.disabled(!canCreateCodeSnapshot)
case .markdownPreview:
Button(action: { toggleMarkdownPreviewFromToolbar() }) {
Label(
"Markdown Preview",
systemImage: showMarkdownPreviewPane ? "doc.richtext.fill" : "doc.richtext"
)
}
.disabled(currentLanguage != "markdown")
case .fontDecrease:
Button(action: { adjustEditorFontSize(-1) }) {
Label("Font -", systemImage: "textformat.size.smaller")
}
case .fontIncrease:
Button(action: { adjustEditorFontSize(1) }) {
Label("Font +", systemImage: "textformat.size.larger")
}
2026-02-19 14:29:53 +00:00
case .toggleSidebar:
Button(action: { toggleSidebarFromToolbar() }) {
Label("Toggle Sidebar", systemImage: "sidebar.left")
}
case .toggleProjectSidebar:
Button(action: { toggleProjectSidebarFromToolbar() }) {
2026-02-19 14:29:53 +00:00
Label("Toggle Project Structure Sidebar", systemImage: "sidebar.right")
}
case .findReplace:
Button(action: { showFindReplace = true }) {
Label("Find & Replace", systemImage: "magnifyingglass")
}
case .findInFiles:
Button(action: { showFindInFiles = true }) {
Label("Find in Files…", systemImage: "text.magnifyingglass")
}
2026-02-19 14:29:53 +00:00
case .settings:
2026-02-27 17:13:12 +00:00
Button(action: { openSettings() }) {
2026-02-19 14:29:53 +00:00
Label("Settings", systemImage: "gearshape")
}
case .codeCompletion:
Button(action: { toggleAutoCompletion() }) {
Label(isAutoCompletionEnabled ? "Disable Code Completion" : "Enable Code Completion", systemImage: "text.badge.plus")
}
case .performanceMode:
Button(action: {
forceLargeFileMode.toggle()
updateLargeFileMode(for: currentContentBinding.wrappedValue)
}) {
Label(forceLargeFileMode ? "Disable Performance Mode" : "Enable Performance Mode", systemImage: "speedometer")
}
case .lineWrap:
Button(action: { viewModel.isLineWrapEnabled.toggle() }) {
Label("Enable Wrap / Disable Wrap", systemImage: "text.justify")
}
case .keyboardAccessory:
Button(action: { toggleKeyboardAccessoryBar() }) {
2026-02-19 14:29:53 +00:00
Label(
showKeyboardAccessoryBarIOS ? "Hide Keyboard Snippet Bar" : "Show Keyboard Snippet Bar",
systemImage: showKeyboardAccessoryBarIOS ? "keyboard.chevron.compact.down.fill" : "keyboard.chevron.compact.down"
)
}
case .clearEditor:
Button(action: { requestClearEditorContent() }) {
Label("Clear Editor", systemImage: "eraser")
2026-02-19 14:29:53 +00:00
}
case .insertTemplate:
Button(action: { insertTemplateForCurrentLanguage() }) {
Label("Insert Template", systemImage: "doc.badge.plus")
}
case .brainDump:
Button(action: {
toggleBrainDumpModeIOSAware()
2026-02-19 14:29:53 +00:00
}) {
Label("Brain Dump Mode", systemImage: "note.text")
}
case .welcomeTour:
Button(action: { showWelcomeTour = true }) {
Label("Welcome Tour", systemImage: "sparkles.rectangle.stack")
}
case .translucentWindow:
Button(action: {
enableTranslucentWindow.toggle()
UserDefaults.standard.set(enableTranslucentWindow, forKey: "EnableTranslucentWindow")
NotificationCenter.default.post(name: .toggleTranslucencyRequested, object: enableTranslucentWindow)
}) {
Label("Translucent Window Background", systemImage: enableTranslucentWindow ? "rectangle.fill" : "rectangle")
}
}
}
2026-02-20 00:31:14 +00:00
Button(action: { saveCurrentTabAsFromToolbar() }) {
Label("Save As…", systemImage: "square.and.arrow.down.on.square")
}
.disabled(viewModel.selectedTab == nil)
.keyboardShortcut("s", modifiers: [.command, .shift])
Button(action: { dismissKeyboard() }) {
Label("Hide Keyboard", systemImage: "keyboard.chevron.compact.down")
}
2026-02-20 00:31:14 +00:00
Button(action: { toolbarIconsBlueIOS.toggle() }) {
Label("Blue Toolbar Icons", systemImage: toolbarIconsBlueIOS ? "checkmark.circle.fill" : "circle")
}
2026-02-19 14:29:53 +00:00
} label: {
Image(systemName: "ellipsis.circle")
.frame(width: 40, height: 40, alignment: .center)
.contentShape(Rectangle())
2026-02-19 14:29:53 +00:00
}
.help("More Actions")
.frame(minWidth: 40, minHeight: 40)
2026-02-19 14:29:53 +00:00
}
}
@ViewBuilder
private var moreActionsControl: some View {
Menu {
Button(action: {
2026-02-27 17:13:12 +00:00
openSettings()
}) {
Label("Settings", systemImage: "gearshape")
}
Button(action: {
requestClearEditorContent()
}) {
Label("Clear Editor", systemImage: "eraser")
}
Button(action: { insertTemplateForCurrentLanguage() }) {
Label("Insert Template", systemImage: "doc.badge.plus")
}
Button(action: { presentLanguageSearchSheet() }) {
Label("Language…", systemImage: "magnifyingglass")
}
.keyboardShortcut("l", modifiers: [.command, .shift])
Button(action: { openFileFromToolbar() }) {
Label("Open File…", systemImage: "folder")
}
2026-02-16 19:02:43 +00:00
.keyboardShortcut("o", modifiers: .command)
Button(action: { undoFromToolbar() }) {
Label("Undo", systemImage: "arrow.uturn.backward")
}
.keyboardShortcut("z", modifiers: .command)
Button(action: { saveCurrentTabFromToolbar() }) {
Label("Save File", systemImage: "square.and.arrow.down")
}
.disabled(viewModel.selectedTab == nil)
2026-02-16 19:02:43 +00:00
.keyboardShortcut("s", modifiers: .command)
Button(action: { presentCodeSnapshotComposer() }) {
Label("Create Code Snapshot", systemImage: "camera.viewfinder")
}
.disabled(!canCreateCodeSnapshot)
2026-03-08 14:31:01 +00:00
Button(action: { toggleMarkdownPreviewFromToolbar() }) {
Label(
"Markdown Preview",
systemImage: showMarkdownPreviewPane ? "doc.richtext.fill" : "doc.richtext"
)
}
.disabled(currentLanguage != "markdown")
2026-04-16 10:37:03 +00:00
if showMarkdownPreviewPane && currentLanguage == "markdown" {
Menu {
markdownPreviewExportToolbarMenuContent
} label: {
Label("Export PDF", systemImage: "square.and.arrow.down")
}
}
2026-03-08 14:31:01 +00:00
Button(action: { requestCloseAllTabsFromToolbar() }) {
Label("Close All Tabs", systemImage: "xmark.square")
}
.disabled(viewModel.tabs.isEmpty)
Button(action: { saveCurrentTabAsFromToolbar() }) {
Label("Save As…", systemImage: "square.and.arrow.down.on.square")
}
.disabled(viewModel.selectedTab == nil)
.keyboardShortcut("s", modifiers: [.command, .shift])
Button(action: { toggleSidebarFromToolbar() }) {
Label("Toggle Sidebar", systemImage: "sidebar.left")
}
2026-02-16 19:02:43 +00:00
.keyboardShortcut("s", modifiers: [.command, .option])
Button(action: { toggleProjectSidebarFromToolbar() }) {
Label("Toggle Project Structure Sidebar", systemImage: "sidebar.right")
}
Button(action: { showFindReplace = true }) {
Label("Find & Replace", systemImage: "magnifyingglass")
}
2026-02-16 19:02:43 +00:00
.keyboardShortcut("f", modifiers: .command)
Button(action: { showFindInFiles = true }) {
Label("Find in Files…", systemImage: "text.magnifyingglass")
}
.keyboardShortcut("f", modifiers: [.command, .shift])
2026-02-16 19:02:43 +00:00
Button(action: { viewModel.isLineWrapEnabled.toggle() }) {
Label("Enable Wrap / Disable Wrap", systemImage: "text.justify")
}
.keyboardShortcut("l", modifiers: [.command, .option])
Button(action: { toggleAutoCompletion() }) {
Label(isAutoCompletionEnabled ? "Disable Code Completion" : "Enable Code Completion", systemImage: "text.badge.plus")
}
Button(action: {
toggleKeyboardAccessoryBar()
}) {
Label(
showKeyboardAccessoryBarIOS ? "Hide Keyboard Snippet Bar" : "Show Keyboard Snippet Bar",
systemImage: showKeyboardAccessoryBarIOS ? "keyboard.chevron.compact.down.fill" : "keyboard.chevron.compact.down"
)
}
Button(action: { dismissKeyboard() }) {
Label("Hide Keyboard", systemImage: "keyboard.chevron.compact.down")
}
Button(action: {
forceLargeFileMode.toggle()
updateLargeFileMode(for: currentContentBinding.wrappedValue)
}) {
Label(forceLargeFileMode ? "Disable Performance Mode" : "Enable Performance Mode", systemImage: "speedometer")
}
Button(action: {
toggleBrainDumpModeIOSAware()
}) {
Label("Brain Dump Mode", systemImage: "note.text")
}
Button(action: {
showWelcomeTour = true
}) {
Label("Welcome Tour", systemImage: "sparkles.rectangle.stack")
}
Button(action: {
enableTranslucentWindow.toggle()
UserDefaults.standard.set(enableTranslucentWindow, forKey: "EnableTranslucentWindow")
NotificationCenter.default.post(name: .toggleTranslucencyRequested, object: enableTranslucentWindow)
}) {
Label("Translucent Window Background", systemImage: enableTranslucentWindow ? "rectangle.fill" : "rectangle")
}
2026-02-20 00:31:14 +00:00
Button(action: {
toolbarIconsBlueIOS.toggle()
}) {
Label("Blue Toolbar Icons", systemImage: toolbarIconsBlueIOS ? "checkmark.circle.fill" : "circle")
}
} label: {
Image(systemName: "ellipsis.circle")
}
.help("More Actions")
}
@ViewBuilder
private var iOSToolbarControls: some View {
2026-02-16 19:02:43 +00:00
openFileControl
undoControl
2026-04-16 10:37:03 +00:00
markdownPreviewExportControl
markdownPreviewStyleControl
if iPhonePromotedActionsCount >= 2 { newTabControl }
if iPhonePromotedActionsCount >= 3 { saveFileControl }
if iPhonePromotedActionsCount >= 4 { findReplaceControl }
if iPhonePromotedActionsCount >= 5 { findInFilesControl }
keyboardAccessoryControl
2026-03-26 18:19:45 +00:00
iOSVerticalSurfaceDivider
moreActionsControl
}
@ViewBuilder
private var iPhonePrimaryToolbarCluster: some View {
GlassSurface(
enabled: shouldUseLiquidGlass,
material: primaryGlassMaterial,
fallbackColor: toolbarFallbackColor,
shape: .capsule,
chromeStyle: iOSToolbarChromeStyle
) {
HStack(spacing: 12) {
languagePickerControl
iOSToolbarControls
}
.padding(.horizontal, 12)
.padding(.vertical, 8)
}
}
@ViewBuilder
var iPhoneUnifiedToolbarRow: some View {
iPhonePrimaryToolbarCluster
.frame(maxWidth: .infinity, alignment: .center)
.scaleEffect(toolbarDensityScale)
.opacity(toolbarDensityOpacity)
.animation(.easeOut(duration: 0.18), value: toolbarDensityScale)
.animation(.easeOut(duration: 0.18), value: toolbarDensityOpacity)
.tint(iOSToolbarTintColor)
}
@ViewBuilder
private var iPadDistributedToolbarControls: some View {
languagePickerControl
2026-04-16 10:37:03 +00:00
markdownPreviewExportControl
markdownPreviewStyleControl
2026-02-19 14:29:53 +00:00
ForEach(iPadPromotedActions, id: \.self) { action in
iPadToolbarActionControl(action)
.frame(minWidth: 40, minHeight: 40)
.contentShape(Rectangle())
2026-02-19 14:29:53 +00:00
}
ForEach(iPadAlwaysVisibleActions, id: \.self) { action in
iPadToolbarActionControl(action)
.frame(minWidth: 40, minHeight: 40)
.contentShape(Rectangle())
}
2026-02-19 14:29:53 +00:00
if !iPadOverflowActions.isEmpty {
2026-03-26 18:19:45 +00:00
iOSVerticalSurfaceDivider
2026-02-19 14:29:53 +00:00
.padding(.horizontal, 2)
iPadOverflowMenuControl
}
}
#endif
private func toolbarCompactLanguageLabel(_ lang: String) -> String {
switch lang {
case "swift": return "Sw"
case "python": return "Py"
case "javascript": return "JS"
case "typescript": return "TS"
case "php": return "PHP"
case "java": return "Jv"
case "kotlin": return "Kt"
case "go": return "Go"
case "ruby": return "Rb"
case "rust": return "Rs"
case "cobol": return "Cob"
case "dotenv": return "Env"
case "proto": return "Prt"
case "graphql": return "GQL"
case "rst": return "RST"
case "nginx": return "Ngnx"
case "sql": return "SQL"
case "html": return "HTML"
case "expressionengine": return "EE"
case "css": return "CSS"
case "c": return "C"
case "cpp": return "C++"
case "csharp": return "C#"
case "objective-c": return "ObjC"
case "json": return "JSON"
case "xml": return "XML"
case "yaml": return "YML"
case "toml": return "TML"
case "csv": return "CSV"
case "ini": return "INI"
case "vim": return "Vim"
case "log": return "Log"
case "ipynb": return "JNB"
case "markdown": return "MD"
case "tex": return "TeX"
case "bash": return "Sh"
case "zsh": return "zsh"
case "powershell": return "PS"
case "standard": return "Std"
case "plain": return "Txt"
default: return lang.capitalized
}
}
@ToolbarContentBuilder
var editorToolbarContent: some ToolbarContent {
#if os(iOS)
if isIPadToolbarLayout {
2026-02-20 00:31:14 +00:00
if #available(iOS 26.0, *) {
ToolbarItem(placement: .topBarTrailing) {
GlassSurface(
enabled: shouldUseLiquidGlass,
material: primaryGlassMaterial,
fallbackColor: toolbarFallbackColor,
shape: .capsule,
chromeStyle: iOSToolbarChromeStyle
) {
HStack(spacing: 6) {
iPadDistributedToolbarControls
}
.padding(.horizontal, 8)
.padding(.vertical, 8)
.frame(maxWidth: iPadToolbarMaxWidth, alignment: .center)
}
.scaleEffect(toolbarDensityScale, anchor: .center)
2026-02-20 00:31:14 +00:00
.opacity(toolbarDensityOpacity)
.animation(.easeOut(duration: 0.18), value: toolbarDensityScale)
.animation(.easeOut(duration: 0.18), value: toolbarDensityOpacity)
.tint(iOSToolbarTintColor)
}
.sharedBackgroundVisibility(.hidden)
} else {
ToolbarItem(placement: .topBarTrailing) {
GlassSurface(
enabled: shouldUseLiquidGlass,
material: primaryGlassMaterial,
fallbackColor: toolbarFallbackColor,
shape: .capsule,
chromeStyle: iOSToolbarChromeStyle
) {
HStack(spacing: 6) {
iPadDistributedToolbarControls
}
.padding(.horizontal, 8)
.padding(.vertical, 8)
.frame(maxWidth: iPadToolbarMaxWidth, alignment: .center)
2026-02-20 00:31:14 +00:00
}
.scaleEffect(toolbarDensityScale, anchor: .center)
2026-02-20 00:31:14 +00:00
.opacity(toolbarDensityOpacity)
.animation(.easeOut(duration: 0.18), value: toolbarDensityScale)
.animation(.easeOut(duration: 0.18), value: toolbarDensityOpacity)
.tint(iOSToolbarTintColor)
}
}
}
#else
ToolbarItemGroup(placement: .primaryAction) {
Button(action: { openFileFromToolbar() }) {
Label("Open", systemImage: "folder")
.foregroundStyle(macToolbarSymbolColor)
}
.help("Open File… (Cmd+O)")
Button(action: { viewModel.addNewTab() }) {
Label("New Tab", systemImage: "plus.square.on.square")
.foregroundStyle(macToolbarSymbolColor)
}
.help("New Tab (Cmd+T)")
2026-03-08 14:31:01 +00:00
Button(action: { requestCloseAllTabsFromToolbar() }) {
Label("Close All Tabs", systemImage: "xmark.square")
.foregroundStyle(macToolbarSymbolColor)
2026-03-08 14:31:01 +00:00
}
.help("Close All Tabs")
Button(action: {
saveCurrentTabFromToolbar()
}) {
Label("Save", systemImage: "square.and.arrow.down")
.foregroundStyle(macToolbarSymbolColor)
}
.disabled(viewModel.selectedTab == nil)
.help("Save File (Cmd+S)")
Button(action: { presentCodeSnapshotComposer() }) {
Label("Code Snapshot", systemImage: "camera.viewfinder")
.foregroundStyle(macToolbarSymbolColor)
}
.disabled(!canCreateCodeSnapshot)
.help("Create Code Snapshot from Selection")
Button(action: {
showFindReplace = true
}) {
Label("Find", systemImage: "magnifyingglass")
.foregroundStyle(macToolbarSymbolColor)
}
.help("Find & Replace (Cmd+F)")
Button(action: {
showFindInFiles = true
}) {
Label("Find in Files", systemImage: "text.magnifyingglass")
.foregroundStyle(macToolbarSymbolColor)
}
.help("Find in Files (Cmd+Shift+F)")
Button(action: {
openSettings()
}) {
Label("Settings", systemImage: "gearshape")
.foregroundStyle(macToolbarSymbolColor)
}
.help("Settings")
}
ToolbarItemGroup(placement: .automatic) {
Menu {
2026-03-26 18:19:45 +00:00
let selectedLanguage = currentLanguagePickerBinding.wrappedValue
Button {
currentLanguagePickerBinding.wrappedValue = selectedLanguage
} label: {
Label(languageLabel(for: selectedLanguage), systemImage: "checkmark")
}
Button(action: { presentLanguageSearchSheet() }) {
Label("Language…", systemImage: "magnifyingglass")
}
Divider()
ForEach(languageOptions.filter { $0 != selectedLanguage }, id: \.self) { lang in
Button {
currentLanguagePickerBinding.wrappedValue = lang
} label: {
2026-03-26 18:19:45 +00:00
Text(languageLabel(for: lang))
}
}
} label: {
Text(toolbarCompactLanguageLabel(currentLanguagePickerBinding.wrappedValue))
.lineLimit(1)
.truncationMode(.tail)
}
.labelsHidden()
.help("Language")
.controlSize(.large)
.frame(width: 92)
.padding(.vertical, 2)
Button(action: { presentLanguageSearchSheet() }) {
Image(systemName: "magnifyingglass")
}
.keyboardShortcut("l", modifiers: [.command, .shift])
.help("Language… (Cmd+Shift+L)")
Text(providerBadgeLabelText)
.font(.caption)
.foregroundColor(providerBadgeForegroundColor)
2026-02-12 09:15:40 +00:00
.lineLimit(1)
.truncationMode(.tail)
.minimumScaleFactor(0.9)
.fixedSize(horizontal: true, vertical: false)
.padding(.horizontal, 6)
.padding(.vertical, 2)
.background(providerBadgeBackgroundColor, in: Capsule())
.padding(.leading, 6)
.help(providerBadgeTooltip)
#if os(macOS) || os(iOS)
if canShowMarkdownPreviewPane {
Button(action: {
toggleMarkdownPreviewFromToolbar()
}) {
Label("Markdown Preview", systemImage: showMarkdownPreviewPane ? "doc.richtext.fill" : "doc.richtext")
.foregroundStyle(macToolbarSymbolColor)
}
.disabled(currentLanguage != "markdown")
.help("Toggle Markdown Preview")
if showMarkdownPreviewPane && currentLanguage == "markdown" {
2026-04-16 10:37:03 +00:00
Menu {
markdownPreviewExportToolbarMenuContent
} label: {
Label("Export PDF", systemImage: "square.and.arrow.down")
.foregroundStyle(macToolbarSymbolColor)
}
.help(NSLocalizedString("Markdown Preview Export Options", comment: "Toolbar help for markdown preview export options"))
Menu {
Button("Default") { markdownPreviewTemplateRaw = "default" }
Button("Docs") { markdownPreviewTemplateRaw = "docs" }
Button("Article") { markdownPreviewTemplateRaw = "article" }
Button("Compact") { markdownPreviewTemplateRaw = "compact" }
2026-03-17 17:40:32 +00:00
Divider()
Button("GitHub Docs") { markdownPreviewTemplateRaw = "github-docs" }
Button("Academic Paper") { markdownPreviewTemplateRaw = "academic-paper" }
Button("Terminal Notes") { markdownPreviewTemplateRaw = "terminal-notes" }
Button("Magazine") { markdownPreviewTemplateRaw = "magazine" }
Button("Minimal Reader") { markdownPreviewTemplateRaw = "minimal-reader" }
Button("Presentation") { markdownPreviewTemplateRaw = "presentation" }
Button("Night Contrast") { markdownPreviewTemplateRaw = "night-contrast" }
Button("Warm Sepia") { markdownPreviewTemplateRaw = "warm-sepia" }
Button("Dense Compact") { markdownPreviewTemplateRaw = "dense-compact" }
Button("Developer Spec") { markdownPreviewTemplateRaw = "developer-spec" }
} label: {
2026-04-16 10:37:03 +00:00
Label(NSLocalizedString("Preview Style", comment: "Markdown preview style menu label"), systemImage: "paintbrush")
.foregroundStyle(macToolbarSymbolColor)
}
2026-04-16 10:37:03 +00:00
.help(NSLocalizedString("Markdown Preview Template", comment: "Toolbar help for markdown preview style menu"))
}
}
#endif
Button(action: { undoFromToolbar() }) {
Label("Undo", systemImage: "arrow.uturn.backward")
.foregroundStyle(macToolbarSymbolColor)
}
.help("Undo (Cmd+Z)")
.keyboardShortcut("z", modifiers: .command)
if ReleaseRuntimePolicy.isUpdaterEnabledForCurrentDistribution {
Button(action: {
showUpdaterDialog(checkNow: true)
}) {
Label("Updates", systemImage: "arrow.triangle.2.circlepath.circle")
.foregroundStyle(macToolbarSymbolColor)
}
.help("Check for Updates")
}
#if os(macOS)
Button(action: {
openWindow(id: "blank-window")
}) {
Label("New Window", systemImage: "macwindow.badge.plus")
.foregroundStyle(macToolbarSymbolColor)
}
.help("New Window (Cmd+N)")
#endif
Button(action: { adjustEditorFontSize(-1) }) {
Label("Font -", systemImage: "textformat.size.smaller")
.foregroundStyle(macToolbarSymbolColor)
}
.help("Decrease Font Size")
Button(action: { adjustEditorFontSize(1) }) {
Label("Font +", systemImage: "textformat.size.larger")
.foregroundStyle(macToolbarSymbolColor)
}
.help("Increase Font Size")
Button(action: {
requestClearEditorContent()
}) {
Label("Clear", systemImage: "eraser")
.foregroundStyle(macToolbarSymbolColor)
}
.help("Clear Editor")
Button(action: {
insertTemplateForCurrentLanguage()
}) {
Label("Template", systemImage: "doc.badge.plus")
.foregroundStyle(macToolbarSymbolColor)
}
.help("Insert Template for Current Language")
Button(action: {
toggleSidebarFromToolbar()
}) {
Label("Sidebar", systemImage: "sidebar.left")
.foregroundStyle(macToolbarSymbolColor)
.symbolVariant(viewModel.showSidebar ? .fill : .none)
}
.help("Toggle Sidebar (Cmd+Opt+S)")
Button(action: {
toggleProjectSidebarFromToolbar()
}) {
Label("Project", systemImage: "sidebar.right")
.foregroundStyle(macToolbarSymbolColor)
.symbolVariant(showProjectStructureSidebar ? .fill : .none)
}
.help("Toggle Project Structure Sidebar")
Button(action: {
toggleAutoCompletion()
}) {
Label("AI", systemImage: "bolt.horizontal.circle")
.foregroundStyle(macToolbarSymbolColor)
.symbolVariant(isAutoCompletionEnabled ? .fill : .none)
}
.help(isAutoCompletionEnabled ? "Disable Code Completion" : "Enable Code Completion")
.accessibilityLabel("Code Completion")
Button(action: {
showBracketHelperBarMac.toggle()
}) {
Label("Brackets", systemImage: "chevron.left.chevron.right")
.foregroundStyle(macToolbarSymbolColor)
.symbolVariant(showBracketHelperBarMac ? .fill : .none)
}
.help(showBracketHelperBarMac ? "Hide Bracket Helper Bar" : "Show Bracket Helper Bar")
.accessibilityLabel("Bracket Helper Bar")
Button(action: {
viewModel.isBrainDumpMode.toggle()
UserDefaults.standard.set(viewModel.isBrainDumpMode, forKey: "BrainDumpModeEnabled")
}) {
Label("Brain Dump", systemImage: "note.text")
.foregroundStyle(macToolbarSymbolColor)
.symbolVariant(viewModel.isBrainDumpMode ? .fill : .none)
}
.help("Brain Dump Mode")
.accessibilityLabel("Brain Dump Mode")
Button(action: {
enableTranslucentWindow.toggle()
UserDefaults.standard.set(enableTranslucentWindow, forKey: "EnableTranslucentWindow")
NotificationCenter.default.post(name: .toggleTranslucencyRequested, object: enableTranslucentWindow)
}) {
Label("Translucency", systemImage: enableTranslucentWindow ? "rectangle.fill" : "rectangle")
.foregroundStyle(macToolbarSymbolColor)
}
.help("Toggle Translucent Window Background")
.accessibilityLabel("Translucent Window Background")
}
#endif
}
}