Add edit functionality to word replacements

This commit is contained in:
Beingpax 2025-08-02 00:43:15 +05:45
parent 080f985059
commit 2a9ead63de
2 changed files with 167 additions and 1 deletions

View File

@ -0,0 +1,134 @@
import SwiftUI
/// A reusable sheet for editing an existing word replacement entry.
/// Mirrors the UI of `AddReplacementSheet` for consistency while pre-populating
/// the fields with the existing values.
struct EditReplacementSheet: View {
@ObservedObject var manager: WordReplacementManager
let originalKey: String
@Environment(\.dismiss) private var dismiss
@State private var originalWord: String
@State private var replacementWord: String
// MARK: Initialiser
init(manager: WordReplacementManager, originalKey: String) {
self.manager = manager
self.originalKey = originalKey
_originalWord = State(initialValue: originalKey)
_replacementWord = State(initialValue: manager.replacements[originalKey] ?? "")
}
var body: some View {
VStack(spacing: 0) {
header
Divider()
formContent
}
.frame(width: 460, height: 480)
}
// MARK: Subviews
private var header: some View {
HStack {
Button("Cancel", role: .cancel) { dismiss() }
.buttonStyle(.borderless)
.keyboardShortcut(.escape, modifiers: [])
Spacer()
Text("Edit Word Replacement")
.font(.headline)
Spacer()
Button("Save") { saveChanges() }
.buttonStyle(.borderedProminent)
.controlSize(.small)
.disabled(originalWord.isEmpty || replacementWord.isEmpty)
.keyboardShortcut(.return, modifiers: [])
}
.padding(.horizontal)
.padding(.vertical, 12)
.background(CardBackground(isSelected: false))
}
private var formContent: some View {
ScrollView {
VStack(spacing: 20) {
descriptionSection
inputSection
}
.padding(.vertical)
}
}
private var descriptionSection: some View {
Text("Update the word or phrase that should be automatically replaced during AI enhancement.")
.font(.subheadline)
.foregroundColor(.secondary)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal)
.padding(.top, 8)
}
private var inputSection: some View {
VStack(spacing: 16) {
// Original Text Field
VStack(alignment: .leading, spacing: 6) {
HStack {
Text("Original Text")
.font(.headline)
Text("Required")
.font(.caption)
.foregroundColor(.secondary)
}
TextField("Enter word or phrase to replace", text: $originalWord)
.textFieldStyle(.roundedBorder)
}
.padding(.horizontal)
// Replacement Text Field
VStack(alignment: .leading, spacing: 6) {
HStack {
Text("Replacement Text")
.font(.headline)
Text("Required")
.font(.caption)
.foregroundColor(.secondary)
}
TextEditor(text: $replacementWord)
.font(.body)
.frame(height: 100)
.padding(8)
.background(Color(.textBackgroundColor))
.cornerRadius(6)
.overlay(
RoundedRectangle(cornerRadius: 6)
.stroke(Color(.separatorColor), lineWidth: 1)
)
}
.padding(.horizontal)
}
}
// MARK: Actions
private func saveChanges() {
let newOriginal = originalWord.trimmingCharacters(in: .whitespacesAndNewlines)
let newReplacement = replacementWord.trimmingCharacters(in: .whitespacesAndNewlines)
guard !newOriginal.isEmpty, !newReplacement.isEmpty else { return }
manager.updateReplacement(oldOriginal: originalKey, newOriginal: newOriginal, newReplacement: newReplacement)
dismiss()
}
}
// MARK: Preview
#if DEBUG
struct EditReplacementSheet_Previews: PreviewProvider {
static var previews: some View {
EditReplacementSheet(manager: WordReplacementManager(), originalKey: "hello")
}
}
#endif

View File

@ -1,5 +1,9 @@
import SwiftUI
extension String: Identifiable {
public var id: String { self }
}
class WordReplacementManager: ObservableObject {
@Published var replacements: [String: String] {
didSet {
@ -25,12 +29,23 @@ class WordReplacementManager: ObservableObject {
func removeReplacement(original: String) {
replacements.removeValue(forKey: original)
}
func updateReplacement(oldOriginal: String, newOriginal: String, newReplacement: String) {
// Remove the old key if the original text has changed
if oldOriginal != newOriginal {
replacements.removeValue(forKey: oldOriginal)
}
// Update (or insert) the new key/value pair
replacements[newOriginal] = newReplacement
}
}
struct WordReplacementView: View {
@StateObject private var manager = WordReplacementManager()
@State private var showAddReplacementModal = false
@State private var showAlert = false
@State private var editingOriginal: String? = nil
@State private var alertMessage = ""
var body: some View {
@ -88,7 +103,8 @@ struct WordReplacementView: View {
ReplacementRow(
original: original,
replacement: manager.replacements[original] ?? "",
onDelete: { manager.removeReplacement(original: original) }
onDelete: { manager.removeReplacement(original: original) },
onEdit: { editingOriginal = original }
)
if original != manager.replacements.keys.sorted().last {
@ -106,6 +122,11 @@ struct WordReplacementView: View {
.sheet(isPresented: $showAddReplacementModal) {
AddReplacementSheet(manager: manager)
}
// Edit existing replacement
.sheet(item: $editingOriginal) { original in
EditReplacementSheet(manager: manager, originalKey: original)
}
}
}
@ -287,6 +308,7 @@ struct ReplacementRow: View {
let original: String
let replacement: String
let onDelete: () -> Void
let onEdit: () -> Void
var body: some View {
HStack(spacing: 16) {
@ -321,6 +343,16 @@ struct ReplacementRow: View {
}
.frame(maxWidth: .infinity)
// Edit Button
Button(action: onEdit) {
Image(systemName: "pencil.circle.fill")
.symbolRenderingMode(.hierarchical)
.foregroundColor(.accentColor)
.font(.system(size: 16))
}
.buttonStyle(.borderless)
.help("Edit replacement")
// Delete Button
Button(action: onDelete) {
Image(systemName: "xmark.circle.fill")