diff --git a/Tests/fuzz-test.swift b/Tests/fuzz-test.swift index 6ae1b2c0b..1c0959c54 100644 --- a/Tests/fuzz-test.swift +++ b/Tests/fuzz-test.swift @@ -387,20 +387,20 @@ struct FuzzTest { } let commands = [ - Candidate(name: "Clipboard History", priority: 80), Candidate(name: "AI Chat", priority: 90), - Candidate(name: "Search Files", priority: 70), Candidate(name: "My Schedule", priority: 60), + Candidate(name: "Search Files", priority: 70), Candidate(name: "Clipboard History", priority: 80), + Candidate(name: "My Schedule", priority: 60), Candidate(name: "Search Emoji & Symbols", priority: 50), Candidate(name: "Create Snippet", priority: 30) ] check( "a new user gets the built-ins, highest priority first", select(commands) == [ - "AI Chat", "Clipboard History", "Search Files", "My Schedule", "Search Emoji & Symbols" + "Clipboard History", "Search Files", "My Schedule", "Search Emoji & Symbols", "Create Snippet" ]) let used = [Candidate(name: "Safari", frecency: 40), Candidate(name: "Slack", frecency: 300)] check( "what the user opens comes first, most frecent first", - select(used + commands).prefix(3) == ["Slack", "Safari", "AI Chat"]) + select(used + commands).prefix(3) == ["Slack", "Safari", "Clipboard History"]) let many = (1...8).map { Candidate(name: "App \($0)", frecency: Double(100 + $0)) } check("never more than five", select(many + commands).count == LauncherSuggestions.limit) check( @@ -408,7 +408,7 @@ struct FuzzTest { !select([Candidate(name: "Slack", frecency: 300, hotKey: true)] + commands).contains("Slack")) check( "the fill skips a built-in the user already aliased", - !select([Candidate(name: "AI Chat", alias: "ai", priority: 90)]).contains("AI Chat")) + select([Candidate(name: "Clipboard History", alias: "cb", priority: 80)]).isEmpty) let fresh = [ Candidate(name: "New One", installedMinutesAgo: 1), Candidate(name: "New Two", installedMinutesAgo: 2), diff --git a/Tinycast/Features/Launcher/Model/CommandID.swift b/Tinycast/Features/Launcher/Model/CommandID.swift index d7dbc53d9..337c56df4 100644 --- a/Tinycast/Features/Launcher/Model/CommandID.swift +++ b/Tinycast/Features/Launcher/Model/CommandID.swift @@ -166,7 +166,6 @@ enum CommandID: String, CaseIterable, Sendable { /// Suggested, highest first, until the user's own habits fill the section. var suggestionPriority: Int? { switch self { - case .quickAI: 90 case .clipboardHistory: 80 case .searchFiles: 70 case .mySchedule: 60 diff --git a/Tinycast/Features/Launcher/Service/AppIndex.swift b/Tinycast/Features/Launcher/Service/AppIndex.swift index 2f1dfeecd..32ee7d60e 100644 --- a/Tinycast/Features/Launcher/Service/AppIndex.swift +++ b/Tinycast/Features/Launcher/Service/AppIndex.swift @@ -714,12 +714,13 @@ final class AppIndex { return ordered } - /// Meetings keep their own card, and Tinycast opening Tinycast goes nowhere. + /// Meetings keep their own card, AI is never pushed, and Tinycast opening Tinycast goes nowhere. private func suggestions( from entries: [AppEntry], usage: LauncherRankingStore.Snapshot, hotKeys: HotKeyManager ) -> [AppEntry] { let eligible = entries.filter { - $0.kind != .meeting && !($0.bundleID?.hasPrefix(Self.ownBundlePrefix) ?? false) + $0.kind != .meeting && $0.settingsOwner != .ai + && !($0.bundleID?.hasPrefix(Self.ownBundlePrefix) ?? false) } return LauncherSuggestions.select(from: eligible, now: usage.now) { entry in // `hotKeyAction` is nil for an extension command, whose shortcut is keyed by entry ID. diff --git a/docs/features/launcher.md b/docs/features/launcher.md index 09916cc5d..d012b25a9 100644 --- a/docs/features/launcher.md +++ b/docs/features/launcher.md @@ -412,14 +412,15 @@ so the sectioned view stays 1:1 with the flat selection. ### Suggestions `LauncherSuggestions.select` chooses at most five from every visible entry that is not a favorite, a -meeting or Tinycast itself: +meeting, an AI command or Tinycast itself. AI is the lowest priority, so Quick AI and AI Chat are +never suggested, however often they are opened: 1. up to two apps or extensions installed in the last five minutes and never opened — `AppEntry.installedAt` is the bundle's added-to-directory date; 2. entries with a score above 1 and no bound shortcut, in empty-list order — a shortcut is already the faster way in; 3. while fewer than five, built-in commands with no alias or shortcut, by - `CommandID.suggestionPriority`: AI Chat, Clipboard History, Search Files, My Schedule, Search Emoji & + `CommandID.suggestionPriority`: Clipboard History, Search Files, My Schedule, Search Emoji & Symbols, then Create Quicklink and Create Snippet. A command whose feature is off is absent from the index, so it is never offered.