3914 Commits

Author SHA1 Message Date
Jack Storment
aa5bfe6dda
Add customizable pixel grid color (#9155)
Let users pick the pixel grid color from a standard color picker.
The grid color was previously hardcoded, making it invisible on
mid-tone canvases. Choice is stored on the file so it persists
across sessions. Defaults preserve the current appearance when
unset.

Closes #7750

Signed-off-by: jack-stormentswe <crazycoder131@gmail.com>
2026-04-27 21:37:27 +02:00
Milos Milic
bd1e0fb23f
Add Alt+click to expand a layer subtree in the Layers sidebar (#9179)
Closes #7736.

The Layers sidebar offered no way to expand every nested level of a
single subtree at once. Unfolding a layer that wraps a deep tree
required clicking each disclosure indicator one level at a time -
O(siblings * depth) clicks. The asymmetry was particularly visible
next to the existing Shift+click gesture, which collapses every
layer in the panel in a single action via `dwc/collapse-all`, with
no expand counterpart for either a single subtree or the whole
tree.

Add a new `dwc/expand-subtree` event in
`app.main.data.workspace.collapse` that uses
`cfh/get-children-ids-with-self` to gather the shape's id together
with every descendant id, then merges `{descendant-id true}` entries
into `[:workspace-local :expanded]` so the entire subtree opens in
one update. Existing expansion state on unrelated branches is left
untouched (`merge`, not `assoc`), matching the per-key shape used by
`toggle-collapse` and `expand-collapse`.

Wire the gesture into `layer_item.cljs` `toggle-collapse` callback as
a third branch:

  - Shift+click while expanded - collapse every layer (existing).
  - Alt+click while collapsed   - expand the entire subtree (new).
  - Otherwise                   - toggle this single level (existing).

Alt is chosen instead of Shift to avoid the ambiguity the issue
author flagged: "for a layer of middle depth it is unclear whether
[Shift+click] should fold all (up to the topmost parent) or expand
all (only the current subtree)". Alt is a common platform
convention for "do this recursively" (Finder, file managers,
several IDEs), so the asymmetric mapping matches user expectations.
The callback's `mf/deps` vector is extended with `id` and `objects`
so the closure refreshes when the shape tree changes.

CHANGES.md entry added under the 2.17.0 New features section.
2026-04-27 21:36:15 +02:00
Andrey Antukh
db8aa9bccc Merge remote-tracking branch 'origin/staging' into develop 2026-04-27 17:27:45 +02:00
Andrey Antukh
ef2fe78aac Merge remote-tracking branch 'origin/main-staging' into staging 2026-04-27 17:27:28 +02:00
Andrey Antukh
a3b9d7bed7 📎 Fix fmt issue 2026-04-27 17:26:59 +02:00
Luis de Dios
edccda2038 🐛 Fix remove prints 2026-04-27 15:26:55 +02:00
Luis de Dios
a5a8ab5de6 🐛 Fix MCP status is displayed as disabled when setting MCP key without expiration date
Fixes #14058 and #14061 in Taiga
2026-04-27 09:37:11 +02:00
Alejandro Alonso
c4e508a606 🐛 Fix text ellipsis merging error 2026-04-27 08:49:22 +02:00
Andrey Antukh
01d68ec09b Merge remote-tracking branch 'origin/staging' into develop 2026-04-24 14:16:03 +02:00
Andrey Antukh
0b6416e53b Merge remote-tracking branch 'origin/main' into main-staging 2026-04-24 14:09:03 +02:00
Eva Marco
6c4ab8940d
🐛 Fix colorpicker eyedropper on gradients tab (#9125)
* 🐛 Fix colorpicker eyedropper on gradients tab

* 🐛 Fix gradient test deleting opacity input
2026-04-24 12:48:58 +02:00
Alejandro Alonso
89a1ee7813 Merge remote-tracking branch 'origin/main-staging' into staging 2026-04-24 12:06:27 +02:00
Andrey Antukh
29ba336928 Merge remote-tracking branch 'origin/main' into main-staging 2026-04-24 11:58:50 +02:00
Eva Marco
4a7140d82d
🐛 Fix theme modal height (#9105)
* 🐛 Fix CI

* 🐛 Fix theme modal height
2026-04-24 11:38:34 +02:00
Alejandro Alonso
58fae0a04d 🐛 Fix text.cljs error from staging merge 2026-04-24 10:10:00 +02:00
Alejandro Alonso
078663b0fa 🔧 Fix rust linter errors 2026-04-24 09:52:51 +02:00
Eva Marco
5a7ba7ee7e
🐛 Fix multiple selection on shapes with token applied to stroke-color (#9110)
*  Remove the need to navigate to page for deletion operation

* 🐛 Fix multiple selection with applied-tokens on stroke-color

* 🐛 Fix button position on page header

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-24 09:47:44 +02:00
Alejandro Alonso
7532bf411c Merge remote-tracking branch 'origin/develop' into develop 2026-04-24 09:32:35 +02:00
Alejandro Alonso
984d292ab2 Merge remote-tracking branch 'origin/staging' into develop 2026-04-24 09:29:24 +02:00
wdeveloper16
50bee5e176
Add clipboard:read/write permissions to plugin system (#6980) (#9053)
*  Add clipboard:read/write permissions to plugin system (#6980)

* 🔧 Fix prettier formatting in clipboard permission files

---------

Co-authored-by: wdeveloper16 <wdeveloer16@protonmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-24 09:07:58 +02:00
Andrey Antukh
7135782e7d Merge remote-tracking branch 'origin/main-staging' into staging 2026-04-24 08:19:47 +02:00
wdeveloper16
e280168de9
Add read-only preview mode for saved versions (#7622) (#8976)
*  Add read-only preview mode for saved versions (#7622)

* 🔧 Address review feedback on version preview (#7622)

* 🐛 Fix version preview for WASM renderer (#7622)

* 🐛 Fix stylelint color-named and color-function-notation in preview banner (#7622)

* 🐛 Fix invalid-arity call to initialize-workspace in exit-preview (#7622)

* 🐛 Fix unclosed defn paren in exit-preview (#7622)

* ♻️ Refactor version preview/restore flow

Separate enter-preview and enter-restore flows with dedicated dialogs
instead of a persistent banner. Removes preview-banner component in favor
of inline actions dialog. Uses backup/restore pattern for exit-preview
instead of full workspace reinitialization. Adds analytics events for
preview/restore actions.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>

*  Extract on-name-input-focus as namespace-level private function

The callback had no dependencies on component-local state or props,
making it a pure function that can be hoisted to a defn-. This avoids
recreating the same callback identity on every render of version-entry*.

*  Extract extract-id-from-event helper to deduplicate snapshot callbacks

Three callbacks in snapshot-entry* shared the same DOM extraction logic
(get current target, read data-id, parse UUID). Extracted into a private
defn- to remove the duplication and simplify each callback.

*  Extract pure state-update callbacks from versions-toolbox* to namespace level

Eight callbacks that only emit fixed Potok events with no meaningful
deps were hoisted out of the component as defn- functions:

- on-create-version
- on-edit-version
- on-cancel-version-edition
- on-rename-version
- on-delete-version
- on-pin-version
- on-lock-version
- on-unlock-version

These no longer need mf/use-fn wrappers since namespace-level functions
have stable identity across renders, avoiding unnecessary callback
recreation on each render cycle.

*  Rename filter parameter to filter-value in on-change-filter to avoid core shadowing

The parameter name 'filter' shadowed clojure.core/filter within the
function scope. Renamed to 'filter-value' for clarity and to prevent
potential bugs if core/filter were needed in future changes.

* 🔧 Fix linter warnings and errors across version-related namespaces

frontend/src/app/main/ui/workspace.cljs:
- Remove unused requires: app.common.data, app.main.data.notifications,
  app.main.data.workspace.versions

frontend/src/app/main/data/workspace/versions.cljs:
- Remove unused require: app.common.uuid
- Fix duplicate reify type: enter-restore used ::restore-version
  (same as the private restore-version fn), renamed to ::enter-restore
- Remove unused bindings: state in enter-restore, team-id in
  exit-preview and restore-version-from-plugin

---------

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Signed-off-by: wdeveloper16 <wdeveloer16@protonmail.com>
Co-authored-by: wdeveloper16 <wdeveloer16@protonmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-24 08:13:16 +02:00
Eva Marco
0c60db56a2
🐛 Fix multiselection error with typography texts (#9071)
* 🐛 Ensure typography-ref attrs are always present and fix nil encoding

Add :typography-ref-file and :typography-ref-id (both defaulting to nil)
to default-text-attrs so these keys are always present in text node maps,
whether or not a typography is attached.

Skip nil values in attrs-to-styles (Draft.js style encoder) and in
attrs->styles (v2 CSS custom-property mapper) so nil typography-ref
entries are never serialised to CSS.

Replace when with if/acc in get-styles-from-style-declaration to prevent
the accumulator from being clobbered to nil when a mixed-value entry is
skipped during style decoding.

* 🎉 Add test

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-04-23 16:08:56 +02:00
Eva Marco
d6b341c053
🐛 Fix color token (#9095) 2026-04-23 10:51:30 +02:00
Eva Marco
5c9696e20c
🐛 Fix color dropdown option update (#9100) 2026-04-23 10:51:20 +02:00
Eva Marco
28b33b9acc
🐛 Fix props on text components (#9099) 2026-04-23 10:49:48 +02:00
Andrey Antukh
ba42cc04b7 ♻️ Derive v-sizing from values instead of passing as prop
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 14:17:15 +00:00
Luis de Dios
b60695f54a 🐛 Fix indicate that the mcp is disabled if the mcp key has expired
If the mcp key has expired, the switch that indicates the status in the dashboard will appear as disabled, and will show a modal for regenerate the key. It will also appear as disabled in the workspace, not allowing the plugin to connect
2026-04-22 16:00:52 +02:00
Andrey Antukh
8ad30e14b6 Merge remote-tracking branch 'origin/staging' into develop 2026-04-22 13:34:00 +02:00
Aitor Moreno
e3981a0cf3
Merge pull request #9077 from penpot/ladybenko-13971-fix-trailing-whitespace
🐛 Fix trailing whitespace behavior in v2 editor
2026-04-22 11:30:01 +02:00
Eva Marco
c02f0a2bc9
🐛 Fix grow options (#9097)
* 🐛 Fix grow options

* 🐛 Fix props when hidden is nil
2026-04-22 11:09:44 +02:00
Andrey Antukh
81faa5a728 Replace duplicate on-blur lambda with stable on-text-blur ref
The inline (fn [] (ts/schedule ...)) passed as :on-blur to text-options
was an exact copy of the on-text-blur callback already defined via
mf/use-fn earlier in the same let block. Pass on-text-blur directly
to avoid allocating a new function object on every render.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh
7751d9a69b Remove spurious deps from toggle callbacks in text-menu*
toggle-main-menu and toggle-more-options close over a state atom and
need no external deps. The declared deps (main-menu-open? /
more-options-open?) were unused inside the function bodies, causing
each callback to be reallocated on every toggle — self-reinforcing
churn. Drop the mf/deps calls to make both callbacks stable.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh
74d1288003 Hoist token-typography-row? flag check to namespace level
(contains? cf/flags :token-typography-row) is a pure constant:
cf/flags is immutable after startup. Define it once as a private
namespace-level var token-typography-row? instead of re-evaluating
the check on every render in text-decoration-options* and text-menu*.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh
d28c0ea066 Memoize label computation in text-menu* component
Wrap the (case type (tr ...) ...) expression in mf/with-memo [type]
so the translation is resolved only when the type prop changes
instead of on every render.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh
a94a7221fb Memoize options in text-decoration-options* component
Wrap the two radio-button option maps in mf/with-memo [token-applied]
so the vector and its (tr ...) calls are evaluated only when the
token-applied prop changes, not on every render.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh
b8aa243c2b Memoize static options in grow-options* component
Wrap the three radio-button option maps in mf/with-memo [] so the
vector and its (tr ...) calls are evaluated once per mount instead
of on every render.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh
dfd992aa49 Memoize static options in text-direction-options* component
Wrap the two radio-button option maps in mf/with-memo [] so the
vector and its (tr ...) calls are evaluated once per mount instead
of on every render.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh
466f27eb7c Memoize static options in text-align-options* component
Wrap the four radio-button option maps in mf/with-memo [] so the
vector and its (tr ...) calls are evaluated once per mount instead
of on every render.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Andrey Antukh
6c19c7c0c4 Memoize static options in vertical-align* component
Wrap the radio-button options vector in `mf/with-memo []` so the
vector allocation and `(tr ...)` calls happen once per component
mount instead of on every render.

Also document the translation memoization rule in frontend/AGENTS.md:
`(tr ...)` must never be called at namespace level (locale is
runtime-only), and static option lists should always be wrapped in
`mf/with-memo []`.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-22 08:55:29 +02:00
Alejandro Alonso
ca97a28408 🐛 Avoid unnecesary repainting of frames when mouse hover 2026-04-22 07:37:38 +02:00
Andrey Antukh
6723e3bbea 🐛 Fix issues after staging merge 2026-04-21 22:52:19 +02:00
Andrey Antukh
c259fbdb5b Merge remote-tracking branch 'origin/staging' into develop 2026-04-21 22:43:46 +02:00
Andrey Antukh
a395768987 🐛 Fix incorrect handlig of version restore operation (#9041)
- Add session ID tracking to RPC layer (backend and frontend)
- Send session ID header with RPC requests for request correlation
- Rename file-restore to file-restored for consistency
- Extract initialize-file function from initialize-workspace flow
- Improve file restoration initialization with wait-for-persistence
- Extract initialize-version event handler for version restoration
- Fix viewport key generation with file version numbers for proper re-renders
- Update layout item schema and constraints to use internal sizing state
- Add v-sizing state retrieval in layout-size-constraints component
- Refactor file-change notifications stream handling with rx/map
- Fix team-id lookup in restore-version-from-plugins

Improves request traceability across frontend/backend sessions and streamlines
the workspace initialization flow for file restoration scenarios.

Signed-off-by: Andrey Antukh <niwi@niwi.nz>
2026-04-21 20:50:41 +02:00
Andrey Antukh
8f2c467b82 Merge remote-tracking branch 'origin/main' into main-staging 2026-04-21 20:44:31 +02:00
Andrey Antukh
c1688edf66 ♻️ Convert frame-grid and grid-options to modern * suffix
Rename grid-options -> grid-options*, frame-grid -> frame-grid*.
Update internal call and call site in shapes/frame.cljs to [:> ...] syntax.
2026-04-21 20:31:30 +02:00
Andrey Antukh
08247aec3f ♻️ Convert svg-attrs-menu and attribute-value to modern * suffix
Rename attribute-value -> attribute-value*, svg-attrs-menu -> svg-attrs-menu*.
Update all call sites (circle, path, svg_raw, rect, group) to use [:> ...] syntax.
2026-04-21 20:31:30 +02:00
Andrey Antukh
95ca68e2b8 ♻️ Convert history-entry-details to modern rumext * format
Rename to history-entry-details* and update internal call site.
2026-04-21 20:31:30 +02:00
Andrey Antukh
e9e6796f05 ♻️ Convert text-menu and sub-components to modern rumext * format
Rename text-align-options, text-direction-options, vertical-align,
grow-options, text-decoration-options and text-menu to their * variants.
Update all call sites in shapes/text.cljs, shapes/group.cljs and
shapes/multiple.cljs.
2026-04-21 20:31:30 +02:00
Andrey Antukh
0e5d3e2619 ♻️ Convert blur-menu, constraints-menu and stroke-menu to modern rumext * format
Rename components to blur-menu*, constraints-menu* and stroke-menu*.

Update :refer imports and all [:& ...] call sites to [:> ...*] for
blur-menu*, constraints-menu* and stroke-menu* across all nine
shapes-specific options panels.
2026-04-21 20:31:30 +02:00