           (assoc-in $ [:pages-by-id pageid :shapes] shapes)
           (update-in $ [:shapes-by-id] dissoc sid))))))
 
+(defn move-shape
+  "Mark a shape selected for drawing in the canvas."
+  [sid [dx dy :as delta]]
+  (reify
+    rs/UpdateEvent
+    (-apply-update [_ state]
+      (let [shape (get-in state [:shapes-by-id sid])]
+        (update-in state [:shapes-by-id sid] shapes/-move {:dx dx :dy dy})))))
+
+(defn update-shape-rotation
+  [sid rotation]
+  {:pre [(number? rotation)
+         (>= rotation 0)
+         (>= 360 rotation)]}
+  (reify
+    rs/UpdateEvent
+    (-apply-update [_ state]
+      (update-in state [:shapes-by-id sid]
+                 shapes/-rotate rotation))))
+
+;; TODO: implement locked resize
+
+(defn update-shape-size
+  [sid {:keys [width height lock] :as opts}]
+  (sc/validate! +shape-update-size-schema+ opts)
+  (reify
+    rs/UpdateEvent
+    (-apply-update [_ state]
+      (let [shape (get-in state [:shapes-by-id sid])
+            size (select-keys shape [:width :height])
+            size (merge size
+                        (when width {:width width})
+                        (when height {:height height}))]
+        (update-in state [:shapes-by-id sid]
+                   shapes/-resize size)))))
+
+(defn update-shape-position
+  [sid {:keys [x y] :as opts}]
+  (sc/validate! +shape-update-position-schema+ opts)
+  (reify
+    rs/UpdateEvent
+    (-apply-update [_ state]
+      (update-in state [:shapes-by-id sid]
+                 merge
+                 (when x {:x x})
+                 (when y {:y y})))))
+
+(defn update-shape-fill
+  [sid {:keys [fill opacity] :as opts}]
+  (sc/validate! +shape-update-fill-schema+ opts)
+  (reify
+    rs/UpdateEvent
+    (-apply-update [_ state]
+      (update-in state [:shapes-by-id sid]
+                 merge
+                 (when fill {:fill fill})
+                 (when opacity {:opacity opacity})))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Events (for selected)
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defn deselect-all
+  "Mark a shape selected for drawing in the canvas."
+  []
+  (reify
+    rs/UpdateEvent
+    (-apply-update [_ state]
+      (assoc-in state [:workspace :selected] #{}))))
+
 (defn remove-selected
   "Deselect all and remove all selected shapes."
   []

                        (map :id))]
        (assoc-in state [:workspace :selected] (into #{} shapes))))))

(defn deselect-all
  "Mark a shape selected for drawing in the canvas."
  []
  (reify
    rs/UpdateEvent
    (-apply-update [_ state]
      (assoc-in state [:workspace :selected] #{}))))

(defn add-shape
  "Mark a shape selected for drawing in the canvas."
  [shape props]

      (let [selected (get-in state [:workspace :selected])]
        (rx/from-coll
         (into [(deselect-all)] (map #(delete-shape %) selected)))))))

(defn move-selected
  "Move a minimal position unit the selected shapes."
