mac: bring onboarding layout closer to vibetunnel
parent
ddfb76e9e0
commit
b0ecafcb8d
|
|
@ -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)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue