From a9303c37c4ab7edd1412298ba3c7d4c3b4290135 Mon Sep 17 00:00:00 2001
From: "alonso.torres" <alonso.torres@kaleidos.net>
Date: Tue, 31 May 2022 11:21:00 +0200
Subject: [PATCH] :sparkles: Allow for nested frames

---
 common/src/app/common/pages.cljc              |   4 +-
 common/src/app/common/pages/changes.cljc      |   2 +-
 common/src/app/common/pages/common.cljc       |   2 +
 common/src/app/common/pages/focus.cljc        |  15 +-
 common/src/app/common/pages/helpers.cljc      |  95 ++++++-
 common/src/app/common/pages/indices.cljc      |  16 +-
 common/src/app/common/spec/shape.cljc         |   9 +-
 frontend/src/app/main/constants.cljs          | 167 ++++++++++++
 frontend/src/app/main/data/workspace.cljs     |  24 +-
 .../src/app/main/data/workspace/common.cljs   |  32 +--
 .../app/main/data/workspace/drawing/box.cljs  |   7 +-
 .../src/app/main/data/workspace/groups.cljs   |   1 -
 .../src/app/main/data/workspace/indices.cljs  |  78 ++++++
 .../data/workspace/indices/object_tree.cljs   |  28 ++
 frontend/src/app/main/ui/shapes/frame.cljs    |  12 +-
 .../app/main/ui/workspace/shapes/frame.cljs   |   7 +-
 .../sidebar/options/menus/measures.cljs       | 249 +++++-------------
 .../app/main/ui/workspace/viewport/hooks.cljs |   3 +-
 .../main/ui/workspace/viewport/selection.cljs |   2 +-
 .../main/ui/workspace/viewport/widgets.cljs   |  20 +-
 frontend/src/app/worker/selection.cljs        |  38 ++-
 frontend/translations/en.po                   |   6 +
 frontend/translations/es.po                   |   6 +
 23 files changed, 540 insertions(+), 283 deletions(-)
 create mode 100644 frontend/src/app/main/data/workspace/indices.cljs
 create mode 100644 frontend/src/app/main/data/workspace/indices/object_tree.cljs

diff --git a/common/src/app/common/pages.cljc b/common/src/app/common/pages.cljc
index 0e6a91a94..e500d372d 100644
--- a/common/src/app/common/pages.cljc
+++ b/common/src/app/common/pages.cljc
@@ -26,8 +26,8 @@
 (dm/export focus/is-in-focus?)
 
 ;; Indices
-(dm/export indices/calculate-z-index)
-(dm/export indices/update-z-index)
+#_(dm/export indices/calculate-z-index)
+#_(dm/export indices/update-z-index)
 (dm/export indices/generate-child-all-parents-index)
 (dm/export indices/generate-child-parent-index)
 (dm/export indices/create-clip-index)
diff --git a/common/src/app/common/pages/changes.cljc b/common/src/app/common/pages/changes.cljc
index 7d511d9ff..393791cd4 100644
--- a/common/src/app/common/pages/changes.cljc
+++ b/common/src/app/common/pages/changes.cljc
@@ -211,7 +211,7 @@
             (let [invalid-targets (calculate-invalid-targets objects shape-id)]
               (and (contains? objects shape-id)
                    (not (invalid-targets parent-id))
-                   (cph/valid-frame-target? objects parent-id shape-id))))
+                   #_(cph/valid-frame-target? objects parent-id shape-id))))
 
           (insert-items [prev-shapes index shapes]
             (let [prev-shapes (or prev-shapes [])]
diff --git a/common/src/app/common/pages/common.cljc b/common/src/app/common/pages/common.cljc
index c412176f6..8756ac472 100644
--- a/common/src/app/common/pages/common.cljc
+++ b/common/src/app/common/pages/common.cljc
@@ -82,6 +82,8 @@
             :r1 :r2 :r3 :r4
             :selrect
             :points
+            :show-content
+            :hide-in-viewer
 
             :opacity
             :blend-mode
diff --git a/common/src/app/common/pages/focus.cljc b/common/src/app/common/pages/focus.cljc
index df0f2d351..f4dda2b97 100644
--- a/common/src/app/common/pages/focus.cljc
+++ b/common/src/app/common/pages/focus.cljc
@@ -13,21 +13,16 @@
 
 (defn focus-objects
   [objects focus]
-  (let [[ids-with-children z-index]
+  (let [ids-with-children
         (when (d/not-empty? focus)
-          [(into (conj focus uuid/zero)
-                 (mapcat (partial cph/get-children-ids objects))
-                 focus)
-           (cpi/calculate-z-index objects)])
-
-        sort-by-z-index
-        (fn [coll]
-          (->> coll (sort-by (fn [a b] (- (get z-index a) (get z-index b))))))]
+          (into (conj focus uuid/zero)
+                (mapcat (partial cph/get-children-ids objects))
+                focus))]
 
     (cond-> objects
       (some? ids-with-children)
       (-> (select-keys ids-with-children)
-          (assoc-in [uuid/zero :shapes] (sort-by-z-index focus))))))
+          (assoc-in [uuid/zero :shapes] (cph/sort-z-index objects focus))))))
 
 (defn filter-not-focus
   [objects focus ids]
diff --git a/common/src/app/common/pages/helpers.cljc b/common/src/app/common/pages/helpers.cljc
index b768680eb..901b43812 100644
--- a/common/src/app/common/pages/helpers.cljc
+++ b/common/src/app/common/pages/helpers.cljc
@@ -141,6 +141,38 @@
           (keep lookup)))))
 
 (defn get-frames-ids
+  "Retrieves all frame objects as vector. It is not implemented in
+  function of `get-immediate-children` for performance reasons. This
+  function is executed in the render hot path."
+  [objects]
+  (let [lookup (d/getf objects)
+        xform  (comp (remove #(= uuid/zero %))
+                     (keep lookup)
+                     (filter frame-shape?)
+                     (map :id))]
+    (->> (keys objects)
+         (into [] xform))))
+
+(defn get-frames
+  "Retrieves all frame objects as vector. It is not implemented in
+  function of `get-immediate-children` for performance reasons. This
+  function is executed in the render hot path."
+  [objects]
+  (let [lookup (d/getf objects)
+        xform  (comp (remove #(= uuid/zero %))
+                     (keep lookup)
+                     (filter frame-shape?))]
+    (->> (keys objects)
+         (into [] xform))))
+
+(defn get-nested-frames
+  [objects frame-id]
+  (into #{}
+        (comp (filter frame-shape?)
+              (map :id))
+        (get-children objects frame-id)))
+
+(defn get-root-frames-ids
   "Retrieves all frame objects as vector. It is not implemented in
   function of `get-immediate-children` for performance reasons. This
   function is executed in the render hot path."
@@ -152,7 +184,7 @@
     (->> (:shapes (lookup uuid/zero))
          (into [] xform))))
 
-(defn get-frames
+(defn get-root-frames
   "Retrieves all frame objects as vector. It is not implemented in
   function of `get-immediate-children` for performance reasons. This
   function is executed in the render hot path."
@@ -163,15 +195,62 @@
     (->> (:shapes (lookup uuid/zero))
          (into [] xform))))
 
+(defn- get-base
+  [objects id-a id-b]
+
+  (let [parents-a (reverse (get-parents-seq objects id-a))
+        parents-b (reverse (get-parents-seq objects id-b))
+
+        [base base-child-a base-child-b]
+        (loop [parents-a (rest parents-a)
+               parents-b (rest parents-b)
+               base uuid/zero]
+          (if (not= (first parents-a) (first parents-b))
+            [base (first parents-a) (first parents-b)]
+            (recur (rest parents-a) (rest parents-b) (first parents-a))))
+
+        index-base-a (when base-child-a (get-position-on-parent objects base-child-a))
+        index-base-b (when base-child-b (get-position-on-parent objects base-child-b))]
+
+    [base index-base-a index-base-b]))
+
+(defn is-shape-over-shape?
+  [objects base-shape-id over-shape-id]
+
+  (let [[base parent-a parent-b] (get-base objects base-shape-id over-shape-id)]
+    (cond
+      (= base base-shape-id)
+      ;; over-shape is a child of base-shape. Will be over if base is a root-frame
+      (= uuid/zero (get-in objects [base-shape-id :parent-id]))
+
+      (= base over-shape-id)
+      (not= uuid/zero (get-in objects [over-shape-id :parent-id]))
+
+      :else
+      (< parent-a parent-b))))
+
+(defn sort-z-index
+  [objects ids]
+  (letfn [(comp [id-a id-b]
+            (cond
+              (= id-a id-b) 0
+              (is-shape-over-shape? objects id-a id-b) 1
+              :else -1))]
+    (sort comp ids)))
+
 (defn frame-id-by-position
   [objects position]
-  (let [frames (get-frames objects)]
-    (or
-     (->> frames
-          (reverse)
-          (d/seek #(and position (gsh/has-point? % position)))
-          :id)
-     uuid/zero)))
+  (let [frames (->> (get-frames objects)
+                    (filter #(and position (gsh/has-point? % position))))
+
+        top-frame
+        (reduce (fn [current-top frame]
+                  (if (is-shape-over-shape? objects (:id current-top) (:id frame))
+                    frame
+                    current-top))
+                (first frames)
+                (rest frames))]
+    (or (:id top-frame) uuid/zero)))
 
 (declare indexed-shapes)
 
diff --git a/common/src/app/common/pages/indices.cljc b/common/src/app/common/pages/indices.cljc
index 3a5a852ee..571de3613 100644
--- a/common/src/app/common/pages/indices.cljc
+++ b/common/src/app/common/pages/indices.cljc
@@ -11,10 +11,12 @@
    [app.common.uuid :as uuid]
    [clojure.set :as set]))
 
-(defn calculate-frame-z-index
+#_(defn calculate-frame-z-index
   [z-index frame-id base-idx objects]
 
-  (let [is-frame?    (fn [id] (= :frame (get-in objects [id :type])))
+  (let [is-root-frame?    (fn [id]
+                            (and (= :frame (get-in objects [id :type]))
+                                 (= uuid/zero (get-in objects [id :parent-id]))))
         children     (or (get-in objects [frame-id :shapes]) [])]
 
     (if (empty? children)
@@ -25,8 +27,8 @@
              z-index z-index]
 
         (let [children  (get-in objects [current :shapes])
-              is-frame? (is-frame? current)
-              pending   (if (not is-frame?)
+              is-root-frame? (is-root-frame? current)
+              pending   (if (not is-root-frame?)
                           (d/concat-vec pending children)
                           pending)]
 
@@ -41,12 +43,12 @@
 ;; internal z-index. To calculate the "final" z-index we add the shape z-index with
 ;; the z-index of its frame. This way we can update the z-index per frame without
 ;; the need of recalculate all the frames
-(defn calculate-z-index
+#_(defn calculate-z-index
   "Given a collection of shapes calculates their z-index. Greater index
   means is displayed over other shapes with less index."
   [objects]
 
-  (let [frames  (cph/get-frames objects)
+  (let [frames  (cph/get-root-frames objects)
 
         by-frame (cph/objects-by-frame objects)
         frame-base-idx (d/update-vals by-frame count)
@@ -57,7 +59,7 @@
           (fn [z-index {:keys [id]}]
             (calculate-frame-z-index z-index id (get frame-base-idx id) objects)) z-index))))
 
-(defn update-z-index
+#_(defn update-z-index
   "Updates the z-index given a set of ids to change and the old and new objects
   representations"
   [z-index changed-ids old-objects new-objects]
diff --git a/common/src/app/common/spec/shape.cljc b/common/src/app/common/spec/shape.cljc
index 42ae67f60..0c1deb156 100644
--- a/common/src/app/common/spec/shape.cljc
+++ b/common/src/app/common/spec/shape.cljc
@@ -52,6 +52,8 @@
 (s/def ::fill-color-ref-id (s/nilable uuid?))
 
 (s/def ::hide-fill-on-export boolean?)
+(s/def ::show-content boolean?)
+(s/def ::hide-in-viewer boolean?)
 
 (s/def ::file-thumbnail boolean?)
 (s/def ::masked-group? boolean?)
@@ -254,8 +256,7 @@
                    :internal.shape.text.position-data/rtl
                    :internal.shape.text.position-data/text
                    :internal.shape.text.position-data/text-decoration
-                   :internal.shape.text.position-data/text-transform]
-          ))
+                   :internal.shape.text.position-data/text-transform]))
 
 (s/def :internal.shape.text.position-data/x ::us/safe-number)
 (s/def :internal.shape.text.position-data/y ::us/safe-number)
@@ -303,7 +304,9 @@
 (defmethod shape-spec :frame [_]
   (s/and ::shape-attrs
          (s/keys :opt-un [::file-thumbnail
-                          ::hide-fill-on-export])))
+                          ::hide-fill-on-export
+                          ::show-content
+                          ::hide-in-viewer])))
 
 (s/def ::shape
   (s/and (s/multi-spec shape-spec :type)
diff --git a/frontend/src/app/main/constants.cljs b/frontend/src/app/main/constants.cljs
index 84087c5e0..5f9e84486 100644
--- a/frontend/src/app/main/constants.cljs
+++ b/frontend/src/app/main/constants.cljs
@@ -25,3 +25,170 @@
 
 (def has-layout-item false)
 
+(def size-presets
+  [{:name "APPLE"}
+   {:name "iPhone 12/12 Pro"
+    :width 390
+    :height 844}
+   {:name "iPhone 12 Mini"
+    :width 360
+    :height 780}
+   {:name "iPhone 12 Pro Max"
+    :width 428
+    :height 926}
+   {:name "iPhone X/XS/11 Pro"
+    :width 375
+    :height 812}
+   {:name "iPhone XS Max/XR/11"
+    :width 414
+    :height 896}
+   {:name "iPhone 6/7/8 Plus"
+    :width 414
+    :height 736}
+   {:name "iPhone 6/7/8/SE2"
+    :width 375
+    :height 667}
+   {:name "iPhone 5/SE"
+    :width 320
+    :height 568}
+   {:name "iPad"
+    :width 768
+    :height 1024}
+   {:name "iPad Pro 10.5in"
+    :width 834
+    :height 1112}
+   {:name "iPad Pro 12.9in"
+    :width 1024
+    :height 1366}
+   {:name "Watch 44mm"
+    :width 368
+    :height 448}
+   {:name "Watch 42mm"
+    :width 312
+    :height 390}
+   {:name "Watch 40mm"
+    :width 324
+    :height 394}
+   {:name "Watch 38mm"
+    :width 272
+    :height 340}
+
+   {:name "ANDROID"}
+   {:name "Mobile"
+    :width 360
+    :height 640}
+   {:name "Tablet"
+    :width 768
+    :height 1024}
+   {:name "Google Pixel 4a/5"
+    :width 393
+    :height 851}
+   {:name "Samsung Galaxy S20+"
+    :width 384
+    :height 854}
+   {:name "Samsung Galaxy A71/A51"
+    :width 412
+    :height 914}
+
+   {:name "MICROSOFT"}
+   {:name "Surface Pro 3"
+    :width 1440
+    :height 960}
+   {:name "Surface Pro 4/5/6/7"
+    :width 1368
+    :height 912}
+
+   {:name "ReMarkable"}
+   {:name "Remarkable 2"
+    :width 840
+    :height 1120}
+
+   {:name "WEB"}
+   {:name "Web 1280"
+    :width 1280
+    :height 800}
+   {:name "Web 1366"
+    :width 1366
+    :height 768}
+   {:name "Web 1024"
+    :width 1024
+    :height 768}
+   {:name "Web 1920"
+    :width 1920
+    :height 1080}
+
+   {:name "PRINT (96dpi)"}
+   {:name "A0"
+    :width 3179
+    :height 4494}
+   {:name "A1"
+    :width 2245
+    :height 3179}
+   {:name "A2"
+    :width 1587
+    :height 2245}
+   {:name "A3"
+    :width 1123
+    :height 1587}
+   {:name "A4"
+    :width 794
+    :height 1123}
+   {:name "A5"
+    :width 559
+    :height 794}
+   {:name "A6"
+    :width 397
+    :height 559}
+   {:name "Letter"
+    :width 816
+    :height 1054}
+   {:name "DIN Lang"
+    :width 835
+    :height 413}
+
+   {:name "SOCIAL MEDIA"}
+   {:name "Instagram profile"
+    :width 320
+    :height 320}
+   {:name "Instagram post"
+    :width 1080
+    :height 1080}
+   {:name "Instagram story"
+    :width 1080
+    :height 1920}
+   {:name "Facebook profile"
+    :width 720
+    :height 720}
+   {:name "Facebook cover"
+    :width 820
+    :height 312}
+   {:name "Facebook post"
+    :width 1200
+    :height 630}
+   {:name "LinkedIn profile"
+    :width 400
+    :height 400}
+   {:name "LinkedIn cover"
+    :width 1584
+    :height 396}
+   {:name "LinkedIn post"
+    :width 1200
+    :height 627}
+   {:name "Twitter profile"
+    :width 400
+    :height 400}
+   {:name "Twitter header"
+    :width 1500
+    :height 500}
+   {:name "Twitter post"
+    :width 1024
+    :height 512}
+   {:name "YouTube profile"
+    :width 800
+    :height 800}
+   {:name "YouTube banner"
+    :width 2560
+    :height 1440}
+   {:name "YouTube thumb"
+    :width 1280
+    :height 720}])
diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs
index 6ada6cc96..abb8c2244 100644
--- a/frontend/src/app/main/data/workspace.cljs
+++ b/frontend/src/app/main/data/workspace.cljs
@@ -6,6 +6,8 @@
 
 (ns app.main.data.workspace
   (:require
+   [app.main.data.workspace.indices :as dwidx]
+   
    [app.common.attrs :as attrs]
    [app.common.data :as d]
    [app.common.data.macros :as dm]
@@ -127,7 +129,8 @@
                               team-id (dm/get-in bundle [:project :team-id])]
                           (rx/merge
                            (rx/of (dwn/initialize team-id file-id)
-                                  (dwp/initialize-file-persistence file-id))
+                                  (dwp/initialize-file-persistence file-id)
+                                  (dwidx/start-indexing file-id))
 
                            (->> stream
                                 (rx/filter #(= ::dwc/index-initialized %))
@@ -194,6 +197,7 @@
     (watch [_ _ _]
       (rx/merge
        (rx/of (dwn/finalize file-id))
+       (rx/of (dwidx/stop-indexing file-id))
        (->> (rx/of ::dwp/finalize)
             (rx/observe-on :async))))))
 
@@ -1214,16 +1218,14 @@
                   ;; selected and its parents
                   objects (cph/selected-subtree objects selected)
 
-                  z-index (cp/calculate-z-index objects)
-                  z-values (->> selected
-                                (map #(vector %
-                                              (+ (get z-index %)
-                                                 (get z-index (get-in objects [% :frame-id]))))))
-                  selected
-                  (->> z-values
-                       (sort-by second)
-                       (map first)
-                       (into (d/ordered-set)))]
+                  ;;z-index (cp/calculate-z-index objects)
+                  ;;z-values (->> selected
+                  ;;              (map #(vector %
+                  ;;                            (+ (get z-index %)
+                  ;;                               (get z-index (get-in objects [% :frame-id]))))))
+
+                  selected (-> (cph/sort-z-index objects selected)
+                               (into (d/ordered-set)))]
 
               (assoc data :selected selected)))
 
diff --git a/frontend/src/app/main/data/workspace/common.cljs b/frontend/src/app/main/data/workspace/common.cljs
index bdc2b6110..5032a70fe 100644
--- a/frontend/src/app/main/data/workspace/common.cljs
+++ b/frontend/src/app/main/data/workspace/common.cljs
@@ -261,25 +261,21 @@
 (defn get-shape-layer-position
   [objects selected attrs]
 
-  (if (= :frame (:type attrs))
-    ;; Frames are always positioned on the root frame
-    [uuid/zero uuid/zero nil]
+  ;; Calculate the frame over which we're drawing
+  (let [position @ms/mouse-position
+        frame-id (:frame-id attrs (cph/frame-id-by-position objects position))
+        shape (when-not (empty? selected)
+                (cph/get-base-shape objects selected))]
 
-    ;; Calculate the frame over which we're drawing
-    (let [position @ms/mouse-position
-          frame-id (:frame-id attrs (cph/frame-id-by-position objects position))
-          shape (when-not (empty? selected)
-                  (cph/get-base-shape objects selected))]
+    ;; When no shapes has been selected or we're over a different frame
+    ;; we add it as the latest shape of that frame
+    (if (or (not shape) (not= (:frame-id shape) frame-id))
+      [frame-id frame-id nil]
 
-      ;; When no shapes has been selected or we're over a different frame
-      ;; we add it as the latest shape of that frame
-      (if (or (not shape) (not= (:frame-id shape) frame-id))
-        [frame-id frame-id nil]
-
-        ;; Otherwise, we add it to next to the selected shape
-        (let [index (cph/get-position-on-parent objects (:id shape))
-              {:keys [frame-id parent-id]} shape]
-          [frame-id parent-id (inc index)])))))
+      ;; Otherwise, we add it to next to the selected shape
+      (let [index (cph/get-position-on-parent objects (:id shape))
+            {:keys [frame-id parent-id]} shape]
+        [frame-id parent-id (inc index)]))))
 
 (defn make-new-shape
   [attrs objects selected]
@@ -325,7 +321,7 @@
                      selected)
 
              changes  (-> (pcb/empty-changes it page-id)
-                          (pcb/add-object shape {:index (when (= :frame (:type shape)) 0)}))]
+                          (pcb/add-object shape #_{:index (when (= :frame (:type shape)) 0)}))]
 
          (rx/concat
           (rx/of (dch/commit-changes changes)
diff --git a/frontend/src/app/main/data/workspace/drawing/box.cljs b/frontend/src/app/main/data/workspace/drawing/box.cljs
index c2eeefc70..b5dcaf107 100644
--- a/frontend/src/app/main/data/workspace/drawing/box.cljs
+++ b/frontend/src/app/main/data/workspace/drawing/box.cljs
@@ -65,12 +65,7 @@
             focus   (:workspace-focus-selected state)
             zoom    (get-in state [:workspace-local :zoom] 1)
 
-            frames  (cph/get-frames objects)
-            fid     (or (->> frames
-                             (filter #(gsh/has-point? % initial))
-                             first
-                             :id)
-                        uuid/zero)
+            fid     (cph/frame-id-by-position objects initial)
 
             shape (-> state
                       (get-in [:workspace-drawing :object])
diff --git a/frontend/src/app/main/data/workspace/groups.cljs b/frontend/src/app/main/data/workspace/groups.cljs
index fc7d905be..3dd032e0c 100644
--- a/frontend/src/app/main/data/workspace/groups.cljs
+++ b/frontend/src/app/main/data/workspace/groups.cljs
@@ -21,7 +21,6 @@
   [objects selected]
   (->> selected
        (map #(get objects %))
-       (filter #(not= :frame (:type %)))
        (map #(assoc % ::index (cph/get-position-on-parent objects (:id %))))
        (sort-by ::index)))
 
diff --git a/frontend/src/app/main/data/workspace/indices.cljs b/frontend/src/app/main/data/workspace/indices.cljs
new file mode 100644
index 000000000..4cf4fb39a
--- /dev/null
+++ b/frontend/src/app/main/data/workspace/indices.cljs
@@ -0,0 +1,78 @@
+;; This Source Code Form is subject to the terms of the Mozilla Public
+;; License, v. 2.0. If a copy of the MPL was not distributed with this
+;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;;
+;; Copyright (c) UXBOX Labs SL
+
+(ns app.main.data.workspace.indices
+  (:require
+   [app.main.data.workspace.state-helpers :as wsh]
+   [app.main.data.workspace.indices.object-tree :as dwi-object-tree]
+   [app.main.refs :as refs]
+   [app.main.data.workspace.changes :as dwc]
+   [beicon.core :as rx]
+   [app.common.data :as d]
+   [potok.core :as ptk]))
+
+(def stop-indexing? (ptk/type? ::stop-indexing))
+
+(def objects-changes #{:add-obj :mod-obj :del-obj :mov-objects})
+
+(defn stop-indexing
+  [file-id]
+  (ptk/reify ::stop-indexing
+    ptk/UpdateEvent
+    (update [_ state]
+      (-> state
+          (dissoc :index-object-tree)))))
+
+(defn process-changes
+  "Simplify changes so we have only the type of operation and the ids"
+  [changes]
+  (->> changes
+       (filter #(contains? objects-changes (:type %)))
+       (mapcat (fn [{:keys [type id shapes]}]
+                 (if (some? shapes)
+                   (->> shapes (map #(vector type %)))
+                   [[type id]])))))
+
+(defn update-indexing
+  [change-type shape-id old-objects new-objects]
+  (ptk/reify ::update-indexing
+    ptk/UpdateEvent
+    (update [_ state]
+      (let [objects (wsh/lookup-page-objects state)]
+        (-> state
+            (update :index-object-tree dwi-object-tree/update-index shape-id change-type old-objects new-objects))))))
+
+(defn start-indexing
+  [file-id]
+
+  (ptk/reify ::start-indexing
+    ptk/UpdateEvent
+    (update [_ state]
+      (let [objects (wsh/lookup-page-objects state)]
+        (-> state
+            (assoc :index-object-tree (dwi-object-tree/init-index objects)))))
+
+    ptk/WatchEvent
+    (watch [_ state stream]
+      (let [stopper (->> stream (rx/filter stop-indexing?) (rx/take 1))
+            objects-delta (->> (rx/from-atom refs/workspace-page-objects {:emit-current-value? true}) (rx/buffer 2 1))]
+        (->> stream
+             (rx/filter dwc/commit-changes?)
+             (rx/flat-map #(->> % deref :changes process-changes))
+             (rx/with-latest-from objects-delta)
+             (rx/map (fn [[[type id] [objects-old objects-new]]]
+                       (update-indexing type id objects-old objects-new)))
+             #_(rx/tap (fn [[[type id] [objects-old objects-new]]]
+                       (let [obj-old (get objects-old id)
+                             obj-new (get objects-new id)]
+                         (prn ">change" (or (:name obj-old) (:name obj-new)))
+                         (prn "  > " type)
+                         (.log js/console "  >" (clj->js obj-old))
+                         (.log js/console "  >" (clj->js obj-new))
+
+                         )))
+             (rx/take-until stopper)
+             (rx/ignore))))))
diff --git a/frontend/src/app/main/data/workspace/indices/object_tree.cljs b/frontend/src/app/main/data/workspace/indices/object_tree.cljs
new file mode 100644
index 000000000..e772b0ad8
--- /dev/null
+++ b/frontend/src/app/main/data/workspace/indices/object_tree.cljs
@@ -0,0 +1,28 @@
+;; This Source Code Form is subject to the terms of the Mozilla Public
+;; License, v. 2.0. If a copy of the MPL was not distributed with this
+;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
+;;
+;; Copyright (c) UXBOX Labs SL
+
+(ns app.main.data.workspace.indices.object-tree
+  (:require
+   [app.common.pages.helpers :as cph]
+   ))
+
+(defn objects-tree
+  [objects]
+
+  
+  
+  )
+
+(defn init-index
+  [objects]
+
+  
+
+  )
+
+(defn update-index
+  [index shape-id change-type old-objects new-objects]
+  )
diff --git a/frontend/src/app/main/ui/shapes/frame.cljs b/frontend/src/app/main/ui/shapes/frame.cljs
index 8e4d07866..3275e6ab7 100644
--- a/frontend/src/app/main/ui/shapes/frame.cljs
+++ b/frontend/src/app/main/ui/shapes/frame.cljs
@@ -45,7 +45,7 @@
   [props]
   (let [shape (obj/get props "shape")]
     (when (:thumbnail shape)
-      (let [{:keys [x y width height]} shape
+      (let [{:keys [x y width height show-content]} shape
             transform (gsh/transform-matrix shape)
             props (-> (attrs/extract-style-attrs shape)
                       (obj/merge!
@@ -59,8 +59,9 @@
             render-id (mf/use-ctx muc/render-ctx)]
 
         [:*
-         [:g {:clip-path (frame-clip-url shape render-id)}
-          [:& frame-clip-def {:shape shape :render-id render-id}]
+         [:g {:clip-path (when (not show-content) (frame-clip-url shape render-id))}
+          (when (not show-content)
+            [:& frame-clip-def {:shape shape :render-id render-id}])
           [:& shape-fills {:shape shape}
            (if path?
              [:> :path props]
@@ -88,7 +89,7 @@
     [props]
     (let [childs     (unchecked-get props "childs")
           shape      (unchecked-get props "shape")
-          {:keys [x y width height]} shape
+          {:keys [x y width height show-content]} shape
 
           transform (gsh/transform-matrix shape)
 
@@ -104,7 +105,8 @@
           render-id (mf/use-ctx muc/render-ctx)]
 
       [:*
-       [:g {:clip-path (frame-clip-url shape render-id)}
+       [:g {:clip-path (when (not show-content)
+                         (frame-clip-url shape render-id))}
         [:& shape-fills {:shape shape}
          (if path?
            [:> :path props]
diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs
index ec70154f7..7f23daf08 100644
--- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs
@@ -6,6 +6,8 @@
 
 (ns app.main.ui.workspace.shapes.frame
   (:require
+   [app.main.store :as st]
+   [app.main.data.workspace.state-helpers :as wsh]
    [app.common.data :as d]
    [app.common.data.macros :as dm]
    [app.common.uuid :as uuid]
@@ -45,8 +47,7 @@
   [new-props old-props]
   (and
        (= (unchecked-get new-props "thumbnail?") (unchecked-get old-props "thumbnail?"))
-       (= (unchecked-get new-props "shape") (unchecked-get old-props "shape"))
-       (= (unchecked-get new-props "objects") (unchecked-get old-props "objects"))))
+       (= (unchecked-get new-props "shape") (unchecked-get old-props "shape"))))
 
 (defn frame-wrapper-factory
   [shape-wrapper]
@@ -59,7 +60,7 @@
 
       (let [shape              (unchecked-get props "shape")
             thumbnail?         (unchecked-get props "thumbnail?")
-            objects            (unchecked-get props "objects")
+            objects            (wsh/lookup-page-objects @st/state)
 
             render-id          (mf/use-memo #(str (uuid/next)))
             fonts              (mf/use-memo (mf/deps shape objects) #(ff/shape->fonts shape objects))
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
index 0977b1a63..898339807 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
@@ -6,6 +6,7 @@
 
 (ns app.main.ui.workspace.sidebar.options.menus.measures
   (:require
+   [app.main.constants :refer [size-presets]]
    [app.common.data :as d]
    [app.common.geom.shapes :as gsh]
    [app.common.spec.radius :as ctr]
@@ -30,12 +31,14 @@
    :rx :ry
    :r1 :r2 :r3 :r4
    :selrect
-   :points])
+   :points
+   :show-content
+   :hide-in-viewer])
 
 (def ^:private type->options
   {:bool    #{:size :position :rotation}
    :circle  #{:size :position :rotation}
-   :frame   #{:presets :size :position :radius}
+   :frame   #{:presets :size :position :radius :clip-content :show-in-viewer}
    :group   #{:size :position :rotation}
    :image   #{:size :position :rotation :radius}
    :path    #{:size :position :rotation}
@@ -43,8 +46,6 @@
    :svg-raw #{:size :position :rotation}
    :text    #{:size :position :rotation}})
 
-(declare +size-presets+)
-
 ;; -- User/drawing coords
 (mf/defc measures-menu
   [{:keys [ids ids-with-children values type all-types shape] :as props}]
@@ -103,6 +104,9 @@
         radius-multi?    (mf/use-state nil)
         radius-input-ref (mf/use-ref nil)
 
+        clip-content-ref (mf/use-ref nil)
+        show-in-viewer-ref (mf/use-ref nil)
+
         on-preset-selected
         (fn [width height]
           (st/emit! (udw/update-dimensions ids :width width)
@@ -146,13 +150,13 @@
 
         change-radius
         (mf/use-callback
-          (mf/deps ids-with-children)
-          (fn [update-fn]
-            (dch/update-shapes ids-with-children
-                               (fn [shape]
-                                 (if (ctr/has-radius? shape)
-                                   (update-fn shape)
-                                   shape)))))
+         (mf/deps ids-with-children)
+         (fn [update-fn]
+           (dch/update-shapes ids-with-children
+                              (fn [shape]
+                                (if (ctr/has-radius? shape)
+                                  (update-fn shape)
+                                  shape)))))
 
         on-switch-to-radius-1
         (mf/use-callback
@@ -200,17 +204,38 @@
         on-radius-r3-change #(on-radius-4-change % :r3)
         on-radius-r4-change #(on-radius-4-change % :r4)
 
+        on-change-clip-content
+        (mf/use-callback
+         (mf/deps ids)
+         (fn [event]
+           (let [value (-> event dom/get-target dom/checked?)]
+             (dch/update-shapes ids (fn [shape]) (assoc shape :show-content (not value))))))
+
+        on-change-clip-content
+        (mf/use-callback
+         (mf/deps ids)
+         (fn [event]
+           (let [value (-> event dom/get-target dom/checked?)]
+             (st/emit! (dch/update-shapes ids (fn [shape] (assoc shape :show-content (not value))))))))
+
+        on-change-show-in-viewer
+        (mf/use-callback
+         (mf/deps ids)
+         (fn [event]
+           (let [value (-> event dom/get-target dom/checked?)]
+             (st/emit! (dch/update-shapes ids (fn [shape] (assoc shape :hide-in-viewer (not value))))))))
+
         select-all #(-> % (dom/get-target) (.select))]
     
-        (mf/use-layout-effect
-         (mf/deps radius-mode @radius-multi?)
-         (fn []
-           (when (and (= radius-mode :radius-1)
-                      (= @radius-multi? false))
-             ;; when going back from radius-multi to normal radius-1,
-             ;; restore focus to the newly created numeric-input
-             (let [radius-input (mf/ref-val radius-input-ref)]
-               (dom/focus! radius-input)))))
+    (mf/use-layout-effect
+     (mf/deps radius-mode @radius-multi?)
+     (fn []
+       (when (and (= radius-mode :radius-1)
+                  (= @radius-multi? false))
+         ;; when going back from radius-multi to normal radius-1,
+         ;; restore focus to the newly created numeric-input
+         (let [radius-input (mf/ref-val radius-input-ref)]
+           (dom/focus! radius-input)))))
 
     [:*
      [:div.element-set
@@ -226,7 +251,7 @@
            [:& dropdown {:show @show-presets-dropdown?
                          :on-close #(reset! show-presets-dropdown? false)}
             [:ul.custom-select-dropdown
-             (for [size-preset +size-presets+]
+             (for [size-preset size-presets]
                (if-not (:width size-preset)
                  [:li.dropdown-label {:key (:name size-preset)}
                   [:span (:name size-preset)]]
@@ -367,172 +392,30 @@
                 :min 0
                 :on-click select-all
                 :on-change on-radius-r4-change
-                :value (:r4 values)}]]])])]]]))
+                :value (:r4 values)}]]])])
 
-     (def +size-presets+
-       [{:name "APPLE"}
-        {:name "iPhone 12/12 Pro"
-         :width 390
-         :height 844}
-        {:name "iPhone 12 Mini"
-         :width 360
-         :height 780}
-        {:name "iPhone 12 Pro Max"
-         :width 428
-         :height 926}
-        {:name "iPhone X/XS/11 Pro"
-         :width 375
-         :height 812}
-        {:name "iPhone XS Max/XR/11"
-         :width 414
-         :height 896}
-        {:name "iPhone 6/7/8 Plus"
-         :width 414
-         :height 736}
-        {:name "iPhone 6/7/8/SE2"
-         :width 375
-         :height 667}
-        {:name "iPhone 5/SE"
-         :width 320
-         :height 568}
-        {:name "iPad"
-         :width 768
-         :height 1024}
-        {:name "iPad Pro 10.5in"
-         :width 834
-         :height 1112}
-        {:name "iPad Pro 12.9in"
-         :width 1024
-         :height 1366}
-        {:name "Watch 44mm"
-         :width 368
-         :height 448}
-        {:name "Watch 42mm"
-         :width 312
-         :height 390}
-        {:name "Watch 40mm"
-         :width 324
-         :height 394}
-        {:name "Watch 38mm"
-         :width 272
-         :height 340}
+       (when (options :clip-content)
+         [:div.input-checkbox
+          [:input {:type "checkbox"
+                   :id "clip-content"
+                   :ref clip-content-ref
+                   :checked (not (:show-content values))
+                   :on-change on-change-clip-content}]
 
-        {:name "ANDROID"}
-        {:name "Mobile"
-         :width 360
-         :height 640}
-        {:name "Tablet"
-         :width 768
-         :height 1024}
-        {:name "Google Pixel 4a/5"
-         :width 393
-         :height 851}
-        {:name "Samsung Galaxy S20+"
-         :width 384
-         :height 854}
-        {:name "Samsung Galaxy A71/A51"
-         :width 412
-         :height 914}
+          [:label {:for "clip-content"}
+           (tr "workspace.options.clip-content")]])
 
-        {:name "MICROSOFT"}
-        {:name "Surface Pro 3"
-         :width 1440
-         :height 960}
-        {:name "Surface Pro 4/5/6/7"
-         :width 1368
-         :height 912}
+       (when (options :show-in-viewer)
+         [:div.input-checkbox
+          [:input {:type "checkbox"
+                   :id "show-in-viewer"
+                   :ref show-in-viewer-ref
+                   :checked (not (:hide-in-viewer values))
+                   :on-change on-change-show-in-viewer}]
 
-        {:name "ReMarkable"}
-        {:name "Remarkable 2"
-         :width 840
-         :height 1120}
+          [:label {:for "show-in-viewer"}
+           (tr "workspace.options.show-in-viewer")]])
 
-        {:name "WEB"}
-        {:name "Web 1280"
-         :width 1280
-         :height 800}
-        {:name "Web 1366"
-         :width 1366
-         :height 768}
-        {:name "Web 1024"
-         :width 1024
-         :height 768}
-        {:name "Web 1920"
-         :width 1920
-         :height 1080}
+       ]]]))
 
-        {:name "PRINT (96dpi)"}
-        {:name "A0"
-         :width 3179
-         :height 4494}
-        {:name "A1"
-         :width 2245
-         :height 3179}
-        {:name "A2"
-         :width 1587
-         :height 2245}
-        {:name "A3"
-         :width 1123
-         :height 1587}
-        {:name "A4"
-         :width 794
-         :height 1123}
-        {:name "A5"
-         :width 559
-         :height 794}
-        {:name "A6"
-         :width 397
-         :height 559}
-        {:name "Letter"
-         :width 816
-         :height 1054}
-        {:name "DIN Lang"
-         :width 835
-         :height 413}
 
-        {:name "SOCIAL MEDIA"}
-        {:name "Instagram profile"
-         :width 320
-         :height 320}
-        {:name "Instagram post"
-         :width 1080
-         :height 1080}
-        {:name "Instagram story"
-         :width 1080
-         :height 1920}
-        {:name "Facebook profile"
-         :width 720
-         :height 720}
-        {:name "Facebook cover"
-         :width 820
-         :height 312}
-        {:name "Facebook post"
-         :width 1200
-         :height 630}
-        {:name "LinkedIn profile"
-         :width 400
-         :height 400}
-        {:name "LinkedIn cover"
-         :width 1584
-         :height 396}
-        {:name "LinkedIn post"
-         :width 1200
-         :height 627}
-        {:name "Twitter profile"
-         :width 400
-         :height 400}
-        {:name "Twitter header"
-         :width 1500
-         :height 500}
-        {:name "Twitter post"
-         :width 1024
-         :height 512}
-        {:name "YouTube profile"
-         :width 800
-         :height 800}
-        {:name "YouTube banner"
-         :width 2560
-         :height 1440}
-        {:name "YouTube thumb"
-         :width 1280
-         :height 720}])
diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs
index 80c600da1..dd4123d48 100644
--- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs
@@ -172,6 +172,7 @@
      over-shapes-stream
      (mf/deps page-id objects)
      (fn [ids]
+       #_(prn "??hover-ids" (->> ids (map #(get-in objects [% :name]))))
        (let [is-group?
              (fn [id]
                (contains? #{:group :bool} (get-in objects [id :type])))
@@ -221,7 +222,7 @@
   [objects hover-ids selected active-frames zoom transform vbox]
 
   (let [frame?                 #(= :frame (get-in objects [% :type]))
-        all-frames             (mf/use-memo (mf/deps objects) #(cph/get-frames-ids objects))
+        all-frames             (mf/use-memo (mf/deps objects) #(cph/get-root-frames-ids objects))
         selected-frames        (mf/use-memo (mf/deps selected) #(->> all-frames (filter selected)))
         xf-selected-frame      (comp (remove frame?) (map #(get-in objects [% :frame-id])))
         selected-shapes-frames (mf/use-memo (mf/deps selected) #(into #{} xf-selected-frame selected))
diff --git a/frontend/src/app/main/ui/workspace/viewport/selection.cljs b/frontend/src/app/main/ui/workspace/viewport/selection.cljs
index dcac1a492..7b1a5ebd3 100644
--- a/frontend/src/app/main/ui/workspace/viewport/selection.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport/selection.cljs
@@ -316,7 +316,7 @@
                              :color color}
                props (map->obj (merge common-props props))]
            (case type
-             :rotation (when (not= :frame (:type shape)) [:> rotation-handler props])
+             :rotation [:> rotation-handler props]
              :resize-point [:> resize-point-handler props]
              :resize-side [:> resize-side-handler props])))])))
 
diff --git a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs
index 67c58faa9..e62258148 100644
--- a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs
@@ -6,6 +6,7 @@
 
 (ns app.main.ui.workspace.viewport.widgets
   (:require
+   [app.common.uuid :as uuid]
    [app.common.data.macros :as dm]
    [app.common.geom.point :as gpt]
    [app.common.geom.shapes :as gsh]
@@ -182,15 +183,16 @@
 
     [:g.frame-titles
      (for [frame frames]
-       [:& frame-title {:key (dm/str "frame-title-" (:id frame))
-                        :frame frame
-                        :selected? (contains? selected (:id frame))
-                        :zoom zoom
-                        :show-artboard-names? show-artboard-names?
-                        :modifiers modifiers
-                        :on-frame-enter on-frame-enter
-                        :on-frame-leave on-frame-leave
-                        :on-frame-select on-frame-select}])]))
+       (when (= (:frame-id frame) uuid/zero)
+         [:& frame-title {:key (dm/str "frame-title-" (:id frame))
+                          :frame frame
+                          :selected? (contains? selected (:id frame))
+                          :zoom zoom
+                          :show-artboard-names? show-artboard-names?
+                          :modifiers modifiers
+                          :on-frame-enter on-frame-enter
+                          :on-frame-leave on-frame-leave
+                          :on-frame-select on-frame-select}]))]))
 
 (mf/defc frame-flow
   [{:keys [flow frame modifiers selected? zoom on-frame-enter on-frame-leave on-frame-select]}]
diff --git a/frontend/src/app/worker/selection.cljs b/frontend/src/app/worker/selection.cljs
index 692cf61ce..dddee6f55 100644
--- a/frontend/src/app/worker/selection.cljs
+++ b/frontend/src/app/worker/selection.cljs
@@ -86,12 +86,17 @@
 
         index              (reduce index-shape initial-quadtree shapes)
 
-        z-index            (cp/calculate-z-index objects)]
+        ;;z-index            (cp/calculate-z-index objects)
+        ]
 
-    {:index index :z-index z-index :bounds bounds}))
+    {:index index
+     ;;:z-index z-index
+     :bounds bounds}))
 
 (defn- update-index
-  [{index :index z-index :z-index :as data} old-objects new-objects]
+  [{index :index
+    ;; z-index :z-index
+    :as data} old-objects new-objects]
   (let [changes? (fn [id]
                    (not= (get old-objects id)
                          (get new-objects id)))
@@ -112,12 +117,16 @@
         index-shape (make-index-shape new-objects parents-index clip-parents-index)
         index (reduce index-shape new-index shapes)
 
-        z-index (cp/update-z-index z-index changed-ids old-objects new-objects)]
+        ;;z-index (cp/update-z-index z-index changed-ids old-objects new-objects)
+        ]
 
-    (assoc data :index index :z-index z-index)))
+    (assoc data :index index ;;:z-index z-index
+           )))
 
 (defn- query-index
-  [{index :index z-index :z-index} rect frame-id full-frame? include-frames? ignore-groups? clip-children? reverse?]
+  [{index :index
+    ;;z-index :z-index
+    } rect frame-id full-frame? include-frames? ignore-groups? clip-children? reverse?]
   (let [result (-> (qdt/search index (clj->js rect))
                    (es6-iterator-seq))
 
@@ -145,10 +154,10 @@
         (fn [clip-parents]
           (->> clip-parents (some (comp not overlaps?)) not))
 
-        add-z-index
-        (fn [{:keys [id frame-id] :as shape}]
-          (assoc shape :z (+ (get z-index id)
-                             (get z-index frame-id 0))))
+        ;;add-z-index
+        ;;(fn [{:keys [id frame-id] :as shape}]
+        ;;  (assoc shape :z (+ (get z-index id)
+        ;;                     (get z-index frame-id 0))))
 
         ;; Shapes after filters of overlapping and criteria
         matching-shapes
@@ -160,14 +169,15 @@
                     (filter (if clip-children?
                               (comp overlaps-parent? :clip-parents)
                               (constantly true)))
-                    (map add-z-index))
+                    #_(map add-z-index))
               result)
 
-        keyfn (if reverse? (comp - :z) :z)]
+        ;;keyfn (if reverse? (comp - :z) :z)
+        ]
 
     (into (d/ordered-set)
           (->> matching-shapes
-               (sort-by keyfn)
+               #_(sort-by keyfn)
                (map :id)))))
 
 
@@ -208,7 +218,7 @@
   (when-let [index (get @state page-id)]
     (query-index index rect frame-id full-frame? include-frames? ignore-groups? clip-children? reverse?)))
 
-(defmethod impl/handler :selection/query-z-index
+#_(defmethod impl/handler :selection/query-z-index
   [{:keys [page-id objects ids]}]
   (when-let [{z-index :z-index} (get @state page-id)]
     (->> ids (map #(+ (get z-index %)
diff --git a/frontend/translations/en.po b/frontend/translations/en.po
index 156b7ffc7..67d209c24 100644
--- a/frontend/translations/en.po
+++ b/frontend/translations/en.po
@@ -3890,6 +3890,12 @@ msgstr "X"
 msgid "workspace.options.y"
 msgstr "Y"
 
+msgid "workspace.options.clip-content"
+msgstr "Clip content"
+
+msgid"workspace.options.show-in-viewer"
+msgstr "Show in view mode"
+
 msgid "workspace.path.actions.add-node"
 msgstr "Add node (%s)"
 
diff --git a/frontend/translations/es.po b/frontend/translations/es.po
index 7d521607a..b6ec5136c 100644
--- a/frontend/translations/es.po
+++ b/frontend/translations/es.po
@@ -4058,6 +4058,12 @@ msgstr "X"
 msgid "workspace.options.y"
 msgstr "Y"
 
+msgid "workspace.options.clip-content"
+msgstr "Truncar contenido"
+
+msgid"workspace.options.show-in-viewer"
+msgstr "Mostrar en modo visualización"
+
 msgid "workspace.path.actions.add-node"
 msgstr "AƱadir nodo (%s)"