diff --git a/VoiceInk/PowerMode/PowerModeConfig.swift b/VoiceInk/PowerMode/PowerModeConfig.swift index 1ea9d90..9524288 100644 --- a/VoiceInk/PowerMode/PowerModeConfig.swift +++ b/VoiceInk/PowerMode/PowerModeConfig.swift @@ -180,6 +180,11 @@ class PowerModeManager: ObservableObject { } } + func moveConfigurations(fromOffsets: IndexSet, toOffset: Int) { + configurations.move(fromOffsets: fromOffsets, toOffset: toOffset) + saveConfigurations() + } + func getConfigurationForURL(_ url: String) -> PowerModeConfig? { let cleanedURL = cleanURL(url) diff --git a/VoiceInk/PowerMode/PowerModeView.swift b/VoiceInk/PowerMode/PowerModeView.swift index e9cd6f9..a031e55 100644 --- a/VoiceInk/PowerMode/PowerModeView.swift +++ b/VoiceInk/PowerMode/PowerModeView.swift @@ -64,6 +64,7 @@ struct PowerModeView: View { @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) { @@ -90,23 +91,41 @@ struct PowerModeView: View { Spacer() - 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)) + 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()) } - .foregroundColor(.white) - .padding(.horizontal, 12) - .padding(.vertical, 6) - .background(Color.accentColor) - .cornerRadius(6) + 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(.white) + .padding(.horizontal, 12) + .padding(.vertical, 6) + .background(Color.accentColor) + .cornerRadius(6) + } + .buttonStyle(PlainButtonStyle()) } - .buttonStyle(PlainButtonStyle()) } } .padding(.horizontal, 24) @@ -118,50 +137,109 @@ struct PowerModeView: View { .frame(height: 1) .padding(.horizontal, 24) - GeometryReader { geometry in - ScrollView { - VStack(spacing: 0) { - if powerModeManager.configurations.isEmpty { - VStack(spacing: 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() - .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)) + + 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) - .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(.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) + } } - ) - .padding(.horizontal, 24) - .padding(.vertical, 20) - - Spacer() - .frame(height: 40) + + 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) + } } } }