BusyBee3333 7694d965c9 feat: Add structured signal editor with app dropdown and action builder
- Add AppDiscovery provider for running app enumeration
- Implement AppDropdownView with auto-launch functionality
- Create SignalAction models for 40+ yabai commands
- Build ActionBuilderView with nested parameter controls
- Add LiveShellPreview for real-time shell command generation
- Implement ActionValidator for conflict detection
- Add migration parser for existing raw action strings
- Include feature flag for safe rollout
- Maintain full backward compatibility
2025-12-31 01:44:13 -05:00

121 lines
3.5 KiB
Swift

//
// main.swift
// AnimationDemo
//
// Created by Jake Shore
// Copyright © 2024 Jake Shore. All rights reserved.
//
import SwiftUI
@main
struct AnimationDemoApp: App {
var body: some Scene {
WindowGroup {
AnimationDemoView()
}
.windowStyle(.titleBar)
.windowToolbarStyle(.unifiedCompact)
}
}
struct AnimationDemoView: View {
@State private var selectedAnimation: AnimationType = .liquidBorder
@State private var isFocused = true
@State private var animationTime = 0.0
@State private var particleCount = 15
var body: some View {
VStack(spacing: 20) {
Text("Metal Liquid Animations Demo")
.font(.title)
.fontWeight(.bold)
// Animation selector
Picker("Animation Type", selection: $selectedAnimation) {
Text("Liquid Border").tag(AnimationType.liquidBorder)
Text("Flowing Particles").tag(AnimationType.flowingParticles)
Text("Ripple Effect").tag(AnimationType.rippleEffect)
Text("Morphing Shape").tag(AnimationType.morphingShape)
}
.pickerStyle(.segmented)
.padding(.horizontal)
// Controls
HStack(spacing: 20) {
Toggle("Focused", isOn: $isFocused)
.toggleStyle(.switch)
VStack {
Text("Particles: \(particleCount)")
Slider(value: .init(get: { Double(particleCount) }, set: { particleCount = Int($0) }),
in: 5...25, step: 1)
}
.frame(width: 150)
Button("Reset") {
animationTime = 0.0
}
}
.padding(.horizontal)
// Animation display
ZStack {
RoundedRectangle(cornerRadius: 12)
.fill(Color.gray.opacity(0.1))
.frame(width: 300, height: 200)
MetalAnimationView(
animationType: selectedAnimation,
windowInfo: AnimationWindowInfo(
id: 1,
isFocused: isFocused,
focusPoint: isFocused ? CGPoint(x: 150, y: 100) : nil,
morphProgress: isFocused ? 1.0 : 0.0
),
time: $animationTime
)
.frame(width: 280, height: 180)
.clipShape(RoundedRectangle(cornerRadius: 8))
}
.frame(width: 320, height: 220)
.background(
RoundedRectangle(cornerRadius: 16)
.stroke(Color.blue.opacity(0.3), lineWidth: 2)
)
// Status
HStack {
Circle()
.fill(MetalAnimationEngine.shared.isMetalAvailable ? Color.green : Color.red)
.frame(width: 12, height: 12)
Text(MetalAnimationEngine.shared.isMetalAvailable ?
"Metal Available - GPU Acceleration Active" :
"Metal Unavailable - Using Canvas Fallback")
.font(.caption)
.foregroundColor(.secondary)
}
}
.padding()
.frame(minWidth: 600, minHeight: 500)
.onAppear {
// Start animation timer
Timer.scheduledTimer(withTimeInterval: 1/60.0, repeats: true) { _ in
animationTime += 1/60.0
}
}
}
}