Slim browser omnibar and add button hover/press states (#271)

This commit is contained in:
Lawrence Chen 2026-02-21 04:39:27 -08:00 committed by GitHub
parent 685996ef0d
commit ed0d246039
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 149 additions and 6 deletions

View file

@ -122,6 +122,39 @@ struct OmnibarInlineCompletion: Equatable {
}
}
private struct OmnibarAddressButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
OmnibarAddressButtonStyleBody(configuration: configuration)
}
}
private struct OmnibarAddressButtonStyleBody: View {
let configuration: OmnibarAddressButtonStyle.Configuration
@Environment(\.isEnabled) private var isEnabled
@State private var isHovered = false
private var backgroundOpacity: Double {
guard isEnabled else { return 0.0 }
if configuration.isPressed { return 0.16 }
if isHovered { return 0.08 }
return 0.0
}
var body: some View {
configuration.label
.background(
RoundedRectangle(cornerRadius: 8, style: .continuous)
.fill(Color.primary.opacity(backgroundOpacity))
)
.onHover { hovering in
isHovered = hovering
}
.animation(.easeOut(duration: 0.12), value: isHovered)
.animation(.easeOut(duration: 0.08), value: configuration.isPressed)
}
}
/// View for rendering a browser panel with address bar
struct BrowserPanelView: View {
@ObservedObject var panel: BrowserPanel
@ -149,7 +182,8 @@ struct BrowserPanelView: View {
@State private var lastHandledAddressBarFocusRequestId: UUID?
private let omnibarPillCornerRadius: CGFloat = 12
private let addressBarButtonSize: CGFloat = 22
private let addressBarButtonHitSize: CGFloat = 32
private let addressBarButtonHitSize: CGFloat = 26
private let addressBarVerticalPadding: CGFloat = 4
private let devToolsButtonIconSize: CGFloat = 11
private var searchEngine: BrowserSearchEngine {
@ -335,7 +369,7 @@ struct BrowserPanelView: View {
developerToolsButton
}
.padding(.horizontal, 8)
.padding(.vertical, 6)
.padding(.vertical, addressBarVerticalPadding)
.background(Color(nsColor: GhosttyApp.shared.defaultBackgroundColor))
// Keep the omnibar stack above WKWebView so the suggestions popup is visible.
.zIndex(1)
@ -354,7 +388,7 @@ struct BrowserPanelView: View {
.frame(width: addressBarButtonHitSize, height: addressBarButtonHitSize, alignment: .center)
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.buttonStyle(OmnibarAddressButtonStyle())
.disabled(!panel.canGoBack)
.opacity(panel.canGoBack ? 1.0 : 0.4)
.help("Go Back")
@ -370,7 +404,7 @@ struct BrowserPanelView: View {
.frame(width: addressBarButtonHitSize, height: addressBarButtonHitSize, alignment: .center)
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.buttonStyle(OmnibarAddressButtonStyle())
.disabled(!panel.canGoForward)
.opacity(panel.canGoForward ? 1.0 : 0.4)
.help("Go Forward")
@ -393,7 +427,7 @@ struct BrowserPanelView: View {
.frame(width: addressBarButtonHitSize, height: addressBarButtonHitSize, alignment: .center)
.contentShape(Rectangle())
}
.buttonStyle(.plain)
.buttonStyle(OmnibarAddressButtonStyle())
.help(panel.isLoading ? "Stop" : "Reload")
if panel.isDownloading {
@ -419,7 +453,7 @@ struct BrowserPanelView: View {
.foregroundStyle(devToolsColorOption.color)
.frame(width: addressBarButtonSize, height: addressBarButtonSize, alignment: .center)
}
.buttonStyle(.plain)
.buttonStyle(OmnibarAddressButtonStyle())
.frame(width: addressBarButtonSize, height: addressBarButtonSize, alignment: .center)
.help("Toggle Developer Tools")
.accessibilityIdentifier("BrowserToggleDevToolsButton")

View file

@ -0,0 +1,109 @@
#!/usr/bin/env python3
"""Static regression guards for compact browser omnibar sizing."""
from __future__ import annotations
import re
import subprocess
from pathlib import Path
def repo_root() -> Path:
result = subprocess.run(
["git", "rev-parse", "--show-toplevel"],
capture_output=True,
text=True,
)
if result.returncode == 0:
return Path(result.stdout.strip())
return Path(__file__).resolve().parents[1]
def extract_block(source: str, signature: str) -> str:
start = source.find(signature)
if start < 0:
raise ValueError(f"Missing signature: {signature}")
brace_start = source.find("{", start)
if brace_start < 0:
raise ValueError(f"Missing opening brace for: {signature}")
depth = 0
for idx in range(brace_start, len(source)):
char = source[idx]
if char == "{":
depth += 1
elif char == "}":
depth -= 1
if depth == 0:
return source[brace_start : idx + 1]
raise ValueError(f"Unbalanced braces for: {signature}")
def parse_cgfloat_constant(source: str, name: str) -> float | None:
match = re.search(
rf"private let {re.escape(name)}: CGFloat = ([0-9]+(?:\.[0-9]+)?)",
source,
)
if not match:
return None
return float(match.group(1))
def main() -> int:
root = repo_root()
failures: list[str] = []
view_path = root / "Sources" / "Panels" / "BrowserPanelView.swift"
view_source = view_path.read_text(encoding="utf-8")
hit_size = parse_cgfloat_constant(view_source, "addressBarButtonHitSize")
if hit_size is None:
failures.append("addressBarButtonHitSize constant is missing")
elif hit_size > 26:
failures.append(
f"addressBarButtonHitSize regressed to {hit_size:g}; expected <= 26 for compact omnibar height"
)
vertical_padding = parse_cgfloat_constant(view_source, "addressBarVerticalPadding")
if vertical_padding is None:
failures.append("addressBarVerticalPadding constant is missing")
elif vertical_padding > 4:
failures.append(
f"addressBarVerticalPadding regressed to {vertical_padding:g}; expected <= 4 for compact omnibar height"
)
address_bar_block = extract_block(view_source, "private var addressBar: some View")
if ".padding(.vertical, addressBarVerticalPadding)" not in address_bar_block:
failures.append("addressBar no longer applies compact vertical padding via addressBarVerticalPadding")
button_bar_block = extract_block(view_source, "private var addressBarButtonBar: some View")
hit_frame_uses = button_bar_block.count("addressBarButtonHitSize")
if hit_frame_uses < 3:
failures.append(
"navigation buttons no longer consistently use addressBarButtonHitSize frames (padding may be lost)"
)
extract_block(view_source, "private struct OmnibarAddressButtonStyle: ButtonStyle")
style_body_block = extract_block(view_source, "private struct OmnibarAddressButtonStyleBody: View")
if "configuration.isPressed" not in style_body_block:
failures.append("OmnibarAddressButtonStyleBody is missing pressed-state styling")
if "isHovered" not in style_body_block or ".onHover" not in style_body_block:
failures.append("OmnibarAddressButtonStyleBody is missing hover-state styling")
style_uses = view_source.count(".buttonStyle(OmnibarAddressButtonStyle())")
if style_uses < 4:
failures.append(
"address bar buttons no longer consistently use OmnibarAddressButtonStyle"
)
if failures:
print("FAIL: browser omnibar compact layout regression guards failed")
for failure in failures:
print(f" - {failure}")
return 1
print("PASS: browser omnibar compact layout regression guards are in place")
return 0
if __name__ == "__main__":
raise SystemExit(main())