feat: Add Delete key and undo/redo for preview changes

- Add Delete key shortcut to remove selected elements from preview
- Make preview changes (move, resize, delete) undoable with Ctrl+Z
- Use QTextCursor with beginEditBlock/endEditBlock for undoable updates
- Add Delete Selected action to Edit menu

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
rob 2025-12-23 14:55:03 -04:00
parent a8cc90c128
commit d7c64c1621
1 changed files with 26 additions and 3 deletions

View File

@ -18,7 +18,7 @@ from typing import Optional
from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QThread, QRectF, QPointF, QSizeF from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QThread, QRectF, QPointF, QSizeF
from PyQt6.QtGui import ( from PyQt6.QtGui import (
QFont, QColor, QAction, QKeySequence, QSyntaxHighlighter, QFont, QColor, QAction, QKeySequence, QSyntaxHighlighter,
QTextCharFormat, QPainter, QPen, QBrush QTextCharFormat, QPainter, QPen, QBrush, QTextCursor
) )
from PyQt6.QtWidgets import ( from PyQt6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
@ -1511,6 +1511,13 @@ class ArtifactEditorWindow(QMainWindow):
redo_action.triggered.connect(self.code_editor.redo) redo_action.triggered.connect(self.code_editor.redo)
edit_menu.addAction(redo_action) edit_menu.addAction(redo_action)
edit_menu.addSeparator()
delete_action = QAction("&Delete Selected", self)
delete_action.setShortcut(QKeySequence.StandardKey.Delete)
delete_action.triggered.connect(self._delete_selected_element)
edit_menu.addAction(delete_action)
# Templates menu (rebuilt when format changes) # Templates menu (rebuilt when format changes)
self.templates_menu = menubar.addMenu("&Templates") self.templates_menu = menubar.addMenu("&Templates")
self._rebuild_templates_menu() self._rebuild_templates_menu()
@ -2578,12 +2585,28 @@ class ArtifactEditorWindow(QMainWindow):
def _on_svg_scene_changed(self): def _on_svg_scene_changed(self):
"""Handle scene changes from the SVG manager (element moved, deleted, etc.).""" """Handle scene changes from the SVG manager (element moved, deleted, etc.)."""
# Regenerate SVG from the scene and update code editor # Regenerate SVG from the scene and update code editor
# Block signals to prevent triggering a re-render # Use undoable text replacement so Ctrl+Z works for preview changes
new_svg = self.svg_selection.generate_svg() new_svg = self.svg_selection.generate_svg()
# Block signals to prevent triggering a re-render loop
self.code_editor.blockSignals(True) self.code_editor.blockSignals(True)
self.code_editor.setPlainText(new_svg)
# Use QTextCursor to replace all text - this is undoable unlike setPlainText
cursor = self.code_editor.textCursor()
cursor.beginEditBlock() # Group as single undo operation
cursor.select(cursor.SelectionType.Document)
cursor.insertText(new_svg)
cursor.endEditBlock()
self.code_editor.blockSignals(False) self.code_editor.blockSignals(False)
def _delete_selected_element(self):
"""Delete the currently selected element in the SVG preview."""
if hasattr(self, 'svg_selection') and self.svg_selection:
self.svg_selection.delete_selected()
# Update the elements list
self._update_elements_list()
def _on_svg_overlay_element_selected(self, element): def _on_svg_overlay_element_selected(self, element):
"""Handle element selection from overlay.""" """Handle element selection from overlay."""
if element is None: if element is None: