mirror of
https://github.com/penpot/penpot.git
synced 2025-06-06 17:31:38 +02:00
✨ Fix guides, grids and constraints for nested frames
This commit is contained in:
parent
2e3f443758
commit
a774f4d4fa
8 changed files with 135 additions and 93 deletions
|
@ -9,6 +9,7 @@
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.common.math :as mth]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.common.spec.page :as spec.page]
|
[app.common.spec.page :as spec.page]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
|
@ -718,3 +719,7 @@
|
||||||
(defn update-object-indices
|
(defn update-object-indices
|
||||||
[file page-id]
|
[file page-id]
|
||||||
(update-in file [:pages-index page-id :objects] update-page-index))
|
(update-in file [:pages-index page-id :objects] update-page-index))
|
||||||
|
|
||||||
|
(defn rotated-frame?
|
||||||
|
[frame]
|
||||||
|
(not (mth/almost-zero? (:rotation frame 0))))
|
||||||
|
|
|
@ -181,50 +181,58 @@
|
||||||
(assoc :grow-type :fixed))))
|
(assoc :grow-type :fixed))))
|
||||||
|
|
||||||
(defn- apply-modifiers
|
(defn- apply-modifiers
|
||||||
[ids]
|
([ids]
|
||||||
(us/verify (s/coll-of uuid?) ids)
|
(apply-modifiers ids nil))
|
||||||
(ptk/reify ::apply-modifiers
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state _]
|
|
||||||
(let [objects (wsh/lookup-page-objects state)
|
|
||||||
ids-with-children (into (vec ids) (mapcat #(cph/get-children-ids objects %)) ids)
|
|
||||||
object-modifiers (get state :workspace-modifiers)
|
|
||||||
shapes (map (d/getf objects) ids)
|
|
||||||
ignore-tree (->> (map #(get-ignore-tree object-modifiers objects %) shapes)
|
|
||||||
(reduce merge {}))]
|
|
||||||
|
|
||||||
(rx/of (dwu/start-undo-transaction)
|
([ids {:keys [undo-transation?] :or {undo-transation? true}}]
|
||||||
(dwg/move-frame-guides ids-with-children)
|
(us/verify (s/coll-of uuid?) ids)
|
||||||
(dch/update-shapes
|
(ptk/reify ::apply-modifiers
|
||||||
ids-with-children
|
ptk/WatchEvent
|
||||||
(fn [shape]
|
(watch [_ state _]
|
||||||
(let [modif (get object-modifiers (:id shape))
|
(let [objects (wsh/lookup-page-objects state)
|
||||||
text-shape? (cph/text-shape? shape)]
|
ids-with-children (into (vec ids) (mapcat #(cph/get-children-ids objects %)) ids)
|
||||||
(-> shape
|
object-modifiers (get state :workspace-modifiers)
|
||||||
(merge modif)
|
shapes (map (d/getf objects) ids)
|
||||||
(gsh/transform-shape)
|
ignore-tree (->> (map #(get-ignore-tree object-modifiers objects %) shapes)
|
||||||
(cond-> text-shape?
|
(reduce merge {}))]
|
||||||
(update-grow-type shape)))))
|
|
||||||
{:reg-objects? true
|
(rx/concat
|
||||||
:ignore-tree ignore-tree
|
(if undo-transation?
|
||||||
;; Attributes that can change in the transform. This way we don't have to check
|
(rx/of (dwu/start-undo-transaction))
|
||||||
;; all the attributes
|
(rx/empty))
|
||||||
:attrs [:selrect
|
(rx/of (dwg/move-frame-guides ids-with-children)
|
||||||
:points
|
(dch/update-shapes
|
||||||
:x
|
ids-with-children
|
||||||
:y
|
(fn [shape]
|
||||||
:width
|
(let [modif (get object-modifiers (:id shape))
|
||||||
:height
|
text-shape? (cph/text-shape? shape)]
|
||||||
:content
|
(-> shape
|
||||||
:transform
|
(merge modif)
|
||||||
:transform-inverse
|
(gsh/transform-shape)
|
||||||
:rotation
|
(cond-> text-shape?
|
||||||
:position-data
|
(update-grow-type shape)))))
|
||||||
:flip-x
|
{:reg-objects? true
|
||||||
:flip-y
|
:ignore-tree ignore-tree
|
||||||
:grow-type]})
|
;; Attributes that can change in the transform. This way we don't have to check
|
||||||
(clear-local-transform)
|
;; all the attributes
|
||||||
(dwu/commit-undo-transaction))))))
|
:attrs [:selrect
|
||||||
|
:points
|
||||||
|
:x
|
||||||
|
:y
|
||||||
|
:width
|
||||||
|
:height
|
||||||
|
:content
|
||||||
|
:transform
|
||||||
|
:transform-inverse
|
||||||
|
:rotation
|
||||||
|
:position-data
|
||||||
|
:flip-x
|
||||||
|
:flip-y
|
||||||
|
:grow-type]})
|
||||||
|
(clear-local-transform))
|
||||||
|
(if undo-transation?
|
||||||
|
(rx/of (dwu/commit-undo-transaction))
|
||||||
|
(rx/empty))))))))
|
||||||
|
|
||||||
(defn- check-delta
|
(defn- check-delta
|
||||||
"If the shape is a component instance, check its relative position respect the
|
"If the shape is a component instance, check its relative position respect the
|
||||||
|
@ -762,9 +770,11 @@
|
||||||
(rx/map (partial set-modifiers ids))
|
(rx/map (partial set-modifiers ids))
|
||||||
(rx/take-until stopper))
|
(rx/take-until stopper))
|
||||||
|
|
||||||
(rx/of (calculate-frame-for-move ids)
|
(rx/of (dwu/start-undo-transaction)
|
||||||
(apply-modifiers ids)
|
(calculate-frame-for-move ids)
|
||||||
(finish-transform)))))))))
|
(apply-modifiers ids {:undo-transation? false})
|
||||||
|
(finish-transform)
|
||||||
|
(dwu/commit-undo-transaction)))))))))
|
||||||
|
|
||||||
(s/def ::direction #{:up :down :right :left})
|
(s/def ::direction #{:up :down :right :left})
|
||||||
|
|
||||||
|
@ -842,6 +852,14 @@
|
||||||
(rx/of (set-modifiers [id] {:displacement displ} false true)
|
(rx/of (set-modifiers [id] {:displacement displ} false true)
|
||||||
(apply-modifiers [id]))))))
|
(apply-modifiers [id]))))))
|
||||||
|
|
||||||
|
(defn check-frame-move?
|
||||||
|
[target-frame-id objects position shape]
|
||||||
|
|
||||||
|
(let [current-frame (get objects (:frame-id shape))]
|
||||||
|
;; If the current frame contains the point and it's a child of the target
|
||||||
|
(and (gsh/has-point? current-frame position)
|
||||||
|
(cph/is-child? objects target-frame-id (:id current-frame)))))
|
||||||
|
|
||||||
(defn- calculate-frame-for-move
|
(defn- calculate-frame-for-move
|
||||||
[ids]
|
[ids]
|
||||||
(ptk/reify ::calculate-frame-for-move
|
(ptk/reify ::calculate-frame-for-move
|
||||||
|
@ -855,16 +873,14 @@
|
||||||
moving-shapes (->> ids
|
moving-shapes (->> ids
|
||||||
(cph/clean-loops objects)
|
(cph/clean-loops objects)
|
||||||
(keep #(get objects %))
|
(keep #(get objects %))
|
||||||
(remove #(= (:frame-id %) frame-id)))
|
(remove (partial check-frame-move? frame-id objects position)))
|
||||||
|
|
||||||
changes (-> (pcb/empty-changes it page-id)
|
changes (-> (pcb/empty-changes it page-id)
|
||||||
(pcb/with-objects objects)
|
(pcb/with-objects objects)
|
||||||
(pcb/change-parent frame-id moving-shapes))]
|
(pcb/change-parent frame-id moving-shapes))]
|
||||||
|
|
||||||
(when-not (empty? changes)
|
(when-not (empty? changes)
|
||||||
(rx/of dwu/pop-undo-into-transaction
|
(rx/of (dch/commit-changes changes)
|
||||||
(dch/commit-changes changes)
|
|
||||||
(dwu/commit-undo-transaction)
|
|
||||||
(dwc/expand-collapse frame-id)))))))
|
(dwc/expand-collapse frame-id)))))))
|
||||||
|
|
||||||
(defn- get-displacement
|
(defn- get-displacement
|
||||||
|
|
|
@ -191,7 +191,6 @@
|
||||||
has-group? (->> shapes (d/seek #(= :group (:type %))))
|
has-group? (->> shapes (d/seek #(= :group (:type %))))
|
||||||
has-bool? (->> shapes (d/seek #(= :bool (:type %))))
|
has-bool? (->> shapes (d/seek #(= :bool (:type %))))
|
||||||
has-mask? (->> shapes (d/seek :masked-group?))
|
has-mask? (->> shapes (d/seek :masked-group?))
|
||||||
has-frame? (->> shapes (d/seek #(= :frame (:type %))))
|
|
||||||
|
|
||||||
is-group? (and single? has-group?)
|
is-group? (and single? has-group?)
|
||||||
is-bool? (and single? has-bool?)
|
is-bool? (and single? has-bool?)
|
||||||
|
@ -207,10 +206,9 @@
|
||||||
:shortcut (sc/get-tooltip :ungroup)
|
:shortcut (sc/get-tooltip :ungroup)
|
||||||
:on-click do-remove-group}])
|
:on-click do-remove-group}])
|
||||||
|
|
||||||
(when (not has-frame?)
|
[:& menu-entry {:title (tr "workspace.shape.menu.group")
|
||||||
[:& menu-entry {:title (tr "workspace.shape.menu.group")
|
:shortcut (sc/get-tooltip :group)
|
||||||
:shortcut (sc/get-tooltip :group)
|
:on-click do-create-group}]
|
||||||
:on-click do-create-group}])
|
|
||||||
|
|
||||||
(when (or multiple? (and is-group? (not has-mask?)) is-bool?)
|
(when (or multiple? (and is-group? (not has-mask?)) is-bool?)
|
||||||
[:& menu-entry {:title (tr "workspace.shape.menu.mask")
|
[:& menu-entry {:title (tr "workspace.shape.menu.mask")
|
||||||
|
@ -222,12 +220,10 @@
|
||||||
:shortcut (sc/get-tooltip :unmask)
|
:shortcut (sc/get-tooltip :unmask)
|
||||||
:on-click do-unmask-group}])
|
:on-click do-unmask-group}])
|
||||||
|
|
||||||
(when (not has-frame?)
|
[:& menu-entry {:title (tr "workspace.shape.menu.create-artboard-from-selection")
|
||||||
[:*
|
:shortcut (sc/get-tooltip :artboard-selection)
|
||||||
[:& menu-entry {:title (tr "workspace.shape.menu.create-artboard-from-selection")
|
:on-click do-create-artboard-from-selection}]
|
||||||
:shortcut (sc/get-tooltip :artboard-selection)
|
[:& menu-separator]]))
|
||||||
:on-click do-create-artboard-from-selection}]
|
|
||||||
[:& menu-separator]])]))
|
|
||||||
|
|
||||||
(mf/defc context-focus-mode-menu
|
(mf/defc context-focus-mode-menu
|
||||||
[{:keys []}]
|
[{:keys []}]
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
(:require
|
(:require
|
||||||
[app.main.constants :refer [has-layout-item]]
|
[app.main.constants :refer [has-layout-item]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs-shape fill-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs-shape fill-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.frame-grid :refer [frame-grid]]
|
[app.main.ui.workspace.sidebar.options.menus.frame-grid :refer [frame-grid]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
stroke-values (select-keys shape stroke-attrs)
|
stroke-values (select-keys shape stroke-attrs)
|
||||||
layer-values (select-keys shape layer-attrs)
|
layer-values (select-keys shape layer-attrs)
|
||||||
measure-values (select-keys shape measure-attrs)
|
measure-values (select-keys shape measure-attrs)
|
||||||
|
constraint-values (select-keys shape constraint-attrs)
|
||||||
layout-values (select-keys shape layout-attrs)
|
layout-values (select-keys shape layout-attrs)
|
||||||
layout-item-values (select-keys shape layout-item-attrs)]
|
layout-item-values (select-keys shape layout-item-attrs)]
|
||||||
[:*
|
[:*
|
||||||
|
@ -32,6 +34,8 @@
|
||||||
:values measure-values
|
:values measure-values
|
||||||
:type type
|
:type type
|
||||||
:shape shape}]
|
:shape shape}]
|
||||||
|
[:& constraints-menu {:ids ids
|
||||||
|
:values constraint-values}]
|
||||||
(when has-layout-item
|
(when has-layout-item
|
||||||
[:& layout-menu {:type type :ids [(:id shape)] :values layout-values}])
|
[:& layout-menu {:type type :ids [(:id shape)] :values layout-values}])
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
|
[app.common.pages.helpers :as cph]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.util.geom.grid :as gg]
|
[app.util.geom.grid :as gg]
|
||||||
|
@ -126,13 +127,15 @@
|
||||||
(mf/defc frame-grid
|
(mf/defc frame-grid
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
[{:keys [zoom transform selected focus]}]
|
[{:keys [zoom transform selected focus]}]
|
||||||
(let [frames (mf/deref refs/workspace-frames)
|
(let [frames (mf/deref refs/workspace-frames)
|
||||||
moving (when (= :move transform) selected)
|
transforming (when (some? transform) selected)
|
||||||
is-moving? #(contains? moving (:id %))]
|
is-transform? #(contains? transforming (:id %))]
|
||||||
|
|
||||||
[:g.grid-display {:style {:pointer-events "none"}}
|
[:g.grid-display {:style {:pointer-events "none"}}
|
||||||
(for [frame (remove is-moving? frames)]
|
(for [frame frames]
|
||||||
(when (or (empty? focus) (contains? focus (:id frame)))
|
(when (and (not (is-transform? frame))
|
||||||
|
(not (cph/rotated-frame? frame))
|
||||||
|
(or (empty? focus) (contains? focus (:id frame))))
|
||||||
[:& grid-display-frame {:key (str "grid-" (:id frame))
|
[:& grid-display-frame {:key (str "grid-" (:id frame))
|
||||||
:zoom zoom
|
:zoom zoom
|
||||||
:frame (gsh/transform-shape frame)}]))]))
|
:frame (gsh/transform-shape frame)}]))]))
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
|
[app.common.pages.helpers :as cph]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
|
@ -286,8 +287,9 @@
|
||||||
guide-pill-corner-radius (/ guide-pill-corner-radius zoom)]
|
guide-pill-corner-radius (/ guide-pill-corner-radius zoom)]
|
||||||
|
|
||||||
(when (or (nil? frame)
|
(when (or (nil? frame)
|
||||||
(is-guide-inside-frame? (assoc guide :position pos) frame)
|
(and (is-guide-inside-frame? (assoc guide :position pos) frame)
|
||||||
(:hover @state true))
|
(cph/root-frame? frame)
|
||||||
|
(not (cph/rotated-frame? frame))))
|
||||||
[:g.guide-area
|
[:g.guide-area
|
||||||
(when-not disabled-guides?
|
(when-not disabled-guides?
|
||||||
(let [{:keys [x y width height]} (guide-area-axis pos vbox zoom frame axis)]
|
(let [{:keys [x y width height]} (guide-area-axis pos vbox zoom frame axis)]
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
(ns app.util.geom.snap-points
|
(ns app.util.geom.snap-points
|
||||||
(:require
|
(:require
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as gsh]))
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.common.pages.helpers :as cph]))
|
||||||
|
|
||||||
(defn selrect-snap-points [{:keys [x y width height] :as selrect}]
|
(defn selrect-snap-points [{:keys [x y width height] :as selrect}]
|
||||||
#{(gpt/point x y)
|
#{(gpt/point x y)
|
||||||
|
@ -29,11 +30,20 @@
|
||||||
(when (and (not blocked) (not hidden))
|
(when (and (not blocked) (not hidden))
|
||||||
(let [shape (gsh/transform-shape shape)]
|
(let [shape (gsh/transform-shape shape)]
|
||||||
(case (:type shape)
|
(case (:type shape)
|
||||||
:frame (-> shape :selrect frame-snap-points)
|
:frame (-> shape :points gsh/points->selrect frame-snap-points)
|
||||||
(into #{(gsh/center-shape shape)} (:points shape))))))
|
(into #{(gsh/center-shape shape)} (:points shape))))))
|
||||||
|
|
||||||
(defn guide-snap-points
|
(defn guide-snap-points
|
||||||
[guide]
|
[guide frame]
|
||||||
(if (= :x (:axis guide))
|
|
||||||
|
(cond
|
||||||
|
(and (some? frame)
|
||||||
|
(not (cph/rotated-frame? frame))
|
||||||
|
(not (cph/root-frame? frame)))
|
||||||
|
#{}
|
||||||
|
|
||||||
|
(= :x (:axis guide))
|
||||||
#{(gpt/point (:position guide) 0)}
|
#{(gpt/point (:position guide) 0)}
|
||||||
|
|
||||||
|
:else
|
||||||
#{(gpt/point 0 (:position guide))}))
|
#{(gpt/point 0 (:position guide))}))
|
||||||
|
|
|
@ -55,16 +55,18 @@
|
||||||
|
|
||||||
(defn get-grids-snap-points
|
(defn get-grids-snap-points
|
||||||
[frame coord]
|
[frame coord]
|
||||||
(let [grid->snap (fn [[grid-type position]]
|
(if (not (cph/rotated-frame? frame))
|
||||||
{:type :layout
|
[]
|
||||||
:id (:id frame)
|
(let [grid->snap (fn [[grid-type position]]
|
||||||
:grid grid-type
|
{:type :layout
|
||||||
:pt position})]
|
:id (:id frame)
|
||||||
(->> (:grids frame)
|
:grid grid-type
|
||||||
(mapcat (fn [grid]
|
:pt position})]
|
||||||
(->> (gg/grid-snap-points frame grid coord)
|
(->> (:grids frame)
|
||||||
(mapv #(vector (:type grid) %)))))
|
(mapcat (fn [grid]
|
||||||
(mapv grid->snap))))
|
(->> (gg/grid-snap-points frame grid coord)
|
||||||
|
(mapv #(vector (:type grid) %)))))
|
||||||
|
(mapv grid->snap)))))
|
||||||
|
|
||||||
(defn- add-frame
|
(defn- add-frame
|
||||||
[page-data frame]
|
[page-data frame]
|
||||||
|
@ -105,9 +107,10 @@
|
||||||
|
|
||||||
|
|
||||||
(defn- add-guide
|
(defn- add-guide
|
||||||
[page-data guide]
|
[objects page-data guide]
|
||||||
|
|
||||||
(let [guide-data (->> (snap/guide-snap-points guide)
|
(let [frame (get objects (:frame-id guide))
|
||||||
|
guide-data (->> (snap/guide-snap-points guide frame)
|
||||||
(mapv #(array-map
|
(mapv #(array-map
|
||||||
:type :guide
|
:type :guide
|
||||||
:id (:id guide)
|
:id (:id guide)
|
||||||
|
@ -178,10 +181,10 @@
|
||||||
(add-shape new-shape)))
|
(add-shape new-shape)))
|
||||||
|
|
||||||
(defn- update-guide
|
(defn- update-guide
|
||||||
[page-data [old-guide new-guide]]
|
[objects page-data [old-guide new-guide]]
|
||||||
(-> page-data
|
(as-> page-data $
|
||||||
(remove-guide old-guide)
|
(remove-guide $ old-guide)
|
||||||
(add-guide new-guide)))
|
(add-guide objects $ new-guide)))
|
||||||
|
|
||||||
;; PUBLIC API
|
;; PUBLIC API
|
||||||
|
|
||||||
|
@ -203,7 +206,7 @@
|
||||||
(add-root-frame $)
|
(add-root-frame $)
|
||||||
(reduce add-frame $ frames)
|
(reduce add-frame $ frames)
|
||||||
(reduce add-shape $ shapes)
|
(reduce add-shape $ shapes)
|
||||||
(reduce add-guide $ guides))]
|
(reduce (partial add-guide objects) $ guides))]
|
||||||
(assoc snap-data (:id page) page-data)))
|
(assoc snap-data (:id page) page-data)))
|
||||||
|
|
||||||
(defn update-page
|
(defn update-page
|
||||||
|
@ -214,7 +217,8 @@
|
||||||
;; Update page
|
;; Update page
|
||||||
(update snap-data (:id page)
|
(update snap-data (:id page)
|
||||||
(fn [page-data]
|
(fn [page-data]
|
||||||
(let [{:keys [change-frame-shapes
|
(let [{:keys [objects]} page
|
||||||
|
{:keys [change-frame-shapes
|
||||||
change-frame-guides
|
change-frame-guides
|
||||||
removed-frames
|
removed-frames
|
||||||
removed-shapes
|
removed-shapes
|
||||||
|
@ -235,10 +239,12 @@
|
||||||
(reduce update-shape $ updated-shapes)
|
(reduce update-shape $ updated-shapes)
|
||||||
(reduce add-frame $ new-frames)
|
(reduce add-frame $ new-frames)
|
||||||
(reduce add-shape $ new-shapes)
|
(reduce add-shape $ new-shapes)
|
||||||
(reduce update-guide $ change-frame-guides)
|
|
||||||
(reduce remove-guide $ removed-guides)
|
(reduce remove-guide $ removed-guides)
|
||||||
(reduce update-guide $ updated-guides)
|
|
||||||
(reduce add-guide $ new-guides)))))
|
;; Guides functions. Need objects to get its frame data
|
||||||
|
(reduce (partial update-guide objects) $ change-frame-guides)
|
||||||
|
(reduce (partial update-guide objects) $ updated-guides)
|
||||||
|
(reduce (partial add-guide objects) $ new-guides)))))
|
||||||
|
|
||||||
;; Page doesn't exist, we create a new entry
|
;; Page doesn't exist, we create a new entry
|
||||||
(add-page snap-data page)))
|
(add-page snap-data page)))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue