mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 22:10:34 +00:00
✨ Generate shadow, blur and corner edits
Add drop/inner shadows, layer and background blur, and the three remaining corner radii to generated style edits. Preserve the existing input order and describe their authored groups independently of production metadata. Check distinct inputs, main propagation, preserved overrides, corrupt values and lost touched groups. Run component update, reset, detach and exact undo/redo histories in common and the async frontend. The input test fails six assertions before the addition. Focused common checks pass 4 tests / 90 assertions; the six frontend histories pass 204 assertions. Full common JVM/JS, frontend, backend and exporter suites and lint/format checks pass, including six 40-program properties at seed 712. F227's parity correction is covered separately. Golden data and production behavior are unchanged. Wider attribute/layout and source-copy generation, pending-work coverage and the five fresh 5,000-program gates remain open. AI-assisted-by: GPT-6
This commit is contained in:
parent
c79b09af24
commit
9483d9f737
@ -91,7 +91,8 @@
|
||||
;; ---------------------------------------------------------------------------
|
||||
|
||||
(def scalar-properties
|
||||
#{:strokes :hidden :blocked :r1 :blend-mode :hide-fill-on-export :show-content
|
||||
#{:strokes :shadow :blur :background-blur :hidden :blocked :r1 :r2 :r3 :r4
|
||||
:blend-mode :hide-fill-on-export :show-content
|
||||
:fixed-scroll :constraints-h :constraints-v :layout-item-margin
|
||||
:layout-gap :layout-padding :layout-align-items})
|
||||
|
||||
|
||||
@ -373,7 +373,28 @@
|
||||
[:layout-item-margin [{:m1 0 :m2 0 :m3 0 :m4 0} {:m1 4 :m2 4 :m3 4 :m4 4}]]
|
||||
[:layout-gap [{:row-gap 0 :column-gap 0} {:row-gap 8 :column-gap 8}]]
|
||||
[:layout-padding [{:p1 0 :p2 0 :p3 0 :p4 0} {:p1 8 :p2 8 :p3 8 :p4 8}]]
|
||||
[:layout-align-items [:start :center :end]]])
|
||||
[:layout-align-items [:start :center :end]]
|
||||
[:shadow [[] [{:id #uuid "10000000-0000-0000-0000-000000000001"
|
||||
:style :drop-shadow :color {:color "#000000" :opacity 0.5}
|
||||
:offset-x 2 :offset-y 3 :blur 4 :spread 1 :hidden false}]
|
||||
[{:id #uuid "10000000-0000-0000-0000-000000000001"
|
||||
:style :inner-shadow :color {:color "#ff0000" :opacity 0.75}
|
||||
:offset-x -2 :offset-y 4 :blur 8 :spread 2 :hidden false}]]]
|
||||
[:blur [{:id #uuid "10000000-0000-0000-0000-000000000002"
|
||||
:type :layer-blur :value 0 :hidden true}
|
||||
{:id #uuid "10000000-0000-0000-0000-000000000002"
|
||||
:type :layer-blur :value 4 :hidden false}
|
||||
{:id #uuid "10000000-0000-0000-0000-000000000002"
|
||||
:type :layer-blur :value 12 :hidden false}]]
|
||||
[:background-blur [{:id #uuid "10000000-0000-0000-0000-000000000003"
|
||||
:type :background-blur :value 0 :hidden true}
|
||||
{:id #uuid "10000000-0000-0000-0000-000000000003"
|
||||
:type :background-blur :value 6 :hidden false}
|
||||
{:id #uuid "10000000-0000-0000-0000-000000000003"
|
||||
:type :background-blur :value 16 :hidden false}]]
|
||||
[:r2 [0 8 16]]
|
||||
[:r3 [0 8 16]]
|
||||
[:r4 [0 8 16]]])
|
||||
|
||||
(def text-style-edits
|
||||
"All twelve style units, independent of the production text group table."
|
||||
|
||||
@ -19,7 +19,9 @@
|
||||
(def groups
|
||||
{:fills :fill-group :opacity :layer-effects-group :name :name-group
|
||||
:strokes :stroke-group :hidden :visibility-group :blocked :modifiable-group
|
||||
:r1 :radius-group :blend-mode :layer-effects-group :hide-fill-on-export :fill-group
|
||||
:r1 :radius-group :r2 :radius-group :r3 :radius-group :r4 :radius-group
|
||||
:shadow :shadow-group :blur :blur-group :background-blur :blur-group
|
||||
:blend-mode :layer-effects-group :hide-fill-on-export :fill-group
|
||||
:show-content :show-content :fixed-scroll :constraints-group
|
||||
:constraints-h :constraints-group :constraints-v :constraints-group
|
||||
:layout-item-margin :layout-item-margin :layout-gap :layout-gap
|
||||
|
||||
@ -85,6 +85,74 @@
|
||||
(t/is (= :frame (get-in resolved [:shape :type]))
|
||||
(str "property " property ", scope " scope))))))
|
||||
|
||||
(t/deftest generated-effect-and-corner-edits-have-distinct-inputs
|
||||
(let [situation (cg/world [2 1 {}])]
|
||||
(doseq [[index attr] [[14 :shadow] [15 :blur] [16 :background-blur]
|
||||
[17 :r2] [18 :r3] [19 :r4]]]
|
||||
(let [resolved (cg/resolve-op situation {} [:style 0 index 1 1])]
|
||||
(t/is (= attr (:property resolved)))
|
||||
(t/is (some? (:op resolved)))
|
||||
(when (= attr (:property resolved))
|
||||
(t/is (not= (get (:shape resolved) attr) (:value resolved))))))))
|
||||
|
||||
(t/deftest effect-and-corner-overrides-reject-lost-values-or-groups
|
||||
(doseq [[index attr group] [[14 :shadow :shadow-group] [15 :blur :blur-group]
|
||||
[16 :background-blur :blur-group] [17 :r2 :radius-group]
|
||||
[18 :r3 :radius-group] [19 :r4 :radius-group]]]
|
||||
(let [initial (cg/initial-state [1 0 {}])
|
||||
resolved (cg/resolve-op (:situation initial) {} [:style 0 index 1 1])
|
||||
shape (:shape resolved)
|
||||
expected (shadow/values (assoc shape attr (:value resolved)) group)
|
||||
edited (-> initial (cg/step [:style 0 index 1 1]) cg/settle-state)
|
||||
situation (:situation edited)
|
||||
model (:model edited)
|
||||
file (tm/file situation)
|
||||
path [:data :pages-index (:id (thf/current-page file)) :objects (:id shape)]
|
||||
invariant (fn [file]
|
||||
(try (oracle/check (tm/with-file situation file) model) nil
|
||||
(catch #?(:clj Exception :cljs js/Error) error
|
||||
(:invariant (ex-data error)))))]
|
||||
(t/is (= expected (get-in model [(:id shape) group])))
|
||||
(t/is (= :I9 (invariant (update-in file path
|
||||
#(merge (dissoc % attr) (select-keys shape [attr]))))))
|
||||
(t/is (= :I3 (invariant (update-in file (conj path :touched) disj group)))))))
|
||||
|
||||
(t/deftest main-effect-and-corner-edits-reach-clean-copies-and-spare-overrides
|
||||
(doseq [property (range 14 20)]
|
||||
(let [initial (cg/initial-state [1 0 {}])
|
||||
situation (:situation initial)
|
||||
resolved (cg/resolve-op situation {} [:style 0 property 1 1])
|
||||
copy (:shape resolved)
|
||||
attr (:property resolved)
|
||||
main-index (first (keep-indexed (fn [index entry]
|
||||
(when (= (:shape-ref copy) (get-in entry [:shape :id])) index))
|
||||
(cg/candidates situation {} :edit 0)))
|
||||
main-edit [:style main-index property 0 0]
|
||||
copies (filter #(= (:shape-ref copy) (get-in % [:shape :shape-ref])) (cg/tree situation))
|
||||
inherited (-> initial (cg/step main-edit) cg/settle-state)
|
||||
overridden (-> inherited (cg/step [:style 0 property 1 1]) cg/settle-state)
|
||||
updated (-> overridden (cg/step (assoc main-edit 3 2)) cg/settle-state)
|
||||
requested (:value (cg/resolve-op situation {} (assoc main-edit 3 2)))]
|
||||
(t/is (some? main-index))
|
||||
(t/is (= 2 (count copies)))
|
||||
(t/is (not= requested (:value resolved)))
|
||||
(doseq [{:keys [shape]} copies]
|
||||
(let [actual (tm/shape-by-id (:situation updated) (:id shape))]
|
||||
(t/is (= (if (= (:id copy) (:id shape)) (:value resolved) requested) (get actual attr)))
|
||||
(t/is (= (= (:id copy) (:id shape)) (boolean (seq (:touched actual)))))))
|
||||
(t/is (map? (cg/run {:world [1 0 {}]
|
||||
:ops [main-edit [:style 0 property 1 1] (assoc main-edit 3 2)
|
||||
[:undo] [:redo]]}))))))
|
||||
|
||||
(t/deftest generated-effects-and-corners-survive-component-operations
|
||||
(doseq [property (range 14 20)]
|
||||
(t/is (map? (cg/run {:world [2 1 {:variants true :rich-variants true}]
|
||||
:ops [[:style 0 property 1 1] [:style 1 property 1 0]
|
||||
[:undo] [:redo] [:update-main 0]
|
||||
[:style 0 property 0 1] [:reset 0]
|
||||
[:style 0 property 1 1] [:detach 0]
|
||||
[:undo] [:redo]]})))))
|
||||
|
||||
(t/deftest text-style-edits-cover-every-style-unit
|
||||
(doseq [[unit group] (map-indexed vector
|
||||
[:text-content-family :text-content-weight :text-content-size
|
||||
|
||||
@ -93,6 +93,15 @@
|
||||
:ops [[:delete 9 9 1 2] [:text 5 9 1 3] [:style 3 1 1]
|
||||
[:restore 5 8 1 6] [:nest 5 6 0 2] [:undo] [:redo]]})))
|
||||
|
||||
(t/deftest ^:async generated-effects-and-corners-survive-component-operations
|
||||
(doseq [property (range 14 20)]
|
||||
(await (replay {:world [2 1 {:variants true :rich-variants true}]
|
||||
:ops [[:style 0 property 1 1] [:style 1 property 1 0]
|
||||
[:undo] [:redo] [:update-main 0]
|
||||
[:style 0 property 0 1] [:reset 0]
|
||||
[:style 0 property 1 1] [:detach 0]
|
||||
[:undo] [:redo]]}))))
|
||||
|
||||
(t/deftest ^:async swapping-a-copy-with-authored-position-constraints-stays-settled
|
||||
(await (replay {:world [2 1 {:library true :variants true :rich-variants true
|
||||
:text true :text-styles true :layout :flex :layout-copies true}]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user