Open on startup by default and block duplicate workspace names

- LoginItem registers on the first bundled launch, so the app opens on
  startup without the user finding the toggle; a user who turns it off is
  never overridden. A never-registered bundle reports SMAppService .notFound
  rather than .notRegistered, so the guard only treats enabled/approval as
  done. --diag now prints the raw status.
- New Workspace refuses a name that already exists (case-insensitive) and
  says so inline; Return cannot bypass it. The composer's '# Name' still
  switches to the existing workspace, which is the quick-capture gesture.
This commit is contained in:
2026-10-26 20:46:02 +08:00
parent e0c47ad010
commit 15a2aa92f7
3 changed files with 57 additions and 5 deletions
+27
View File
@@ -61,6 +61,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
setupStatusItem()
promptForAccessibilityIfNeeded()
LoginItem.autoRegisterOnce()
// Let SwiftUI finish installing its main menu, then augment it.
DispatchQueue.main.async { self.installMenuItems() }
@@ -140,6 +141,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
@MainActor
private func writeDiagnostics() {
var lines = ["Prompt diagnostics", "accessibility: \(AXIsProcessTrusted())"]
lines.append("login item status: \(SMAppService.mainApp.status.rawValue)")
if let window = statusItem?.button?.window {
lines.append("status item frame: \(window.frame)")
if let screen = window.screen ?? NSScreen.main {
@@ -266,6 +268,31 @@ enum LoginItem {
SMAppService.mainApp.status == .enabled
}
/// Registers the app as a login item on first launch so Prompt opens on
/// startup without the user hunting for the toggle. Runs once (tracked in
/// UserDefaults) so a user who disables it is never overridden, and skips
/// `swift run`, where there is no bundle to register.
static func autoRegisterOnce() {
let key = "loginItem.autoRegistered"
guard Bundle.main.bundleIdentifier != nil,
!UserDefaults.standard.bool(forKey: key) else { return }
// A never-registered bundle reports `.notFound` on current macOS
// (BTM has no record), not `.notRegistered`, so only skip when there
// is nothing left to do.
switch SMAppService.mainApp.status {
case .enabled, .requiresApproval:
UserDefaults.standard.set(true, forKey: key)
default:
do {
try SMAppService.mainApp.register()
UserDefaults.standard.set(true, forKey: key)
NSLog("Prompt: registered as a login item (%d)", SMAppService.mainApp.status.rawValue)
} catch {
NSLog("Prompt: login item registration failed: %@", error.localizedDescription)
}
}
}
static func toggle() -> Bool {
let service = SMAppService.mainApp
do {