Commit 8847b010a7
8847b010a7b834c174ec4eaa9c7ab0c5ffa1ca46
parent: 9518e3948b
Verified · cmc
cmc <hello@cleberg.net> · 2026-10-05 04:55 UTC
Org options in Settings
Layout: unified · split
Sources/Orgstar/ContentView.swift
+10 −1
| @@ -12,6 +12,14 @@ struct ContentView: View { |
| 12 | 12 | @AppStorage("keymap") private var preset = KeymapPreset.emacs |
| 13 | 13 | @AppStorage("optionAsMeta") private var optionAsMeta = OptionAsMeta.left.rawValue |
| 14 | 14 | @State private var keymap = KeymapPreset.emacs.keymap |
| 15 | @AppStorage("tagsColumn") private var tagsColumn = -77 |
| 16 | @AppStorage("headingAfterSubtree") private var headingAfterSubtree = true |
| 17 | @AppStorage("splitLine") private var splitLine = false |
| 18 | @AppStorage("alphabeticalLists") private var alphabeticalLists = true |
| 19 | |
| 20 | private var options: EditingOptions { |
| 21 | Preferences.options(tagsColumn: tagsColumn, headingAfterSubtree: headingAfterSubtree, splitLine: splitLine, alphabeticalLists: alphabeticalLists) |
| 22 | } |
| 15 | 23 | @State private var selectedFile: String? |
| 16 | 24 | @State private var query = "" |
| 17 | 25 | @FocusState private var searchFocused: Bool |
| @@ -35,7 +43,7 @@ struct ContentView: View { |
| 35 | 43 | .navigationSplitViewColumnWidth(min: 160, ideal: 220, max: 360) |
| 36 | 44 | } detail: { |
| 37 | 45 | if session.path != nil { |
| 38 | | EditorView(session: session, commands: commands, keymap: keymap, optionAsMeta: OptionAsMeta(rawValue: optionAsMeta), onAppCommand: perform) |
| 46 | EditorView(session: session, commands: commands, keymap: keymap, optionAsMeta: OptionAsMeta(rawValue: optionAsMeta), options: options, onAppCommand: perform) |
| 39 | 47 | .safeAreaInset(edge: .bottom, spacing: 0) { EchoArea(commands: commands) } |
| 40 | 48 | } else { |
| 41 | 49 | ContentUnavailableView( |
| @@ -65,6 +73,7 @@ struct ContentView: View { |
| 65 | 73 | } |
| 66 | 74 | } |
| 67 | 75 | .onChange(of: saveMode, initial: true) { _, mode in session.saveMode = mode } |
| 76 | .onChange(of: options, initial: true) { _, options in session.options = options } |
| 68 | 77 | .onChange(of: preset, initial: true) { loadKeymap() } |
| 69 | 78 | .sheet(isPresented: $commands.showingPalette) { |
| 70 | 79 | PaletteView(keymap: keymap) { id in |
Sources/Orgstar/EditorView.swift
+2
| @@ -11,6 +11,7 @@ struct EditorView: NSViewRepresentable { |
| 11 | 11 | let commands: AppCommands |
| 12 | 12 | let keymap: Keymap |
| 13 | 13 | let optionAsMeta: OptionAsMeta |
| 14 | let options: EditingOptions |
| 14 | 15 | let onAppCommand: (String) -> Void |
| 15 | 16 | |
| 16 | 17 | final class Coordinator { |
| @@ -54,6 +55,7 @@ struct EditorView: NSViewRepresentable { |
| 54 | 55 | if let editor = coordinator.editor { |
| 55 | 56 | if editor.keymap != keymap { editor.keymap = keymap } |
| 56 | 57 | editor.optionAsMeta = optionAsMeta |
| 58 | editor.options = options |
| 57 | 59 | } |
| 58 | 60 | container.window?.isDocumentEdited = session.isDirty |
| 59 | 61 | if let request = session.revealRequest, request.id != coordinator.revealID { |
Sources/Orgstar/OrgstarApp.swift
+26 −3
| @@ -90,9 +90,15 @@ enum UnsavedChanges { |
| 90 | 90 | @main |
| 91 | 91 | struct OrgstarApp: App { |
| 92 | 92 | @NSApplicationDelegateAdaptor private var delegate: AppDelegate |
| 93 | | @State private var workspace = Result { try WorkspaceModel(directory: WorkspaceModel.defaultDirectory) } |
| 93 | @State private var workspace = Result { |
| 94 | try WorkspaceModel(directory: WorkspaceModel.defaultDirectory, settings: .with(todoKeywords: Preferences.orgSettings)) |
| 95 | } |
| 94 | 96 | @State private var commands = AppCommands() |
| 95 | | @State private var session = DocumentSession() |
| 97 | @State private var session: DocumentSession = { |
| 98 | let session = DocumentSession() |
| 99 | session.defaults = Preferences.orgSettings |
| 100 | return session |
| 101 | }() |
| 96 | 102 | |
| 97 | 103 | var body: some Scene { |
| 98 | 104 | Window("Orgstar", id: "main") { |
| @@ -144,6 +150,11 @@ struct SettingsView: View { |
| 144 | 150 | @AppStorage("saveMode") private var saveMode = SaveMode.automatic |
| 145 | 151 | @AppStorage("keymap") private var preset = KeymapPreset.emacs |
| 146 | 152 | @AppStorage("optionAsMeta") private var optionAsMeta = OptionAsMeta.left.rawValue |
| 153 | @AppStorage("tagsColumn") private var tagsColumn = -77 |
| 154 | @AppStorage("headingAfterSubtree") private var headingAfterSubtree = true |
| 155 | @AppStorage("splitLine") private var splitLine = false |
| 156 | @AppStorage("alphabeticalLists") private var alphabeticalLists = true |
| 157 | @AppStorage("todoKeywords") private var todoKeywords = Preferences.defaultTodoKeywords |
| 147 | 158 | |
| 148 | 159 | var body: some View { |
| 149 | 160 | Form { |
| @@ -165,8 +176,20 @@ struct SettingsView: View { |
| 165 | 176 | .font(.caption) |
| 166 | 177 | .foregroundStyle(.secondary) |
| 167 | 178 | .textSelection(.enabled) |
| 179 | Divider() |
| 180 | Picker("Tags:", selection: $tagsColumn) { |
| 181 | Text("Aligned to end at column 77").tag(-77) |
| 182 | Text("One space after the title").tag(0) |
| 183 | } |
| 184 | Toggle("M-RET adds the new heading after the subtree", isOn: $headingAfterSubtree) |
| 185 | Toggle("M-RET splits the line at the caret", isOn: $splitLine) |
| 186 | Toggle("Lists can use letters (a. b. c.)", isOn: $alphabeticalLists) |
| 187 | TextField("Default TODO keywords:", text: $todoKeywords) |
| 188 | Text("For files without #+TODO, in #+TODO syntax. Takes effect when Orgstar restarts.") |
| 189 | .font(.caption) |
| 190 | .foregroundStyle(.secondary) |
| 168 | 191 | } |
| 169 | 192 | .padding(20) |
| 170 | | .frame(width: 420) |
| 193 | .frame(width: 480) |
| 171 | 194 | } |
| 172 | 195 | } |
Sources/Orgstar/Preferences.swift
added
+20
| @@ -0,0 +1,20 @@ |
| 1 | import Foundation |
| 2 | import OrgCore |
| 3 | |
| 4 | /// Org preferences, stored in user defaults by the Settings window. |
| 5 | enum Preferences { |
| 6 | static let defaultTodoKeywords = "TODO PROJ LOOP STRT WAIT HOLD IDEA | DONE KILL" |
| 7 | |
| 8 | /// TODO keywords for files without `#+TODO`, read at launch. |
| 9 | static var orgSettings: OrgSettings { |
| 10 | let line = UserDefaults.standard.string(forKey: "todoKeywords") ?? defaultTodoKeywords |
| 11 | return OrgSettings.default.withTodoKeywords(line) ?? .default |
| 12 | } |
| 13 | |
| 14 | static func options(tagsColumn: Int, headingAfterSubtree: Bool, splitLine: Bool, alphabeticalLists: Bool) -> EditingOptions { |
| 15 | EditingOptions( |
| 16 | tagsColumn: tagsColumn, insertHeadingRespectContent: headingAfterSubtree, |
| 17 | metaReturnMaySplitLine: splitLine, listAllowAlphabetical: alphabeticalLists |
| 18 | ) |
| 19 | } |
| 20 | } |