* ✨ Improve error handling and exception formatting
- Enhance exception formatting with visual separators and cause chaining
- Add new handler for :internal error type
- Refine error types: change assertion-related errors to :assertion type
- Improve error messages and hints consistency
- Clean up error handling in zip utilities and HTTP modules
* 🐛 Properly handle AbortError on fetch request unsubscription
When a fetch request in-flight is cancelled due to RxJS unsubscription
(e.g. navigating away from the workspace while thumbnail loads are
pending), the AbortController.abort() call triggers a catch handler
that previously relied solely on a @unsubscribed? flag to suppress the
error.
This was unreliable: nested observables spawned inside rx/mapcat (such
as datauri->blob-uri conversions within get-file-object-thumbnails)
could abort independently, with their own AbortController instances,
meaning the outer unsubscribed? flag was never set and the AbortError
propagated as an unhandled exception.
Add an explicit AbortError name check as a disjunctive condition so
that abort errors originating from any observable in the chain are
suppressed at the source, regardless of subscription state.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
* 🐛 Fix dissoc error when detaching stroke color from library
The detach-value function in color-row was only passing index to
on-detach, but the stroke's on-color-detach handler expects both
index and color arguments. This caused a protocol error when trying
to dissoc from a number instead of a map.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
* 🐛 Fix crash when detaching color asset from stroke
The color_row detach-value callback calls on-detach with (index, color),
but stroke_row's local on-color-detach wrapper only took a single argument
(fn [color] ...), so it received index as color and passed it to
stroke.cljs which then called (dissoc index :ref-id :ref-file), crashing
with 'No protocol method IMap.-dissoc defined for type number'.
Fix the wrapper to accept (fn [_ color] ...) so it correctly ignores the
index passed by color_row (it already has index in the closure) and
forwards the actual color map to the parent handler.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
---------
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
When pasting an image (with no text content) into the text editor,
Draft.js calls handlePastedText with null/empty text. The previous fix
guarded splitTextIntoTextBlocks against null, but insertText still
attempted to build a fragment from an empty block array, causing
Modifier.replaceWithFragment to crash with 'Cannot read properties of
undefined (reading getLength)'.
Fix insertText to return the original state unchanged when there are no
text blocks to insert. Also guard handle-pasted-text in the ClojureScript
editor to skip the insert-text call entirely when text is nil or empty.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
The splitTextIntoTextBlocks function in @penpot/draft-js called
.split() on the text parameter without a null check. When pasting
content without text data (e.g., images only), Draft.js passes null
to handlePastedText, causing a TypeError.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
The save-path-content function only converted content to PathData when
there was a trailing :move-to command. When there was no trailing
:move-to, the content from get-path was stored as-is, which could be
a plain vector if the shape was already a :path type with non-PathData
content. This caused segment/get-points to fail with 'can't access
property "get", cache is undefined' when the with-cache macro tried
to access the cache field on a non-PathData object.
The fix ensures content is always converted to PathData via path/content
before being stored in the state.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
The get-parent-with-data function traverses the DOM using parentElement
to find an ancestor with a specific data-* attribute. When the current
node is a non-Element DOM node (e.g. Document node reached from event
handlers on window), accessing .-dataset returns undefined, causing
obj/in? to throw "right-hand side of 'in' should be an object".
This adds a nodeType check to skip non-Element nodes during traversal
and continue up the parent chain.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
When the browser denies clipboard read permission (NotAllowedError),
the unhandled exception handler was showing a generic 'Something wrong
has happened' toast. This change adds proper error handling for
clipboard permission errors in paste operations and shows a
user-friendly warning message instead.
Changes:
- Add error handling in paste-from-clipboard for NotAllowedError
- Improve error handling in paste-selected-props to detect permission errors
- Mark clipboard NotAllowedError as ignorable in the uncaught error handler
to prevent duplicate generic error toasts
- Add translation key for clipboard permission denied message
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
The dedicated-container portal pattern was repeated across 7 components.
Extract it into a reusable use-portal-container hook under app.main.ui.hooks.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
The previous fix (80b64c440c) only addressed portal-on-document* but
there were 6 additional components that portaled directly to
document.body, causing the same race condition when React attempted
to remove a node that had already been detached during concurrent
state updates (e.g. navigating away while a context menu is open).
Apply the dedicated-container pattern consistently to all portal
sites: modal, context menus, combobox dropdown, theme selector, and
tooltip. Each component now creates a dedicated <div> container
appended to body on mount and removed on cleanup, giving React an
exclusive containerInfo for each portal instance.
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Add support for 'page' as a special shapeId value in the MCP export_shape
tool. It resolves to penpot.root, exporting the entire current page as a
PNG or SVG snapshot.
Previously only 'selection' and explicit shape IDs were supported. The new
'page' shortcut is useful for AI agents needing a bird's-eye view of the
design without having to know a specific shape ID.
Closes https://github.com/penpot/penpot/issues/8689
Signed-off-by: Abhishek Mittal <abhishekmittaloffice@gmail.com>