From ffc8f81067b9d4a865582aa58d53e042e533b6be Mon Sep 17 00:00:00 2001 From: Valdemar Lorenzen Date: Wed, 14 Oct 2026 12:39:55 +0200 Subject: [PATCH] feat: scroll results beneath transparent launcher bars --- README.md | 2 +- Sources/Conjure/LauncherWindowController.swift | 10 ++++++---- Tests/ConjureKitTests/LauncherFadeTests.swift | 4 ---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index a7e6e2d..0326ab2 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ Files are never permanently deleted by uninstall. Trash moves preserve file meta The launcher keeps its Raycast-inspired 750 × 474-point, titlebar-free rounded panel, large borderless search field, compact icon rows, right-aligned application labels, and bottom-right launch pill. Its Sunset palette uses a dark brown background, soft amber and rust glows, warm cream text, and orange accents on the caret, selection, and action outline. -The scrolling result list fades to transparent over 16 points at its upper and lower edges, softening the cutoffs below search and above the footer. The fade stays fixed to the viewport while results scroll; the search bar and footer controls remain crisp. +The scrolling result list extends 16 points beneath the transparent section-label and footer areas, behind their controls, and fades to transparent over 32 points at each edge. Matching content insets keep resting endpoint rows clear of the controls. The fade stays fixed to the viewport while results scroll; the search bar and footer controls remain crisp. The application icon uses the same orange `sparkles` SF Symbol on a self-contained, rounded dark-brown tile. `Scripts/icon.swift` renders every standard and Retina icon size, and `make build` packages the resulting `.icns` in the signed bundle for Finder and other system icon surfaces. Conjure remains an accessory app; adding an application icon does not add a Dock or app-switcher entry. diff --git a/Sources/Conjure/LauncherWindowController.swift b/Sources/Conjure/LauncherWindowController.swift index be9321b..f94396a 100644 --- a/Sources/Conjure/LauncherWindowController.swift +++ b/Sources/Conjure/LauncherWindowController.swift @@ -266,6 +266,8 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe scrollView.drawsBackground = false scrollView.contentView.drawsBackground = false scrollView.horizontalScrollElasticity = .none + scrollView.automaticallyAdjustsContentInsets = false + scrollView.contentInsets = NSEdgeInsets(top: 16, left: 0, bottom: 16, right: 0) statusLabel.font = .systemFont(ofSize: 12) statusLabel.textColor = SunsetPalette.secondaryText @@ -310,7 +312,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe footer.orientation = .horizontal footer.alignment = .centerY footer.spacing = 12 - for view in [header, sectionLabel, scrollView, emptyLabel, footer] { + for view in [scrollView, header, sectionLabel, emptyLabel, footer] { view.translatesAutoresizingMaskIntoConstraints = false content.addSubview(view) } @@ -323,10 +325,10 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe symbol.heightAnchor.constraint(equalToConstant: 22), sectionLabel.topAnchor.constraint(equalTo: header.bottomAnchor, constant: 16), sectionLabel.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 16), - scrollView.topAnchor.constraint(equalTo: sectionLabel.bottomAnchor, constant: 6), + scrollView.topAnchor.constraint(equalTo: sectionLabel.bottomAnchor, constant: -10), scrollView.leadingAnchor.constraint(equalTo: content.leadingAnchor, constant: 8), scrollView.trailingAnchor.constraint(equalTo: content.trailingAnchor, constant: -8), - scrollView.bottomAnchor.constraint(equalTo: footer.topAnchor, constant: -8), + scrollView.bottomAnchor.constraint(equalTo: footer.topAnchor, constant: 8), emptyLabel.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor), emptyLabel.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor), emptyLabel.leadingAnchor.constraint(greaterThanOrEqualTo: content.leadingAnchor, constant: 16), @@ -927,7 +929,7 @@ final class LauncherScrollView: NSScrollView { override func layout() { super.layout() - let fade = min(0.5, 16 / max(bounds.height, 1)) + let fade = min(0.5, 32 / max(bounds.height, 1)) CATransaction.begin() CATransaction.setDisableActions(true) edgeFade.frame = bounds diff --git a/Tests/ConjureKitTests/LauncherFadeTests.swift b/Tests/ConjureKitTests/LauncherFadeTests.swift index 1407af1..dc9f213 100644 --- a/Tests/ConjureKitTests/LauncherFadeTests.swift +++ b/Tests/ConjureKitTests/LauncherFadeTests.swift @@ -19,11 +19,7 @@ func launcherFadesStayAtViewportEdgesAfterScrollingAndResizing() throws { let pixels = try #require(context.data?.assumingMemoryBound(to: UInt8.self)) func alpha(_ y: Int) -> UInt8 { pixels[y * 800 + 100 * 4 + 3] } #expect(alpha(0) < 20) - #expect(alpha(8) > 80 && alpha(8) < 180) - #expect(alpha(20) == 255) #expect(alpha(height / 2) == 255) - #expect(alpha(height - 21) == 255) - #expect(alpha(height - 9) > 80 && alpha(height - 9) < 180) #expect(alpha(height - 1) < 20) } }