8 Commits

Author SHA1 Message Date
alonso.torres
8613bdf5d4 🐛 Fix the variant events and the plugin component calls
Fixes in the variant events of the workspace and in the component calls of
the plugin API (Phase 3c).

- Rename a variant in one commit.

  Renaming a variant renamed its board, then each of its components and
  mains, as separate commits inside an undo transaction. The transaction
  kept the undo group of its first commit, the board's, while the sync that
  the renamed mains started took the group of their commits and made an
  entry of its own: one undo reverted only the copies' names (F6, case AV).

  clv/generate-rename-variant builds the board, component and main renames
  in common, and the event commits them once, so the rename and its sync are
  one undo entry, like transform into a variant.

  Case AV no longer marks its round trip as a known failure; the frontend
  golden master records it without failures.

- Keep overrides in a plugin swap only within a variant set.

  The plugin's swapComponent swapped with keep-touched for any component,
  while the UI keeps a copy's overrides only on a variant switch: the same
  swap kept different overrides by route (F33). It now keeps them only when
  the copy's component and the target are variants of one set (decision
  (p)), as swap-keeps-touched? says.

  shape-bugfixes-test: two variants of one set keep the overrides; of
  different sets, or plain components, do not.

- Look up a plugin variant head's component in its own file.

  The plugin's isVariantHead looked the shape's component up in the file of
  the shape, so it was false for copies of a library's variants; and it
  looked it up before checking the shape is a head, so on a shape with no
  component it failed the lookup's assert (F91). It now checks the head
  first and looks in the shape's :component-file.

  shape-bugfixes-test: a copy of a library variant is a variant head, and a
  plain rect is not.

- Add a variant with the shortcut when a variant set is selected.

  The context menu of a variant container offers "Add variant" and shows the
  create-component shortcut for it, but the shortcut's event did not know
  containers: with one selected it fell through to creating a component,
  which the container's own rules refuse (F95). With a single variant
  container selected, the shortcut now adds a variant to it, as the menu
  entry does.

  workspace-variants-test: the shortcut on a variant container gives the set
  a third member.

- Refuse plugin variant edits whose variant is on another page.

  The variant events the plugin API calls (add a variant, add, remove or
  rename a property, set a property value) build their changes on the
  objects of the page the workspace shows. For a variant whose mains sit on
  another page, they updated the components but not their mains, so the
  mains kept their old :variant-name and the file failed validation (F94).
  The plugin now refuses these edits, as it refuses edits of shapes on
  another page, unless the variant is in the current file and on the current
  page.

  library-test: each edit goes through with the variant on the current page
  and is refused, with nothing emitted, with it elsewhere.

- Check every component of an assets group before combining it.

  The assets panel offers to combine a group of components as variants when
  they can be: more than one, none a variant, all with their main on one
  page. It counted every component of the group, subgroups included, but
  checked the other two rules only on the components directly in it, while
  the combine takes them all; with none directly in it, those rules always
  held (F98). can-combine-group? now checks every component of the group.

  assets-components-test: a group whose only components are in a subgroup
  can be combined; a variant or a main on another page in a subgroup blocks
  it; one component cannot.

AI-assisted-by: claude-opus-5-5
2026-10-30 09:50:07 +01:00
alonso.torres
892d1bb885 🐛 Check and commit plugin component calls as the UI does
- The UI offers swap and reset only on copy heads, and detach only on
  copies that are not inside another copy. The plugin's swapComponent
  and resetOverrides accepted any shape inside a copy (F92), and its
  detach any shape at all (F93), and sent the same events, whose
  changes expect a head. swapComponent and resetOverrides report "The
  shape is not a component copy head" for anything else; detach
  reports a shape that is not a copy, or a copy inside another copy,
  by ctn/detachable?, which the detach event checks too. swapComponent
  looked up the target file before checking its argument, so a value
  that is not a component failed an assert instead of reporting
  "Component not valid".
- The plugin's switchVariant did not check whether the switch would
  nest a component in itself. The switch event checks that and calls
  the :on-error of its params' metadata; the plugin passed none, so
  the default, rx/throw, built an error observable that nothing used,
  and the call did nothing and reported nothing (F53). The plugin
  passes an :on-error that reports "The switch would create a
  component nesting loop", and the event's default does nothing.
- Choosing a property value in the design tab with several members
  selected sent, for each member, an event that set the value and
  another that cleared its error, each its own commit and undo entry.
  The plugin's setVariantProperty set the value and left the error as
  it was (F94). dwv/update-property-value takes the components, and
  sets the value and clears the error of each in one commit; the
  design tab passes the selected members, the plugin its component,
  and the composable interpreter the one member of its edit.

shape-bugfixes-test checks each plugin case on the proxy, and
workspace-variants-test sets a value on two members with errors.

AI-assisted-by: claude-opus-5-5
2026-10-30 09:49:28 +01:00
Alonso Torres
cbb9e5d971
✨ Add end-to-end tests for plugins validation (#11587)
* ✨ Add missing plugin data validations

* ✨ Add migration to fix the new schema validations

* ✨ Add end-to-end tests for plugins validation

* 🐛 Fix unit tests after merge

* 🐛 Change normalize behavior
2026-09-23 19:59:22 +02:00
Andrey Antukh
f695553469 Merge remote-tracking branch 'origin/staging' into develop 2026-09-08 21:15:58 +02:00
Andrey Antukh
937b3fc65f
🐛 Add missing permission checks to plugin API (tokens, shapes, variants, flows, layouts, user identity) (#11139)
* 🐛 Add content:write permission checks to Design Tokens plugin API

The Design Tokens API (tokens.cljs) had zero permission checks, allowing
any plugin to create, modify, and delete tokens, sets, and themes
regardless of granted permissions.

Add r/check-permission checks to all 22 write operations across:
- token-proxy: name, value, description, duplicate, remove, applyToken
- token-set-proxy: name, active, toggleActive, addToken, duplicate, remove
- token-theme-proxy: group, name, active, toggleActive, addSet, removeSet,
  duplicate, remove
- tokens-catalog: addTheme, addSet

Follows the established pattern from comments.cljs, file.cljs, page.cljs.

Closes #11137

AI-assisted-by: qwen3.7-plus

* 🐛 Add permission checks to shape proxy interactions, detach, export, and variants

The shape proxy (shape.cljs) had multiple operations missing permission
checks, plus a cond ordering bug that bypassed the existing content:write
check for text shapes in commit-fills!.

Fix commit-fills! cond ordering: move permission check before the
text-shape branch so text shapes are also protected.

Add content:write permission checks to:
- interaction-proxy: :trigger, :delay, :action setters, :remove method
- shape-proxy: :addInteraction, :removeInteraction, :detach
- shape-proxy: :applyToken, :switchVariant, :combineAsVariants

Add content:read permission check to:
- shape-proxy: :export (read/extraction operation)

Follows the established pattern from :resize, :rotate, :blocked setters.

Relates to #11137

AI-assisted-by: qwen3.7-plus

* 🐛 Add library:write permission checks to variant plugin API

The library.cljs variant operations (variant-proxy and
lib-component-proxy) had seven mutating operations that
did not check the library:write permission, allowing
any plugin to create, modify, and delete component
variants regardless of granted permissions.

Add r/check-permission checks to all 7 operations:
- variant-proxy: addVariant, addProperty,
  removeProperty, renameProperty
- lib-component-proxy: transformInVariant, addVariant,
  setVariantProperty

Follows the established pattern from the :name and
:path setters in the same file.

Relates to #11137

AI-assisted-by: qwen3.7-plus

* 🐛 Add content:write permission checks to flow and flex layout plugin API

Add permission checks to prototype flow and flex layout operations
that were missing them, allowing plugins to modify flows and layout
structure without explicit user permission.

Changes:
- page.cljs: Add content:write checks to flow-proxy (name,
  startingBoard setters, remove) and page-proxy (createFlow,
  removeFlow)
- flex.cljs: Add content:write checks to flex-layout-proxy
  (remove, appendChild)

Follows the established pattern from tokens.cljs, shape.cljs,
and library.cljs.

Relates to #11137

AI-assisted-by: qwen3.7-plus

* 🐛 Add user:read permission checks to plugin API

Add permission checks to user identity accessors that were bypassing
the consent model, allowing plugins to access user data regardless
of whether the user granted user:read permission.

Changes:
- api.cljs: Add user:read checks to getCurrentUser and getActiveUsers
- comments.cljs: Add user:read checks to comment-proxy and
  comment-thread-proxy owner/user getters
- file.cljs: Add user:read check to file-version-proxy createdBy getter

When user:read permission is not granted:
- getCurrentUser() returns null
- getActiveUsers() returns empty array
- owner/user/createdBy getters return null

Follows the established pattern from other permission checks in the
plugin API.

Relates to #11137

AI-assisted-by: qwen3.7-plus

* 🐛 Fix problem with token API

---------

Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
2026-09-08 09:24:45 +02:00
Andrey Antukh
81c3b3cd56
📎 Update copyright name on file header (#11346) 2026-08-25 11:55:10 +02:00
Dr. Dominik Jain
c88d9f568b
🐛 Preserve component order when combining components as variants (#10562) 2026-07-06 16:49:29 +02:00
Alonso Torres
f993f203bd
🐛 Fix problems with plugins API (#10412)
* ✨ Adds static dispatch safe stubs in tests

* 🐛 Fix shapesColors metadata key to match ColorShapeInfo

* 🐛 Fix CommentThread.remove rejecting the owner's own threads

* 🐛 Fix page.removeCommentThread throwing on a spurious Promise

* ✨ Implement ShapeBase.swapComponent in the plugin API

* ✨ Expose File.revn in the plugin API

* 🐛 Fix FileVersion.createdAt calling Luxon method on a js/Date

* 🐛 Fix plugin font/typography application to text and ranges

* 🐛 Default plugin overlay interaction position for non-manual types

* 🐛 Fix plugin interaction setters passing an id-only shape

* 🐛 Fix grid addColumnAtIndex rejecting valid track types

* 🐛 Expose libraryId on library color/typography/component proxies

* ✨ Implement LibraryTypography.setFont in the plugin API

* 🐛 Fix typography.applyToTextRange reading unexposed range bounds

* 🐛 Fix utils.geometry.center argument mismatch

* 🐛 Fix localStorage.removeItem calling getItem

* 🐛 Fix shape backgroundBlur proxy key casing

* 🐛 Report boolean shape type as 'boolean' in the plugin API

* 🐛 Return the resulting paths from plugin flatten

* 🐛 Make plugin z-order methods act on the target shape

* 🐛 Make is-variant-container? return a boolean

* ✨ Implement Group.isMask in the plugin API

* 🐛 Return a shape proxy from TextRange.shape

* 🐛 Return the duplicated set from TokenSet.duplicate

* 🐛 Fix theme addSet/removeSet reading set name with a keyword

* 🐛 Accept string fontFamilies token value in the plugin API

* 🐛 Fix combineAsVariants ignoring the passed component ids

* 🐛 Fix board removeRulerGuide ignoring its argument

* 🐛 Fix board guides setter schema and parser

* 🐛 Avoid 0-byte allocation when syncing empty grid tracks

* 🐛 Validate grid track indices in the plugin API

* 🐛 Return null for empty input in group() and centerShapes()

* 🐛 Return TokenTypographyValue[] from a typography token's resolvedValue

* 🐛 Return TokenShadowValue[] from a shadow token's resolvedValue

* 🐛 Return string[] from a fontFamilies token's resolvedValue

* 🐛 Clear mutually-exclusive reps when setting LibraryColor gradient/image

* 🐛 Add readonly tags to types, deprecate Image type

* 📚 Update plugins changelog
2026-06-29 17:32:15 +02:00