Delete a timeline row with everything in it

Delete or Backspace, or the × on the selected row, takes the node out of its
symbol along with every node hanging off it. One edit, so undo brings it back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Olive Vaughn 2026-09-30 01:11:16 -04:00
parent 7246534505
commit 4b8123d5e2
5 changed files with 34 additions and 7 deletions

View file

@ -144,6 +144,11 @@
[nodes id]
(into #{} (filter #(some #{id} (symbol/lineage nodes %))) (keys nodes)))
(defn delete-node
"Take node `id` out of symbol `sid`, with everything hanging off it."
[clip sid id]
(clip/update-symbol clip sid update :nodes #(apply dissoc % (subtree % id))))
(defn- transplant
"Move node `id` from symbol `host`, where frame `frame` is showing, into symbol
`target`, keeping where it is on screen and when. `carry` is the matrix from

View file

@ -7,6 +7,7 @@
[arthur.domain.leaf :as leaf]
[arthur.events.edit :as edit]
[arthur.events.playback :as pb]
[arthur.events.ui :as ui]
[arthur.footage.store :as store]
[re-frame.core :as rf]))
@ -66,16 +67,19 @@
(or (#{"INPUT" "TEXTAREA" "SELECT"} (.-tagName target)) (.-isContentEditable target)))
(defn install-keys!
"⌘Z / Ctrl+Z undoes, with Shift redoes, and Ctrl+Y redoes. Not while typing
in a field, where the browser's own undo is the one wanted."
"⌘Z / Ctrl+Z undoes, with Shift redoes, and Ctrl+Y redoes; Delete or
Backspace deletes the selected node, which undo brings back. Not while typing
in a field, where the browser's own keys are the ones wanted."
[]
(.addEventListener
js/window "keydown"
(fn [^js e]
(when (and (or (.-metaKey e) (.-ctrlKey e)) (not (typing? (.-target e))))
(when-not (typing? (.-target e))
(let [k (.toLowerCase (.-key e))]
(when-let [ev (cond (and (= k "z") (.-shiftKey e)) ::redo
(= k "z") ::undo
(= k "y") ::redo)]
(when-let [ev (if (or (.-metaKey e) (.-ctrlKey e))
(cond (and (= k "z") (.-shiftKey e)) ::redo
(= k "z") ::undo
(= k "y") ::redo)
(when (#{"delete" "backspace"} k) ::ui/delete-selected))]
(.preventDefault e)
(rf/dispatch [ev])))))))

View file

@ -195,6 +195,16 @@
(:refused r) (refused db (:refused r))
:else (edit/edit db (constantly (:clip r)))))))
(rf/reg-event-db
::delete-selected
(fn [db _]
(let [[kind sid id] (get-in db [:ui :selection])]
(if (= :node kind)
(-> db
(edit/edit #(nest/delete-node % sid id))
(assoc-in [:ui :selection] nil))
db))))
(rf/reg-event-db
::restack
;; Onto the edge of a row in another symbol, it goes into that symbol first:

View file

@ -259,7 +259,13 @@
(rf/dispatch [::ui/toggle-row path]))}
(when expandable? (if expanded? "▾" "▸"))]
[:span.name label]
(when node? [:span.kind (str "·" (name node-kind))])]))
(when node? [:span.kind (str "·" (name node-kind))])
(when (and node? (= select selection))
[:button.tl-delete {:title "delete, with everything in it (⌫)"
:on-click (fn [^js e]
(.stopPropagation e)
(rf/dispatch [::ui/delete-selected]))}
"×"])]))
(defn- track-cell
"`sliding` is the pointer's side of a bar being dragged, `{:path :x :width