274 lines
12 KiB
Swift
274 lines
12 KiB
Swift
import SwiftUI
|
|
|
|
extension View {
|
|
func placeholder<Content: View>(
|
|
when shouldShow: Bool,
|
|
alignment: Alignment = .center,
|
|
@ViewBuilder placeholder: () -> Content) -> some View {
|
|
|
|
ZStack(alignment: alignment) {
|
|
placeholder().opacity(shouldShow ? 1 : 0)
|
|
self
|
|
}
|
|
}
|
|
}
|
|
|
|
enum ConfigurationMode: Hashable {
|
|
case add
|
|
case edit(PowerModeConfig)
|
|
|
|
var isAdding: Bool {
|
|
if case .add = self { return true }
|
|
return false
|
|
}
|
|
|
|
var title: String {
|
|
switch self {
|
|
case .add: return "Add Power Mode"
|
|
case .edit: return "Edit Power Mode"
|
|
}
|
|
}
|
|
|
|
func hash(into hasher: inout Hasher) {
|
|
switch self {
|
|
case .add:
|
|
hasher.combine(0)
|
|
case .edit(let config):
|
|
hasher.combine(1)
|
|
hasher.combine(config.id)
|
|
}
|
|
}
|
|
|
|
static func == (lhs: ConfigurationMode, rhs: ConfigurationMode) -> Bool {
|
|
switch (lhs, rhs) {
|
|
case (.add, .add):
|
|
return true
|
|
case (.edit(let lhsConfig), .edit(let rhsConfig)):
|
|
return lhsConfig.id == rhsConfig.id
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
enum ConfigurationType {
|
|
case application
|
|
case website
|
|
}
|
|
|
|
let commonEmojis = ["🏢", "🏠", "💼", "🎮", "📱", "📺", "🎵", "📚", "✏️", "🎨", "🧠", "⚙️", "💻", "🌐", "📝", "📊", "🔍", "💬", "📈", "🔧"]
|
|
|
|
struct PowerModeView: View {
|
|
@StateObject private var powerModeManager = PowerModeManager.shared
|
|
@EnvironmentObject private var enhancementService: AIEnhancementService
|
|
@EnvironmentObject private var aiService: AIService
|
|
@State private var configurationMode: ConfigurationMode?
|
|
@State private var navigationPath = NavigationPath()
|
|
@State private var isReorderMode = false
|
|
|
|
var body: some View {
|
|
NavigationStack(path: $navigationPath) {
|
|
VStack(spacing: 0) {
|
|
VStack(spacing: 12) {
|
|
HStack {
|
|
VStack(alignment: .leading, spacing: 4) {
|
|
HStack(spacing: 8) {
|
|
Text("Power Modes")
|
|
.font(.system(size: 28, weight: .bold, design: .default))
|
|
.foregroundColor(.primary)
|
|
|
|
InfoTip(
|
|
title: "What is Power Mode?",
|
|
message: "Automatically apply custom configurations based on the app/website you are using",
|
|
learnMoreURL: "https://www.youtube.com/@tryvoiceink/videos"
|
|
)
|
|
}
|
|
|
|
Text("Automate your workflows with context-aware configurations.")
|
|
.font(.system(size: 14))
|
|
.foregroundColor(.secondary)
|
|
}
|
|
|
|
Spacer()
|
|
|
|
HStack(spacing: 8) {
|
|
if !isReorderMode {
|
|
Button(action: {
|
|
configurationMode = .add
|
|
navigationPath.append(configurationMode!)
|
|
}) {
|
|
HStack(spacing: 6) {
|
|
Image(systemName: "plus")
|
|
.font(.system(size: 12, weight: .medium))
|
|
Text("Add Power Mode")
|
|
.font(.system(size: 13, weight: .medium))
|
|
}
|
|
.foregroundColor(.white)
|
|
.padding(.horizontal, 12)
|
|
.padding(.vertical, 6)
|
|
.background(Color.accentColor)
|
|
.cornerRadius(6)
|
|
}
|
|
.buttonStyle(PlainButtonStyle())
|
|
}
|
|
Button(action: { withAnimation { isReorderMode.toggle() } }) {
|
|
HStack(spacing: 6) {
|
|
Image(systemName: isReorderMode ? "checkmark" : "arrow.up.arrow.down")
|
|
.font(.system(size: 12, weight: .medium))
|
|
Text(isReorderMode ? "Done" : "Reorder")
|
|
.font(.system(size: 13, weight: .medium))
|
|
}
|
|
.foregroundColor(.primary)
|
|
.padding(.horizontal, 12)
|
|
.padding(.vertical, 6)
|
|
.background(Color(NSColor.controlBackgroundColor))
|
|
.cornerRadius(6)
|
|
.overlay(
|
|
RoundedRectangle(cornerRadius: 6)
|
|
.stroke(Color(NSColor.separatorColor), lineWidth: 1)
|
|
)
|
|
}
|
|
.buttonStyle(PlainButtonStyle())
|
|
}
|
|
}
|
|
}
|
|
.padding(.horizontal, 24)
|
|
.padding(.top, 20)
|
|
.padding(.bottom, 16)
|
|
|
|
Rectangle()
|
|
.fill(Color(NSColor.separatorColor))
|
|
.frame(height: 1)
|
|
.padding(.horizontal, 24)
|
|
|
|
if isReorderMode {
|
|
VStack(spacing: 12) {
|
|
List {
|
|
ForEach(powerModeManager.configurations) { config in
|
|
HStack(spacing: 12) {
|
|
ZStack {
|
|
Circle()
|
|
.fill(Color(NSColor.controlBackgroundColor))
|
|
.frame(width: 40, height: 40)
|
|
Text(config.emoji)
|
|
.font(.system(size: 20))
|
|
}
|
|
|
|
Text(config.name)
|
|
.font(.system(size: 15, weight: .semibold))
|
|
|
|
Spacer()
|
|
|
|
HStack(spacing: 6) {
|
|
if config.isDefault {
|
|
Text("Default")
|
|
.font(.system(size: 11, weight: .medium))
|
|
.padding(.horizontal, 6)
|
|
.padding(.vertical, 2)
|
|
.background(Capsule().fill(Color.accentColor))
|
|
.foregroundColor(.white)
|
|
}
|
|
if !config.isEnabled {
|
|
Text("Disabled")
|
|
.font(.system(size: 11, weight: .medium))
|
|
.padding(.horizontal, 8)
|
|
.padding(.vertical, 4)
|
|
.background(Capsule().fill(Color(NSColor.controlBackgroundColor)))
|
|
.overlay(
|
|
Capsule().stroke(Color(NSColor.separatorColor), lineWidth: 0.5)
|
|
)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
}
|
|
}
|
|
.padding(.vertical, 12)
|
|
.padding(.horizontal, 14)
|
|
.background(CardBackground(isSelected: false))
|
|
.listRowInsets(EdgeInsets())
|
|
.listRowBackground(Color.clear)
|
|
.listRowSeparator(.hidden)
|
|
.padding(.vertical, 6)
|
|
}
|
|
.onMove(perform: powerModeManager.moveConfigurations)
|
|
}
|
|
.listStyle(.plain)
|
|
.listRowSeparator(.hidden)
|
|
.scrollContentBackground(.hidden)
|
|
.background(Color(NSColor.controlBackgroundColor))
|
|
}
|
|
.padding(.horizontal, 24)
|
|
.padding(.vertical, 20)
|
|
} else {
|
|
GeometryReader { geometry in
|
|
ScrollView {
|
|
VStack(spacing: 0) {
|
|
if powerModeManager.configurations.isEmpty {
|
|
VStack(spacing: 24) {
|
|
Spacer()
|
|
.frame(height: geometry.size.height * 0.2)
|
|
|
|
VStack(spacing: 16) {
|
|
Image(systemName: "square.grid.2x2.fill")
|
|
.font(.system(size: 48, weight: .regular))
|
|
.foregroundColor(.secondary.opacity(0.6))
|
|
|
|
VStack(spacing: 8) {
|
|
Text("No Power Modes Yet")
|
|
.font(.system(size: 20, weight: .medium))
|
|
.foregroundColor(.primary)
|
|
|
|
Text("Create first power mode to automate your VoiceInk workflow based on apps/website you are using")
|
|
.font(.system(size: 14))
|
|
.foregroundColor(.secondary)
|
|
.multilineTextAlignment(.center)
|
|
.lineSpacing(2)
|
|
}
|
|
}
|
|
|
|
Spacer()
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
.frame(minHeight: geometry.size.height)
|
|
} else {
|
|
VStack(spacing: 0) {
|
|
PowerModeConfigurationsGrid(
|
|
powerModeManager: powerModeManager,
|
|
onEditConfig: { config in
|
|
configurationMode = .edit(config)
|
|
navigationPath.append(configurationMode!)
|
|
}
|
|
)
|
|
.padding(.horizontal, 24)
|
|
.padding(.vertical, 20)
|
|
|
|
Spacer()
|
|
.frame(height: 40)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.background(Color(NSColor.controlBackgroundColor))
|
|
.navigationDestination(for: ConfigurationMode.self) { mode in
|
|
ConfigurationView(mode: mode, powerModeManager: powerModeManager)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// New component for section headers
|
|
struct SectionHeader: View {
|
|
let title: String
|
|
|
|
var body: some View {
|
|
Text(title)
|
|
.font(.system(size: 16, weight: .bold))
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
.padding(.bottom, 8)
|
|
}
|
|
}
|