- Updated the error message for missing content write permission in the removeRulerGuide function.
- Renamed the ruler guide proxy from "RuleGuideProxy" to "RulerGuideProxy" for consistency.
- Adjusted variable naming in the addRulerGuide function for clarity.
Signed-off-by: Stas Haas <stas@girafic.de>
* ✨ Use update-when for update dashboard state
This make updates more consistent and reduces possible eventual
consistency issues in out of order events execution.
* 🐛 Detect stale JS modules at boot and force reload
When the browser serves cached JS files from a previous deployment
alongside a fresh index.html, code-split modules reference keyword
constants that do not exist in the stale shared.js, causing TypeError
crashes.
This adds a compile-time version tag (via goog-define / closure-defines)
that is baked into the JS bundle. At boot, it is compared against the
runtime version tag from index.html (which is always fresh due to
no-cache headers). If they differ, the app forces a hard page reload
before initializing, ensuring all JS modules come from the same build.
* 📎 Ensure consistent version across builds on github e2e test workflow
---------
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
* ✨ Make the MCP plugin switching between tabs work correctly
* 🎉 Show notification when the plugin is loaded in another tab
* 📎 PR changes
* ✨ Add events
Add a nil guard before subscribing to the stream in the use-stream
hook. When a nil/undefined stream is passed (e.g., from a conditional
expression or timing edge case during React rendering), the subscribe
call on undefined causes a TypeError. The guard ensures we only
subscribe when the stream is defined.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
When the plugin sandbox calls harden() (SES lockdown) on any proxy object
returned from the penpot.* API, SES traverses the prototype chain up to
Proxy.prototype and freezes the CLJS Proxy constructor function. Transit's
typeTag helper later fails with "object is not extensible" when trying to
set its cache property on that frozen constructor.
Fix by deleting the constructor data property from Proxy.prototype so that
harden never traverses to the CLJS Proxy constructor function.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
The with-cache macro in impl.cljc assumed the target was always a
PathData instance (which has a cache field). When content was a plain
vector, (.-cache content) returned undefined in JS, causing:
TypeError: Cannot read properties of undefined (reading 'get')
Fix:
- path/get-points (app.common.types.path) is now the canonical safe
entry point: converts non-PathData content via impl/path-data and
handles nil safely before delegating to segment/get-points
- segment/get-points remains a low-level function that expects a
PathData instance (no defensive logic at that level)
- streams.cljs: replace direct call to path.segm/get-points with
path/get-points so the safe conversion path is always used
- with-cache macro: guards against nil/undefined cache, falling back
to direct evaluation for non-PathData targets
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
- Change the default for the newWindow param from true to false, so
openPage() navigates in the same tab instead of opening a new one
- Accept a UUID string as the page argument in addition to a Page object,
avoiding the need to call penpot.getPage(uuid) first
- Add validation error when an invalid page argument is passed
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
When clipboard items have types that don't match the allowed types
list, the filtering results in an empty array. Calling getType with
undefined throws a NotFoundError. This change adds a check for null/undefined
types and filters them from the result.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
- Change the default for the newWindow param from true to false, so
openPage() navigates in the same tab instead of opening a new one
- Accept a UUID string as the page argument in addition to a Page object,
avoiding the need to call penpot.getPage(uuid) first
- Add validation error when an invalid page argument is passed
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Guard against transit-decoded clipboard content that is not a map
before calling assoc, which caused a runtime crash ('No protocol
method IAssociative.-assoc defined for type number').
Also route :copied-props paste data to paste-transit-props instead
of incorrectly sending it to paste-transit-shapes.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Identify and silence "signal is aborted without reason" errors by:
- Providing an explicit reason to AbortController when subscriptions are disposed.
- Updating the global error handler to ignore AbortError exceptions.
- Ensuring unhandled rejections use the ignorable exception filter.
The root cause was RxJS disposal calling .abort() without a reason, combined
with the on-unhandled-rejection handler missing the ignorable error filter.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Guard get-option fallback with (when (seq options) ...) to avoid
"No item 0 in vector of length 0" when options is an empty vector.
Also guard the selected-option memo in select* to mirror the same
pattern already present in combobox*.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
* 💄 Removed forgotten print (#8594)
* 🐛 Fix number token applying rotation when line-height attr is specified
toggle-token always used the on-update-shape from token-properties,
which for :number tokens is unconditionally update-rotation. So calling
applyToken(token, ["line-height"]) on a :number token would correctly
set the line-height text attribute but also invoke update-rotation with
the token value, silently rotating the shape.
Added an :on-update-shape-per-attr map to the :number token properties
entry mapping each valid attribute subset to its correct update function.
toggle-token now resolves the update function from that map when explicit
attrs are provided, falling back to the default on-update-shape otherwise.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
* ♻️ Centralise attr->update-fn map and use it generically in toggle-token
The attributes->shape-update map was only defined in propagation.cljs.
Move it to application.cljs (where all the update functions live) and
have propagation.cljs reference it via dwta/attributes->shape-update,
eliminating the duplication.
Build a private flattened attr->shape-update map (one entry per
individual keyword) from that same source of truth. toggle-token now
uses it to resolve the correct on-update-shape when explicit attrs are
passed, instead of always taking the default from token-properties.
This fixes the :number token side-effect without any per-type special
casing: any token type whose explicit attrs map to a different update
function than the type default will now dispatch correctly.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
* ✨ Backport obj/reify changes from develop
* ✨ Add missing error handler on shape proxy on plugins objects
---------
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Alonso Torres <alonso.torres@kaleidos.net>
Replace int? with number? in on-change handlers for layout item margins,
min/max sizes, and layer opacity. Using int? caused float values like 8.5
to fall into the design token branch, calling (first 8.5) and crashing.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
* 🐛 Fix error when creating guides without frame
The error 'Cannot read properties of undefined (reading
$cljs$core$IFn$_invoke$arity$0$)' occurred when creating a new
guide. It is probably a race condition because it is not reproducible
from the user point of view.
The cause is mainly because of use incorrect jsx handler :& where :>
should be used. This caused that some props pased with incorrect casing
and the relevant callback props received as nil on the component and
on the use-guide hook.
The fix is simple: use correct jsx handler
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
* 💄 Add cosmetic changes to viewport guides components
---------
Signed-off-by: Andrey Antukh <niwi@niwi.nz>