mirror of
https://github.com/penpot/penpot.git
synced 2026-09-13 07:30:24 +00:00
♻️ Update frontend path editing data layer for :path-data
Update all path editing data namespaces in the frontend to use :path-data
instead of :content when accessing or modifying path shape content.
Also update transform-attrs to include :path-data alongside :content.
Affected namespaces:
- app.main.data.workspace.path.{state,changes,drawing,edition,streams,
selection,helpers,common,undo,tools,shapes-to-path}
- app.main.data.workspace.drawing.curve
- app.main.data.workspace.modifiers
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
This commit is contained in:
parent
ab8577f957
commit
dc11d19909
@ -32,7 +32,7 @@
|
|||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [objects (dsh/lookup-page-objects state)
|
(let [objects (dsh/lookup-page-objects state)
|
||||||
content (dm/get-in state [:workspace-drawing :object :content])
|
content (dm/get-in state [:workspace-drawing :object :path-data])
|
||||||
position (path.segment/get-handler-point content 0 nil)
|
position (path.segment/get-handler-point content 0 nil)
|
||||||
|
|
||||||
frame-id (->> (ctst/top-nested-frame objects position)
|
frame-id (->> (ctst/top-nested-frame objects position)
|
||||||
@ -70,7 +70,7 @@
|
|||||||
points' (grc/rect->points selrect)]
|
points' (grc/rect->points selrect)]
|
||||||
(-> object
|
(-> object
|
||||||
(assoc ::points points)
|
(assoc ::points points)
|
||||||
(assoc :content content)
|
(assoc :path-data content)
|
||||||
(assoc :selrect selrect)
|
(assoc :selrect selrect)
|
||||||
(assoc :points points'))))))))
|
(assoc :points points'))))))))
|
||||||
|
|
||||||
@ -88,7 +88,7 @@
|
|||||||
|
|
||||||
(-> shape
|
(-> shape
|
||||||
(dissoc ::points)
|
(dissoc ::points)
|
||||||
(assoc :content content)
|
(assoc :path-data content)
|
||||||
(assoc :selrect selrect)
|
(assoc :selrect selrect)
|
||||||
(assoc :points points)
|
(assoc :points points)
|
||||||
(cond-> (or (empty? points)
|
(cond-> (or (empty? points)
|
||||||
|
|||||||
@ -53,6 +53,7 @@
|
|||||||
:width
|
:width
|
||||||
:height
|
:height
|
||||||
:content
|
:content
|
||||||
|
:path-data
|
||||||
:transform
|
:transform
|
||||||
:transform-inverse
|
:transform-inverse
|
||||||
:rotation
|
:rotation
|
||||||
@ -435,8 +436,8 @@
|
|||||||
[objects path-modifiers]
|
[objects path-modifiers]
|
||||||
(letfn [(apply-path-modifier
|
(letfn [(apply-path-modifier
|
||||||
[shape {:keys [content-modifiers]}]
|
[shape {:keys [content-modifiers]}]
|
||||||
(let [shape (update shape :content upc/apply-content-modifiers content-modifiers)
|
(let [shape (update shape :path-data upc/apply-content-modifiers content-modifiers)
|
||||||
[points selrect] (helpers/content->points+selrect shape (:content shape))]
|
[points selrect] (helpers/content->points+selrect shape (:path-data shape))]
|
||||||
(assoc shape :selrect selrect :points points)))]
|
(assoc shape :selrect selrect :points points)))]
|
||||||
(loop [modifiers (seq path-modifiers)
|
(loop [modifiers (seq path-modifiers)
|
||||||
result objects]
|
result objects]
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
(update objects shape-id
|
(update objects shape-id
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
(-> shape
|
(-> shape
|
||||||
(assoc :content old-content)
|
(assoc :path-data old-content)
|
||||||
(path/update-geometry))))
|
(path/update-geometry))))
|
||||||
|
|
||||||
changes
|
changes
|
||||||
@ -54,7 +54,7 @@
|
|||||||
(pcb/update-shapes [shape-id]
|
(pcb/update-shapes [shape-id]
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
(-> shape
|
(-> shape
|
||||||
(assoc :content new-content)
|
(assoc :path-data new-content)
|
||||||
(path/update-geometry))))
|
(path/update-geometry))))
|
||||||
(pcb/resize-parents [shape-id])))))
|
(pcb/resize-parents [shape-id])))))
|
||||||
|
|
||||||
@ -65,7 +65,7 @@
|
|||||||
(ptk/reify ::save-path-content
|
(ptk/reify ::save-path-content
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [content (st/get-path state :content)
|
(let [content (st/get-path state :path-data)
|
||||||
content (if (and (not preserve-move-to)
|
content (if (and (not preserve-move-to)
|
||||||
(= (-> content last :command) :move-to))
|
(= (-> content last :command) :move-to))
|
||||||
(path/content (take (dec (count content)) content))
|
(path/content (take (dec (count content)) content))
|
||||||
@ -84,7 +84,7 @@
|
|||||||
;; do nothing
|
;; do nothing
|
||||||
(when-let [shape (get objects id)]
|
(when-let [shape (get objects id)]
|
||||||
(when-let [old-content (dm/get-in local [:edit-path id :old-content])]
|
(when-let [old-content (dm/get-in local [:edit-path id :old-content])]
|
||||||
(let [new-content (get shape :content)
|
(let [new-content (get shape :path-data)
|
||||||
changes (generate-path-changes it objects page-id shape old-content new-content)]
|
changes (generate-path-changes it objects page-id shape old-content new-content)]
|
||||||
(rx/of (dch/commit-changes changes))))))))))
|
(rx/of (dch/commit-changes changes))))))))))
|
||||||
|
|
||||||
|
|||||||
@ -25,4 +25,4 @@
|
|||||||
(let [id (st/get-path-id state)]
|
(let [id (st/get-path-id state)]
|
||||||
(-> state
|
(-> state
|
||||||
(update-in [:workspace-local :edit-path id] clean-edit-state)
|
(update-in [:workspace-local :edit-path id] clean-edit-state)
|
||||||
(update-in (st/get-path-location state :content) path/close-subpaths))))))
|
(update-in (st/get-path-location state :path-data) path/close-subpaths))))))
|
||||||
|
|||||||
@ -93,7 +93,7 @@
|
|||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [id (st/get-path-id state)
|
(let [id (st/get-path-id state)
|
||||||
content (st/get-path state :content)
|
content (st/get-path state :path-data)
|
||||||
|
|
||||||
index (or index (count content))
|
index (or index (count content))
|
||||||
prefix (or prefix :c1)
|
prefix (or prefix :c1)
|
||||||
@ -123,7 +123,7 @@
|
|||||||
(let [id (st/get-path-id state)
|
(let [id (st/get-path-id state)
|
||||||
|
|
||||||
modifiers (get-in state [:workspace-local :edit-path id :content-modifiers])
|
modifiers (get-in state [:workspace-local :edit-path id :content-modifiers])
|
||||||
content (-> (st/get-path state :content)
|
content (-> (st/get-path state :path-data)
|
||||||
(path/apply-content-modifiers modifiers))
|
(path/apply-content-modifiers modifiers))
|
||||||
|
|
||||||
handler (get-in state [:workspace-local :edit-path id :drag-handler])]
|
handler (get-in state [:workspace-local :edit-path id :drag-handler])]
|
||||||
@ -147,7 +147,7 @@
|
|||||||
(ptk/reify ::close-path-drag-start
|
(ptk/reify ::close-path-drag-start
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [content (st/get-path state :content)
|
(let [content (st/get-path state :path-data)
|
||||||
handlers (-> (path.segment/get-handlers content)
|
handlers (-> (path.segment/get-handlers content)
|
||||||
(get position))
|
(get position))
|
||||||
|
|
||||||
@ -279,7 +279,7 @@
|
|||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [objects (dsh/lookup-page-objects state)
|
(let [objects (dsh/lookup-page-objects state)
|
||||||
content (get-in state [:workspace-drawing :object :content] [])
|
content (get-in state [:workspace-drawing :object :path-data] [])
|
||||||
|
|
||||||
;; FIXME: use native operation for retrieve the first position
|
;; FIXME: use native operation for retrieve the first position
|
||||||
position (-> (nth content 0)
|
position (-> (nth content 0)
|
||||||
@ -305,7 +305,7 @@
|
|||||||
(ptk/reify ::handle-drawing-end
|
(ptk/reify ::handle-drawing-end
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [content (some-> (dm/get-in state [:workspace-drawing :object :content])
|
(let [content (some-> (dm/get-in state [:workspace-drawing :object :path-data])
|
||||||
(path/check-content))]
|
(path/check-content))]
|
||||||
(if (> (count content) 1)
|
(if (> (count content) 1)
|
||||||
(assoc-in state [:workspace-drawing :object :initialized?] true)
|
(assoc-in state [:workspace-drawing :object :initialized?] true)
|
||||||
@ -313,7 +313,7 @@
|
|||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(when-let [content (dm/get-in state [:workspace-drawing :object :content])]
|
(when-let [content (dm/get-in state [:workspace-drawing :object :path-data])]
|
||||||
(if (> (count content) 1)
|
(if (> (count content) 1)
|
||||||
(rx/of (setup-frame)
|
(rx/of (setup-frame)
|
||||||
(dwdc/handle-finish-drawing)
|
(dwdc/handle-finish-drawing)
|
||||||
@ -350,7 +350,7 @@
|
|||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [id (dm/get-in state [:workspace-local :edition])
|
(let [id (dm/get-in state [:workspace-local :edition])
|
||||||
objects (dsh/lookup-page-objects state)
|
objects (dsh/lookup-page-objects state)
|
||||||
content (dm/get-in objects [id :content])]
|
content (dm/get-in objects [id :path-data])]
|
||||||
(if content
|
(if content
|
||||||
(update-in state [:workspace-local :edit-path id] assoc :old-content content)
|
(update-in state [:workspace-local :edit-path id] assoc :old-content content)
|
||||||
state)))
|
state)))
|
||||||
@ -412,7 +412,7 @@
|
|||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [id (st/get-path-id state)
|
(let [id (st/get-path-id state)
|
||||||
content (st/get-path state :content)
|
content (st/get-path state :path-data)
|
||||||
old-content (get-in state [:workspace-local :edit-path id :old-content])
|
old-content (get-in state [:workspace-local :edit-path id :old-content])
|
||||||
mode (get-in state [:workspace-local :edit-path id :edit-mode])
|
mode (get-in state [:workspace-local :edit-path id :edit-mode])
|
||||||
empty-content? (empty? content)]
|
empty-content? (empty? content)]
|
||||||
|
|||||||
@ -32,7 +32,7 @@
|
|||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
|
|
||||||
(let [content (st/get-path state :content)
|
(let [content (st/get-path state :path-data)
|
||||||
modifiers (helpers/move-handler-modifiers content index prefix false match-opposite? dx dy)
|
modifiers (helpers/move-handler-modifiers content index prefix false match-opposite? dx dy)
|
||||||
[cx cy] (if (= prefix :c1) [:c1x :c1y] [:c2x :c2y])
|
[cx cy] (if (= prefix :c1) [:c1x :c1y] [:c2x :c2y])
|
||||||
point (gpt/point (+ (dm/get-in content [index :params cx]) dx)
|
point (gpt/point (+ (dm/get-in content [index :params cx]) dx)
|
||||||
@ -56,7 +56,7 @@
|
|||||||
(let [page-id (get state :current-page-id state)
|
(let [page-id (get state :current-page-id state)
|
||||||
objects (dsh/lookup-page-objects state)
|
objects (dsh/lookup-page-objects state)
|
||||||
|
|
||||||
content (get shape :content)
|
content (get shape :path-data)
|
||||||
new-content (path/apply-content-modifiers content content-modifiers)
|
new-content (path/apply-content-modifiers content content-modifiers)
|
||||||
|
|
||||||
old-points (path/get-points content)
|
old-points (path/get-points content)
|
||||||
@ -64,7 +64,7 @@
|
|||||||
point-change (->> (map hash-map old-points new-points) (reduce merge))]
|
point-change (->> (map hash-map old-points new-points) (reduce merge))]
|
||||||
|
|
||||||
(when (and (some? new-content) (some? shape))
|
(when (and (some? new-content) (some? shape))
|
||||||
(let [changes (changes/generate-path-changes it objects page-id shape (:content shape) new-content)]
|
(let [changes (changes/generate-path-changes it objects page-id shape (:path-data shape) new-content)]
|
||||||
(if (empty? new-content)
|
(if (empty? new-content)
|
||||||
(rx/of (dch/commit-changes changes)
|
(rx/of (dch/commit-changes changes)
|
||||||
(dwe/clear-edition-mode))
|
(dwe/clear-edition-mode))
|
||||||
@ -99,7 +99,7 @@
|
|||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [id (st/get-path-id state)
|
(let [id (st/get-path-id state)
|
||||||
content (st/get-path state :content)
|
content (st/get-path state :path-data)
|
||||||
modifiers-reducer (partial modify-content-point content move-modifier)
|
modifiers-reducer (partial modify-content-point content move-modifier)
|
||||||
content-modifiers (dm/get-in state [:workspace-local :edit-path id :content-modifiers] {})
|
content-modifiers (dm/get-in state [:workspace-local :edit-path id :content-modifiers] {})
|
||||||
content-modifiers (->> points
|
content-modifiers (->> points
|
||||||
@ -113,7 +113,7 @@
|
|||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [id (st/get-path-id state)
|
(let [id (st/get-path-id state)
|
||||||
content (st/get-path state :content)
|
content (st/get-path state :path-data)
|
||||||
to-point (cond-> to-point
|
to-point (cond-> to-point
|
||||||
(:shift? to-point) (path.helpers/position-fixed-angle from-point))
|
(:shift? to-point) (path.helpers/position-fixed-angle from-point))
|
||||||
|
|
||||||
@ -161,7 +161,7 @@
|
|||||||
|
|
||||||
start-position (apply min-key #(gpt/distance start-position %) selected-points)
|
start-position (apply min-key #(gpt/distance start-position %) selected-points)
|
||||||
|
|
||||||
content (st/get-path state :content)
|
content (st/get-path state :path-data)
|
||||||
points (path/get-points content)]
|
points (path/get-points content)]
|
||||||
|
|
||||||
(rx/concat
|
(rx/concat
|
||||||
@ -254,7 +254,7 @@
|
|||||||
start-delta-x (dm/get-in modifiers [index cx] 0)
|
start-delta-x (dm/get-in modifiers [index cx] 0)
|
||||||
start-delta-y (dm/get-in modifiers [index cy] 0)
|
start-delta-y (dm/get-in modifiers [index cy] 0)
|
||||||
|
|
||||||
content (st/get-path state :content)
|
content (st/get-path state :path-data)
|
||||||
points (path/get-points content)
|
points (path/get-points content)
|
||||||
|
|
||||||
point (-> content (nth (if (= prefix :c1) (dec index) index)) (path.helpers/segment->point))
|
point (-> content (nth (if (= prefix :c1) (dec index) index)) (path.helpers/segment->point))
|
||||||
@ -298,7 +298,7 @@
|
|||||||
shape (get objects id)]
|
shape (get objects id)]
|
||||||
|
|
||||||
(-> state
|
(-> state
|
||||||
(st/set-content (path/close-subpaths (:content shape)))
|
(st/set-content (path/close-subpaths (:path-data shape)))
|
||||||
(update-in [:workspace-local :edit-path id]
|
(update-in [:workspace-local :edit-path id]
|
||||||
(fn [state]
|
(fn [state]
|
||||||
(let [state (if state
|
(let [state (if state
|
||||||
@ -308,7 +308,7 @@
|
|||||||
{:edit-mode :move
|
{:edit-mode :move
|
||||||
:selected #{}
|
:selected #{}
|
||||||
:snap-toggled false})]
|
:snap-toggled false})]
|
||||||
(assoc state :old-content (:content shape))))))))
|
(assoc state :old-content (:path-data shape))))))))
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ _ stream]
|
(watch [_ _ stream]
|
||||||
@ -340,7 +340,7 @@
|
|||||||
(ptk/reify ::split-segments
|
(ptk/reify ::split-segments
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [content (st/get-path state :content)]
|
(let [content (st/get-path state :path-data)]
|
||||||
(-> state
|
(-> state
|
||||||
(assoc-in [:workspace-local :edit-path id :old-content] content)
|
(assoc-in [:workspace-local :edit-path id :old-content] content)
|
||||||
(st/set-content (-> content
|
(st/set-content (-> content
|
||||||
|
|||||||
@ -15,9 +15,9 @@
|
|||||||
(defn append-node
|
(defn append-node
|
||||||
"Creates a new node in the path. Usually used when drawing."
|
"Creates a new node in the path. Usually used when drawing."
|
||||||
[shape position prev-point prev-handler]
|
[shape position prev-point prev-handler]
|
||||||
(let [segment (path.segment/next-node (:content shape) position prev-point prev-handler)]
|
(let [segment (path.segment/next-node (:path-data shape) position prev-point prev-handler)]
|
||||||
(-> shape
|
(-> shape
|
||||||
(update :content path.segment/append-segment segment)
|
(update :path-data path.segment/append-segment segment)
|
||||||
(path/update-geometry))))
|
(path/update-geometry))))
|
||||||
|
|
||||||
(defn angle-points [common p1 p2]
|
(defn angle-points [common p1 p2]
|
||||||
|
|||||||
@ -52,7 +52,7 @@
|
|||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [selrect (dm/get-in state [:workspace-local :selrect])
|
(let [selrect (dm/get-in state [:workspace-local :selrect])
|
||||||
id (dm/get-in state [:workspace-local :edition])
|
id (dm/get-in state [:workspace-local :edition])
|
||||||
content (st/get-path state :content)
|
content (st/get-path state :path-data)
|
||||||
|
|
||||||
selected-point? (if (some? selrect)
|
selected-point? (if (some? selrect)
|
||||||
(partial gsh/has-point-rect? selrect)
|
(partial gsh/has-point-rect? selrect)
|
||||||
|
|||||||
@ -112,7 +112,7 @@
|
|||||||
:name (str (:name shape) " (stroke)")
|
:name (str (:name shape) " (stroke)")
|
||||||
:parent-id parent-id
|
:parent-id parent-id
|
||||||
:frame-id frame-id
|
:frame-id frame-id
|
||||||
:content content
|
:path-data content
|
||||||
:fills [(stroke->fill stroke)]
|
:fills [(stroke->fill stroke)]
|
||||||
:strokes []})))))
|
:strokes []})))))
|
||||||
(:strokes shape)))
|
(:strokes shape)))
|
||||||
|
|||||||
@ -36,5 +36,5 @@
|
|||||||
|
|
||||||
(defn set-content
|
(defn set-content
|
||||||
[state content]
|
[state content]
|
||||||
(let [path-loc (get-path-location state :content)]
|
(let [path-loc (get-path-location state :path-data)]
|
||||||
(assoc-in state path-loc content)))
|
(assoc-in state path-loc content)))
|
||||||
|
|||||||
@ -158,7 +158,7 @@
|
|||||||
[state]
|
[state]
|
||||||
(let [zoom (get-in state [:workspace-local :zoom] 1)
|
(let [zoom (get-in state [:workspace-local :zoom] 1)
|
||||||
d-pos (/ snap/snap-path-accuracy zoom)
|
d-pos (/ snap/snap-path-accuracy zoom)
|
||||||
get-content #(pst/get-path % :content)
|
get-content #(pst/get-path % :path-data)
|
||||||
|
|
||||||
content-stream
|
content-stream
|
||||||
(-> (l/derived get-content st/state)
|
(-> (l/derived get-content st/state)
|
||||||
|
|||||||
@ -40,11 +40,11 @@
|
|||||||
|
|
||||||
(when (and (seq points) (some? shape))
|
(when (and (seq points) (some? shape))
|
||||||
(let [new-content
|
(let [new-content
|
||||||
(-> (tool-fn (:content shape) points)
|
(-> (tool-fn (:path-data shape) points)
|
||||||
(path/close-subpaths))
|
(path/close-subpaths))
|
||||||
|
|
||||||
changes
|
changes
|
||||||
(changes/generate-path-changes it objects page-id shape (:content shape) new-content)]
|
(changes/generate-path-changes it objects page-id shape (:path-data shape) new-content)]
|
||||||
|
|
||||||
(rx/concat
|
(rx/concat
|
||||||
(rx/of (dwsh/update-shapes [id] path/convert-to-path)
|
(rx/of (dwsh/update-shapes [id] path/convert-to-path)
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
(defn- make-entry [state]
|
(defn- make-entry [state]
|
||||||
(let [id (st/get-path-id state)
|
(let [id (st/get-path-id state)
|
||||||
shape (st/get-path state)]
|
shape (st/get-path state)]
|
||||||
{:content (:content shape)
|
{:content (:path-data shape)
|
||||||
:selrect (:selrect shape)
|
:selrect (:selrect shape)
|
||||||
:points (:points shape)
|
:points (:points shape)
|
||||||
:preview (get-in state [:workspace-local :edit-path id :preview])
|
:preview (get-in state [:workspace-local :edit-path id :preview])
|
||||||
@ -40,9 +40,9 @@
|
|||||||
|
|
||||||
(defn- load-entry [state {:keys [content selrect points preview last-point prev-handler]}]
|
(defn- load-entry [state {:keys [content selrect points preview last-point prev-handler]}]
|
||||||
(let [id (st/get-path-id state)
|
(let [id (st/get-path-id state)
|
||||||
old-content (st/get-path state :content)]
|
old-content (st/get-path state :path-data)]
|
||||||
(-> state
|
(-> state
|
||||||
(d/assoc-in-when (st/get-path-location state :content) content)
|
(d/assoc-in-when (st/get-path-location state :path-data) content)
|
||||||
(d/assoc-in-when (st/get-path-location state :selrect) selrect)
|
(d/assoc-in-when (st/get-path-location state :selrect) selrect)
|
||||||
(d/assoc-in-when (st/get-path-location state :points) points)
|
(d/assoc-in-when (st/get-path-location state :points) points)
|
||||||
(d/update-in-when
|
(d/update-in-when
|
||||||
@ -140,7 +140,7 @@
|
|||||||
|
|
||||||
(def path-content-ref
|
(def path-content-ref
|
||||||
(letfn [(selector [state]
|
(letfn [(selector [state]
|
||||||
(st/get-path state :content))]
|
(st/get-path state :path-data))]
|
||||||
(l/derived selector store/state)))
|
(l/derived selector store/state)))
|
||||||
|
|
||||||
(defn start-path-undo
|
(defn start-path-undo
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user