16 Commits

Author SHA1 Message Date
BitCompass
fbb1f9e634
🐛 Fix plugin API error message for nested malli validation paths (#9486)
When a plugin call fails malli validation, the frontend renders one
"plugins.validation.message" line per error via
`app.plugins.utils/error-messages`, which reduces the explain via
`csm/interpret-schema-problem` and then destructures each entry as
`[field {:keys [message]}]` for translation.

That works only when the underlying malli error path has a single
element. `interpret-schema-problem` calls `(assoc-in acc field ...)`
where `field` can be a multi-element vector (e.g. `[:sets 0 :name]`).
For single-element paths the resulting map is flat
(`{:group {:message "..."}}`); for multi-element paths it is nested
(`{:sets {0 {:name {:message "..."}}}}`). The destructure assumes the
flat shape, so for a nested error the consumer reads:

    field   -> :sets
    message -> nil (the nested entry has no :message at the top level)

and the produced i18n line resolves to `Field sets is invalid: ` --
or, when several errors are merged together at the same outer key,
to the user-facing `Field message is invalid` that the bug report
calls out, because `:message` then becomes the field name of the
deepest nested entry.

The original consumer carried a `#_(mapcat (comp seq val))` FIXME
that hinted at the missing flattening but did not implement one,
because the data shape produced by `interpret-schema-problem` is
not uniform.

Fix
---

Add a private `flatten-error-map` helper inside `app.plugins.utils`
that walks the error map produced by `interpret-schema-problem` and
yields `[path message]` pairs where `path` is the dot-joined field
path. Keywords use `(name k)`, strings pass through, anything else
(such as numeric indices from vector positions in the malli path)
is coerced via `str`. The recursion descends until it hits a leaf
that carries `:message`, which matches what
`interpret-schema-problem` produces in every branch.

The producer side (`csm/interpret-schema-problem` in
`common/src/app/common/schema/messages.cljc`) is left alone: it
already has another consumer (`collect-schema-errors` + the
form-validators pipeline) that depends on the keyed-by-field-path
shape, so normalising it at the source would require auditing every
validator. Flattening at the plugin consumer is the narrowest fix.

The FIXME comment is removed because the new helper supersedes it.

Tests
-----

`frontend-tests.plugins.utils-test` (new file, registered in
`runner.cljs`) covers:

- flat single-segment paths (`{:group {:message "..."}}`)
- nested multi-segment paths
  (`{:sets {0 {:name {:message "..."}}}}`) -- the case from #9417
- mixed single- and multi-segment paths at the same explain
- mixed key types (keyword / string / numeric index)
- empty explain (no validation errors)

Closes #9417

Signed-off-by: bitcompass <devwiz.sh@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-14 12:43:57 +02:00
Renzo
691679d90b
🐛 Fix plugin API fills/strokes arrays read-only (#9161)
* 🐛 Fix plugin API fills/strokes arrays read-only

Signed-off-by: RenzoMXD <170978465+RenzoMXD@users.noreply.github.com>

* 🐛 Support mutable plugin fill and stroke gradients

---------

Signed-off-by: RenzoMXD <170978465+RenzoMXD@users.noreply.github.com>
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-05-07 13:10:48 +02:00
boskodev790
1eac3e2be5
🐛 Fix Plugin API token application for JS array of strings (#9166)
* 🐛 Fix Plugin API token application for JS array of strings

Plugin code calling `shape.applyToken(token, ["fill"])` or
`token.applyToShapes([rect], ["fill"])` from JavaScript supplies a JS
array of strings. The plugin proxies expected a Clojure set of
keywords, and two coupled defects made the calls silently no-op (or,
with `throwValidationErrors` enabled, throw "check error"):

1. `token-attr-plugin->token-attr` only consulted its alias map when
   the input was already a keyword. String inputs like "fill" fell
   through to the identity branch, so the downstream
   `cto/token-attr?` predicate (which checks against a set of
   keywords) returned false for every string. Coerce strings to
   keywords first.

2. The `applyToken` / `applyToShapes` / `applyToSelected` schemas
   used plain `[:set ...]`, which has no `:decode/json` transformer
   for JS array → Clojure set coercion. Switch to the registered
   `[::sm/set ...]` (in `app.common.schema`) which provides the
   array → set decoder. After the switch, the standard JSON pipeline
   converts `["fill"]` to `#{"fill"}`, then the inner
   `[:and ::sm/keyword [:fn token-attr?]]` decodes each element to a
   keyword and validates it.

Also extends the docstring on `token-attr-plugin->token-attr` to make
the string-friendly contract explicit, and registers a new
`tokens-test` ns under `frontend/test/frontend_tests/plugins/` with
six `deftest` blocks covering:

- known keywords passing through unchanged
- keyword aliases (`:r1` → `:border-radius-top-left`, etc.)
- string inputs coerced to keywords (regression for #9162)
- `token-attr?` accepting both keyword and string inputs
- `token-attr?` rejecting unknown attrs and nil

Closes #9162

* 🐛 Fix wrong direction in plugin-name alias tests

The added tests in tokens_test.cljs and the new docstring in tokens.cljs
described the alias resolution in the wrong direction. The map is
{:r1 :border-radius-top-left, …} then map-invert'd, so
token-attr-plugin->token-attr maps verbose plugin-side names
(:border-radius-top-left) to canonical internal short names (:r1),
not the other way around. Inputs already in canonical form (:r1, :fill,
"fill", …) pass through unchanged. Flipped the alias-resolution test
expectations and the keyword/string-input cases, refreshed the docstring
and the regression-coverage comment to match.

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-29 11:02:01 +02:00
FairyPiggyDev
361c1c574b
🐛 Fix plugin parse-point returning plain map instead of Point record (#9129)
The plugin parser's parse-point returned a plain `{:x … :y …}` map,
but shape interaction schemas (for example schema:open-overlay-interaction)
require the attribute to be a `::gpt/point` record. `(instance? Point {:x 0 :y 0})`
is false, so validation silently rejected plugin `addInteraction` calls
that passed `manualPositionLocation`; only a console warning was produced.

Change parse-point to return a `gpt/point` record via `gpt/point`.
All three call sites (parser.cljs:open-overlay, plugins/page.cljs,
plugins/comments.cljs) continue to work because Point records support
the same `:x`/`:y` access plain maps do.

Add a unit test that covers nil input and verifies the returned value
satisfies `gpt/point?`.

Github #8409

Signed-off-by: FairyPigDev <luislee3108@gmail.com>
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-24 09:12:13 +02:00
Andrey Antukh
43cdb91063
♻️ Recycle frontend tests with wasm mocks (#8681) 2026-03-23 12:11:27 +01:00
Juanfran
449aa65f8d 🐛 Fix e2e tests for plugins 2026-02-13 13:17:08 +01:00
alonso.torres
29466b47fe Adds skipChildren to export parameters 2025-06-10 15:06:30 +02:00
Andrey Antukh
0b7b6e2c23 ♻️ Refactor penpot library 2025-05-08 09:51:25 +02:00
Andrey Fedorov
6fed0f3b58 ♻️ Remove unused bidings and requirements 2025-03-10 13:29:10 +01:00
Andrey Antukh
85746e7cb2 ♻️ Refactor state locality
The main purpose of this refactor is reduce
a custom state from different pages and unify
them under common access patterns
2025-01-16 15:31:18 +01:00
Alejandro Alonso
642b6b1621 🐛 Fix strokes don't update correctly 2025-01-13 13:14:39 +01:00
Eva Marco
73e48b3d81 ♻️ Remove unnecesary RX and RY from shapes 2024-12-10 10:47:55 +01:00
alonso.torres
a63ded1ba1 Change type names in plugins 2024-09-04 13:29:56 +02:00
alonso.torres
395a91c00c Plugins permissions review 2024-07-02 11:01:43 +02:00
alonso.torres
ac58a5b8fa Improved transformation from and to JS for plugins 2024-07-02 10:41:06 +02:00
alonso.torres
1794859468 Review input validation for plugins 2024-06-21 09:29:09 +02:00