From 7c8c804dacd1747101c0949a3ca26c94c690af79 Mon Sep 17 00:00:00 2001
From: Ariver <shanghai3168@gmail.com>
Date: Wed, 20 May 2026 02:35:59 +0800
Subject: [PATCH] Release TagLauncher 7.5.1
---
Apptag/ApptagApp.swift | 258 +++
Apptag/Localization/cs.json | 41
Apptag/Localization/no.json | 41
Apptag/Localization/zh-Hant.json | 41
Apptag/Localization/ja.json | 41
Apptag/Info.plist | 4
Apptag/Localization/th.json | 41
Apptag/Localization/nl.json | 41
Apptag/Localization/sr-Cyrl.json | 41
Apptag/Localization/zh-Hans.json | 41
Apptag/Localization/de.json | 41
Apptag/Localization/ms.json | 41
Apptag/Localization/nn.json | 41
Apptag/Localization/pt-BR.json | 41
Apptag/Localization/es.json | 41
Apptag/Localization/tr.json | 41
Apptag/Localization/en.json | 41
Apptag/Localization/nb.json | 41
Apptag/Localization/ar.json | 41
Apptag/PreferencesView.swift | 602 +++++++--
Apptag/Localization/vi.json | 41
Apptag/Localization/fr.json | 41
Apptag/Localization/id.json | 41
Apptag/Localization/da.json | 41
CHANGELOG.md | 19
Apptag/Localization/uk.json | 41
Apptag/QuickSearch.swift | 1080 ++++++++++++++++++
Apptag/Localization/ar-Najdi.json | 41
Apptag/Localization/ko.json | 41
Apptag/ContentView.swift | 317 ++++
Apptag/DataLayer.swift | 22
Apptag/Localization/it.json | 41
Apptag/Localization/sv.json | 41
Apptag/Localization/ro.json | 41
Apptag/AppDefaults.swift | 24
Apptag/Localization/pl.json | 41
Apptag/Localization/ru.json | 41
37 files changed, 3,285 insertions(+), 230 deletions(-)
diff --git a/Apptag/AppDefaults.swift b/Apptag/AppDefaults.swift
index 973798e..2f2f720 100644
--- a/Apptag/AppDefaults.swift
+++ b/Apptag/AppDefaults.swift
@@ -2,7 +2,7 @@
enum AppDefaults {
static let schemaVersionKey = "initialDefaultsSchemaVersion"
- static let currentSchemaVersion = 1
+ static let currentSchemaVersion = 2
static let tagFontSize: Double = 22
static let iconSize: Double = 80
@@ -24,10 +24,32 @@
"launchAtLogin": launchAtLogin,
"showUncommonAppBubbles": showUncommonAppBubbles
])
+ migrateShortcutDefaultsIfNeeded()
}
static func hasStoredValue(for key: String) -> Bool {
let domain = Bundle.main.bundleIdentifier ?? "com.apptag.launcher"
return UserDefaults.standard.persistentDomain(forName: domain)?[key] != nil
}
+
+ private static func migrateShortcutDefaultsIfNeeded() {
+ let defaults = UserDefaults.standard
+ guard defaults.integer(forKey: schemaVersionKey) < currentSchemaVersion else { return }
+
+ defaults.set(LauncherHotkey.defaultMain.serialized, forKey: LauncherHotkeyKind.main.storageKey)
+ defaults.removeObject(forKey: LauncherHotkeyKind.main.pendingStorageKey)
+ defaults.removeObject(forKey: LauncherHotkeyKind.main.statusKey)
+ defaults.removeObject(forKey: LauncherHotkeyKind.main.conflictMessageKey)
+
+ let quickSearchKey = LauncherHotkeyKind.quickSearch.storageKey
+ let storedQuickSearch = defaults.string(forKey: quickSearchKey) ?? ""
+ if storedQuickSearch.isEmpty {
+ defaults.set(LauncherHotkey.defaultQuickSearch.serialized, forKey: quickSearchKey)
+ defaults.removeObject(forKey: LauncherHotkeyKind.quickSearch.pendingStorageKey)
+ defaults.removeObject(forKey: LauncherHotkeyKind.quickSearch.statusKey)
+ defaults.removeObject(forKey: LauncherHotkeyKind.quickSearch.conflictMessageKey)
+ }
+
+ defaults.set(currentSchemaVersion, forKey: schemaVersionKey)
+ }
}
diff --git a/Apptag/ApptagApp.swift b/Apptag/ApptagApp.swift
index 72a1665..cd12e0b 100644
--- a/Apptag/ApptagApp.swift
+++ b/Apptag/ApptagApp.swift
@@ -33,7 +33,6 @@
private static let statusItemButtonIdentifier = NSUserInterfaceItemIdentifier("TagLauncherStatusItemButton")
private static let statusItemAccessibilityLabel = "TagLauncher"
private static let showAppListMenuItemIdentifier = NSUserInterfaceItemIdentifier("TagLauncherShowAppListMenuItem")
- private static let showAppListShortcutGlyphs = "⌥⇧␣"
private static let overlayDefaultLevel = NSWindow.Level(rawValue: Int(CGWindowLevelForKey(.maximumWindow)))
private static let overlayTextInputLevel = NSWindow.Level.modalPanel
@@ -41,7 +40,12 @@
private var overlayWindow: NSWindow?
private var overlayKeyMonitor: Any?
private var settingsWindow: NSWindow? // Track Settings window to keep it above overlay
- private var hotkeyRef: EventHotKeyRef?
+ private var mainHotkeyRef: EventHotKeyRef?
+ private var quickSearchHotkeyRef: EventHotKeyRef?
+ private var hotkeyEventHandlerInstalled = false
+ private var isQuickSearchOpen = false
+ private var isModalInteractionActive = false
+ private var areHotkeysPausedForCapture = false
private var isInEditMode = false // Suppress auto-dismiss during editing
private var isEditingAppNote = false
private var isConfiguringApplicationMenu = false
@@ -61,11 +65,12 @@
migrateDefaultGroupName()
TagDatabase.seedDefaultTags()
syncChromeSettings(force: true)
- registerHotkey()
+ registerConfiguredHotkeys()
observeOtherWindows()
observeSettingsClose()
observeEditMode()
observeAppNoteEditing()
+ observeQuickSearch()
observePreferencesRequests()
observeApplicationMenuChanges()
observeChromeSettings()
@@ -333,7 +338,10 @@
}
private var showAppListMenuTitle: String {
- "\(tr("menu.showAppList")) \(Self.showAppListShortcutGlyphs)"
+ if let hotkey = LauncherHotkeyStore.hotkey(for: .main) {
+ return "\(tr("menu.showAppList")) \(hotkey.displayString)"
+ }
+ return tr("menu.showAppList")
}
private func observeApplicationMenuChanges() {
@@ -502,7 +510,7 @@
}
}
- private func showOverlay() {
+ private func showOverlay(initialQuickSearchSource: String? = nil) {
// Use the screen under the mouse cursor — works in fullscreen spaces
let mousePoint = NSEvent.mouseLocation
guard let screen = NSScreen.screens.first(where: {
@@ -513,7 +521,7 @@
if let existingWindow = overlayWindow {
window = existingWindow
} else {
- window = makeOverlayWindow(on: screen)
+ window = makeOverlayWindow(on: screen, initialQuickSearchSource: initialQuickSearchSource)
overlayWindow = window
}
@@ -521,6 +529,14 @@
window.level = isEditingAppNote ? Self.overlayTextInputLevel : Self.overlayDefaultLevel
installOverlayKeyMonitor()
+
+ if let initialQuickSearchSource {
+ NotificationCenter.default.post(
+ name: .tagLauncherQuickSearchRequested,
+ object: nil,
+ userInfo: ["source": initialQuickSearchSource]
+ )
+ }
window.makeKeyAndOrderFront(nil)
window.orderFrontRegardless()
@@ -530,12 +546,58 @@
private func installOverlayKeyMonitor() {
guard overlayKeyMonitor == nil else { return }
overlayKeyMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
+ guard let self else { return event }
if event.keyCode == 53 { // Escape
- self?.hideOverlay()
+ if self.isQuickSearchOpen {
+ NotificationCenter.default.post(name: .tagLauncherQuickSearchDismissRequested, object: nil)
+ return nil
+ }
+ self.hideOverlay()
+ return nil
+ }
+ if self.shouldOpenQuickSearch(for: event) {
+ NotificationCenter.default.post(
+ name: .tagLauncherQuickSearchRequested,
+ object: nil,
+ userInfo: ["source": QuickSearchOpenSource.mainOverlay]
+ )
return nil
}
return event
}
+ }
+
+ private func shouldOpenQuickSearch(for event: NSEvent) -> Bool {
+ guard event.keyCode == UInt16(kVK_Space),
+ !event.isARepeat,
+ event.modifierFlags.intersection(.deviceIndependentFlagsMask).isEmpty,
+ overlayWindow?.isVisible == true,
+ !isQuickSearchOpen,
+ !isInEditMode,
+ !isEditingAppNote,
+ !isModalInteractionActive
+ else { return false }
+
+ guard let firstResponder = overlayWindow?.firstResponder else { return true }
+ if firstResponder is NSText || firstResponder is NSTextField {
+ return false
+ }
+ if let responderView = firstResponder as? NSView,
+ viewOrAncestorHandlesSpace(responderView) {
+ return false
+ }
+ return true
+ }
+
+ private func viewOrAncestorHandlesSpace(_ view: NSView) -> Bool {
+ var current: NSView? = view
+ while let candidate = current {
+ if candidate is NSButton || candidate is NSSegmentedControl || candidate is NSSlider {
+ return true
+ }
+ current = candidate.superview
+ }
+ return false
}
private func removeOverlayKeyMonitor() {
@@ -545,7 +607,7 @@
}
}
- private func makeOverlayWindow(on screen: NSScreen) -> NSWindow {
+ private func makeOverlayWindow(on screen: NSScreen, initialQuickSearchSource: String? = nil) -> NSWindow {
let panel = OverlayPanel(
contentRect: screen.frame,
styleMask: [.borderless, .fullSizeContentView, .nonactivatingPanel],
@@ -568,9 +630,12 @@
panel.titleVisibility = .hidden
panel.isReleasedWhenClosed = false
panel.contentView = DismissibleHostingView(
- rootView: ContentView(hideOverlay: { [weak self] in
- self?.hideOverlay(force: true)
- }),
+ rootView: ContentView(
+ hideOverlay: { [weak self] in
+ self?.hideOverlay(force: true)
+ },
+ initialQuickSearchSource: initialQuickSearchSource
+ ),
onBackdropTap: { [weak self] in
self?.hideOverlay()
}
@@ -592,48 +657,106 @@
}
}
- // MARK: - Global Hotkey (Shift+Option+Space)
+ // MARK: - Global Hotkeys
- /// Carbon RegisterEventHotKey. If it fails (sandbox, etc.), falls back to menu bar only.
- private func registerHotkey() {
+ private func registerConfiguredHotkeys() {
+ installHotkeyEventHandlerIfNeeded()
+ registerStoredHotkey(.main)
+ registerStoredHotkey(.quickSearch)
+ }
+
+ @discardableResult
+ func applyHotkey(_ hotkey: LauncherHotkey?, for kind: LauncherHotkeyKind) -> Bool {
+ installHotkeyEventHandlerIfNeeded()
+
+ if hotkey == nil {
+ unregisterHotkey(for: kind)
+ LauncherHotkeyStore.save(nil, for: kind)
+ LauncherHotkeyStore.setStatus(.disabled, message: nil, for: kind)
+ return true
+ }
+
+ guard let hotkey else { return false }
+ let previousRef = hotkeyRef(for: kind)
+ if previousRef != nil, LauncherHotkeyStore.hotkey(for: kind) == hotkey {
+ LauncherHotkeyStore.setStatus(.active, message: nil, for: kind)
+ return true
+ }
+ setHotkeyRef(nil, for: kind)
+
var hotkeyID = EventHotKeyID()
hotkeyID.signature = OSType(0x41505447) // 'APTG'
- hotkeyID.id = 1
+ hotkeyID.id = kind.eventID
- let modifiers = UInt32(shiftKey | optionKey)
-
- var ref: EventHotKeyRef?
+ var newRef: EventHotKeyRef?
let status = RegisterEventHotKey(
- UInt32(kVK_Space),
- modifiers,
+ hotkey.keyCode,
+ hotkey.modifiers,
hotkeyID,
GetApplicationEventTarget(),
0,
- &ref
+ &newRef
)
- hotkeyRef = ref
- if status != noErr {
- print("[TagLauncher] Hotkey registration failed: \(status). Falling back to menu bar only.")
- return
+ if status == noErr, let newRef {
+ if let previousRef { UnregisterEventHotKey(previousRef) }
+ setHotkeyRef(newRef, for: kind)
+ LauncherHotkeyStore.save(hotkey, for: kind)
+ LauncherHotkeyStore.setStatus(.active, message: nil, for: kind)
+ return true
}
+
+ setHotkeyRef(previousRef, for: kind)
+ let message = LauncherHotkeyStore.knownConflictMessage(for: hotkey, status: status)
+ ?? tr("quickSearch.hotkeyConflict.generic")
+ LauncherHotkeyStore.savePending(hotkey, for: kind)
+ LauncherHotkeyStore.setStatus(.conflict, message: message, for: kind)
+ print("[TagLauncher] Hotkey registration failed for \(kind.rawValue): \(status)")
+ return false
+ }
+
+ func retryHotkeyRegistration(for kind: LauncherHotkeyKind) {
+ _ = applyHotkey(
+ LauncherHotkeyStore.pendingHotkey(for: kind) ?? LauncherHotkeyStore.hotkey(for: kind),
+ for: kind
+ )
+ }
+
+ private func registerStoredHotkey(_ kind: LauncherHotkeyKind) {
+ _ = applyHotkey(LauncherHotkeyStore.hotkey(for: kind), for: kind)
+ }
+
+ private func installHotkeyEventHandlerIfNeeded() {
+ guard !hotkeyEventHandlerInstalled else { return }
+ hotkeyEventHandlerInstalled = true
var eventSpec = EventTypeSpec(
eventClass: OSType(kEventClassKeyboard),
eventKind: UInt32(kEventHotKeyPressed)
)
-
let selfPtr = Unmanaged.passUnretained(self).toOpaque()
InstallEventHandler(
GetApplicationEventTarget(),
- { (_, _, userData) -> OSStatus in
- guard let userData else { return noErr }
+ { (_, event, userData) -> OSStatus in
+ guard let event, let userData else { return noErr }
+ var hotkeyID = EventHotKeyID()
+ let status = GetEventParameter(
+ event,
+ EventParamName(kEventParamDirectObject),
+ EventParamType(typeEventHotKeyID),
+ nil,
+ MemoryLayout<EventHotKeyID>.size,
+ nil,
+ &hotkeyID
+ )
+ guard status == noErr else { return noErr }
+
let delegate = Unmanaged<AppDelegate>
.fromOpaque(userData)
.takeUnretainedValue()
DispatchQueue.main.async {
- delegate.toggleOverlay()
+ delegate.handleHotkeyEvent(id: hotkeyID.id)
}
return noErr
},
@@ -642,6 +765,40 @@
selfPtr,
nil
)
+ }
+
+ private func handleHotkeyEvent(id: UInt32) {
+ if id == LauncherHotkeyKind.quickSearch.eventID {
+ showQuickSearchFromGlobalHotkey()
+ } else {
+ toggleOverlay()
+ }
+ }
+
+ private func showQuickSearchFromGlobalHotkey() {
+ guard overlayWindow?.isVisible != true else { return }
+ showOverlay(initialQuickSearchSource: QuickSearchOpenSource.globalHidden)
+ }
+
+ private func hotkeyRef(for kind: LauncherHotkeyKind) -> EventHotKeyRef? {
+ switch kind {
+ case .main: return mainHotkeyRef
+ case .quickSearch: return quickSearchHotkeyRef
+ }
+ }
+
+ private func setHotkeyRef(_ ref: EventHotKeyRef?, for kind: LauncherHotkeyKind) {
+ switch kind {
+ case .main: mainHotkeyRef = ref
+ case .quickSearch: quickSearchHotkeyRef = ref
+ }
+ }
+
+ private func unregisterHotkey(for kind: LauncherHotkeyKind) {
+ if let ref = hotkeyRef(for: kind) {
+ UnregisterEventHotKey(ref)
+ setHotkeyRef(nil, for: kind)
+ }
}
// MARK: - Preferences
@@ -780,6 +937,38 @@
}
}
+ private func observeQuickSearch() {
+ NotificationCenter.default.addObserver(
+ forName: .tagLauncherQuickSearchVisibilityChanged,
+ object: nil,
+ queue: .main
+ ) { [weak self] notification in
+ self?.isQuickSearchOpen = (notification.userInfo?["active"] as? Bool) ?? false
+ }
+
+ NotificationCenter.default.addObserver(
+ forName: .tagLauncherModalInteractionChanged,
+ object: nil,
+ queue: .main
+ ) { [weak self] notification in
+ guard let self else { return }
+ let active = (notification.userInfo?["active"] as? Bool) ?? false
+ self.isModalInteractionActive = active
+ if (notification.userInfo?["source"] as? String) == "hotkeyRecording" {
+ self.setHotkeysPausedForCapture(active)
+ }
+ }
+
+ NotificationCenter.default.addObserver(
+ forName: .tagLauncherHotkeysChanged,
+ object: nil,
+ queue: .main
+ ) { [weak self] _ in
+ self?.setupMenuBar()
+ self?.configureApplicationMenuWhenAvailable(retries: 2)
+ }
+ }
+
private func updateOverlayLevelForTextInput() {
guard let overlayWindow else { return }
overlayWindow.level = isEditingAppNote ? Self.overlayTextInputLevel : Self.overlayDefaultLevel
@@ -788,6 +977,17 @@
}
}
+ private func setHotkeysPausedForCapture(_ paused: Bool) {
+ guard areHotkeysPausedForCapture != paused else { return }
+ areHotkeysPausedForCapture = paused
+ if paused {
+ unregisterHotkey(for: .main)
+ unregisterHotkey(for: .quickSearch)
+ } else {
+ registerConfiguredHotkeys()
+ }
+ }
+
/// The overlay buttons live inside SwiftUI; use an app-level notification like edit mode does.
private func observePreferencesRequests() {
NotificationCenter.default.addObserver(
diff --git a/Apptag/ContentView.swift b/Apptag/ContentView.swift
index b9bb245..8e1cf0e 100644
--- a/Apptag/ContentView.swift
+++ b/Apptag/ContentView.swift
@@ -337,6 +337,7 @@
struct ContentView: View {
let hideOverlay: () -> Void
+ private let initialQuickSearchSource: String?
@State private var allApps: [AppInfo] = []
@State private var tagColors: [String: Int] = [:]
@@ -377,6 +378,27 @@
@State private var bubbleDraftNote = ""
@FocusState private var bubbleNoteFocused: Bool
+ // Quick Search
+ @State private var quickSearchVisible = false
+ @State private var quickSearchQuery = ""
+ @State private var quickSearchDocuments: [QuickSearchDocument] = []
+ @State private var quickSearchResults: [QuickSearchResult] = []
+ @State private var quickSearchSelectedID: URL? = nil
+ @State private var quickSearchManualSelection = false
+ @State private var quickSearchFocusToken = 0
+ @State private var quickSearchCloseHidesOverlay = false
+ @State private var quickSearchErrorMessage: String? = nil
+ @State private var initialQuickSearchConsumed = false
+
+ init(hideOverlay: @escaping () -> Void, initialQuickSearchSource: String? = nil) {
+ self.hideOverlay = hideOverlay
+ self.initialQuickSearchSource = initialQuickSearchSource
+ let startsInQuickSearch = initialQuickSearchSource != nil
+ _quickSearchVisible = State(initialValue: startsInQuickSearch)
+ _quickSearchCloseHidesOverlay = State(initialValue: initialQuickSearchSource == QuickSearchOpenSource.globalHidden)
+ _quickSearchFocusToken = State(initialValue: startsInQuickSearch ? 1 : 0)
+ }
+
// Configurable defaults
@AppStorage("defaultGroupName") private var defaultGroupName = "Other"
@AppStorage("tagFontSize") private var tagFontSize: Double = AppDefaults.tagFontSize
@@ -411,37 +433,47 @@
displayMode == "coloredContainer" || displayMode == "coloredGridContainer"
}
+ private var quickSearchOnlyMode: Bool {
+ quickSearchVisible && quickSearchCloseHidesOverlay
+ }
+
var body: some View {
ZStack {
- VisualEffectView(material: .hudWindow, blendingMode: .behindWindow)
- .ignoresSafeArea()
- .allowsHitTesting(false)
+ if !quickSearchOnlyMode {
+ VisualEffectView(material: .hudWindow, blendingMode: .behindWindow)
+ .ignoresSafeArea()
+ .allowsHitTesting(false)
+ }
- if notchHeight > 0 {
- VStack {
- Rectangle().fill(.black)
- .frame(height: notchHeight)
- .ignoresSafeArea(edges: .top)
- Spacer()
+ if !quickSearchOnlyMode {
+ if notchHeight > 0 {
+ VStack {
+ Rectangle().fill(.black)
+ .frame(height: notchHeight)
+ .ignoresSafeArea(edges: .top)
+ Spacer()
+ }
+ .allowsHitTesting(false)
}
- .allowsHitTesting(false)
+
+ switch editPhase {
+ case .none:
+ normalContent
+ case .editingTags:
+ editTagsView
+ case .editingApps:
+ editAppsView
+ }
+
+ uncommonAppBubbleOverlay
+ smartStartNoticeOverlay
+ editActionFeedbackOverlay
+ uncategorizedDropConfirmOverlay
}
- switch editPhase {
- case .none:
- normalContent
- case .editingTags:
- editTagsView
- case .editingApps:
- editAppsView
- }
+ quickSearchOverlay
- uncommonAppBubbleOverlay
- smartStartNoticeOverlay
- editActionFeedbackOverlay
- uncategorizedDropConfirmOverlay
-
- if let message = dropWarningToast {
+ if !quickSearchOnlyMode, let message = dropWarningToast {
Text(message)
.font(.system(size: 16, weight: .semibold))
.foregroundStyle(.primary)
@@ -456,7 +488,7 @@
.allowsHitTesting(false)
}
- if dropRefreshVisible {
+ if !quickSearchOnlyMode && dropRefreshVisible {
Color.black.opacity(0.08)
.ignoresSafeArea()
.transition(.opacity)
@@ -479,6 +511,17 @@
.onAppear {
refreshNotchHeight()
refreshApps()
+ if let initialQuickSearchSource, !initialQuickSearchConsumed {
+ initialQuickSearchConsumed = true
+ quickSearchCloseHidesOverlay = initialQuickSearchSource == QuickSearchOpenSource.globalHidden
+ quickSearchVisible = true
+ quickSearchFocusToken &+= 1
+ NotificationCenter.default.post(
+ name: .tagLauncherQuickSearchVisibilityChanged,
+ object: nil,
+ userInfo: ["active": true]
+ )
+ }
}
.onReceive(NotificationCenter.default.publisher(for: .tagLauncherOverlayDidShow)) { _ in
resetTransientDragState()
@@ -487,6 +530,14 @@
}
.onReceive(NotificationCenter.default.publisher(for: .tagLauncherOverlayDidHide)) { _ in
resetTransientDragState()
+ closeQuickSearch(notify: true, hideOverlayIfNeeded: false)
+ }
+ .onReceive(NotificationCenter.default.publisher(for: .tagLauncherQuickSearchRequested)) { notification in
+ let source = notification.userInfo?["source"] as? String ?? QuickSearchOpenSource.mainOverlay
+ openQuickSearch(source: source)
+ }
+ .onReceive(NotificationCenter.default.publisher(for: .tagLauncherQuickSearchDismissRequested)) { _ in
+ closeQuickSearch()
}
.onReceive(NotificationCenter.default.publisher(for: NSApplication.didBecomeActiveNotification)) { _ in
guard allApps.isEmpty, !refreshInProgress else { return }
@@ -517,6 +568,10 @@
if editingBubble != nil && !focused {
commitBubbleNote()
}
+ }
+ .onChange(of: quickSearchQuery) { _, _ in
+ quickSearchErrorMessage = nil
+ refreshQuickSearchResults()
}
.onChange(of: pendingUncategorizedDrop != nil) { _, active in
NotificationCenter.default.post(
@@ -551,6 +606,179 @@
NotificationCenter.default.post(name: .tagLauncherEditModeChanged, object: nil, userInfo: ["active": false])
}
}
+ }
+
+ // MARK: - Quick Search
+
+ private var quickSearchOverlay: some View {
+ GeometryReader { proxy in
+ if quickSearchVisible {
+ QuickSearchBackdropClickView {
+ closeQuickSearch()
+ }
+ .frame(width: proxy.size.width, height: proxy.size.height)
+ .ignoresSafeArea()
+ .zIndex(899)
+
+ QuickSearchOverlayView(
+ query: $quickSearchQuery,
+ results: quickSearchResults,
+ selectedID: quickSearchSelectedID,
+ focusToken: quickSearchFocusToken,
+ isLoading: quickSearchDocuments.isEmpty && refreshInProgress,
+ maxVisibleRows: quickSearchMaxVisibleRows(in: proxy.size),
+ errorMessage: quickSearchErrorMessage,
+ onCommand: handleQuickSearchCommand,
+ onHover: selectQuickSearchResult,
+ onLaunch: launchQuickSearchResult
+ )
+ .position(
+ x: proxy.size.width / 2,
+ y: quickSearchPanelCenterY(in: proxy.size)
+ )
+ .transition(.scale(scale: 0.98).combined(with: .opacity))
+ .zIndex(900)
+ }
+ }
+ .ignoresSafeArea()
+ .animation(.easeOut(duration: 0.12), value: quickSearchVisible)
+ }
+
+ private func quickSearchPanelCenterY(in size: CGSize) -> CGFloat {
+ let visibleRows = max(1, min(quickSearchResults.isEmpty ? 1 : quickSearchResults.count, quickSearchMaxVisibleRows(in: size)))
+ let estimatedPanelHeight = CGFloat(visibleRows) * 76 + 122
+ return quickSearchPanelTopY(in: size) + estimatedPanelHeight / 2
+ }
+
+ private func quickSearchPanelTopY(in size: CGSize) -> CGFloat {
+ max(notchHeight + 54, min(112, size.height * 0.12))
+ }
+
+ private func quickSearchMaxVisibleRows(in size: CGSize) -> Int {
+ let bottomClearance: CGFloat = 84
+ let chromeHeight: CGFloat = 122
+ let rowHeightWithSpacing: CGFloat = 76
+ let availableHeight = max(0, size.height - quickSearchPanelTopY(in: size) - bottomClearance - chromeHeight)
+ return max(1, min(8, Int(floor(availableHeight / rowHeightWithSpacing))))
+ }
+
+ private func openQuickSearch(source: String) {
+ guard canOpenQuickSearch else { return }
+ dismissAppBubble()
+ quickSearchCloseHidesOverlay = source == QuickSearchOpenSource.globalHidden
+ quickSearchVisible = true
+ quickSearchQuery = ""
+ quickSearchManualSelection = false
+ quickSearchErrorMessage = nil
+ quickSearchFocusToken &+= 1
+ refreshQuickSearchResults()
+ NotificationCenter.default.post(
+ name: .tagLauncherQuickSearchVisibilityChanged,
+ object: nil,
+ userInfo: ["active": true]
+ )
+ }
+
+ private var canOpenQuickSearch: Bool {
+ editPhase == .none
+ && pendingUncategorizedDrop == nil
+ && smartStartNotice == nil
+ && !dropRefreshVisible
+ && !quickSearchVisible
+ }
+
+ private func closeQuickSearch(notify: Bool = true, hideOverlayIfNeeded: Bool = true) {
+ let shouldHideOverlay = quickSearchVisible && quickSearchCloseHidesOverlay && hideOverlayIfNeeded
+ guard quickSearchVisible else { return }
+ quickSearchVisible = false
+ quickSearchQuery = ""
+ quickSearchResults = []
+ quickSearchSelectedID = nil
+ quickSearchManualSelection = false
+ quickSearchErrorMessage = nil
+ quickSearchCloseHidesOverlay = false
+ if notify {
+ NotificationCenter.default.post(
+ name: .tagLauncherQuickSearchVisibilityChanged,
+ object: nil,
+ userInfo: ["active": false]
+ )
+ }
+ if shouldHideOverlay {
+ hideOverlay()
+ }
+ }
+
+ private func refreshQuickSearchResults() {
+ let previousSelection = quickSearchSelectedID
+ quickSearchResults = QuickSearchEngine.search(quickSearchQuery, documents: quickSearchDocuments)
+
+ if quickSearchResults.isEmpty {
+ quickSearchSelectedID = nil
+ quickSearchManualSelection = false
+ return
+ }
+
+ if quickSearchManualSelection,
+ let previousSelection,
+ quickSearchResults.contains(where: { $0.id == previousSelection }) {
+ quickSearchSelectedID = previousSelection
+ } else {
+ quickSearchSelectedID = quickSearchResults.first?.id
+ quickSearchManualSelection = false
+ }
+ }
+
+ private func handleQuickSearchCommand(_ command: QuickSearchCommand) {
+ switch command {
+ case .moveUp:
+ moveQuickSearchSelection(by: -1)
+ case .moveDown:
+ moveQuickSearchSelection(by: 1)
+ case .submit:
+ guard let selected = selectedQuickSearchResult else { return }
+ launchQuickSearchResult(selected)
+ case .dismiss:
+ closeQuickSearch()
+ }
+ }
+
+ private var selectedQuickSearchResult: QuickSearchResult? {
+ guard let quickSearchSelectedID else { return nil }
+ return quickSearchResults.first { $0.id == quickSearchSelectedID }
+ }
+
+ private func moveQuickSearchSelection(by delta: Int) {
+ guard !quickSearchResults.isEmpty else { return }
+ let currentIndex = quickSearchSelectedID.flatMap { id in
+ quickSearchResults.firstIndex { $0.id == id }
+ } ?? 0
+ let nextIndex = min(max(currentIndex + delta, 0), quickSearchResults.count - 1)
+ quickSearchSelectedID = quickSearchResults[nextIndex].id
+ quickSearchManualSelection = true
+ }
+
+ private func selectQuickSearchResult(_ result: QuickSearchResult) {
+ quickSearchSelectedID = result.id
+ quickSearchManualSelection = true
+ }
+
+ private func launchQuickSearchResult(_ result: QuickSearchResult) {
+ quickSearchErrorMessage = nil
+ launchApp(
+ result.app,
+ closeQuickSearchOnSuccess: true,
+ closeOverlayOnSuccess: true,
+ onFailure: {
+ quickSearchErrorMessage = tr("quickSearch.launchFailed")
+ quickSearchFocusToken &+= 1
+ NSAccessibility.post(
+ element: NSApp.mainWindow as Any,
+ notification: .announcementRequested,
+ userInfo: [.announcement: tr("quickSearch.launchFailed")]
+ )
+ }
+ )
}
// MARK: - Normal Content
@@ -2304,12 +2532,17 @@
)
let store = smartStartResult.store
let apps = TagEditor.annotate(apps: scannedApps, store: store)
+ let quickSearchDocs = QuickSearchEngine.makeDocuments(apps: apps, store: store)
let colors = store.tags.mapValues { $0.color }
let order = TagEditor.orderedTagNames()
DispatchQueue.main.async {
allApps = apps
+ quickSearchDocuments = quickSearchDocs
tagColors = colors
draggedTagNames = order
+ if quickSearchVisible {
+ refreshQuickSearchResults()
+ }
handleSmartStartRunResult(smartStartResult)
if forceLayoutRefresh {
finishDropRefreshAfterMinimumDuration()
@@ -2325,15 +2558,37 @@
}
}
- func openApp(_ app: AppInfo) {
+ private func launchApp(
+ _ app: AppInfo,
+ closeQuickSearchOnSuccess: Bool = false,
+ closeOverlayOnSuccess: Bool = true,
+ onFailure: (() -> Void)? = nil
+ ) {
appDragModeActive = false
endTagNavReorder()
- hideOverlay()
- DispatchQueue.global(qos: .utility).async {
- TagEditor.recordLauncherOpen(for: app.path.path)
- }
let configuration = NSWorkspace.OpenConfiguration()
- NSWorkspace.shared.openApplication(at: app.path, configuration: configuration)
+ NSWorkspace.shared.openApplication(at: app.path, configuration: configuration) { _, error in
+ DispatchQueue.main.async {
+ guard error == nil else {
+ onFailure?()
+ return
+ }
+
+ DispatchQueue.global(qos: .utility).async {
+ TagEditor.recordLauncherOpen(for: app.path.path)
+ }
+ if closeQuickSearchOnSuccess {
+ closeQuickSearch(hideOverlayIfNeeded: false)
+ }
+ if closeOverlayOnSuccess {
+ hideOverlay()
+ }
+ }
+ }
+ }
+
+ func openApp(_ app: AppInfo) {
+ launchApp(app)
}
}
diff --git a/Apptag/DataLayer.swift b/Apptag/DataLayer.swift
index 779beac..59371d9 100644
--- a/Apptag/DataLayer.swift
+++ b/Apptag/DataLayer.swift
@@ -339,6 +339,7 @@
var uncommonAppPaths: [String] = [] // special marker; does not affect normal groups
var uncommonSources: [String: UncommonSource] = [:] // current uncommon source: auto/manual
var appOpenCounts: [String: Int] = [:] // launches opened from TagLauncher
+ var appLastOpenedAt: [String: Date] = [:] // successful launches opened from TagLauncher
var knownAppPaths: [String] = [] // baseline set to detect newly installed apps
var appNotes: [String: String] = [:] // path → user note; retained even if marker is removed
var disabledSystemCategoryIDs: [SmartCategoryID] = [] // system categories the user deleted
@@ -353,6 +354,7 @@
case uncommonAppPaths
case uncommonSources
case appOpenCounts
+ case appLastOpenedAt
case knownAppPaths
case appNotes
case disabledSystemCategoryIDs
@@ -371,6 +373,7 @@
uncommonAppPaths = try container.decodeIfPresent([String].self, forKey: .uncommonAppPaths) ?? []
uncommonSources = try container.decodeIfPresent([String: UncommonSource].self, forKey: .uncommonSources) ?? [:]
appOpenCounts = try container.decodeIfPresent([String: Int].self, forKey: .appOpenCounts) ?? [:]
+ appLastOpenedAt = try container.decodeIfPresent([String: Date].self, forKey: .appLastOpenedAt) ?? [:]
knownAppPaths = try container.decodeIfPresent([String].self, forKey: .knownAppPaths) ?? []
appNotes = try container.decodeIfPresent([String: String].self, forKey: .appNotes) ?? [:]
disabledSystemCategoryIDs = try container.decodeIfPresent(
@@ -1181,7 +1184,21 @@
}
let newPaths = scannedPaths.subtracting(knownPaths)
- guard !newPaths.isEmpty else { return store }
+ let removedPaths = knownPaths.subtracting(scannedPaths)
+ if !removedPaths.isEmpty {
+ for path in removedPaths {
+ store.appOpenCounts.removeValue(forKey: path)
+ store.appLastOpenedAt.removeValue(forKey: path)
+ }
+ store.knownAppPaths = knownPaths.subtracting(removedPaths).sorted()
+ }
+
+ guard !newPaths.isEmpty else {
+ if !removedPaths.isEmpty {
+ TagDatabase.save(store)
+ }
+ return store
+ }
var uncommonPaths = Set(store.uncommonAppPaths)
let appsByPath = Dictionary(uniqueKeysWithValues: apps.map { ($0.path.path, $0) })
@@ -1201,7 +1218,7 @@
let newApps = apps.filter { newPaths.contains($0.path.path) }
_ = seedDefaultAppleAppNotes(for: newApps, in: &store)
store.uncommonAppPaths = uncommonPaths.sorted()
- store.knownAppPaths = knownPaths.union(newPaths).sorted()
+ store.knownAppPaths = Set(store.knownAppPaths).union(newPaths).sorted()
TagDatabase.save(store)
return store
}
@@ -1253,6 +1270,7 @@
static func recordLauncherOpen(for path: String) {
var store = TagDatabase.load()
store.appOpenCounts[path] = (store.appOpenCounts[path] ?? 0) + 1
+ store.appLastOpenedAt[path] = Date()
if store.uncommonSources[path] == .auto,
store.appOpenCounts[path, default: 0] >= TagDatabase.autoUncommonOpenThreshold {
diff --git a/Apptag/Info.plist b/Apptag/Info.plist
index fc5cc64..796803c 100644
--- a/Apptag/Info.plist
+++ b/Apptag/Info.plist
@@ -19,9 +19,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
- <string>7.3.5</string>
+ <string>7.5.1</string>
<key>CFBundleVersion</key>
- <string>744</string>
+ <string>751</string>
<key>LSMinimumSystemVersion</key>
<string>15.0</string>
<key>NSHighResolutionCapable</key>
diff --git a/Apptag/Localization/ar-Najdi.json b/Apptag/Localization/ar-Najdi.json
index 555de22..acc328b 100644
--- a/Apptag/Localization/ar-Najdi.json
+++ b/Apptag/Localization/ar-Najdi.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "مخطط البداية الذكي للنظام",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/ar.json b/Apptag/Localization/ar.json
index 923f6e1..be9d14c 100644
--- a/Apptag/Localization/ar.json
+++ b/Apptag/Localization/ar.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "مخطط البداية الذكي للنظام",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/cs.json b/Apptag/Localization/cs.json
index 13f25ff..e574f01 100644
--- a/Apptag/Localization/cs.json
+++ b/Apptag/Localization/cs.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Chytré počáteční schéma systému",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/da.json b/Apptag/Localization/da.json
index 0c8254a..faea69c 100644
--- a/Apptag/Localization/da.json
+++ b/Apptag/Localization/da.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Systemets smarte startskema",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/de.json b/Apptag/Localization/de.json
index 696c07b..65e3def 100644
--- a/Apptag/Localization/de.json
+++ b/Apptag/Localization/de.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Intelligentes System-Startschema",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/en.json b/Apptag/Localization/en.json
index 2be6257..70bdeae 100644
--- a/Apptag/Localization/en.json
+++ b/Apptag/Localization/en.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "System smart initial scheme",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/es.json b/Apptag/Localization/es.json
index 50e9885..0c0aad0 100644
--- a/Apptag/Localization/es.json
+++ b/Apptag/Localization/es.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Esquema inicial inteligente del sistema",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/fr.json b/Apptag/Localization/fr.json
index c4c39c5..12fc2da 100644
--- a/Apptag/Localization/fr.json
+++ b/Apptag/Localization/fr.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Schéma initial intelligent du système",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/id.json b/Apptag/Localization/id.json
index 8134636..870fb63 100644
--- a/Apptag/Localization/id.json
+++ b/Apptag/Localization/id.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Skema awal pintar sistem",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/it.json b/Apptag/Localization/it.json
index 0dfa553..7702525 100644
--- a/Apptag/Localization/it.json
+++ b/Apptag/Localization/it.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Schema iniziale intelligente di sistema",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/ja.json b/Apptag/Localization/ja.json
index ec069c4..f74f5bd 100644
--- a/Apptag/Localization/ja.json
+++ b/Apptag/Localization/ja.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "システム初期スマート分類",
"settings.bubbleDisplayScope": "吹き出しの表示対象:",
"settings.bubbleAllApps": "すべてのアプリ",
- "settings.bubbleUncommonOnly": "あまり使わないアプリのみ"
+ "settings.bubbleUncommonOnly": "あまり使わないアプリのみ",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/ko.json b/Apptag/Localization/ko.json
index 844b0df..353341e 100644
--- a/Apptag/Localization/ko.json
+++ b/Apptag/Localization/ko.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "시스템 스마트 초기 분류",
"settings.bubbleDisplayScope": "말풍선 표시 대상:",
"settings.bubbleAllApps": "모든 앱",
- "settings.bubbleUncommonOnly": "자주 쓰지 않는 앱만"
+ "settings.bubbleUncommonOnly": "자주 쓰지 않는 앱만",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/ms.json b/Apptag/Localization/ms.json
index c5f57d9..54a3db7 100644
--- a/Apptag/Localization/ms.json
+++ b/Apptag/Localization/ms.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Skema awal pintar sistem",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/nb.json b/Apptag/Localization/nb.json
index 656c969..4241d07 100644
--- a/Apptag/Localization/nb.json
+++ b/Apptag/Localization/nb.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Systemets smarte startskjema",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/nl.json b/Apptag/Localization/nl.json
index 1f752ad..56029ab 100644
--- a/Apptag/Localization/nl.json
+++ b/Apptag/Localization/nl.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Slim beginschema van systeem",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/nn.json b/Apptag/Localization/nn.json
index 41227a6..2e59d4c 100644
--- a/Apptag/Localization/nn.json
+++ b/Apptag/Localization/nn.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Systemets smarte startskjema",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/no.json b/Apptag/Localization/no.json
index 656c969..4241d07 100644
--- a/Apptag/Localization/no.json
+++ b/Apptag/Localization/no.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Systemets smarte startskjema",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/pl.json b/Apptag/Localization/pl.json
index 1ebcbca..0dff021 100644
--- a/Apptag/Localization/pl.json
+++ b/Apptag/Localization/pl.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Inteligentny schemat początkowy systemu",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/pt-BR.json b/Apptag/Localization/pt-BR.json
index 3f20cb4..c542ab2 100644
--- a/Apptag/Localization/pt-BR.json
+++ b/Apptag/Localization/pt-BR.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Esquema inicial inteligente do sistema",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/ro.json b/Apptag/Localization/ro.json
index bfa075a..0f7d8a5 100644
--- a/Apptag/Localization/ro.json
+++ b/Apptag/Localization/ro.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Schema inteligentă inițială a sistemului",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/ru.json b/Apptag/Localization/ru.json
index a778443..6123e30 100644
--- a/Apptag/Localization/ru.json
+++ b/Apptag/Localization/ru.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Интеллектуальная начальная схема системы",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/sr-Cyrl.json b/Apptag/Localization/sr-Cyrl.json
index ca61f58..97a8b71 100644
--- a/Apptag/Localization/sr-Cyrl.json
+++ b/Apptag/Localization/sr-Cyrl.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Паметна почетна шема система",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/sv.json b/Apptag/Localization/sv.json
index 5c49131..e84b55c 100644
--- a/Apptag/Localization/sv.json
+++ b/Apptag/Localization/sv.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Systemets smarta startschema",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/th.json b/Apptag/Localization/th.json
index 4d4c8f6..80a48ac 100644
--- a/Apptag/Localization/th.json
+++ b/Apptag/Localization/th.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "ชุดเริ่มต้นอัจฉริยะของระบบ",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/tr.json b/Apptag/Localization/tr.json
index 9be388a..7415cb1 100644
--- a/Apptag/Localization/tr.json
+++ b/Apptag/Localization/tr.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Sistem akıllı başlangıç şeması",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/uk.json b/Apptag/Localization/uk.json
index a8e5d2b..4b725ef 100644
--- a/Apptag/Localization/uk.json
+++ b/Apptag/Localization/uk.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Розумна початкова схема системи",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/vi.json b/Apptag/Localization/vi.json
index af84dbf..a168d4b 100644
--- a/Apptag/Localization/vi.json
+++ b/Apptag/Localization/vi.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "Sơ đồ khởi tạo thông minh của hệ thống",
"settings.bubbleDisplayScope": "Bubble tips show for:",
"settings.bubbleAllApps": "All apps",
- "settings.bubbleUncommonOnly": "Uncommon apps only"
+ "settings.bubbleUncommonOnly": "Uncommon apps only",
+ "quickSearch.title": "Quick Search",
+ "quickSearch.placeholder": "Search apps, tags, and notes",
+ "quickSearch.inputAccessibility": "Quick Search input",
+ "quickSearch.emptyPrompt": "Start typing to search apps, tags, and notes.",
+ "quickSearch.noResults": "No apps found.",
+ "quickSearch.launchFailed": "Unable to open this app. Please try again or choose another app.",
+ "quickSearch.tagPrefix": "Tag",
+ "quickSearch.hotkeys": "Keyboard shortcuts",
+ "quickSearch.mainHotkey": "App list",
+ "quickSearch.mainHotkeyDesc": "Opens the TagLauncher app list.",
+ "quickSearch.directHotkey": "Quick Search (global)",
+ "quickSearch.directHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.record": "Change",
+ "quickSearch.recording": "Recording",
+ "quickSearch.clear": "Clear",
+ "quickSearch.restoreDefault": "Restore default",
+ "quickSearch.retry": "Retry registration",
+ "quickSearch.recordingHint": "Press the new global shortcut. Press Esc to cancel.",
+ "quickSearch.disabled": "Disabled",
+ "quickSearch.blocked": "Blocked",
+ "quickSearch.previousStillActive": "Previous shortcut remains active:",
+ "quickSearch.status.Active": "Active",
+ "quickSearch.status.Conflict": "Conflict",
+ "quickSearch.status.Disabled": "Disabled",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space is used by Spotlight. Change Spotlight in macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space is used by the Finder search window. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space is used to switch to the previous input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space is used to switch to the next input source. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space is used for Emoji & Symbols. Change macOS Keyboard Shortcuts, or choose another shortcut.",
+ "quickSearch.hotkeyConflict.generic": "This shortcut is already used by macOS or another app. Choose another shortcut, or change the shortcut in the other app and retry.",
+ "quickSearch.loading": "Preparing search index…",
+ "quickSearch.openKeyboardSettings": "Open macOS Keyboard Shortcuts settings",
+ "quickSearch.internalHotkey": "Quick Search (inside TagLauncher)",
+ "quickSearch.internalHotkeyDesc": "Works only while the TagLauncher app list is open.",
+ "quickSearch.internalHotkeyStatus": "App list only",
+ "quickSearch.globalHotkey": "Quick Search (global)",
+ "quickSearch.globalHotkeyDesc": "Open Quick Search globally when the TagLauncher app list is not visible.",
+ "quickSearch.spaceDisplay": "Space",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space may already be used by macOS input source or Globe/Fn shortcuts. Open macOS Keyboard Shortcuts to change that system shortcut, or choose another shortcut here."
}
diff --git a/Apptag/Localization/zh-Hans.json b/Apptag/Localization/zh-Hans.json
index 4708a12..d3f85f9 100644
--- a/Apptag/Localization/zh-Hans.json
+++ b/Apptag/Localization/zh-Hans.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "系统智能化初始分类",
"settings.bubbleDisplayScope": "气泡提示框显示位置:",
"settings.bubbleAllApps": "全部应用",
- "settings.bubbleUncommonOnly": "仅不常用应用"
+ "settings.bubbleUncommonOnly": "仅不常用应用",
+ "quickSearch.title": "快捷搜索",
+ "quickSearch.placeholder": "搜索 App、标签和备注",
+ "quickSearch.inputAccessibility": "快捷搜索输入框",
+ "quickSearch.emptyPrompt": "开始输入以搜索 App、标签和备注。",
+ "quickSearch.noResults": "未找到 App。",
+ "quickSearch.launchFailed": "无法打开这个 App。请重试或选择其他 App。",
+ "quickSearch.tagPrefix": "标签",
+ "quickSearch.hotkeys": "快捷键",
+ "quickSearch.mainHotkey": "主界面",
+ "quickSearch.mainHotkeyDesc": "打开 TagLauncher 应用列表。",
+ "quickSearch.directHotkey": "快捷搜索(全局)",
+ "quickSearch.directHotkeyDesc": "不在 TagLauncher 主界面时,直接打开快捷搜索。全局有效。",
+ "quickSearch.record": "修改",
+ "quickSearch.recording": "录制中",
+ "quickSearch.clear": "清除",
+ "quickSearch.restoreDefault": "恢复默认",
+ "quickSearch.retry": "重试注册",
+ "quickSearch.recordingHint": "按下新的全局快捷键。按 Esc 取消。",
+ "quickSearch.disabled": "已关闭",
+ "quickSearch.blocked": "被阻塞",
+ "quickSearch.previousStillActive": "之前可用的快捷键仍保持可用:",
+ "quickSearch.status.Active": "已启用",
+ "quickSearch.status.Conflict": "冲突",
+ "quickSearch.status.Disabled": "已关闭",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space 已被 Spotlight 使用。请在 macOS 键盘快捷键中修改 Spotlight,或选择其他快捷键。",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space 已被 Finder 搜索窗口使用。请在 macOS 键盘快捷键中修改,或选择其他快捷键。",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space 已被“上一个输入法”使用。请在 macOS 键盘快捷键中修改,或选择其他快捷键。",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space 已被“下一个输入法”使用。请在 macOS 键盘快捷键中修改,或选择其他快捷键。",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space 已被“表情与符号”使用。请在 macOS 键盘快捷键中修改,或选择其他快捷键。",
+ "quickSearch.hotkeyConflict.generic": "这个快捷键已被 macOS 或其他 App 使用。请选择其他快捷键,或先修改其他 App 中的快捷键后再重试。",
+ "quickSearch.loading": "正在准备搜索索引…",
+ "quickSearch.openKeyboardSettings": "打开 macOS 键盘快捷键设置",
+ "quickSearch.internalHotkey": "快捷搜索(TagLauncher 内部)",
+ "quickSearch.internalHotkeyDesc": "仅在 TagLauncher 主界面有效。",
+ "quickSearch.internalHotkeyStatus": "仅主界面有效",
+ "quickSearch.globalHotkey": "快捷搜索(全局)",
+ "quickSearch.globalHotkeyDesc": "不在 TagLauncher 主界面时,直接打开快捷搜索。全局有效。",
+ "quickSearch.spaceDisplay": "Space(空格键)",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space 可能已被 macOS 的输入法或地球键快捷键占用。请打开 macOS 键盘快捷键设置关闭或改掉对应系统快捷键,或在这里选择其他组合。"
}
diff --git a/Apptag/Localization/zh-Hant.json b/Apptag/Localization/zh-Hant.json
index 66c44e1..61b776c 100644
--- a/Apptag/Localization/zh-Hant.json
+++ b/Apptag/Localization/zh-Hant.json
@@ -142,5 +142,44 @@
"scheme.systemSmartStart": "系統智慧化初始分類",
"settings.bubbleDisplayScope": "氣泡提示框顯示位置:",
"settings.bubbleAllApps": "全部 App",
- "settings.bubbleUncommonOnly": "僅不常用 App"
+ "settings.bubbleUncommonOnly": "僅不常用 App",
+ "quickSearch.title": "快捷搜尋",
+ "quickSearch.placeholder": "搜尋 App、標籤和備註",
+ "quickSearch.inputAccessibility": "快捷搜尋輸入框",
+ "quickSearch.emptyPrompt": "開始輸入以搜尋 App、標籤和備註。",
+ "quickSearch.noResults": "找不到 App。",
+ "quickSearch.launchFailed": "無法打開這個 App。請重試或選擇其他 App。",
+ "quickSearch.tagPrefix": "標籤",
+ "quickSearch.hotkeys": "快捷鍵",
+ "quickSearch.mainHotkey": "主介面",
+ "quickSearch.mainHotkeyDesc": "打開 TagLauncher 應用列表。",
+ "quickSearch.directHotkey": "快捷搜尋(全域)",
+ "quickSearch.directHotkeyDesc": "不在 TagLauncher 主介面時,直接打開快捷搜尋。全域有效。",
+ "quickSearch.record": "修改",
+ "quickSearch.recording": "錄製中",
+ "quickSearch.clear": "清除",
+ "quickSearch.restoreDefault": "恢復預設",
+ "quickSearch.retry": "重試註冊",
+ "quickSearch.recordingHint": "按下新的全域快捷鍵。按 Esc 取消。",
+ "quickSearch.disabled": "已關閉",
+ "quickSearch.blocked": "被阻塞",
+ "quickSearch.previousStillActive": "之前可用的快捷鍵仍保持可用:",
+ "quickSearch.status.Active": "已啟用",
+ "quickSearch.status.Conflict": "衝突",
+ "quickSearch.status.Disabled": "已關閉",
+ "quickSearch.hotkeyConflict.spotlight": "Command + Space 已被 Spotlight 使用。請在 macOS 鍵盤快捷鍵中修改 Spotlight,或選擇其他快捷鍵。",
+ "quickSearch.hotkeyConflict.finderSearch": "Option + Command + Space 已被 Finder 搜尋視窗使用。請在 macOS 鍵盤快捷鍵中修改,或選擇其他快捷鍵。",
+ "quickSearch.hotkeyConflict.previousInput": "Control + Space 已被「上一個輸入法」使用。請在 macOS 鍵盤快捷鍵中修改,或選擇其他快捷鍵。",
+ "quickSearch.hotkeyConflict.nextInput": "Control + Option + Space 已被「下一個輸入法」使用。請在 macOS 鍵盤快捷鍵中修改,或選擇其他快捷鍵。",
+ "quickSearch.hotkeyConflict.emoji": "Control + Command + Space 已被「表情與符號」使用。請在 macOS 鍵盤快捷鍵中修改,或選擇其他快捷鍵。",
+ "quickSearch.hotkeyConflict.generic": "這個快捷鍵已被 macOS 或其他 App 使用。請選擇其他快捷鍵,或先修改其他 App 中的快捷鍵後再重試。",
+ "quickSearch.loading": "正在準備搜尋索引…",
+ "quickSearch.openKeyboardSettings": "打開 macOS 鍵盤快捷鍵設定",
+ "quickSearch.internalHotkey": "快捷搜尋(TagLauncher 內部)",
+ "quickSearch.internalHotkeyDesc": "僅在 TagLauncher 主介面有效。",
+ "quickSearch.internalHotkeyStatus": "僅主介面有效",
+ "quickSearch.globalHotkey": "快捷搜尋(全域)",
+ "quickSearch.globalHotkeyDesc": "不在 TagLauncher 主介面時,直接打開快捷搜尋。全域有效。",
+ "quickSearch.spaceDisplay": "Space(空格鍵)",
+ "quickSearch.hotkeyConflict.fnSpace": "Fn + Space 可能已被 macOS 的輸入法或地球鍵快捷鍵佔用。請打開 macOS 鍵盤快捷鍵設定關閉或改掉對應系統快捷鍵,或在這裡選擇其他組合。"
}
diff --git a/Apptag/PreferencesView.swift b/Apptag/PreferencesView.swift
index d0a1843..16d4dee 100644
--- a/Apptag/PreferencesView.swift
+++ b/Apptag/PreferencesView.swift
@@ -31,6 +31,9 @@
@State private var categoryScheme = TagDatabase.CategorySchemeState()
@State private var isApplyingSystemScheme = false
@State private var showApplySystemSchemeConfirmation = false
+ @State private var recordingHotkeyKind: LauncherHotkeyKind? = nil
+ @State private var hotkeyCaptureMonitor: Any? = nil
+ @State private var hotkeyRefreshToken = 0
private func scanApps() {
DispatchQueue.global(qos: .userInitiated).async {
@@ -339,6 +342,130 @@
.contentShape(RoundedRectangle(cornerRadius: 8, style: .continuous))
}
+ private func applyHotkey(_ hotkey: LauncherHotkey?, for kind: LauncherHotkeyKind) {
+ (NSApp.delegate as? AppDelegate)?.applyHotkey(hotkey, for: kind)
+ hotkeyRefreshToken &+= 1
+ }
+
+ private func retryHotkey(_ kind: LauncherHotkeyKind) {
+ (NSApp.delegate as? AppDelegate)?.retryHotkeyRegistration(for: kind)
+ hotkeyRefreshToken &+= 1
+ }
+
+ private func openKeyboardShortcutsSettings() {
+ let urls = [
+ URL(string: "x-apple.systempreferences:com.apple.Keyboard-Settings.extension?Shortcuts"),
+ URL(string: "x-apple.systempreferences:com.apple.preference.keyboard?shortcuts")
+ ].compactMap { $0 }
+ guard let url = urls.first else { return }
+ NSWorkspace.shared.open(url)
+ }
+
+ private func hotkeySettingsPanel() -> some View {
+ VStack(alignment: .leading, spacing: 12) {
+ HStack {
+ Text(tr("quickSearch.hotkeys"))
+ .font(.system(size: 13, weight: .semibold))
+ .foregroundStyle(.secondary)
+ Spacer()
+ Button(action: openKeyboardShortcutsSettings) {
+ Label(tr("quickSearch.openKeyboardSettings"), systemImage: "keyboard")
+ .font(.system(size: 13, weight: .semibold))
+ .lineLimit(1)
+ }
+ .buttonStyle(.bordered)
+ .help(tr("quickSearch.openKeyboardSettings"))
+ .accessibilityLabel(tr("quickSearch.openKeyboardSettings"))
+ }
+
+ HotkeySettingRow(
+ title: tr("quickSearch.mainHotkey"),
+ description: tr("quickSearch.mainHotkeyDesc"),
+ kind: .main,
+ isRecording: recordingHotkeyKind == .main,
+ allowsEditing: false,
+ refreshToken: hotkeyRefreshToken,
+ onBeginRecording: { },
+ onRestoreDefault: nil,
+ onRetry: { },
+ onCancelRecording: cancelHotkeyRecording
+ )
+
+ StaticHotkeyInfoRow(
+ title: tr("quickSearch.internalHotkey"),
+ description: tr("quickSearch.internalHotkeyDesc"),
+ displayText: tr("quickSearch.spaceDisplay"),
+ statusText: tr("quickSearch.internalHotkeyStatus")
+ )
+
+ HotkeySettingRow(
+ title: tr("quickSearch.globalHotkey"),
+ description: tr("quickSearch.globalHotkeyDesc"),
+ kind: .quickSearch,
+ isRecording: recordingHotkeyKind == .quickSearch,
+ allowsEditing: true,
+ refreshToken: hotkeyRefreshToken,
+ onBeginRecording: { beginHotkeyRecording(.quickSearch) },
+ onRestoreDefault: { applyHotkey(.defaultQuickSearch, for: .quickSearch) },
+ onRetry: { retryHotkey(.quickSearch) },
+ onCancelRecording: cancelHotkeyRecording
+ )
+ }
+ .id(hotkeyRefreshToken)
+ }
+
+ private func beginHotkeyRecording(_ kind: LauncherHotkeyKind) {
+ removeHotkeyCaptureMonitor()
+ recordingHotkeyKind = kind
+ postHotkeyRecordingState(active: true)
+ hotkeyCaptureMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { event in
+ handleCapturedHotkeyEvent(event, for: kind)
+ }
+ }
+
+ private func handleCapturedHotkeyEvent(_ event: NSEvent, for kind: LauncherHotkeyKind) -> NSEvent? {
+ if event.keyCode == UInt16(kVK_Escape) {
+ cancelHotkeyRecording()
+ return nil
+ }
+ guard let hotkey = LauncherHotkey.from(event: event) else {
+ NSSound.beep()
+ return nil
+ }
+ finishHotkeyRecording()
+ applyHotkey(hotkey, for: kind)
+ return nil
+ }
+
+ private func cancelHotkeyRecording() {
+ finishHotkeyRecording()
+ }
+
+ private func finishHotkeyRecording() {
+ guard recordingHotkeyKind != nil || hotkeyCaptureMonitor != nil else { return }
+ recordingHotkeyKind = nil
+ removeHotkeyCaptureMonitor()
+ postHotkeyRecordingState(active: false)
+ }
+
+ private func removeHotkeyCaptureMonitor() {
+ if let hotkeyCaptureMonitor {
+ NSEvent.removeMonitor(hotkeyCaptureMonitor)
+ self.hotkeyCaptureMonitor = nil
+ }
+ }
+
+ private func postHotkeyRecordingState(active: Bool) {
+ NotificationCenter.default.post(
+ name: .tagLauncherModalInteractionChanged,
+ object: nil,
+ userInfo: [
+ "active": active,
+ "source": "hotkeyRecording"
+ ]
+ )
+ }
+
var body: some View {
ZStack {
TabView {
@@ -380,102 +507,112 @@
.frame(maxHeight: 250)
Spacer(minLength: 0)
- ShortcutHintView()
- .frame(maxWidth: .infinity, alignment: .center)
- .padding(.bottom, 2)
}
.frame(maxWidth: settingsContentWidth, alignment: .leading)
.tabItem { Label(tr("settings.language"), systemImage: "globe") }
.padding()
// Tab 2: General
- VStack(alignment: .leading, spacing: 0) {
- // Toggle row — centered as a rectangular block
- HStack(spacing: 20) {
- if AppDelegate.supportsLaunchAtLogin {
- Toggle(tr("settings.launchAtLogin"), isOn: $launchAtLogin)
- .onChange(of: launchAtLogin) { _, enabled in
- if enabled {
- AppDelegate.enableLaunchAtLogin()
- } else {
- AppDelegate.disableLaunchAtLogin()
+ ScrollView(.vertical, showsIndicators: true) {
+ VStack(alignment: .leading, spacing: 0) {
+ // Toggle row — centered as a rectangular block
+ HStack(spacing: 20) {
+ if AppDelegate.supportsLaunchAtLogin {
+ Toggle(tr("settings.launchAtLogin"), isOn: $launchAtLogin)
+ .onChange(of: launchAtLogin) { _, enabled in
+ if enabled {
+ AppDelegate.enableLaunchAtLogin()
+ } else {
+ AppDelegate.disableLaunchAtLogin()
+ }
+ }
+ }
+ Toggle(tr("settings.showInDock"), isOn: $showDockIcon)
+ .onChange(of: showDockIcon) { _, _ in
+ AppDelegate.refreshChromeSettings()
}
- }
- }
- Toggle(tr("settings.showInDock"), isOn: $showDockIcon)
- .onChange(of: showDockIcon) { _, _ in
- AppDelegate.refreshChromeSettings()
+ Toggle(tr("settings.hideAppNames"), isOn: $hideAppNames)
}
- Toggle(tr("settings.hideAppNames"), isOn: $hideAppNames)
- }
- .frame(maxWidth: .infinity, alignment: .center)
- .padding(.bottom, 16)
+ .frame(maxWidth: .infinity, alignment: .center)
+ .padding(.bottom, 16)
- Divider()
- .padding(.bottom, 16)
+ Divider()
+ .padding(.bottom, 16)
- VStack(spacing: 18) {
- generalSettingRow(tr("settings.appListStyle"), description: tr("settings.flatDesc")) {
- VStack(spacing: 8) {
- DisplayModeOptionButton(
- mode: "flat",
- title: tr("settings.flat"),
- isSelected: displayMode == "flat"
- ) {
- displayMode = "flat"
- }
-
- LazyVGrid(columns: displayModeColumns, alignment: .leading, spacing: 8) {
- ForEach(containerDisplayModeOptions, id: \.id) { option in
+ VStack(spacing: 18) {
+ generalSettingRow(tr("settings.appListStyle"), description: tr("settings.flatDesc")) {
+ VStack(spacing: 8) {
DisplayModeOptionButton(
- mode: option.id,
- title: option.title,
- isSelected: displayMode == option.id
+ mode: "flat",
+ title: tr("settings.flat"),
+ isSelected: displayMode == "flat"
) {
- displayMode = option.id
+ displayMode = "flat"
+ }
+
+ LazyVGrid(columns: displayModeColumns, alignment: .leading, spacing: 8) {
+ ForEach(containerDisplayModeOptions, id: \.id) { option in
+ DisplayModeOptionButton(
+ mode: option.id,
+ title: option.title,
+ isSelected: displayMode == option.id
+ ) {
+ displayMode = option.id
+ }
+ }
}
}
+ .frame(width: generalControlWidth, alignment: .leading)
+ }
+
+ generalSettingRow(tr("settings.tagPosition"), description: tr("settings.tagPosDesc")) {
+ Picker("", selection: $tagPosition) {
+ Text(tr("settings.left")).tag("left")
+ Text(tr("settings.right")).tag("right")
+ Text(tr("settings.top")).tag("top")
+ }
+ .pickerStyle(.segmented)
+ .frame(width: compactPickerWidth, alignment: .leading)
+ }
+
+ generalSettingRow(tr("settings.tagFontSize"), description: tr("settings.tagFontDesc")) {
+ Picker("", selection: $tagFontSize) {
+ ForEach([16.0, 18.0, 20.0, 22.0, 24.0, 26.0], id: \.self) { size in
+ Text("\(Int(size))").tag(size)
+ }
+ }
+ .pickerStyle(.segmented)
+ .frame(width: compactPickerWidth, alignment: .leading)
+ }
+
+ generalSettingRow(tr("settings.iconSize"), description: tr("settings.iconSizeDesc")) {
+ Picker("", selection: $iconSize) {
+ ForEach([40.0, 48.0, 56.0, 64.0, 72.0, 80.0], id: \.self) { size in
+ Text("\(Int(size))").tag(size)
+ }
+ }
+ .pickerStyle(.segmented)
+ .frame(width: compactPickerWidth, alignment: .leading)
}
}
- .frame(width: generalControlWidth, alignment: .leading)
}
-
- generalSettingRow(tr("settings.tagPosition"), description: tr("settings.tagPosDesc")) {
- Picker("", selection: $tagPosition) {
- Text(tr("settings.left")).tag("left")
- Text(tr("settings.right")).tag("right")
- Text(tr("settings.top")).tag("top")
- }
- .pickerStyle(.segmented)
- .frame(width: compactPickerWidth, alignment: .leading)
- }
-
- generalSettingRow(tr("settings.tagFontSize"), description: tr("settings.tagFontDesc")) {
- Picker("", selection: $tagFontSize) {
- ForEach([16.0, 18.0, 20.0, 22.0, 24.0, 26.0], id: \.self) { size in
- Text("\(Int(size))").tag(size)
- }
- }
- .pickerStyle(.segmented)
- .frame(width: compactPickerWidth, alignment: .leading)
- }
-
- generalSettingRow(tr("settings.iconSize"), description: tr("settings.iconSizeDesc")) {
- Picker("", selection: $iconSize) {
- ForEach([40.0, 48.0, 56.0, 64.0, 72.0, 80.0], id: \.self) { size in
- Text("\(Int(size))").tag(size)
- }
- }
- .pickerStyle(.segmented)
- .frame(width: compactPickerWidth, alignment: .leading)
- }
+ .frame(maxWidth: generalContentWidth, alignment: .center)
+ .padding()
}
- }
- .frame(maxWidth: generalContentWidth, alignment: .center)
- .padding()
- .tabItem { Label(tr("settings.general"), systemImage: "gearshape") }
+ .tabItem { Label(tr("settings.general"), systemImage: "gearshape") }
- // Tab 3: Tags
+ // Tab 3: Hotkeys
+ ScrollView(.vertical, showsIndicators: true) {
+ VStack(alignment: .leading, spacing: 16) {
+ hotkeySettingsPanel()
+ .frame(width: generalContentWidth, alignment: .leading)
+ }
+ .frame(maxWidth: generalContentWidth, alignment: .center)
+ .padding()
+ }
+ .tabItem { Label(tr("quickSearch.hotkeys"), systemImage: "keyboard") }
+
+ // Tab 4: Tags
VStack(spacing: 0) {
TagEditorView(
tagColors: $tagColors,
@@ -486,7 +623,7 @@
.tabItem { Label(tr("settings.tags"), systemImage: "tag.fill") }
.onAppear { scanApps() }
- // Tab 4: Data
+ // Tab 5: Data
VStack(spacing: 0) {
Spacer(minLength: 32)
@@ -626,7 +763,7 @@
.padding()
.onAppear { refreshDataState() }
- // Tab 5: About
+ // Tab 6: About
VStack(spacing: 0) {
Spacer(minLength: 72)
@@ -661,8 +798,6 @@
.frame(width: 560, alignment: .leading)
Spacer(minLength: 30)
- ShortcutHintView()
- .frame(maxWidth: .infinity, alignment: .center)
Spacer(minLength: 18)
}
.tabItem { Label(tr("settings.about"), systemImage: "info.circle") }
@@ -683,6 +818,9 @@
}
.onReceive(NotificationCenter.default.publisher(for: .tagLauncherDataDidChange)) { _ in
refreshDataState()
+ }
+ .onReceive(NotificationCenter.default.publisher(for: .tagLauncherHotkeysChanged)) { _ in
+ hotkeyRefreshToken &+= 1
}
if isRefreshingLanguage {
@@ -718,6 +856,9 @@
.onAppear {
syncSelectedLanguage()
}
+ .onDisappear {
+ finishHotkeyRecording()
+ }
}
private func showLanguageRefresh() {
@@ -727,6 +868,250 @@
isRefreshingLanguage = false
}
}
+ }
+}
+
+private struct StaticHotkeyInfoRow: View {
+ let title: String
+ let description: String
+ let displayText: String
+ let statusText: String
+
+ var body: some View {
+ VStack(alignment: .leading, spacing: 12) {
+ HStack(alignment: .top, spacing: 12) {
+ VStack(alignment: .leading, spacing: 3) {
+ Text(title)
+ .font(.system(size: 13, weight: .semibold))
+ Text(description)
+ .font(.caption)
+ .foregroundStyle(.secondary)
+ .fixedSize(horizontal: false, vertical: true)
+ }
+ .frame(maxWidth: .infinity, alignment: .leading)
+
+ VStack(alignment: .leading, spacing: 4) {
+ Text(displayText)
+ .font(.system(size: 15, weight: .semibold))
+ .lineLimit(1)
+ .minimumScaleFactor(0.85)
+ Text(statusText)
+ .font(.system(size: 11, weight: .semibold))
+ .foregroundStyle(Color.secondary)
+ .padding(.horizontal, 8)
+ .padding(.vertical, 3)
+ .background(Capsule().fill(Color.secondary.opacity(0.13)))
+ }
+ .frame(width: 180, alignment: .leading)
+ }
+ }
+ .padding(12)
+ .background(
+ RoundedRectangle(cornerRadius: 8, style: .continuous)
+ .fill(Color(nsColor: .controlBackgroundColor))
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 8, style: .continuous)
+ .stroke(Color.secondary.opacity(0.14), lineWidth: 1)
+ )
+ }
+}
+
+private struct HotkeySettingRow: View {
+ let title: String
+ let description: String
+ let kind: LauncherHotkeyKind
+ let isRecording: Bool
+ let allowsEditing: Bool
+ let refreshToken: Int
+ let onBeginRecording: () -> Void
+ let onRestoreDefault: (() -> Void)?
+ let onRetry: () -> Void
+ let onCancelRecording: () -> Void
+
+ private var activeHotkey: LauncherHotkey? {
+ _ = refreshToken
+ return LauncherHotkeyStore.hotkey(for: kind)
+ }
+
+ private var pendingHotkey: LauncherHotkey? {
+ _ = refreshToken
+ return LauncherHotkeyStore.pendingHotkey(for: kind)
+ }
+
+ private var status: LauncherHotkeyStatus {
+ _ = refreshToken
+ return LauncherHotkeyStore.status(for: kind)
+ }
+
+ private var conflictMessage: String {
+ _ = refreshToken
+ return LauncherHotkeyStore.conflictMessage(for: kind)
+ }
+
+ var body: some View {
+ VStack(alignment: .leading, spacing: 12) {
+ HStack(alignment: .top, spacing: 12) {
+ VStack(alignment: .leading, spacing: 3) {
+ Text(title)
+ .font(.system(size: 13, weight: .semibold))
+ Text(description)
+ .font(.caption)
+ .foregroundStyle(.secondary)
+ .fixedSize(horizontal: false, vertical: true)
+ }
+ .frame(maxWidth: .infinity, alignment: .leading)
+
+ VStack(alignment: .leading, spacing: 4) {
+ Text(displayText)
+ .font(.system(size: 15, weight: .semibold))
+ .lineLimit(1)
+ .minimumScaleFactor(0.85)
+ statusLabel
+ }
+ .frame(width: 180, alignment: .leading)
+ }
+
+ if allowsEditing {
+ HStack(spacing: 8) {
+ hotkeyButton(
+ isRecording ? tr("quickSearch.recording") : tr("quickSearch.record"),
+ systemImage: isRecording ? "keyboard.badge.ellipsis" : "keyboard",
+ prominent: true,
+ action: onBeginRecording
+ )
+ if let onRestoreDefault {
+ hotkeyButton(
+ tr("quickSearch.restoreDefault"),
+ systemImage: "arrow.counterclockwise",
+ prominent: false,
+ action: onRestoreDefault
+ )
+ }
+ if status == .conflict {
+ hotkeyButton(
+ tr("quickSearch.retry"),
+ systemImage: "arrow.clockwise",
+ prominent: false,
+ action: onRetry
+ )
+ }
+ Spacer(minLength: 0)
+ }
+ }
+
+ if allowsEditing && isRecording {
+ HStack(spacing: 8) {
+ Image(systemName: "keyboard")
+ Text(tr("quickSearch.recordingHint"))
+ Spacer(minLength: 0)
+ Button(tr("settings.cancel"), action: onCancelRecording)
+ .buttonStyle(.borderless)
+ }
+ .font(.system(size: 13, weight: .semibold))
+ .foregroundStyle(Color.accentColor)
+ .padding(.horizontal, 10)
+ .padding(.vertical, 10)
+ .background(RoundedRectangle(cornerRadius: 7).fill(Color.accentColor.opacity(0.10)))
+ }
+
+ if status == .conflict && !conflictMessage.isEmpty {
+ HotkeyConflictNotice(text: conflictDetailText)
+ }
+ }
+ .padding(12)
+ .background(
+ RoundedRectangle(cornerRadius: 8, style: .continuous)
+ .fill(Color(nsColor: .controlBackgroundColor))
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 8, style: .continuous)
+ .stroke(Color.secondary.opacity(0.14), lineWidth: 1)
+ )
+ }
+
+ private var displayText: String {
+ if status == .conflict, let pendingHotkey {
+ return "\(pendingHotkey.displayString) · \(tr("quickSearch.blocked"))"
+ }
+ return activeHotkey?.displayString ?? tr("quickSearch.disabled")
+ }
+
+ private var conflictDetailText: String {
+ if let activeHotkey {
+ return "\(conflictMessage) \(tr("quickSearch.previousStillActive")) \(activeHotkey.displayString)"
+ }
+ return conflictMessage
+ }
+
+ private var statusLabel: some View {
+ Text(tr("quickSearch.status.\(status.rawValue)"))
+ .font(.system(size: 11, weight: .semibold))
+ .foregroundStyle(statusColor)
+ .padding(.horizontal, 8)
+ .padding(.vertical, 3)
+ .background(Capsule().fill(statusColor.opacity(0.13)))
+ }
+
+ private var statusColor: Color {
+ switch status {
+ case .active: return .green
+ case .conflict: return .orange
+ case .disabled: return .secondary
+ }
+ }
+
+ private func hotkeyButton(
+ _ title: String,
+ systemImage: String,
+ prominent: Bool,
+ action: @escaping () -> Void
+ ) -> some View {
+ Button(action: action) {
+ Label(title, systemImage: systemImage)
+ .font(.system(size: 13, weight: .semibold))
+ .lineLimit(1)
+ .minimumScaleFactor(0.88)
+ .foregroundStyle(prominent ? Color.white : Color.primary)
+ .padding(.horizontal, 10)
+ .frame(height: 30)
+ .background(
+ RoundedRectangle(cornerRadius: 7, style: .continuous)
+ .fill(prominent ? Color.accentColor : Color(nsColor: .controlBackgroundColor))
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 7, style: .continuous)
+ .stroke(prominent ? Color.accentColor.opacity(0.24) : Color.secondary.opacity(0.22), lineWidth: 1)
+ )
+ }
+ .buttonStyle(.plain)
+ .help(title)
+ .accessibilityLabel(title)
+ }
+}
+
+private struct HotkeyConflictNotice: View {
+ let text: String
+
+ var body: some View {
+ HStack(alignment: .top, spacing: 8) {
+ Image(systemName: "exclamationmark.triangle.fill")
+ .font(.system(size: 13, weight: .semibold))
+ .foregroundStyle(Color.white.opacity(0.94))
+ .padding(.top, 1)
+ Text(text)
+ .font(.system(size: 12, weight: .medium))
+ .foregroundStyle(Color.white)
+ .fixedSize(horizontal: false, vertical: true)
+ Spacer(minLength: 0)
+ }
+ .padding(.horizontal, 10)
+ .padding(.vertical, 9)
+ .background(
+ RoundedRectangle(cornerRadius: 8, style: .continuous)
+ .fill(Color.black.opacity(0.92))
+ )
+ .shadow(color: .black.opacity(0.24), radius: 16, y: 8)
}
}
@@ -924,60 +1309,5 @@
}
let palette: [Color] = [.green, .purple, .blue, .orange]
return palette[index % palette.count].opacity(0.78)
- }
-}
-
-private struct ShortcutHintView: View {
- var body: some View {
- HStack(spacing: 12) {
- ShortcutKeycap(symbol: "⌥")
- Text("+")
- .font(.system(size: 22, weight: .semibold))
- .foregroundStyle(.tertiary)
- ShortcutKeycap(symbol: "⇧")
- Text("+")
- .font(.system(size: 22, weight: .semibold))
- .foregroundStyle(.tertiary)
- SpacebarKeycap()
- }
- .accessibilityLabel("⌥ ⇧ Space")
- .allowsHitTesting(false)
- }
-}
-
-private struct ShortcutKeycap: View {
- let symbol: String
-
- var body: some View {
- Text(symbol)
- .font(.system(size: 36, weight: .medium))
- .foregroundStyle(.secondary)
- .frame(width: 74, height: 58)
- .background(KeycapBackground())
- }
-}
-
-private struct SpacebarKeycap: View {
- var body: some View {
- ZStack {
- KeycapBackground()
- Capsule()
- .fill(Color.secondary.opacity(0.38))
- .frame(width: 72, height: 3)
- .offset(y: 13)
- }
- .frame(width: 190, height: 58)
- }
-}
-
-private struct KeycapBackground: View {
- var body: some View {
- RoundedRectangle(cornerRadius: 10, style: .continuous)
- .fill(Color(nsColor: .controlBackgroundColor))
- .overlay(
- RoundedRectangle(cornerRadius: 10, style: .continuous)
- .stroke(Color.primary.opacity(0.12), lineWidth: 1)
- )
- .shadow(color: .black.opacity(0.10), radius: 8, x: 0, y: 3)
}
}
diff --git a/Apptag/QuickSearch.swift b/Apptag/QuickSearch.swift
new file mode 100644
index 0000000..679f4c6
--- /dev/null
+++ b/Apptag/QuickSearch.swift
@@ -0,0 +1,1080 @@
+import SwiftUI
+import AppKit
+import Carbon
+
+extension Notification.Name {
+ static let tagLauncherQuickSearchRequested = Notification.Name("TagLauncherQuickSearchRequested")
+ static let tagLauncherQuickSearchDismissRequested = Notification.Name("TagLauncherQuickSearchDismissRequested")
+ static let tagLauncherQuickSearchVisibilityChanged = Notification.Name("TagLauncherQuickSearchVisibilityChanged")
+ static let tagLauncherHotkeysChanged = Notification.Name("TagLauncherHotkeysChanged")
+}
+
+enum QuickSearchOpenSource {
+ static let mainOverlay = "mainOverlay"
+ static let globalHidden = "globalHidden"
+ static let globalVisible = "globalVisible"
+}
+
+// MARK: - Hotkeys
+
+struct LauncherHotkey: Equatable {
+ let keyCode: UInt32
+ let modifiers: UInt32
+
+ var serialized: String {
+ "\(keyCode):\(modifiers)"
+ }
+
+ var displayString: String {
+ let ordered: [(UInt32, String)] = [
+ (UInt32(controlKey), "⌃"),
+ (UInt32(optionKey), "⌥"),
+ (UInt32(shiftKey), "⇧"),
+ (UInt32(cmdKey), "⌘"),
+ (UInt32(kEventKeyModifierFnMask), "Fn+")
+ ]
+ let modifierGlyphs = ordered
+ .filter { modifiers & $0.0 != 0 }
+ .map(\.1)
+ .joined()
+ return modifierGlyphs + LauncherHotkey.keyDisplayName(for: keyCode)
+ }
+
+ static var defaultMain: LauncherHotkey {
+ LauncherHotkey(keyCode: UInt32(kVK_Space), modifiers: UInt32(shiftKey | optionKey))
+ }
+
+ static var defaultQuickSearch: LauncherHotkey {
+ LauncherHotkey(keyCode: UInt32(kVK_Space), modifiers: UInt32(kEventKeyModifierFnMask))
+ }
+
+ static func deserialize(_ value: String?) -> LauncherHotkey? {
+ guard let value, !value.isEmpty else { return nil }
+ let parts = value.split(separator: ":")
+ guard parts.count == 2,
+ let keyCode = UInt32(parts[0]),
+ let modifiers = UInt32(parts[1])
+ else { return nil }
+ return LauncherHotkey(keyCode: keyCode, modifiers: modifiers)
+ }
+
+ static func from(event: NSEvent) -> LauncherHotkey? {
+ let ignoredKeyCodes: Set<UInt16> = [54, 55, 56, 57, 58, 59, 60, 61, 62, 63]
+ guard !ignoredKeyCodes.contains(event.keyCode) else { return nil }
+
+ var carbonModifiers: UInt32 = 0
+ if event.modifierFlags.contains(.shift) { carbonModifiers |= UInt32(shiftKey) }
+ if event.modifierFlags.contains(.option) { carbonModifiers |= UInt32(optionKey) }
+ if event.modifierFlags.contains(.control) { carbonModifiers |= UInt32(controlKey) }
+ if event.modifierFlags.contains(.command) { carbonModifiers |= UInt32(cmdKey) }
+ if event.modifierFlags.contains(.function) { carbonModifiers |= UInt32(kEventKeyModifierFnMask) }
+ guard carbonModifiers != 0 else { return nil }
+
+ return LauncherHotkey(keyCode: UInt32(event.keyCode), modifiers: carbonModifiers)
+ }
+
+ static func keyDisplayName(for keyCode: UInt32) -> String {
+ switch Int(keyCode) {
+ case kVK_Space: return "Space"
+ case kVK_Return: return "Return"
+ case kVK_Escape: return "Esc"
+ case kVK_Delete: return "Delete"
+ case kVK_Tab: return "Tab"
+ case kVK_LeftArrow: return "←"
+ case kVK_RightArrow: return "→"
+ case kVK_UpArrow: return "↑"
+ case kVK_DownArrow: return "↓"
+ case kVK_F1: return "F1"
+ case kVK_F2: return "F2"
+ case kVK_F3: return "F3"
+ case kVK_F4: return "F4"
+ case kVK_F5: return "F5"
+ case kVK_F6: return "F6"
+ case kVK_F7: return "F7"
+ case kVK_F8: return "F8"
+ case kVK_F9: return "F9"
+ case kVK_F10: return "F10"
+ case kVK_F11: return "F11"
+ case kVK_F12: return "F12"
+ default:
+ if let scalar = keyCodeToPrintableScalar[Int(keyCode)] {
+ return String(scalar)
+ }
+ return "Key \(keyCode)"
+ }
+ }
+
+ private static let keyCodeToPrintableScalar: [Int: Character] = [
+ kVK_ANSI_A: "A", kVK_ANSI_B: "B", kVK_ANSI_C: "C", kVK_ANSI_D: "D",
+ kVK_ANSI_E: "E", kVK_ANSI_F: "F", kVK_ANSI_G: "G", kVK_ANSI_H: "H",
+ kVK_ANSI_I: "I", kVK_ANSI_J: "J", kVK_ANSI_K: "K", kVK_ANSI_L: "L",
+ kVK_ANSI_M: "M", kVK_ANSI_N: "N", kVK_ANSI_O: "O", kVK_ANSI_P: "P",
+ kVK_ANSI_Q: "Q", kVK_ANSI_R: "R", kVK_ANSI_S: "S", kVK_ANSI_T: "T",
+ kVK_ANSI_U: "U", kVK_ANSI_V: "V", kVK_ANSI_W: "W", kVK_ANSI_X: "X",
+ kVK_ANSI_Y: "Y", kVK_ANSI_Z: "Z", kVK_ANSI_0: "0", kVK_ANSI_1: "1",
+ kVK_ANSI_2: "2", kVK_ANSI_3: "3", kVK_ANSI_4: "4", kVK_ANSI_5: "5",
+ kVK_ANSI_6: "6", kVK_ANSI_7: "7", kVK_ANSI_8: "8", kVK_ANSI_9: "9"
+ ]
+}
+
+enum LauncherHotkeyKind: String {
+ case main
+ case quickSearch
+
+ var storageKey: String {
+ switch self {
+ case .main: return "mainHotkey"
+ case .quickSearch: return "quickSearchHotkey"
+ }
+ }
+
+ var statusKey: String {
+ switch self {
+ case .main: return "mainHotkeyStatus"
+ case .quickSearch: return "quickSearchHotkeyStatus"
+ }
+ }
+
+ var conflictMessageKey: String {
+ switch self {
+ case .main: return "mainHotkeyConflictMessage"
+ case .quickSearch: return "quickSearchHotkeyConflictMessage"
+ }
+ }
+
+ var pendingStorageKey: String {
+ switch self {
+ case .main: return "mainHotkeyPending"
+ case .quickSearch: return "quickSearchHotkeyPending"
+ }
+ }
+
+ var eventID: UInt32 {
+ switch self {
+ case .main: return 1
+ case .quickSearch: return 2
+ }
+ }
+}
+
+enum LauncherHotkeyStatus: String {
+ case active = "Active"
+ case conflict = "Conflict"
+ case disabled = "Disabled"
+}
+
+enum LauncherHotkeyStore {
+ static func hotkey(for kind: LauncherHotkeyKind) -> LauncherHotkey? {
+ if kind == .main {
+ return .defaultMain
+ }
+ if kind == .quickSearch && !AppDefaults.hasStoredValue(for: kind.storageKey) {
+ return .defaultQuickSearch
+ }
+ return LauncherHotkey.deserialize(UserDefaults.standard.string(forKey: kind.storageKey))
+ }
+
+ static func save(_ hotkey: LauncherHotkey?, for kind: LauncherHotkeyKind) {
+ UserDefaults.standard.set(hotkey?.serialized ?? "", forKey: kind.storageKey)
+ UserDefaults.standard.removeObject(forKey: kind.pendingStorageKey)
+ }
+
+ static func savePending(_ hotkey: LauncherHotkey, for kind: LauncherHotkeyKind) {
+ UserDefaults.standard.set(hotkey.serialized, forKey: kind.pendingStorageKey)
+ }
+
+ static func pendingHotkey(for kind: LauncherHotkeyKind) -> LauncherHotkey? {
+ LauncherHotkey.deserialize(UserDefaults.standard.string(forKey: kind.pendingStorageKey))
+ }
+
+ static func status(for kind: LauncherHotkeyKind) -> LauncherHotkeyStatus {
+ let raw = UserDefaults.standard.string(forKey: kind.statusKey)
+ return LauncherHotkeyStatus(rawValue: raw ?? "") ?? (hotkey(for: kind) == nil ? .disabled : .active)
+ }
+
+ static func setStatus(_ status: LauncherHotkeyStatus, message: String?, for kind: LauncherHotkeyKind) {
+ UserDefaults.standard.set(status.rawValue, forKey: kind.statusKey)
+ if let message, !message.isEmpty {
+ UserDefaults.standard.set(message, forKey: kind.conflictMessageKey)
+ } else {
+ UserDefaults.standard.removeObject(forKey: kind.conflictMessageKey)
+ }
+ NotificationCenter.default.post(name: .tagLauncherHotkeysChanged, object: nil)
+ }
+
+ static func conflictMessage(for kind: LauncherHotkeyKind) -> String {
+ UserDefaults.standard.string(forKey: kind.conflictMessageKey) ?? ""
+ }
+
+ static func knownConflictMessage(for hotkey: LauncherHotkey, status: OSStatus) -> String? {
+ guard status != noErr else { return nil }
+ switch (hotkey.keyCode, hotkey.modifiers) {
+ case (UInt32(kVK_Space), UInt32(cmdKey)):
+ return tr("quickSearch.hotkeyConflict.spotlight")
+ case (UInt32(kVK_Space), UInt32(optionKey | cmdKey)):
+ return tr("quickSearch.hotkeyConflict.finderSearch")
+ case (UInt32(kVK_Space), UInt32(controlKey)):
+ return tr("quickSearch.hotkeyConflict.previousInput")
+ case (UInt32(kVK_Space), UInt32(controlKey | optionKey)):
+ return tr("quickSearch.hotkeyConflict.nextInput")
+ case (UInt32(kVK_Space), UInt32(controlKey | cmdKey)):
+ return tr("quickSearch.hotkeyConflict.emoji")
+ case (UInt32(kVK_Space), UInt32(kEventKeyModifierFnMask)):
+ return tr("quickSearch.hotkeyConflict.fnSpace")
+ default:
+ return nil
+ }
+ }
+}
+
+// MARK: - Search Documents
+
+struct QuickSearchDocument: Identifiable {
+ var id: URL { app.id }
+ let app: AppInfo
+ let localizedNames: [String]
+ let internalBundleNames: [String]
+ let tagNames: [String]
+ let note: String
+ let bundleIdentifier: String
+ let lastOpenedAt: Date?
+ let openCount: Int
+}
+
+struct QuickSearchResult: Identifiable {
+ var id: URL { document.id }
+ let document: QuickSearchDocument
+ let finalScore: Double
+ let textScore: Double
+ let bestFieldRank: Int
+ let matchedTagName: String?
+ let noteSnippet: String?
+
+ var app: AppInfo { document.app }
+}
+
+enum QuickSearchEngine {
+ private enum FieldKind: Int {
+ case name = 0
+ case tag = 1
+ case note = 2
+ case bundleIdentifier = 3
+ case internalBundleName = 4
+
+ var weight: Double {
+ switch self {
+ case .name: return 100
+ case .tag: return 70
+ case .note: return 45
+ case .internalBundleName: return 30
+ case .bundleIdentifier: return 20
+ }
+ }
+ }
+
+ private enum MatchKind {
+ case exact
+ case prefix
+ case substring
+ case acronym
+ case fuzzy
+
+ var weight: Double {
+ switch self {
+ case .exact: return 100
+ case .prefix: return 80
+ case .substring: return 60
+ case .acronym: return 55
+ case .fuzzy: return 35
+ }
+ }
+ }
+
+ private struct Field {
+ let kind: FieldKind
+ let text: String
+ let normalized: String
+ let acronym: String
+ let pinyinCandidates: [String]
+ }
+
+ private struct MatchOptions {
+ let allowSubstring: Bool
+ let allowFuzzySubsequence: Bool
+ }
+
+ private struct TokenMatch {
+ let score: Double
+ let fieldRank: Int
+ let fieldKind: FieldKind
+ let originalText: String
+ }
+
+ static func makeDocuments(apps: [AppInfo], store: TagDatabase.Store) -> [QuickSearchDocument] {
+ apps.map { app in
+ QuickSearchDocument(
+ app: app,
+ localizedNames: localizedNames(for: app),
+ internalBundleNames: internalBundleNames(for: app),
+ tagNames: app.tags,
+ note: store.appNotes[app.path.path] ?? app.note ?? "",
+ bundleIdentifier: app.bundleIdentifier ?? "",
+ lastOpenedAt: store.appLastOpenedAt[app.path.path],
+ openCount: store.appOpenCounts[app.path.path] ?? 0
+ )
+ }
+ }
+
+ static func search(_ query: String, documents: [QuickSearchDocument], limit: Int = 50) -> [QuickSearchResult] {
+ let normalizedQuery = normalizeQuery(query)
+ guard !normalizedQuery.isEmpty else {
+ return emptyQueryResults(documents: documents, limit: min(limit, 6))
+ }
+
+ let tokens = normalizedQuery.split(separator: " ").map(String.init)
+ let results = documents.compactMap { result(for: $0, tokens: tokens) }
+ return results.sorted(by: rank).prefix(limit).map { $0 }
+ }
+
+ static func normalizeQuery(_ value: String) -> String {
+ value
+ .trimmingCharacters(in: .whitespacesAndNewlines)
+ .components(separatedBy: .whitespacesAndNewlines)
+ .filter { !$0.isEmpty }
+ .joined(separator: " ")
+ .folding(options: [.caseInsensitive, .diacriticInsensitive], locale: .current)
+ .lowercased()
+ }
+
+ private static func result(for document: QuickSearchDocument, tokens: [String]) -> QuickSearchResult? {
+ let fields = searchableFields(for: document)
+ var textScore: Double = 0
+ var bestFieldRank = Int.max
+ var matchedTagName: String?
+ var noteSnippet: String?
+
+ for token in tokens {
+ guard let tokenMatch = fields
+ .compactMap({ match(token: token, field: $0) })
+ .max(by: { $0.score < $1.score })
+ else {
+ return nil
+ }
+ textScore += tokenMatch.score
+ bestFieldRank = min(bestFieldRank, tokenMatch.fieldRank)
+ if tokenMatch.fieldKind == .tag {
+ matchedTagName = tokenMatch.originalText
+ } else if tokenMatch.fieldKind == .note {
+ noteSnippet = snippet(from: document.note, token: token)
+ }
+ }
+
+ let finalScore = textScore + behaviorBoost(for: document)
+ return QuickSearchResult(
+ document: document,
+ finalScore: finalScore,
+ textScore: textScore,
+ bestFieldRank: bestFieldRank,
+ matchedTagName: matchedTagName,
+ noteSnippet: noteSnippet
+ )
+ }
+
+ private static func searchableFields(for document: QuickSearchDocument) -> [Field] {
+ let names = uniqueOrdered([document.app.name] + document.localizedNames)
+ let nameFields = names.map {
+ Field(
+ kind: .name,
+ text: $0,
+ normalized: normalizeField($0),
+ acronym: acronym(for: $0),
+ pinyinCandidates: pinyinCandidates(for: $0)
+ )
+ }
+ let tagFields = document.tagNames.map {
+ Field(
+ kind: .tag,
+ text: $0,
+ normalized: normalizeField($0),
+ acronym: "",
+ pinyinCandidates: pinyinCandidates(for: $0)
+ )
+ }
+ let noteFields = document.note.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? [] : [
+ Field(
+ kind: .note,
+ text: document.note,
+ normalized: normalizeField(document.note),
+ acronym: "",
+ pinyinCandidates: pinyinCandidates(for: document.note)
+ )
+ ]
+ let bundleFields = document.bundleIdentifier.isEmpty ? [] : [
+ Field(
+ kind: .bundleIdentifier,
+ text: document.bundleIdentifier,
+ normalized: normalizeField(document.bundleIdentifier),
+ acronym: "",
+ pinyinCandidates: []
+ )
+ ]
+ let internalBundleNameFields = document.internalBundleNames.map {
+ Field(
+ kind: .internalBundleName,
+ text: $0,
+ normalized: normalizeField($0),
+ acronym: "",
+ pinyinCandidates: []
+ )
+ }
+ return nameFields + tagFields + noteFields + bundleFields + internalBundleNameFields
+ }
+
+ private static func match(token: String, field: Field) -> TokenMatch? {
+ guard let candidate = bestMatchCandidate(token: token, field: field) else { return nil }
+ let positionBoost = candidate.0 == .exact ? 0 : max(0, 10 - min(candidate.1, 10))
+ let score = field.kind.weight + candidate.0.weight + Double(positionBoost)
+ return TokenMatch(
+ score: score,
+ fieldRank: field.kind.rawValue,
+ fieldKind: field.kind,
+ originalText: field.text
+ )
+ }
+
+ private static func bestMatchCandidate(token: String, field: Field) -> (MatchKind, Int)? {
+ var candidates: [(MatchKind, Int)] = []
+
+ if let textCandidate = matchCandidate(
+ token: token,
+ normalized: field.normalized,
+ options: matchOptions(for: field.kind)
+ ) {
+ candidates.append(textCandidate)
+ }
+ if field.kind == .name && !field.acronym.isEmpty && field.acronym.hasPrefix(token) {
+ candidates.append((.acronym, 0))
+ }
+ for pinyin in field.pinyinCandidates {
+ if let pinyinCandidate = matchPinyinCandidate(token: token, normalized: pinyin) {
+ candidates.append(pinyinCandidate)
+ }
+ }
+
+ return candidates.max { lhs, rhs in
+ if lhs.0.weight != rhs.0.weight { return lhs.0.weight < rhs.0.weight }
+ return lhs.1 > rhs.1
+ }
+ }
+
+ private static func matchOptions(for fieldKind: FieldKind) -> MatchOptions {
+ switch fieldKind {
+ case .name:
+ return MatchOptions(allowSubstring: true, allowFuzzySubsequence: true)
+ case .tag:
+ return MatchOptions(allowSubstring: true, allowFuzzySubsequence: true)
+ case .note:
+ return MatchOptions(allowSubstring: true, allowFuzzySubsequence: false)
+ case .bundleIdentifier, .internalBundleName:
+ return MatchOptions(allowSubstring: false, allowFuzzySubsequence: false)
+ }
+ }
+
+ private static func matchCandidate(
+ token: String,
+ normalized: String,
+ options: MatchOptions
+ ) -> (MatchKind, Int)? {
+ guard !normalized.isEmpty else { return nil }
+ if normalized == token {
+ return (.exact, 0)
+ }
+ if normalized.hasPrefix(token) {
+ return (.prefix, 0)
+ }
+ if options.allowSubstring, let range = normalized.range(of: token) {
+ return (.substring, normalized.distance(from: normalized.startIndex, to: range.lowerBound))
+ }
+ if options.allowFuzzySubsequence, token.count >= 4, isSubsequence(token, of: normalized) {
+ return (.fuzzy, 10)
+ }
+ return nil
+ }
+
+ private static func matchPinyinCandidate(token: String, normalized: String) -> (MatchKind, Int)? {
+ guard !normalized.isEmpty else { return nil }
+ if normalized == token {
+ return (.exact, 0)
+ }
+ if normalized.hasPrefix(token) {
+ return (.prefix, 0)
+ }
+ return nil
+ }
+
+ private static func emptyQueryResults(documents: [QuickSearchDocument], limit: Int) -> [QuickSearchResult] {
+ var used = Set<URL>()
+ let recent = documents
+ .filter { $0.lastOpenedAt != nil }
+ .sorted {
+ if ($0.lastOpenedAt ?? .distantPast) != ($1.lastOpenedAt ?? .distantPast) {
+ return ($0.lastOpenedAt ?? .distantPast) > ($1.lastOpenedAt ?? .distantPast)
+ }
+ return $0.app.name.localizedStandardCompare($1.app.name) == .orderedAscending
+ }
+
+ let frequent = documents
+ .filter { $0.openCount > 0 }
+ .sorted {
+ if $0.openCount != $1.openCount { return $0.openCount > $1.openCount }
+ return $0.app.name.localizedStandardCompare($1.app.name) == .orderedAscending
+ }
+
+ let ordered = (recent + frequent).filter { used.insert($0.id).inserted }
+ return ordered.prefix(limit).map {
+ QuickSearchResult(
+ document: $0,
+ finalScore: behaviorBoost(for: $0),
+ textScore: 0,
+ bestFieldRank: Int.max,
+ matchedTagName: nil,
+ noteSnippet: nil
+ )
+ }
+ }
+
+ private static func rank(_ lhs: QuickSearchResult, _ rhs: QuickSearchResult) -> Bool {
+ if lhs.finalScore != rhs.finalScore { return lhs.finalScore > rhs.finalScore }
+ if lhs.textScore != rhs.textScore { return lhs.textScore > rhs.textScore }
+ if lhs.bestFieldRank != rhs.bestFieldRank { return lhs.bestFieldRank < rhs.bestFieldRank }
+ let leftDate = lhs.document.lastOpenedAt ?? .distantPast
+ let rightDate = rhs.document.lastOpenedAt ?? .distantPast
+ if leftDate != rightDate { return leftDate > rightDate }
+ if lhs.document.openCount != rhs.document.openCount {
+ return lhs.document.openCount > rhs.document.openCount
+ }
+ if lhs.app.name.count != rhs.app.name.count {
+ return lhs.app.name.count < rhs.app.name.count
+ }
+ return lhs.app.name.localizedStandardCompare(rhs.app.name) == .orderedAscending
+ }
+
+ private static func behaviorBoost(for document: QuickSearchDocument) -> Double {
+ min(recentBoost(for: document.lastOpenedAt) + frequencyBoost(for: document.openCount), 20)
+ }
+
+ private static func recentBoost(for date: Date?) -> Double {
+ guard let date else { return 0 }
+ let age = Date().timeIntervalSince(date)
+ if age <= 24 * 60 * 60 { return 15 }
+ if age <= 7 * 24 * 60 * 60 { return 10 }
+ if age <= 30 * 24 * 60 * 60 { return 5 }
+ return 2
+ }
+
+ private static func frequencyBoost(for openCount: Int) -> Double {
+ min(Double(openCount), 10)
+ }
+
+ private static func normalizeField(_ value: String) -> String {
+ value
+ .folding(options: [.caseInsensitive, .diacriticInsensitive], locale: .current)
+ .lowercased()
+ }
+
+ private static func pinyinCandidates(for value: String) -> [String] {
+ guard value.range(of: #"\p{Han}"#, options: .regularExpression) != nil else { return [] }
+ let mutable = NSMutableString(string: value)
+ CFStringTransform(mutable, nil, kCFStringTransformToLatin, false)
+ CFStringTransform(mutable, nil, kCFStringTransformStripCombiningMarks, false)
+
+ let spaced = normalizeField(mutable as String)
+ .components(separatedBy: CharacterSet.alphanumerics.inverted)
+ .filter { !$0.isEmpty }
+ .joined(separator: " ")
+ guard !spaced.isEmpty else { return [] }
+
+ let compact = spaced.replacingOccurrences(of: " ", with: "")
+ let initials = spaced
+ .split(separator: " ")
+ .compactMap(\.first)
+ .map(String.init)
+ .joined()
+ return uniqueOrdered([spaced, compact, initials].filter { !$0.isEmpty })
+ }
+
+ private static func isSubsequence(_ token: String, of value: String) -> Bool {
+ var searchStart = value.startIndex
+ for character in token {
+ guard let index = value[searchStart...].firstIndex(of: character) else { return false }
+ searchStart = value.index(after: index)
+ }
+ return true
+ }
+
+ private static func acronym(for value: String) -> String {
+ var parts: [Character] = []
+ var nextStartsWord = true
+ var previousWasLowercase = false
+ for character in value {
+ let current = String(character)
+ let isAlphanumeric = current.rangeOfCharacter(from: .alphanumerics) != nil
+ guard isAlphanumeric else {
+ nextStartsWord = true
+ previousWasLowercase = false
+ continue
+ }
+
+ let isUppercase = current.rangeOfCharacter(from: .uppercaseLetters) != nil
+ let isLowercase = current.rangeOfCharacter(from: .lowercaseLetters) != nil
+ if nextStartsWord || (isUppercase && previousWasLowercase) {
+ parts.append(character)
+ }
+ nextStartsWord = false
+ previousWasLowercase = isLowercase
+ }
+ return normalizeField(String(parts))
+ }
+
+ private static func localizedNames(for app: AppInfo) -> [String] {
+ guard let bundle = Bundle(url: app.path) else { return [] }
+ let values = [
+ bundle.localizedInfoDictionary?["CFBundleDisplayName"] as? String,
+ bundle.infoDictionary?["CFBundleDisplayName"] as? String,
+ FileManager.default.displayName(atPath: app.path.path).replacingOccurrences(of: ".app", with: "")
+ ]
+ return uniqueOrdered(values.compactMap { $0 }.compactMap { value in
+ let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
+ return trimmed.isEmpty || trimmed == app.name ? nil : trimmed
+ })
+ }
+
+ private static func internalBundleNames(for app: AppInfo) -> [String] {
+ guard let bundle = Bundle(url: app.path) else { return [] }
+ let values = [
+ bundle.localizedInfoDictionary?["CFBundleName"] as? String,
+ bundle.infoDictionary?["CFBundleName"] as? String
+ ]
+ return uniqueOrdered(values.compactMap { $0 }.compactMap { value in
+ let trimmed = value.trimmingCharacters(in: .whitespacesAndNewlines)
+ return trimmed.isEmpty || trimmed == app.name ? nil : trimmed
+ })
+ }
+
+ private static func snippet(from note: String, token: String) -> String {
+ let trimmed = note.trimmingCharacters(in: .whitespacesAndNewlines)
+ guard trimmed.count > 80 else { return trimmed }
+ return String(trimmed.prefix(77)) + "..."
+ }
+
+ private static func uniqueOrdered(_ values: [String]) -> [String] {
+ var seen = Set<String>()
+ return values.filter { seen.insert(normalizeField($0)).inserted }
+ }
+}
+
+// MARK: - Quick Search UI
+
+enum QuickSearchCommand {
+ case moveUp
+ case moveDown
+ case submit
+ case dismiss
+}
+
+struct QuickSearchOverlayView: View {
+ @Binding var query: String
+ let results: [QuickSearchResult]
+ let selectedID: URL?
+ let focusToken: Int
+ let isLoading: Bool
+ let maxVisibleRows: Int
+ let errorMessage: String?
+ let onCommand: (QuickSearchCommand) -> Void
+ let onHover: (QuickSearchResult) -> Void
+ let onLaunch: (QuickSearchResult) -> Void
+
+ private let panelWidth: CGFloat = 760
+ private let rowHeight: CGFloat = 74
+
+ var body: some View {
+ VStack(alignment: .leading, spacing: 0) {
+ HStack(spacing: 18) {
+ Image(systemName: "magnifyingglass")
+ .font(.system(size: 29, weight: .regular))
+ .foregroundStyle(Color.primary.opacity(0.48))
+ .frame(width: 34)
+
+ QuickSearchTextField(
+ text: $query,
+ placeholder: tr("quickSearch.placeholder"),
+ focusToken: focusToken,
+ onCommand: onCommand
+ )
+ .frame(height: 44)
+ }
+ .padding(.horizontal, 28)
+ .padding(.top, 22)
+ .padding(.bottom, 18)
+
+ Divider().opacity(0.35)
+
+ if isLoading {
+ HStack(spacing: 10) {
+ ProgressView()
+ .controlSize(.small)
+ Text(tr("quickSearch.loading"))
+ .font(.system(size: 15, weight: .medium))
+ .foregroundStyle(.secondary)
+ Spacer(minLength: 0)
+ }
+ .padding(.horizontal, 28)
+ .padding(.vertical, 24)
+ .frame(minHeight: 86)
+ } else if let errorMessage {
+ QuickSearchMessageRow(
+ systemImage: "exclamationmark.triangle.fill",
+ message: errorMessage,
+ tint: .orange
+ )
+ } else if results.isEmpty {
+ QuickSearchMessageRow(
+ systemImage: query.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? "keyboard" : "magnifyingglass",
+ message: query.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
+ ? tr("quickSearch.emptyPrompt")
+ : tr("quickSearch.noResults"),
+ tint: .secondary
+ )
+ } else {
+ ScrollViewReader { scrollProxy in
+ ScrollView(.vertical, showsIndicators: results.count > maxVisibleRows) {
+ LazyVStack(spacing: 2) {
+ ForEach(results) { result in
+ QuickSearchResultRow(
+ result: result,
+ isSelected: result.id == selectedID
+ )
+ .frame(height: rowHeight)
+ .id(result.id)
+ .contentShape(Rectangle())
+ .onHover { hovering in
+ if hovering { onHover(result) }
+ }
+ .onTapGesture {
+ onLaunch(result)
+ }
+ }
+ }
+ .padding(.horizontal, 10)
+ .padding(.vertical, 10)
+ }
+ .frame(height: CGFloat(min(results.count, maxVisibleRows)) * (rowHeight + 2) + 20)
+ .onChange(of: selectedID) { _, id in
+ guard let id else { return }
+ withAnimation(.easeOut(duration: 0.08)) {
+ scrollProxy.scrollTo(id, anchor: .center)
+ }
+ }
+ }
+ }
+ }
+ .frame(width: panelWidth)
+ .background(
+ RoundedRectangle(cornerRadius: 34, style: .continuous)
+ .fill(.regularMaterial)
+ .shadow(color: .black.opacity(0.18), radius: 36, y: 18)
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 34, style: .continuous)
+ .stroke(Color.primary.opacity(0.10), lineWidth: 1)
+ )
+ .accessibilityElement(children: .contain)
+ .accessibilityLabel(tr("quickSearch.title"))
+ }
+}
+
+private struct QuickSearchResultRow: View {
+ let result: QuickSearchResult
+ let isSelected: Bool
+ @Environment(\.colorScheme) private var colorScheme
+
+ var body: some View {
+ HStack(spacing: 16) {
+ Image(nsImage: result.app.icon)
+ .resizable()
+ .frame(width: 46, height: 46)
+ .cornerRadius(10)
+
+ VStack(alignment: .leading, spacing: 4) {
+ Text(result.app.name)
+ .font(.system(size: 20, weight: .semibold))
+ .foregroundStyle(.primary)
+ .lineLimit(1)
+ .truncationMode(.tail)
+
+ if let detailText {
+ Text(detailText)
+ .font(.system(size: 16, weight: .medium))
+ .foregroundStyle(Color.primary.opacity(0.38))
+ .lineLimit(1)
+ .truncationMode(.tail)
+ }
+ }
+ .layoutPriority(1)
+
+ Spacer(minLength: 8)
+
+ if let tagName = rightTagName {
+ Text(tagName)
+ .font(.system(size: 14, weight: .semibold))
+ .foregroundStyle(Color.primary.opacity(0.46))
+ .lineLimit(1)
+ .truncationMode(.tail)
+ .padding(.horizontal, 12)
+ .frame(height: 32)
+ .frame(maxWidth: 128)
+ .background(
+ Capsule(style: .continuous)
+ .fill(Color.primary.opacity(colorScheme == .dark ? 0.12 : 0.07))
+ )
+ }
+ }
+ .padding(.horizontal, 18)
+ .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .leading)
+ .background(
+ RoundedRectangle(cornerRadius: 18, style: .continuous)
+ .fill(isSelected ? selectedFill : Color.clear)
+ )
+ .accessibilityLabel(accessibilityText)
+ }
+
+ private var selectedFill: Color {
+ colorScheme == .dark ? Color.white.opacity(0.14) : Color.black.opacity(0.075)
+ }
+
+ private var detailText: String? {
+ if let note = result.noteSnippet, !note.isEmpty {
+ return note
+ }
+ let note = result.document.note.trimmingCharacters(in: .whitespacesAndNewlines)
+ if !note.isEmpty {
+ return note.count > 72 ? String(note.prefix(69)) + "..." : note
+ }
+ return nil
+ }
+
+ private var rightTagName: String? {
+ let tag = result.matchedTagName ?? result.document.tagNames.first
+ guard let tag, !tag.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return nil }
+ return tag
+ }
+
+ private var accessibilityText: String {
+ [result.app.name, detailText].compactMap { $0 }.joined(separator: ", ")
+ }
+}
+
+private struct QuickSearchMessageRow: View {
+ let systemImage: String
+ let message: String
+ let tint: Color
+
+ var body: some View {
+ HStack(spacing: 10) {
+ Image(systemName: systemImage)
+ .font(.system(size: 20, weight: .regular))
+ .foregroundStyle(tint)
+ .frame(width: 28)
+ Text(message)
+ .font(.system(size: 15, weight: .medium))
+ .foregroundStyle(.secondary)
+ .lineLimit(2)
+ .fixedSize(horizontal: false, vertical: true)
+ Spacer(minLength: 0)
+ }
+ .padding(.horizontal, 28)
+ .padding(.vertical, 24)
+ .frame(minHeight: 86)
+ .accessibilityLabel(message)
+ }
+}
+
+private struct QuickSearchTextField: NSViewRepresentable {
+ @Binding var text: String
+ let placeholder: String
+ let focusToken: Int
+ let onCommand: (QuickSearchCommand) -> Void
+
+ func makeNSView(context: Context) -> QuickSearchNativeTextField {
+ let field = QuickSearchNativeTextField()
+ field.isBordered = false
+ field.isBezeled = false
+ field.drawsBackground = false
+ field.focusRingType = .none
+ field.font = NSFont.systemFont(ofSize: 28, weight: .regular)
+ field.placeholderString = placeholder
+ field.delegate = context.coordinator
+ field.onCommand = onCommand
+ field.setAccessibilityLabel(tr("quickSearch.inputAccessibility"))
+ context.coordinator.field = field
+ DispatchQueue.main.async {
+ field.window?.makeFirstResponder(field)
+ }
+ return field
+ }
+
+ func updateNSView(_ field: QuickSearchNativeTextField, context: Context) {
+ if field.stringValue != text {
+ field.stringValue = text
+ }
+ field.placeholderString = placeholder
+ field.onCommand = onCommand
+ if context.coordinator.lastFocusToken != focusToken {
+ context.coordinator.lastFocusToken = focusToken
+ DispatchQueue.main.async {
+ field.window?.makeFirstResponder(field)
+ }
+ }
+ }
+
+ func makeCoordinator() -> Coordinator {
+ Coordinator(text: $text)
+ }
+
+ final class Coordinator: NSObject, NSTextFieldDelegate {
+ var text: Binding<String>
+ var lastFocusToken = 0
+ weak var field: NSTextField?
+
+ init(text: Binding<String>) {
+ self.text = text
+ }
+
+ func controlTextDidChange(_ obj: Notification) {
+ guard let field = obj.object as? NSTextField else { return }
+ text.wrappedValue = field.stringValue
+ }
+ }
+}
+
+private final class QuickSearchNativeTextField: NSTextField {
+ var onCommand: ((QuickSearchCommand) -> Void)?
+
+ override func keyDown(with event: NSEvent) {
+ switch Int(event.keyCode) {
+ case kVK_UpArrow:
+ onCommand?(.moveUp)
+ case kVK_DownArrow:
+ onCommand?(.moveDown)
+ case kVK_Return:
+ onCommand?(.submit)
+ case kVK_Escape:
+ onCommand?(.dismiss)
+ default:
+ super.keyDown(with: event)
+ }
+ }
+}
+
+struct QuickSearchBackdropClickView: NSViewRepresentable {
+ let onClick: () -> Void
+
+ func makeNSView(context: Context) -> QuickSearchBackdropNSView {
+ let view = QuickSearchBackdropNSView()
+ view.onClick = onClick
+ return view
+ }
+
+ func updateNSView(_ view: QuickSearchBackdropNSView, context: Context) {
+ view.onClick = onClick
+ }
+}
+
+final class QuickSearchBackdropNSView: NSView {
+ var onClick: (() -> Void)?
+
+ override func hitTest(_ point: NSPoint) -> NSView? {
+ bounds.contains(point) ? self : nil
+ }
+
+ override func mouseDown(with event: NSEvent) {
+ onClick?()
+ }
+}
+
+// MARK: - Hotkey Capture UI
+
+struct HotkeyCaptureView: NSViewRepresentable {
+ let isActive: Bool
+ let onCapture: (LauncherHotkey) -> Void
+ let onCancel: () -> Void
+
+ func makeNSView(context: Context) -> HotkeyCaptureNSView {
+ let view = HotkeyCaptureNSView()
+ view.onCapture = onCapture
+ view.onCancel = onCancel
+ return view
+ }
+
+ func updateNSView(_ view: HotkeyCaptureNSView, context: Context) {
+ view.onCapture = onCapture
+ view.onCancel = onCancel
+ view.setActive(isActive)
+ }
+}
+
+final class HotkeyCaptureNSView: NSView {
+ var onCapture: ((LauncherHotkey) -> Void)?
+ var onCancel: (() -> Void)?
+ private var keyMonitor: Any?
+
+ override var acceptsFirstResponder: Bool { true }
+
+ deinit {
+ removeKeyMonitor()
+ }
+
+ func setActive(_ active: Bool) {
+ if active {
+ installKeyMonitorIfNeeded()
+ DispatchQueue.main.async { [weak self] in
+ guard let self else { return }
+ self.window?.makeFirstResponder(self)
+ }
+ } else {
+ removeKeyMonitor()
+ }
+ }
+
+ override func keyDown(with event: NSEvent) {
+ handle(event)
+ }
+
+ private func installKeyMonitorIfNeeded() {
+ guard keyMonitor == nil else { return }
+ keyMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
+ guard let self else { return event }
+ self.handle(event)
+ return nil
+ }
+ }
+
+ private func removeKeyMonitor() {
+ if let keyMonitor {
+ NSEvent.removeMonitor(keyMonitor)
+ self.keyMonitor = nil
+ }
+ }
+
+ private func handle(_ event: NSEvent) {
+ if event.keyCode == UInt16(kVK_Escape) {
+ onCancel?()
+ return
+ }
+ if let hotkey = LauncherHotkey.from(event: event) {
+ onCapture?(hotkey)
+ } else {
+ NSSound.beep()
+ }
+ }
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 15fcbe7..182cf6f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,24 @@
# Apptag Changelog
+## [7.5.1] — 2026-05-19
+
+- 设置页新增独立「快捷键」页签,将主界面快捷键与 Quick Search 直接快捷键管理从「通用」页拆出
+- 移除设置页中与当前快捷键设置重复的静态按键帽提示,避免「关于」等页面继续展示旧快捷键视觉元素
+- 按 C 版规则收窄「快捷搜索」匹配:短查询不再放大无关结果,备注恢复拼音前缀匹配,Tag 支持原文非连续字符匹配,Bundle ID 与内部 Bundle 名仅支持精确/前缀命中
+- 版本号更新为 `7.5.1`,Build 更新为 `751`
+
+## [7.5.0] — 2026-05-19
+
+- 新增 Quick Search:在 App list 中按 `Space` 打开快速搜索浮层,支持按 App 名称、本地化名称、标签、备注和 Bundle ID 本地搜索
+- Quick Search 支持精确、前缀、子串、首字母缩写、非连续字符、中文直接匹配和本地中文拼音候选匹配
+- 空查询显示最近/常用 App 建议,支持键盘上下选择、回车启动、鼠标悬停选择和点击启动
+- 新增直接打开 Quick Search 的可配置全局快捷键,默认关闭,并支持快捷键录制、清除、恢复默认、重试注册和冲突状态提示
+- 快捷键冲突状态区分 `Active` / `Conflict` / `Disabled`,已知 macOS 默认快捷键显示专用说明,注册失败时保留旧快捷键可用
+- Quick Search 使用 macOS Spotlight 风格玻璃面板,支持外部点击关闭、Esc 两级退出、结果滚动和稳定顶部定位
+- 启动历史增加最近成功启动时间,用于 Quick Search 空查询建议排序;启动失败不更新历史
+- 所有 Quick Search 用户可见文案接入本地化资源
+- 版本号更新为 `7.5.0`,Build 更新为 `750`
+
## [7.3.5] — 2026-05-18
- 为菜单栏图标设置稳定 `NSStatusItem.autosaveName`,让 macOS 与 Thaw 更容易识别为同一个菜单栏项
--
Gitblit v1.9.3