mac: bring onboarding layout closer to vibetunnel

This commit is contained in:
Peter Steinberger
2025-12-06 00:50:22 +01:00
parent ddfb76e9e0
commit b0ecafcb8d

View File

@@ -414,7 +414,8 @@ private struct CritterStatusLabel: View {
Image(nsImage: CritterIconRenderer.makeIcon( Image(nsImage: CritterIconRenderer.makeIcon(
blink: blinkAmount, blink: blinkAmount,
legWiggle: legWiggle, legWiggle: legWiggle,
earWiggle: earWiggle earWiggle: earWiggle,
isPaused: isPaused
)) ))
.renderingMode(.template) .renderingMode(.template)
.frame(width: 18, height: 16) .frame(width: 18, height: 16)
@@ -514,7 +515,7 @@ private struct CritterStatusLabel: View {
enum CritterIconRenderer { enum CritterIconRenderer {
private static let size = NSSize(width: 18, height: 16) private static let size = NSSize(width: 18, height: 16)
static func makeIcon(blink: CGFloat, legWiggle: CGFloat = 0, earWiggle: CGFloat = 0) -> NSImage { static func makeIcon(blink: CGFloat, legWiggle: CGFloat = 0, earWiggle: CGFloat = 0, isPaused: Bool = false) -> NSImage {
let image = NSImage(size: size) let image = NSImage(size: size)
image.lockFocus() image.lockFocus()
defer { image.unlockFocus() } defer { image.unlockFocus() }
@@ -548,7 +549,8 @@ enum CritterIconRenderer {
let eyeY = bodyY + bodyH * 0.56 let eyeY = bodyY + bodyH * 0.56
let eyeOffset = bodyW * 0.24 let eyeOffset = bodyW * 0.24
ctx.setFillColor(NSColor.labelColor.cgColor) let baseAlpha: CGFloat = isPaused ? 0.38 : 1.0
ctx.setFillColor(NSColor.labelColor.withAlphaComponent(baseAlpha).cgColor)
// Body // Body
ctx.addPath(CGPath(roundedRect: CGRect(x: bodyX, y: bodyY, width: bodyW, height: bodyH), cornerWidth: bodyCorner, cornerHeight: bodyCorner, transform: nil)) ctx.addPath(CGPath(roundedRect: CGRect(x: bodyX, y: bodyY, width: bodyW, height: bodyH), cornerWidth: bodyCorner, cornerHeight: bodyCorner, transform: nil))
@@ -1172,66 +1174,60 @@ struct OnboardingView: View {
var body: some View { var body: some View {
let step = steps[stepIndex] let step = steps[stepIndex]
VStack(spacing: 18) { VStack(spacing: 18) {
heroHeader(step: step) heroCard(step: step)
contentCard(step: step) contentPanel(step: step)
progressDots progressDots
footerButtons footerButtons
} }
.padding(22) .padding(18)
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.background(Color(NSColor.windowBackgroundColor)) .background(Color(NSColor.windowBackgroundColor))
.task { await refreshPerms() } .task { await refreshPerms() }
} }
@ViewBuilder @ViewBuilder
private func heroHeader(step: OnboardingStep) -> some View { private func heroCard(step: OnboardingStep) -> some View {
HStack(spacing: 16) { HStack(alignment: .center, spacing: 14) {
ZStack { Image(systemName: step.systemImage)
LinearGradient(colors: [.accentColor.opacity(0.75), .purple.opacity(0.65)], startPoint: .topLeading, endPoint: .bottomTrailing) .font(.title3.bold())
.frame(width: 96, height: 96) .foregroundStyle(.white)
.clipShape(RoundedRectangle(cornerRadius: 22, style: .continuous)) .padding(10)
.shadow(color: .accentColor.opacity(0.35), radius: 12, y: 6) .background(.white.opacity(0.12))
Image(nsImage: CritterIconRenderer.makeIcon(blink: 0)) .clipShape(Circle())
.resizable()
.renderingMode(.template)
.foregroundStyle(.white)
.frame(width: 54, height: 48)
}
VStack(alignment: .leading, spacing: 6) { VStack(alignment: .leading, spacing: 6) {
Label(step.title, systemImage: step.systemImage) Text(step.title)
.font(.title3.bold()) .font(.title3.bold())
.foregroundColor(.white)
Text(step.detail) Text(step.detail)
.font(.subheadline) .font(.subheadline)
.foregroundStyle(.secondary) .foregroundColor(.white.opacity(0.92))
} }
Spacer() Spacer()
} }
.padding(.horizontal, 18)
.padding(.vertical, 14)
.background(
LinearGradient(colors: [Color.blue.opacity(0.9), Color.purple.opacity(0.85)], startPoint: .topLeading, endPoint: .bottomTrailing)
.clipShape(RoundedRectangle(cornerRadius: 18, style: .continuous))
)
.shadow(color: .black.opacity(0.18), radius: 10, y: 4)
} }
@ViewBuilder @ViewBuilder
private func contentCard(step: OnboardingStep) -> some View { private func contentPanel(step: OnboardingStep) -> some View {
VStack(alignment: .leading, spacing: 14) { VStack(alignment: .leading, spacing: 14) {
if step.showsPermissions { if step.showsPermissions { permissionsCard }
permissionsCard if step.showsCLI { CLIInstallCard(copied: $copied) }
} if step.showsLoginToggle { loginCard }
if step.showsCLI { if !step.showsPermissions && !step.showsCLI && !step.showsLoginToggle {
CLIInstallCard(copied: $copied)
}
if step.showsLoginToggle {
loginCard
}
if !step.showsPermissions && !step.showsCLI {
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
Text("Keep Clawdis running in your menu bar. Pause from the menu if you need silence, or open Settings to adjust permissions later.") Text("Keep Clawdis running in your menu bar. Pause from the menu if you need silence, or open Settings to adjust permissions later.")
Button("Open Settings") { .font(.body)
NSApp.activate(ignoringOtherApps: true)
SettingsTabRouter.request(.general)
NSApplication.shared.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil)
}
} }
} }
} }
.padding(16) .padding(18)
.frame(maxWidth: .infinity, alignment: .leading)
.background(RoundedRectangle(cornerRadius: 16, style: .continuous).fill(Color(NSColor.controlBackgroundColor))) .background(RoundedRectangle(cornerRadius: 16, style: .continuous).fill(Color(NSColor.controlBackgroundColor)))
} }