← All repositories

listless

A simple list app for Apple platforms

git clone https://code.inqk.net/listless.git


commit 13720997db6d6f6ae9c822905513b17ea4b2f2dc
parent df715444409c0e5546b7ff6b98eb8087c591b511
Author: Michael Camilleri <[email protected]>
Date:   Tue, 24 Feb 2026 04:26:18 +0900

Improve macOS menus further

Co-Authored-By: Codex GPT 5.3 <[email protected]>

Diffstat

MListless/Extensions/TaskListView+Logic.swift | 51---------------------------------------------------
MListlessMac/Helpers/AppCommands.swift | 1+
MListlessMac/Helpers/ClickableTextField.swift | 6------
MListlessMac/ListlessMacApp.swift | 300++++++++++++++++++++++++++++++++++++++++----------------------------------------
MListlessMac/Views/TaskListView.swift | 9++-------

5 files changed, 153 insertions(+), 214 deletions(-)

diff --git a/Listless/Extensions/TaskListView+Logic.swift b/Listless/Extensions/TaskListView+Logic.swift

@@ -101,17 +101,12 @@ extension TaskListView {
     }
 
     func handleFocusChange(from oldValue: FocusField?, to newValue: FocusField?) {
-        print(
-            "🟣 handleFocusChange() from: \(String(describing: oldValue)) to: \(String(describing: newValue))"
-        )
         let oldID = taskID(from: oldValue)
         let newID = taskID(from: newValue)
 
         guard oldID != newID, let oldID else {
-            print("🟣 handleFocusChange() no action needed")
             return
         }
-        print("🟣 handleFocusChange() calling deleteIfEmpty for task \(oldID)")
         deleteIfEmpty(taskID: oldID)
     }
 
@@ -122,7 +117,6 @@ extension TaskListView {
 
     private func deleteIfEmpty(taskID: UUID) {
         if case .task(let pendingTaskID) = pendingFocus, pendingTaskID == taskID {
-            print("πŸ”΄ deleteIfEmpty() skipping - task is pending focus")
             return
         }
 
@@ -175,14 +169,11 @@ extension TaskListView {
 
     func deleteTask(_ task: TaskItem) {
         let taskID = task.id
-        print("πŸ”΄ deleteTask() called for task \(taskID)")
         do {
             try store.delete(taskID: taskID)
             if selectedTaskID == taskID {
-                print("πŸ”΄ deleteTask() clearing selectedTaskID")
                 selectedTaskID = nil
             }
-            print("πŸ”΄ deleteTask() completed")
         } catch {
             presentStoreError(error)
         }
@@ -201,9 +192,7 @@ extension TaskListView {
     // MARK: - Keyboard Navigation
 
     func navigateUp() -> KeyPress.Result {
-        print("⬆️ navigateUp() called, focusedField: \(String(describing: focusedField))")
         guard focusedField == .scrollView else {
-            print("⬆️ navigateUp() IGNORED - focus is not .scrollView")
             return .ignored
         }
 
@@ -224,9 +213,7 @@ extension TaskListView {
     }
 
     func navigateDown() -> KeyPress.Result {
-        print("⬇️ navigateDown() called, focusedField: \(String(describing: focusedField))")
         guard focusedField == .scrollView else {
-            print("⬇️ navigateDown() IGNORED - focus is not .scrollView")
             return .ignored
         }
 
@@ -256,24 +243,6 @@ extension TaskListView {
         return .handled
     }
 
-    func createNewTaskFromShortcut() -> KeyPress.Result {
-        createNewTask()
-        focusedField = nil
-        return .handled
-    }
-
-    func moveSelectedTaskUpFromShortcut() -> KeyPress.Result {
-        guard focusedField == .scrollView else { return .ignored }
-        moveSelectedTaskUp()
-        return .handled
-    }
-
-    func moveSelectedTaskDownFromShortcut() -> KeyPress.Result {
-        guard focusedField == .scrollView else { return .ignored }
-        moveSelectedTaskDown()
-        return .handled
-    }
-
     func focusSelectedTask() -> KeyPress.Result {
         guard focusedField == .scrollView else { return .ignored }
         guard let currentID = selectedTaskID else { return .handled }
@@ -286,20 +255,15 @@ extension TaskListView {
     }
 
     func deleteSelectedTask() -> KeyPress.Result {
-        print("πŸ—‘οΈ deleteSelectedTask() called, focusedField: \(String(describing: focusedField))")
         guard focusedField == .scrollView else {
-            print("πŸ—‘οΈ deleteSelectedTask() IGNORED - focus is not .scrollView")
             return .ignored
         }
         guard let currentID = selectedTaskID else {
-            print("πŸ—‘οΈ deleteSelectedTask() no task selected")
             return .handled
         }
         guard let task = allTasksInDisplayOrder.first(where: { $0.id == currentID }) else {
-            print("πŸ—‘οΈ deleteSelectedTask() task not found")
             return .handled
         }
-        print("πŸ—‘οΈ deleteSelectedTask() deleting task \(currentID)")
         deleteTask(task)
         return .handled
     }
@@ -344,17 +308,12 @@ extension TaskListView {
     }
 
     func startEditing(_ taskID: UUID) {
-        print("🟒 startEditing called for task \(taskID)")
         selectedTaskID = taskID
         focusedField = .task(taskID)
         pendingFocus = nil
-        print("🟒 startEditing set focusedField = .task(\(taskID))")
     }
 
     func endEditing(_ taskID: UUID, shouldCreateNewTask: Bool) {
-        print(
-            "🟒 endEditing() called for task \(taskID), shouldCreateNewTask: \(shouldCreateNewTask)"
-        )
         do {
             try store.save()
         } catch {
@@ -363,25 +322,15 @@ extension TaskListView {
 
         let wasLastActiveTask = isLastActiveTask(taskID)
         let willBeDeleted = shouldDeleteIfEmpty(taskID: taskID)
-        print(
-            "🟒 endEditing() wasLastActiveTask: \(wasLastActiveTask), willBeDeleted: \(willBeDeleted)"
-        )
 
         if willBeDeleted {
-            print("🟒 endEditing() deleting task - focus will be repaired automatically by onChange")
             selectedTaskID = nil
             deleteIfEmpty(taskID: taskID)
         } else if wasLastActiveTask && shouldCreateNewTask {
-            print("🟒 endEditing() creating new task")
             createNewTask()
         } else if shouldCreateNewTask {
-            print("🟒 endEditing() Return on non-last task β€” dismiss keyboard, enter navigation mode")
             focusedField = .scrollView
-        } else {
-            print("🟒 endEditing() done, selection unchanged")
         }
-
-        print("🟒 endEditing() completed, final focus: \(String(describing: focusedField))")
     }
 
     private func shouldDeleteIfEmpty(taskID: UUID) -> Bool {

diff --git a/ListlessMac/Helpers/AppCommands.swift b/ListlessMac/Helpers/AppCommands.swift

@@ -8,6 +8,7 @@ final class MenuCoordinator {
 
     // Actions β€” set by TaskListView on each relevant state change.
     var newTask: (() -> Void)?
+    var newWindow: (() -> Void)?
     var deleteSelectedTask: (() -> Void)?
     var moveSelectedTaskUp: (() -> Void)?
     var moveSelectedTaskDown: (() -> Void)?

diff --git a/ListlessMac/Helpers/ClickableTextField.swift b/ListlessMac/Helpers/ClickableTextField.swift

@@ -156,12 +156,10 @@ struct ClickableTextField: NSViewRepresentable {
         }
 
         func handleBecomeFirstResponder() {
-            print("🟑 ClickableTextField.becomeFirstResponder - calling onEditingChanged(true)")
             onEditingChanged(true, false)
         }
 
         func controlTextDidEndEditing(_ obj: Notification) {
-            print("🟑 ClickableTextField.controlTextDidEndEditing fired - reason: \(editEndReason)")
             let shouldCreateNewTask = editEndReason == .returnKey
             editEndReason = .focusLost  // Reset for next time
             onEditingChanged(false, shouldCreateNewTask)
@@ -177,22 +175,18 @@ struct ClickableTextField: NSViewRepresentable {
         )
             -> Bool
         {
-            print("🟑 ClickableTextField.doCommandBy selector: \(commandSelector)")
             if commandSelector == #selector(NSResponder.insertNewline(_:)) {
                 // Return key pressed
-                print("🟑 ClickableTextField.doCommandBy RETURN key detected")
                 editEndReason = .returnKey
                 control.window?.makeFirstResponder(nil)
                 return true  // Prevent newline insertion
             }
             if commandSelector == #selector(NSResponder.cancelOperation(_:)) {
                 // Escape key pressed
-                print("🟑 ClickableTextField.doCommandBy ESCAPE key detected")
                 editEndReason = .escape
                 control.window?.makeFirstResponder(nil)
                 return true
             }
-            print("🟑 ClickableTextField.doCommandBy unhandled selector, returning false")
             return false
         }
     }

diff --git a/ListlessMac/ListlessMacApp.swift b/ListlessMac/ListlessMacApp.swift

@@ -1,160 +1,18 @@
 import SwiftUI
 import AppKit
 
-private let customMenuTag = 1001
+private enum MenuSelectors {
+    static let showSettingsWindow = Selector(("showSettingsWindow:"))
+    static let closeAll = Selector(("closeAll:"))
+    static let undo = Selector(("undo:"))
+    static let redo = Selector(("redo:"))
+}
 
 @MainActor
 class AppDelegate: NSObject, NSApplicationDelegate, NSMenuItemValidation {
     func applicationDidFinishLaunching(_ notification: Notification) {
-        removeFormatMenu()
-
-        // SwiftUI builds menus asynchronously; watch for items appearing.
-        NotificationCenter.default.addObserver(
-            self,
-            selector: #selector(handleMenuDidAddItem(_:)),
-            name: NSMenu.didAddItemNotification,
-            object: nil
-        )
-        NotificationCenter.default.addObserver(
-            self,
-            selector: #selector(handleAppDidBecomeActive(_:)),
-            name: NSApplication.didBecomeActiveNotification,
-            object: nil
-        )
-        NotificationCenter.default.addObserver(
-            self,
-            selector: #selector(handleWindowDidBecomeKey(_:)),
-            name: NSWindow.didBecomeKeyNotification,
-            object: nil
-        )
-
-        // SwiftUI/AppKit can finish menu construction after launch callbacks; apply
-        // our menu patch repeatedly in the first moments to avoid startup races.
-        refreshMenus()
-        let startupDelays: [TimeInterval] = [0.0, 0.05, 0.15, 0.35]
-        for delay in startupDelays {
-            DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
-                self?.refreshMenus()
-            }
-        }
-    }
-
-    deinit {
-        NotificationCenter.default.removeObserver(self)
-    }
-
-    @objc private func handleMenuDidAddItem(_ notification: Notification) {
-        Task { @MainActor [weak self] in
-            self?.removeFormatMenu()
-            self?.setupFileMenuIfNeeded()
-            self?.setupEditMenuIfNeeded()
-        }
-    }
-
-    @objc private func handleAppDidBecomeActive(_ notification: Notification) {
-        refreshMenus()
-    }
-
-    @objc private func handleWindowDidBecomeKey(_ notification: Notification) {
-        refreshMenus()
-    }
-
-    private func refreshMenus() {
-        removeFormatMenu()
-        setupFileMenuIfNeeded()
-        setupEditMenuIfNeeded()
-    }
-
-    // MARK: - Menu Setup
-
-    @MainActor private func removeFormatMenu() {
-        guard let mainMenu = NSApp.mainMenu else { return }
-        mainMenu.items
-            .filter { $0.title == "Format" }
-            .forEach { mainMenu.removeItem($0) }
-    }
-
-    @MainActor private func setupFileMenuIfNeeded() {
-        guard let fileMenu = NSApp.mainMenu?.items.first(where: { $0.title == "File" })?.submenu else { return }
-
-        // Always update the New Window shortcut in-place β€” runs on every notification
-        // in case SwiftUI re-adds a fresh item. Never remove it; removal causes SwiftUI
-        // to re-add it at the bottom with the original shortcut.
-        if let newWindowItem = fileMenu.items.first(where: { $0.action == NSSelectorFromString("menuAction:") }) {
-            newWindowItem.keyEquivalent = "n"
-            newWindowItem.keyEquivalentModifierMask = [.command, .shift]
-        }
-
-        guard !fileMenu.items.contains(where: { $0.tag == customMenuTag }) else { return }
-
-        // Defer until both Close and Close All are present β€” the build order of these
-        // items is non-deterministic, so we wait for both before touching anything.
-        guard fileMenu.items.contains(where: { $0.action == NSSelectorFromString("performClose:") }),
-              fileMenu.items.contains(where: { $0.action == NSSelectorFromString("closeAll:") }) else { return }
-
-        guard let newWindowItem = fileMenu.items.first(where: { $0.action == NSSelectorFromString("menuAction:") }) else { return }
-        let insertIndex = fileMenu.index(of: newWindowItem)
-
-        // Insert sep1 and New Task before New Window, then sep2 immediately after it.
-        // We own both separators so the layout is stable regardless of where the
-        // system separator between Close/Close All ends up.
-        let sep1 = NSMenuItem.separator()
-        sep1.tag = customMenuTag
-        fileMenu.insertItem(sep1, at: insertIndex)
-
-        let newTaskItem = NSMenuItem(title: "New Task", action: #selector(handleNewTask), keyEquivalent: "n")
-        newTaskItem.keyEquivalentModifierMask = .command
-        newTaskItem.target = self
-        newTaskItem.tag = customMenuTag
-        fileMenu.insertItem(newTaskItem, at: insertIndex)
-
-        // New Window is now at insertIndex + 2; place sep2 immediately after it.
-        let sep2 = NSMenuItem.separator()
-        sep2.tag = customMenuTag
-        fileMenu.insertItem(sep2, at: insertIndex + 3)
-    }
-
-    @MainActor private func setupEditMenuIfNeeded() {
-        guard let editMenu = NSApp.mainMenu?.items.first(where: { $0.title == "Edit" })?.submenu else { return }
-
-        // NOTE: Any new menu key equivalents added in this file should also be mapped in
-        // TaskListView.keyboardNavigation(...). SwiftUI's onKeyPress layer can intercept
-        // events before AppKit key equivalents, so duplicating shortcut bindings at the
-        // SwiftUI layer keeps shortcut handling reliable.
-
-        // Modify the system "Delete" item in-place so it stays in its expected position
-        // but dispatches our action with our preferred shortcut (⌫). Runs on every
-        // notification in case SwiftUI re-adds a fresh item; left untagged so the guard
-        // below can still fire correctly on first setup.
-        if let systemDelete = editMenu.items.first(where: { $0.action == NSSelectorFromString("delete:") }) {
-            systemDelete.action = #selector(handleDeleteTask)
-            systemDelete.target = self
-            systemDelete.keyEquivalent = "\u{08}"
-            systemDelete.keyEquivalentModifierMask = []
-        }
-
-        guard !editMenu.items.contains(where: { $0.tag == customMenuTag }) else { return }
-
-        func addSep() {
-            let s = NSMenuItem.separator()
-            s.tag = customMenuTag
-            editMenu.addItem(s)
-        }
-
-        func addItem(title: String, action: Selector, key: String, modifiers: NSEvent.ModifierFlags) {
-            let i = NSMenuItem(title: title, action: action, keyEquivalent: key)
-            i.keyEquivalentModifierMask = modifiers
-            i.target = self
-            i.tag = customMenuTag
-            editMenu.addItem(i)
-        }
-
-        addSep()
-        addItem(title: "Move Up",           action: #selector(handleMoveUp),         key: "\u{F700}", modifiers: .command)
-        addItem(title: "Move Down",         action: #selector(handleMoveDown),        key: "\u{F701}", modifiers: .command)
-        addItem(title: "Mark as Completed", action: #selector(handleMarkCompleted),   key: " ",        modifiers: [])
-        addSep()
-        addItem(title: "Clear Completed",   action: #selector(handleClearCompleted),  key: "",         modifiers: [])
+        NSWindow.allowsAutomaticWindowTabbing = false
+        installMainMenu()
     }
 
     // MARK: - NSMenuItemValidation
@@ -164,6 +22,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuItemValidation {
     func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
         let coord = MenuCoordinator.shared
         switch menuItem.action {
+        case #selector(handleNewWindow):      return coord.newWindow != nil
         case #selector(handleDeleteTask):     return coord.canDeleteSelectedTask
         case #selector(handleMoveUp):         return coord.canMoveSelectedTaskUp
         case #selector(handleMoveDown):       return coord.canMoveSelectedTaskDown
@@ -185,6 +44,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuItemValidation {
         MenuCoordinator.shared.deleteSelectedTask?()
     }
 
+    @objc private func handleNewWindow() {
+        MenuCoordinator.shared.newWindow?()
+    }
+
     @objc private func handleMoveUp() {
         MenuCoordinator.shared.moveSelectedTaskUp?()
     }
@@ -200,6 +63,142 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuItemValidation {
     @objc private func handleClearCompleted() {
         MenuCoordinator.shared.clearCompletedTasks?()
     }
+
+    // MARK: - Main Menu
+
+    private func installMainMenu() {
+        let mainMenu = NSMenu()
+        let appName = ProcessInfo.processInfo.processName
+
+        let appMenu = NSMenu()
+        appMenu.addItem(withTitle: "About \(appName)", action: #selector(NSApplication.orderFrontStandardAboutPanel(_:)), keyEquivalent: "")
+        appMenu.addItem(NSMenuItem.separator())
+
+        let settingsItem = NSMenuItem(
+            title: "Settings…",
+            action: MenuSelectors.showSettingsWindow,
+            keyEquivalent: ","
+        )
+        settingsItem.target = nil
+        appMenu.addItem(settingsItem)
+        appMenu.addItem(NSMenuItem.separator())
+
+        let servicesItem = NSMenuItem(title: "Services", action: nil, keyEquivalent: "")
+        let servicesMenu = NSMenu(title: "Services")
+        servicesItem.submenu = servicesMenu
+        appMenu.addItem(servicesItem)
+        NSApp.servicesMenu = servicesMenu
+
+        appMenu.addItem(NSMenuItem.separator())
+        appMenu.addItem(withTitle: "Hide \(appName)", action: #selector(NSApplication.hide(_:)), keyEquivalent: "h")
+
+        let hideOthersItem = NSMenuItem(
+            title: "Hide Others",
+            action: #selector(NSApplication.hideOtherApplications(_:)),
+            keyEquivalent: "h"
+        )
+        hideOthersItem.keyEquivalentModifierMask = [.command, .option]
+        appMenu.addItem(hideOthersItem)
+        appMenu.addItem(withTitle: "Show All", action: #selector(NSApplication.unhideAllApplications(_:)), keyEquivalent: "")
+        appMenu.addItem(NSMenuItem.separator())
+        appMenu.addItem(withTitle: "Quit \(appName)", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
+
+        let appMenuItem = NSMenuItem()
+        appMenuItem.submenu = appMenu
+        mainMenu.addItem(appMenuItem)
+
+        let fileMenu = NSMenu(title: "File")
+        let newTaskItem = NSMenuItem(title: "New Task", action: #selector(handleNewTask), keyEquivalent: "n")
+        newTaskItem.keyEquivalentModifierMask = [.command]
+        newTaskItem.target = self
+        fileMenu.addItem(newTaskItem)
+        fileMenu.addItem(NSMenuItem.separator())
+
+        let newWindowItem = NSMenuItem(title: "New Window", action: #selector(handleNewWindow), keyEquivalent: "n")
+        newWindowItem.keyEquivalentModifierMask = [.command, .shift]
+        newWindowItem.target = self
+        fileMenu.addItem(newWindowItem)
+        fileMenu.addItem(NSMenuItem.separator())
+        fileMenu.addItem(withTitle: "Close", action: #selector(NSWindow.performClose(_:)), keyEquivalent: "w")
+
+        let closeAllItem = NSMenuItem(title: "Close All", action: MenuSelectors.closeAll, keyEquivalent: "w")
+        closeAllItem.keyEquivalentModifierMask = [.command, .option]
+        closeAllItem.target = nil
+        fileMenu.addItem(closeAllItem)
+
+        let fileMenuItem = NSMenuItem(title: "File", action: nil, keyEquivalent: "")
+        fileMenuItem.submenu = fileMenu
+        mainMenu.addItem(fileMenuItem)
+
+        let editMenu = NSMenu(title: "Edit")
+        editMenu.addItem(withTitle: "Undo", action: MenuSelectors.undo, keyEquivalent: "z")
+        let redoItem = NSMenuItem(title: "Redo", action: MenuSelectors.redo, keyEquivalent: "z")
+        redoItem.keyEquivalentModifierMask = [.command, .shift]
+        editMenu.addItem(redoItem)
+        editMenu.addItem(NSMenuItem.separator())
+        editMenu.addItem(withTitle: "Cut", action: #selector(NSText.cut(_:)), keyEquivalent: "x")
+        editMenu.addItem(withTitle: "Copy", action: #selector(NSText.copy(_:)), keyEquivalent: "c")
+        editMenu.addItem(withTitle: "Paste", action: #selector(NSText.paste(_:)), keyEquivalent: "v")
+        editMenu.addItem(withTitle: "Select All", action: #selector(NSText.selectAll(_:)), keyEquivalent: "a")
+        let deleteItem = NSMenuItem(title: "Delete", action: #selector(handleDeleteTask), keyEquivalent: "\u{08}")
+        deleteItem.keyEquivalentModifierMask = []
+        deleteItem.target = self
+        editMenu.addItem(deleteItem)
+
+        editMenu.addItem(NSMenuItem.separator())
+
+        let moveUpItem = NSMenuItem(title: "Move Up", action: #selector(handleMoveUp), keyEquivalent: "\u{F700}")
+        moveUpItem.keyEquivalentModifierMask = [.command]
+        moveUpItem.target = self
+        editMenu.addItem(moveUpItem)
+
+        let moveDownItem = NSMenuItem(title: "Move Down", action: #selector(handleMoveDown), keyEquivalent: "\u{F701}")
+        moveDownItem.keyEquivalentModifierMask = [.command]
+        moveDownItem.target = self
+        editMenu.addItem(moveDownItem)
+
+        let markCompletedItem = NSMenuItem(title: "Mark as Completed", action: #selector(handleMarkCompleted), keyEquivalent: " ")
+        markCompletedItem.keyEquivalentModifierMask = []
+        markCompletedItem.target = self
+        editMenu.addItem(markCompletedItem)
+
+        editMenu.addItem(NSMenuItem.separator())
+
+        let clearCompletedItem = NSMenuItem(title: "Clear Completed", action: #selector(handleClearCompleted), keyEquivalent: "")
+        clearCompletedItem.target = self
+        editMenu.addItem(clearCompletedItem)
+
+        let editMenuItem = NSMenuItem(title: "Edit", action: nil, keyEquivalent: "")
+        editMenuItem.submenu = editMenu
+        mainMenu.addItem(editMenuItem)
+
+        let viewMenu = NSMenu(title: "View")
+        let fullScreenItem = NSMenuItem(title: "Enter Full Screen", action: #selector(NSWindow.toggleFullScreen(_:)), keyEquivalent: "f")
+        fullScreenItem.keyEquivalentModifierMask = [.command, .control]
+        viewMenu.addItem(fullScreenItem)
+        let viewMenuItem = NSMenuItem(title: "View", action: nil, keyEquivalent: "")
+        viewMenuItem.submenu = viewMenu
+        mainMenu.addItem(viewMenuItem)
+
+        let windowMenu = NSMenu(title: "Window")
+        windowMenu.addItem(withTitle: "Minimize", action: #selector(NSWindow.performMiniaturize(_:)), keyEquivalent: "m")
+        windowMenu.addItem(withTitle: "Zoom", action: #selector(NSWindow.performZoom(_:)), keyEquivalent: "")
+        windowMenu.addItem(NSMenuItem.separator())
+        windowMenu.addItem(withTitle: "Bring All to Front", action: #selector(NSApplication.arrangeInFront(_:)), keyEquivalent: "")
+        let windowMenuItem = NSMenuItem(title: "Window", action: nil, keyEquivalent: "")
+        windowMenuItem.submenu = windowMenu
+        mainMenu.addItem(windowMenuItem)
+        NSApp.windowsMenu = windowMenu
+
+        let helpMenu = NSMenu(title: "Help")
+        helpMenu.addItem(withTitle: "\(appName) Help", action: #selector(NSApplication.showHelp(_:)), keyEquivalent: "?")
+        let helpMenuItem = NSMenuItem(title: "Help", action: nil, keyEquivalent: "")
+        helpMenuItem.submenu = helpMenu
+        mainMenu.addItem(helpMenuItem)
+        NSApp.helpMenu = helpMenu
+
+        NSApp.mainMenu = mainMenu
+    }
 }
 
 @main
@@ -221,5 +220,6 @@ struct ListlessMacApp: App {
             .environment(\.managedObjectContext, persistenceController.viewContext)
         }
         .windowStyle(.hiddenTitleBar)
+        .defaultSize(width: 400, height: 350)
     }
 }

diff --git a/ListlessMac/Views/TaskListView.swift b/ListlessMac/Views/TaskListView.swift

@@ -13,6 +13,7 @@ struct TaskListView: View {
 
     @Environment(\.undoManager) var undoManager
     @Environment(\.managedObjectContext) var managedObjectContext
+    @Environment(\.openWindow) var openWindow
 
     let store: TaskStore
     @ObservedObject var syncMonitor: CloudKitSyncMonitor
@@ -89,6 +90,7 @@ struct TaskListView: View {
     func updateMenuCoordinator() {
         let coord = MenuCoordinator.shared
         coord.newTask = { createNewTask(); focusedField = nil }
+        coord.newWindow = { openWindow(id: "main") }
         coord.deleteSelectedTask = { _ = deleteSelectedTask() }
         coord.moveSelectedTaskUp = { moveSelectedTaskUp() }
         coord.moveSelectedTaskDown = { moveSelectedTaskDown() }
@@ -218,14 +220,9 @@ struct TaskListView: View {
         .focusEffectDisabled()
         .accessibilityIdentifier("task-list-scrollview")
         .keyboardNavigation([
-            ShortcutKey(key: "n", modifiers: .command): createNewTaskFromShortcut,
             ShortcutKey(key: .upArrow): navigateUp,
             ShortcutKey(key: .downArrow): navigateDown,
-            ShortcutKey(key: .upArrow, modifiers: .command): moveSelectedTaskUpFromShortcut,
-            ShortcutKey(key: .downArrow, modifiers: .command): moveSelectedTaskDownFromShortcut,
             ShortcutKey(key: .return): focusSelectedTask,
-            ShortcutKey(key: .space): toggleSelectedTask,
-            ShortcutKey(key: .delete): deleteSelectedTask,
         ])
         .onAppear {
             if focusedField == nil {
@@ -238,11 +235,9 @@ struct TaskListView: View {
 
             if newValue == nil {
                 if let pending = pendingFocus {
-                    print("🟣 onChange resolving pendingFocus: \(pending)")
                     focusedField = pending
                     pendingFocus = nil
                 } else {
-                    print("🟣 onChange repairing nil focus to .scrollView")
                     focusedField = .scrollView
                 }
             }