Merge pull request #272 from gdmka/feat/enhancement-prompt-reorder
Added capability for reordering Enhancement Prompts
This commit is contained in:
commit
1004b8d465
@ -1,4 +1,5 @@
|
||||
import SwiftUI
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
struct EnhancementSettingsView: View {
|
||||
@EnvironmentObject private var enhancementService: AIEnhancementService
|
||||
@ -79,25 +80,22 @@ struct EnhancementSettingsView: View {
|
||||
Text("Enhancement Prompt")
|
||||
.font(.headline)
|
||||
|
||||
// Prompts Section
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
PromptSelectionGrid(
|
||||
prompts: enhancementService.allPrompts,
|
||||
selectedPromptId: enhancementService.selectedPromptId,
|
||||
onPromptSelected: { prompt in
|
||||
enhancementService.setActivePrompt(prompt)
|
||||
},
|
||||
onEditPrompt: { prompt in
|
||||
selectedPromptForEdit = prompt
|
||||
},
|
||||
onDeletePrompt: { prompt in
|
||||
enhancementService.deletePrompt(prompt)
|
||||
},
|
||||
onAddNewPrompt: {
|
||||
isEditingPrompt = true
|
||||
}
|
||||
)
|
||||
}
|
||||
// Reorderable prompts grid with drag-and-drop
|
||||
ReorderablePromptGrid(
|
||||
selectedPromptId: enhancementService.selectedPromptId,
|
||||
onPromptSelected: { prompt in
|
||||
enhancementService.setActivePrompt(prompt)
|
||||
},
|
||||
onEditPrompt: { prompt in
|
||||
selectedPromptForEdit = prompt
|
||||
},
|
||||
onDeletePrompt: { prompt in
|
||||
enhancementService.deletePrompt(prompt)
|
||||
},
|
||||
onAddNewPrompt: {
|
||||
isEditingPrompt = true
|
||||
}
|
||||
)
|
||||
}
|
||||
.padding()
|
||||
.background(CardBackground(isSelected: false))
|
||||
@ -115,3 +113,151 @@ struct EnhancementSettingsView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Drag & Drop Reorderable Grid
|
||||
private struct ReorderablePromptGrid: View {
|
||||
@EnvironmentObject private var enhancementService: AIEnhancementService
|
||||
|
||||
let selectedPromptId: UUID?
|
||||
let onPromptSelected: (CustomPrompt) -> Void
|
||||
let onEditPrompt: ((CustomPrompt) -> Void)?
|
||||
let onDeletePrompt: ((CustomPrompt) -> Void)?
|
||||
let onAddNewPrompt: (() -> Void)?
|
||||
|
||||
@State private var draggingItem: CustomPrompt?
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
if enhancementService.customPrompts.isEmpty {
|
||||
Text("No prompts available")
|
||||
.foregroundColor(.secondary)
|
||||
.font(.caption)
|
||||
} else {
|
||||
let columns = [
|
||||
GridItem(.adaptive(minimum: 80, maximum: 100), spacing: 36)
|
||||
]
|
||||
|
||||
LazyVGrid(columns: columns, spacing: 16) {
|
||||
ForEach(enhancementService.customPrompts) { prompt in
|
||||
prompt.promptIcon(
|
||||
isSelected: selectedPromptId == prompt.id,
|
||||
onTap: {
|
||||
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
|
||||
onPromptSelected(prompt)
|
||||
}
|
||||
},
|
||||
onEdit: onEditPrompt,
|
||||
onDelete: onDeletePrompt
|
||||
)
|
||||
.opacity(draggingItem?.id == prompt.id ? 0.3 : 1.0)
|
||||
.scaleEffect(draggingItem?.id == prompt.id ? 1.05 : 1.0)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 14)
|
||||
.stroke(
|
||||
draggingItem != nil && draggingItem?.id != prompt.id
|
||||
? Color.accentColor.opacity(0.25)
|
||||
: Color.clear,
|
||||
lineWidth: 1
|
||||
)
|
||||
)
|
||||
.animation(.easeInOut(duration: 0.15), value: draggingItem?.id == prompt.id)
|
||||
.onDrag {
|
||||
draggingItem = prompt
|
||||
return NSItemProvider(object: prompt.id.uuidString as NSString)
|
||||
}
|
||||
.onDrop(
|
||||
of: [UTType.text],
|
||||
delegate: PromptDropDelegate(
|
||||
item: prompt,
|
||||
prompts: $enhancementService.customPrompts,
|
||||
draggingItem: $draggingItem
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if let onAddNewPrompt = onAddNewPrompt {
|
||||
CustomPrompt.addNewButton {
|
||||
onAddNewPrompt()
|
||||
}
|
||||
.help("Add new prompt")
|
||||
.onDrop(
|
||||
of: [UTType.text],
|
||||
delegate: PromptEndDropDelegate(
|
||||
prompts: $enhancementService.customPrompts,
|
||||
draggingItem: $draggingItem
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 12)
|
||||
.padding(.horizontal, 16)
|
||||
|
||||
HStack {
|
||||
Image(systemName: "info.circle")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
Text("Double-click to edit • Right-click for more options")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.padding(.top, 8)
|
||||
.padding(.horizontal, 16)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Drop Delegates
|
||||
private struct PromptDropDelegate: DropDelegate {
|
||||
let item: CustomPrompt
|
||||
@Binding var prompts: [CustomPrompt]
|
||||
@Binding var draggingItem: CustomPrompt?
|
||||
|
||||
func dropEntered(info: DropInfo) {
|
||||
guard let draggingItem = draggingItem, draggingItem != item else { return }
|
||||
guard let fromIndex = prompts.firstIndex(of: draggingItem),
|
||||
let toIndex = prompts.firstIndex(of: item) else { return }
|
||||
|
||||
// Move item as you hover for immediate visual update
|
||||
if prompts[toIndex].id != draggingItem.id {
|
||||
withAnimation(.easeInOut(duration: 0.12)) {
|
||||
let from = fromIndex
|
||||
let to = toIndex
|
||||
prompts.move(fromOffsets: IndexSet(integer: from), toOffset: to > from ? to + 1 : to)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dropUpdated(info: DropInfo) -> DropProposal? {
|
||||
DropProposal(operation: .move)
|
||||
}
|
||||
|
||||
func performDrop(info: DropInfo) -> Bool {
|
||||
draggingItem = nil
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private struct PromptEndDropDelegate: DropDelegate {
|
||||
@Binding var prompts: [CustomPrompt]
|
||||
@Binding var draggingItem: CustomPrompt?
|
||||
|
||||
func validateDrop(info: DropInfo) -> Bool { true }
|
||||
func dropUpdated(info: DropInfo) -> DropProposal? { DropProposal(operation: .move) }
|
||||
|
||||
func performDrop(info: DropInfo) -> Bool {
|
||||
guard let draggingItem = draggingItem,
|
||||
let currentIndex = prompts.firstIndex(of: draggingItem) else {
|
||||
self.draggingItem = nil
|
||||
return false
|
||||
}
|
||||
|
||||
// Move to end if dropped on the trailing "Add New" tile
|
||||
withAnimation(.easeInOut(duration: 0.12)) {
|
||||
prompts.move(fromOffsets: IndexSet(integer: currentIndex), toOffset: prompts.endIndex)
|
||||
}
|
||||
self.draggingItem = nil
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user