Restore pasting and rename sections to workspaces

Replacing the main menu dropped the Edit menu that AppKit routes
Cmd+V/C/A/Z through, so the composer could not paste at all. The menu is
rebuilt with those items, which --diag now dumps so the wiring can be
checked without a keyboard.

Workspaces also gained an order: the Cmd+K switcher has up/down controls,
creating one opens a naming modal instead of prefilling the composer, and
the UI now says workspace throughout. The stored JSON keeps the original
`sections` keys so existing data and sync payloads stay valid.
This commit is contained in:
2026-10-09 17:29:16 +08:00
parent c5011d03e7
commit 93cd6f2056
5 changed files with 160 additions and 37 deletions
+30 -4
View File
@@ -32,6 +32,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
// which locks the user out once the panel is hidden.
if CommandLine.arguments.contains("--diag") {
setupStatusItem()
setupMainMenu()
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
self.writeDiagnostics()
NSApp.terminate(nil)
@@ -113,24 +114,40 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
/// Accessory apps have no visible menu bar, but the main menu still
/// services key equivalents — this is what makes Cmd+Q and Cmd+W work
/// while the panel has focus.
///
/// The Edit menu is not decoration: AppKit routes Cmd+C/V/X/A/Z through
/// these items, so a minimal main menu silently breaks pasting and
/// selecting in the composer.
private func setupMainMenu() {
let mainMenu = NSMenu()
let appMenuItem = NSMenuItem()
mainMenu.addItem(appMenuItem)
let appMenu = NSMenu()
let appMenu = NSMenu(title: "Prompt")
appMenuItem.submenu = appMenu
let hide = NSMenuItem(title: "Hide Panel", action: #selector(hidePanel), keyEquivalent: "w")
hide.target = self
appMenu.addItem(hide)
appMenu.addItem(.separator())
let quit = NSMenuItem(title: "Quit Prompt", action: #selector(quit), keyEquivalent: "q")
quit.target = self
appMenu.addItem(quit)
let editMenuItem = NSMenuItem()
mainMenu.addItem(editMenuItem)
let editMenu = NSMenu(title: "Edit")
editMenuItem.submenu = editMenu
editMenu.addItem(withTitle: "Undo", action: Selector(("undo:")), keyEquivalent: "z")
let redo = editMenu.addItem(withTitle: "Redo", action: Selector(("redo:")), keyEquivalent: "z")
redo.keyEquivalentModifierMask = [.command, .shift]
editMenu.addItem(.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")
NSApp.mainMenu = mainMenu
}
@@ -153,6 +170,15 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
lines.append("status item has no window (not in the menu bar)")
}
lines.append("preferred position: \(UserDefaults.standard.object(forKey: "NSStatusItem Preferred Position Prompt") ?? "unset")")
lines.append("main menu:")
for item in NSApp.mainMenu?.items ?? [] {
lines.append(" \(item.submenu?.title ?? item.title)")
for entry in item.submenu?.items ?? [] {
guard !entry.isSeparatorItem else { continue }
let action = entry.action.map(NSStringFromSelector) ?? "-"
lines.append(" \(entry.title) [\(entry.keyEquivalentModifierMask.rawValue)] \(action)")
}
}
try? lines.joined(separator: "\n").write(
to: URL(fileURLWithPath: "/tmp/prompt-diag.txt"), atomically: true, encoding: .utf8
)