From 2e169c42e8334bdd343b17cd3489281d32cd5044 Mon Sep 17 00:00:00 2001 From: Lukas <134181853+bo2themax@users.noreply.github.com> Date: Mon, 13 Apr 2026 11:44:18 +0200 Subject: [PATCH] macOS: Support initials matching in command palette search Extend String.matchedIndices(for:) to fall back to initials matching when no substring match is found. Typing the first letter of each word now matches commands, e.g. "tbo" matches "Toggle Background Opacity", with each matched initial highlighted. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../Command Palette/CommandPalette.swift | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/macos/Sources/Features/Command Palette/CommandPalette.swift b/macos/Sources/Features/Command Palette/CommandPalette.swift index d5a7fc11e..7321745a7 100644 --- a/macos/Sources/Features/Command Palette/CommandPalette.swift +++ b/macos/Sources/Features/Command Palette/CommandPalette.swift @@ -460,15 +460,31 @@ private struct ShortcutSymbolsView: View { } extension String { - /// Returns the character indices that match `query` + /// Returns the character indices that match `query`, trying a substring match first, + /// then falling back to initials matching (first letter of each word). /// - Returns: `nil` if neither matches. func matchedIndices(for query: String) -> [String.Index]? { guard !query.isEmpty else { return nil } + // Prefer substring match. if let range = self.range(of: query, options: .caseInsensitive) { return Array(self[range].indices) } - return nil + // Fall back to initials match. + let words = self.split(whereSeparator: \.isWhitespace) + var queryIndex = query.startIndex + var matched: [String.Index] = [] + + for word in words { + guard queryIndex < query.endIndex else { break } + + if word.first?.lowercased() == query[queryIndex].lowercased() { + matched.append(word.startIndex) + queryIndex = query.index(after: queryIndex) + } + } + + return queryIndex == query.endIndex ? matched : nil } }