diff --git a/README.md b/README.md index 57e6154..c09e162 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ Rates are shared across queries and refreshed on the next currency query after t Search for **Conjure Emoji**, identified by its centered 🤔 icon, and open it. Browse the grid or search by name, keyword, `:shortcode:`, or emoji. Use the category menu to filter results or show **Recently Used**. Your last 30 selections are saved locally. Favorites appear first, followed by recent selections when browsing All Emoji. -Square emoji tiles show the same subtle rounded border used elsewhere in Conjure only when selected or hovered; the selected tile retains its accent fill. +Square 64-point emoji tiles use compact 1-point grid spacing and show the same subtle rounded border used elsewhere in Conjure only when selected or hovered; the selected tile retains its accent fill. Use the arrow keys or Control-H/J/K/L to navigate left/down/up/right; the grid scrolls to keep the selected emoji visible. Return, clicking an emoji, or **Copy and Paste** copies it, dismisses Conjure, and tries to paste into the app that was focused when the launcher opened. Automatic pasting requires **System Settings → Privacy & Security → Accessibility → Conjure**; macOS prompts if permission is missing. After granting it, select an emoji again. If permission is denied, the previous app is unavailable, or focus cannot be restored promptly, the emoji stays copied for manual Command-V. The Back chevron returns to the launcher and restores your search. diff --git a/Sources/Conjure/EmojiPickerController.swift b/Sources/Conjure/EmojiPickerController.swift index d9201e8..b4a3206 100644 --- a/Sources/Conjure/EmojiPickerController.swift +++ b/Sources/Conjure/EmojiPickerController.swift @@ -38,8 +38,8 @@ final class EmojiPickerController: NSViewController, NSCollectionViewDataSource, category.action = #selector(categoryChanged) let layout = NSCollectionViewFlowLayout() layout.itemSize = NSSize(width: 64, height: 64) - layout.minimumInteritemSpacing = 6 - layout.minimumLineSpacing = 6 + layout.minimumInteritemSpacing = 1 + layout.minimumLineSpacing = 1 layout.sectionInset = NSEdgeInsets(top: LauncherScrollView.fadeHeight, left: 16, bottom: 12, right: 16) collection.collectionViewLayout = layout collection.backgroundColors = [.clear] @@ -149,7 +149,7 @@ final class EmojiPickerController: NSViewController, NSCollectionViewDataSource, collection.scrollToItems(at: [indexPath(for: selected)], scrollPosition: .nearestHorizontalEdge) } - private var columns: Int { max(1, Int((collection.bounds.width - 32 + 6) / 70)) } + private var columns: Int { max(1, Int((collection.bounds.width - 32 + 1) / 65)) } private func indexPath(for index: Int) -> IndexPath { splitFavorites && index >= favoriteCount diff --git a/Tests/ConjureKitTests/EmojiGridTests.swift b/Tests/ConjureKitTests/EmojiGridTests.swift new file mode 100644 index 0000000..a0cafb5 --- /dev/null +++ b/Tests/ConjureKitTests/EmojiGridTests.swift @@ -0,0 +1,29 @@ +import AppKit +import ConjureKit +import Testing +@testable import Conjure + +@Test @MainActor func emojiDownNavigationFollowsRenderedColumns() throws { + _ = NSApplication.shared + let suite = "EmojiGrid.\(UUID().uuidString)" + let defaults = UserDefaults(suiteName: suite)! + let pasteboard = NSPasteboard.withUniqueName() + defer { defaults.removePersistentDomain(forName: suite); pasteboard.releaseGlobally() } + let picker = EmojiPickerController(defaults: defaults, pasteboard: pasteboard) + let window = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 750, height: 400), + styleMask: [.borderless], backing: .buffered, defer: false) + window.contentView = picker.view + picker.view.layoutSubtreeIfNeeded() + let scroll = try #require(picker.view.subviews.compactMap { $0 as? NSScrollView }.first) + let grid = try #require(scroll.documentView as? NSCollectionView) + let layout = try #require(grid.collectionViewLayout) + let first = try #require(layout.layoutAttributesForItem(at: IndexPath(item: 0, section: 0))) + let nextRow = try #require((1.. first.frame.minY + }) + #expect(picker.handle(#selector(NSResponder.moveDown(_:)))) + picker.copySelection() + #expect(pasteboard.string(forType: .string) == EmojiCatalog.all[nextRow].value) + print("Emoji grid: \(nextRow) columns at 750 points; down navigation follows the rendered row.") +}