311 lines
12 KiB
Swift
311 lines
12 KiB
Swift
import SwiftUI
|
|
// Supporting Views
|
|
struct PowerModeEmptyStateView: View {
|
|
@Binding var showAddModal: Bool
|
|
@Binding var configMode: ConfigurationMode?
|
|
|
|
var body: some View {
|
|
VStack(spacing: 16) {
|
|
Image(systemName: "bolt.circle.fill")
|
|
.font(.system(size: 48))
|
|
.foregroundColor(.secondary)
|
|
|
|
Text("No Applications Configured")
|
|
.font(.title2)
|
|
.fontWeight(.semibold)
|
|
|
|
Text("Add applications to customize their AI enhancement settings.")
|
|
.foregroundColor(.secondary)
|
|
.multilineTextAlignment(.center)
|
|
|
|
Button(action: {
|
|
print("🔍 Empty state Add Application button clicked")
|
|
configMode = .add
|
|
print("🔍 Configuration mode set to: \(String(describing: configMode))")
|
|
showAddModal = true
|
|
print("🔍 Empty state showAddModal set to: \(showAddModal)")
|
|
}) {
|
|
Label("Add Application", systemImage: "plus.circle.fill")
|
|
.font(.headline)
|
|
}
|
|
.buttonStyle(.borderedProminent)
|
|
}
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
|
|
}
|
|
}
|
|
|
|
struct ConfiguredAppsGrid: View {
|
|
@ObservedObject var powerModeManager: PowerModeManager
|
|
@EnvironmentObject var enhancementService: AIEnhancementService
|
|
@State private var editingConfig: PowerModeConfig?
|
|
@State private var showingConfigSheet = false
|
|
|
|
var body: some View {
|
|
ScrollView {
|
|
VStack(spacing: 8) {
|
|
ForEach(powerModeManager.configurations.sorted(by: { $0.appName.localizedCaseInsensitiveCompare($1.appName) == .orderedAscending })) { config in
|
|
ConfiguredAppRow(
|
|
config: config,
|
|
isEditing: editingConfig?.id == config.id,
|
|
action: {
|
|
editingConfig = config
|
|
showingConfigSheet = true
|
|
}
|
|
)
|
|
.contextMenu {
|
|
Button(action: {
|
|
editingConfig = config
|
|
showingConfigSheet = true
|
|
}) {
|
|
Label("Edit", systemImage: "pencil")
|
|
}
|
|
Button(role: .destructive, action: {
|
|
powerModeManager.removeConfiguration(for: config.bundleIdentifier)
|
|
}) {
|
|
Label("Remove", systemImage: "trash")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.padding()
|
|
}
|
|
.sheet(isPresented: $showingConfigSheet, onDismiss: { editingConfig = nil }) {
|
|
if let config = editingConfig {
|
|
ConfigurationSheet(
|
|
mode: .edit(config),
|
|
isPresented: $showingConfigSheet,
|
|
powerModeManager: powerModeManager
|
|
)
|
|
.environmentObject(enhancementService)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct ConfiguredAppRow: View {
|
|
let config: PowerModeConfig
|
|
let isEditing: Bool
|
|
let action: () -> Void
|
|
@EnvironmentObject var enhancementService: AIEnhancementService
|
|
|
|
private var selectedPrompt: CustomPrompt? {
|
|
guard let promptId = config.selectedPrompt,
|
|
let uuid = UUID(uuidString: promptId) else { return nil }
|
|
return enhancementService.allPrompts.first { $0.id == uuid }
|
|
}
|
|
|
|
private var isWebsiteConfig: Bool {
|
|
return config.urlConfigs != nil && !config.urlConfigs!.isEmpty
|
|
}
|
|
|
|
var body: some View {
|
|
Button(action: action) {
|
|
HStack(spacing: 12) {
|
|
// Icon
|
|
if isWebsiteConfig {
|
|
Image(systemName: "globe")
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fit)
|
|
.frame(width: 32, height: 32)
|
|
.foregroundColor(.accentColor)
|
|
} else if let appURL = NSWorkspace.shared.urlForApplication(withBundleIdentifier: config.bundleIdentifier) {
|
|
Image(nsImage: NSWorkspace.shared.icon(forFile: appURL.path))
|
|
.resizable()
|
|
.frame(width: 32, height: 32)
|
|
}
|
|
|
|
// Info
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text(config.appName)
|
|
.font(.headline)
|
|
if isWebsiteConfig {
|
|
if let urlConfig = config.urlConfigs?.first {
|
|
Text(urlConfig.url)
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
} else {
|
|
Text(config.bundleIdentifier)
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
}
|
|
|
|
Spacer()
|
|
|
|
HStack(spacing: 8) {
|
|
HStack(spacing: 4) {
|
|
Image(systemName: config.isAIEnhancementEnabled ? "checkmark.circle.fill" : "circle")
|
|
.foregroundColor(config.isAIEnhancementEnabled ? .accentColor : .secondary)
|
|
.font(.system(size: 14))
|
|
Text("AI Enhancement")
|
|
.font(.system(size: 12, weight: .medium))
|
|
.foregroundColor(config.isAIEnhancementEnabled ? .accentColor : .secondary)
|
|
}
|
|
.padding(.horizontal, 8)
|
|
.padding(.vertical, 4)
|
|
.background(RoundedRectangle(cornerRadius: 6)
|
|
.fill(config.isAIEnhancementEnabled ? Color.accentColor.opacity(0.1) : Color.secondary.opacity(0.1)))
|
|
|
|
if config.isAIEnhancementEnabled {
|
|
if let prompt = selectedPrompt {
|
|
HStack(spacing: 4) {
|
|
Image(systemName: prompt.icon.rawValue)
|
|
.foregroundColor(.accentColor)
|
|
.font(.system(size: 14))
|
|
Text(prompt.title)
|
|
.font(.system(size: 12))
|
|
.foregroundColor(.secondary)
|
|
}
|
|
.padding(.horizontal, 8)
|
|
.padding(.vertical, 4)
|
|
.background(RoundedRectangle(cornerRadius: 6)
|
|
.fill(Color.accentColor.opacity(0.1)))
|
|
} else {
|
|
Text("No Prompt")
|
|
.font(.system(size: 12))
|
|
.foregroundColor(.secondary)
|
|
.padding(.horizontal, 8)
|
|
.padding(.vertical, 4)
|
|
.background(RoundedRectangle(cornerRadius: 6)
|
|
.fill(Color.secondary.opacity(0.1)))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.contentShape(Rectangle())
|
|
.padding(12)
|
|
.background(RoundedRectangle(cornerRadius: 8)
|
|
.fill(isEditing ? Color.accentColor.opacity(0.1) : Color(.windowBackgroundColor).opacity(0.4)))
|
|
.overlay(RoundedRectangle(cornerRadius: 8)
|
|
.stroke(isEditing ? Color.accentColor : Color.clear, lineWidth: 1))
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
}
|
|
|
|
struct AppConfigurationFormView: View {
|
|
let appName: String?
|
|
let appIcon: NSImage?
|
|
let isDefaultConfig: Bool
|
|
@Binding var isAIEnhancementEnabled: Bool
|
|
@Binding var selectedPromptId: UUID?
|
|
@EnvironmentObject var enhancementService: AIEnhancementService
|
|
|
|
var body: some View {
|
|
VStack(spacing: 20) {
|
|
VStack(spacing: 16) {
|
|
if !isDefaultConfig {
|
|
if let appIcon = appIcon {
|
|
HStack {
|
|
Image(nsImage: appIcon)
|
|
.resizable()
|
|
.frame(width: 32, height: 32)
|
|
Text(appName ?? "")
|
|
.font(.headline)
|
|
Spacer()
|
|
}
|
|
}
|
|
} else {
|
|
HStack {
|
|
Image(systemName: "gearshape.fill")
|
|
.font(.system(size: 24))
|
|
.foregroundColor(.accentColor)
|
|
Text("Default Settings")
|
|
.font(.headline)
|
|
Spacer()
|
|
}
|
|
|
|
Text("These settings will be applied to all applications that don't have specific configurations.")
|
|
.font(.subheadline)
|
|
.foregroundColor(.secondary)
|
|
.padding(.bottom, 8)
|
|
}
|
|
|
|
Toggle("AI Enhancement", isOn: $isAIEnhancementEnabled)
|
|
}
|
|
.padding(.horizontal)
|
|
|
|
if isAIEnhancementEnabled {
|
|
Divider()
|
|
|
|
VStack(alignment: .leading) {
|
|
Text("Select Prompt")
|
|
.font(.headline)
|
|
.padding(.horizontal)
|
|
|
|
let columns = [
|
|
GridItem(.adaptive(minimum: 80, maximum: 100), spacing: 36)
|
|
]
|
|
|
|
LazyVGrid(columns: columns, spacing: 24) {
|
|
ForEach(enhancementService.allPrompts) { prompt in
|
|
prompt.promptIcon(
|
|
isSelected: selectedPromptId == prompt.id,
|
|
onTap: { selectedPromptId = prompt.id }
|
|
)
|
|
}
|
|
}
|
|
.padding(.vertical, 12)
|
|
.padding(.horizontal, 16)
|
|
}
|
|
}
|
|
}
|
|
.padding(.vertical)
|
|
}
|
|
}
|
|
|
|
struct AppGridItem: View {
|
|
let app: (url: URL, name: String, bundleId: String, icon: NSImage)
|
|
let isSelected: Bool
|
|
let action: () -> Void
|
|
|
|
var body: some View {
|
|
Button(action: action) {
|
|
VStack(spacing: 8) {
|
|
Image(nsImage: app.icon)
|
|
.resizable()
|
|
.frame(width: 48, height: 48)
|
|
Text(app.name)
|
|
.font(.system(size: 12))
|
|
.lineLimit(2)
|
|
.multilineTextAlignment(.center)
|
|
.frame(height: 32)
|
|
}
|
|
.frame(width: 100)
|
|
.padding(8)
|
|
.background(RoundedRectangle(cornerRadius: 8)
|
|
.fill(isSelected ? Color.accentColor.opacity(0.1) : Color.clear))
|
|
.overlay(RoundedRectangle(cornerRadius: 8)
|
|
.stroke(isSelected ? Color.accentColor : Color.clear, lineWidth: 1))
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
}
|
|
|
|
// New component for feature highlights
|
|
struct FeatureHighlight: View {
|
|
let icon: String
|
|
let title: String
|
|
let description: String
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 8) {
|
|
HStack(spacing: 6) {
|
|
Image(systemName: icon)
|
|
.font(.system(size: 14, weight: .semibold))
|
|
.foregroundStyle(.blue)
|
|
Text(title)
|
|
.font(.system(size: 13, weight: .semibold))
|
|
}
|
|
|
|
Text(description)
|
|
.font(.system(size: 12))
|
|
.foregroundColor(.secondary)
|
|
.lineLimit(2)
|
|
}
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
}
|
|
}
|