174 Commits

Author SHA1 Message Date
Andrey Antukh
74add05226 Merge remote-tracking branch 'origin/staging' into develop 2026-10-23 13:40:44 +02:00
Alejandro Alonso
6508449fa0
🐛 Rebuild text layout when image fills finish loading (#12564)
Text image shaders are baked into Skia paragraphs at layout time.
touch_shapes_with_image only scanned shape.fills/strokes, so text
spans were never invalidated after store_image. Detect text content
image fills and force a layout rebuild before touching tiles.

Closes #12563
2026-10-23 09:55:23 +02:00
Belén Albeza
0505eb3b1d
🐛 Fix pasted gradient fills in text editor v3 (#12516)
* 🐛 Keep text gradients in place when pasting at the caret

Text gradients are stored as fractions of the whole text box. Paste
copied those fractions unchanged, so in a box of another size the
handles and colours moved away from the pasted text.

Copy from the v3 editor now records where each span with a gradient or
image fill starts in its text box, and the box size, in the clipboard
payload. A new WASM export, text_editor_get_range_origin, gives the
start of any text range.

Paste at the caret finds where those spans start in the target and
rewrites their gradients so the handles keep the same pixel offset from
the span. Auto-sized texts use the box size they get when the editor
closes.

AI-assisted-by: claude-opus-5-5

* 🐛 Rebuild text gradient shaders when the text box resizes

Text gradient and image shaders are built into the cached Skia
paragraphs against the text box size. A resize with no content change
reused those paragraphs, so the colours stayed matched to the old box
until a reload.

The text now remembers the box size its shaders were built for and lays
itself out again when that size changes and a span has a gradient or
image fill. Text with only solid fills keeps its cache.

AI-assisted-by: claude-opus-5-5

* 🐛 Keep text gradients in place when pasting on the canvas

A text selection pasted on the canvas becomes a new text whose box
differs from the source, so its gradients moved like they did for caret
paste.

After adding the new text, lay it out, find where its gradient spans
start and move their gradients to keep the same pixel offset from the
span, in the same undo step. The range origin export now takes a shape
id, so it works on texts that are not being edited.

AI-assisted-by: claude-opus-5-5

* 🐛 Keep text gradients in place when pasting copied texts at the caret

Text shapes copied whole, rather than as an editor selection, reached the
caret paste without any geometry, so their gradients still moved away from
the pasted text.

A shape copy now records where the gradient and image spans of each
selected text start, and the box size, by shape id. Paste joins them in
the order of the joined paragraphs, with no origin for texts copied
without one.

Copied texts each have their own box, so every span origin now carries
its own box size instead of one box for the whole payload.

AI-assisted-by: claude-opus-5-5
2026-10-22 13:39:26 +02:00
Andrey Antukh
472ba8dde5 Merge remote-tracking branch 'origin/staging' into develop 2026-10-16 12:49:04 +02:00
Elena Torró
ccdfaffbef
🐛 Fix stroke to path for strokes with different widths per side (#12356) 2026-10-16 12:36:23 +02:00
Andrey Antukh
473a59b134 Merge remote-tracking branch 'origin/staging' into develop 2026-10-09 09:01:12 +02:00
Alejandro Alonso
78d26243c7
🐛 Sync Design tab styles on text-editor click caret (#12205)
Click caret placement only moved the selection and never refreshed
current styles, so the Design tab kept showing the previous fill and
typography. Place the caret through place_caret_from_position so
StylesChanged is emitted like keyboard navigation.

Closes #12195
2026-10-08 15:42:21 +02:00
Elena Torró
29ea6e49b6
🐛 Step over whole emojis in the text editor (#12202)
Caret moves, deletes and caret rects use grapheme boundaries.
Closes #12188
2026-10-08 13:42:25 +02:00
Belén Albeza
1d7c775f72
🐛 Fix not setting mimetype in image fills (#12136) 2026-10-07 12:26:45 +02:00
Danny Shirely
d45c6710b7
🎉 Implement independent image bounds resizing (#11430)
* 🎉 Implement independent image bounds resizing

Add canvas resize interaction mode that allows users to resize an
image object's bounding box independently from the underlying bitmap
content without scaling or distortion while holding the Mod key.

AI-assisted-by: gemini-2.5-pro

* ♻️ Address reviewer feedback from elenatorro

- Remove legacy cfh/image-shape? check in shape-has-image-fill?
- Guard bounds-resize with positive dimensions instead of clamping scalev to preserve flipping
- Remove :metadata from transform-attrs in modifiers.cljs
- Restore preserveAspectRatio logic based on keep-ar? in fills.cljs
- Compute source rect against destination rect for raster and SVG fills in WASM renderer

* 🔧 Fix clippy needless borrow warnings in wasm image fills

---------

Co-authored-by: Andrey Antukh <niwi@niwi.nz>
2026-09-09 15:16:56 +02:00
Alonso Torres
bf2fcba3be
🐛 Fix problems with pixel precission in WASM renderer (#11560) 2026-09-09 15:11:58 +02:00
Alejandro Alonso
2e5157e6aa
✨ Export image fills to WASM SVG as linked images (#11529)
Emit clipped `<image href>` for fills with a registered media URL instead
of Skia base64 embeds. Register URLs from the frontend and exporter via
store_image_url. Cover rect, path, frame, and text (glyph clip) cases in
SVG export tests.

Closes #11383
2026-09-08 17:30:53 +02:00
Elena Torró
d82038a570
🐛 Fix paragraph span restyle on line change (#11503)
* 🐛 Fix paragraph span restyle on line change

* 🐛 Fix text shape boundaries on resize
2026-09-04 13:43:23 +02:00
Elena Torró
0533be100d
🐛 Fix text selection render overlay and double/triple click flash (#11512) 2026-09-04 12:38:31 +02:00
Elena Torró
eb8d3a2fef
🐛 Fix paragraph selection (#11493) 2026-09-03 16:17:38 +02:00
Alejandro Alonso
c5897bc50a
⚡ Paint plain text directly onto Current (#11355)
* ♻️ Share text layout paragraphs across modifier clones

Store Skia paragraphs in Rc so TextContentLayout::clone keeps the
cached layout for rotate/pan modifiers. Add layout.clear() and treat
needs_update as paragraphs-empty only.

* ⚡ Reuse cached Skia paragraphs when painting text

Add try_paint_from_layout_cache to paint from TextContent.layout when
versions match, skipping ParagraphBuilder rebuild and layout on each
frame. Wire into the layered text path for plain fills without strokes
or effects.

* ⚡ Paint plain text directly onto Current

Extend can_render_directly for stroke-free text and skip the empty
save_layer in draw_text when no stroke-group opacity is set. Plain text
paints into Current without the Fills/Strokes blit.
2026-09-02 16:17:06 +02:00
Elena Torró
03e6f119e5
♻️ Clean unnecessary methods (#11472)
* ♻️ Remove duplicated/unused set-children code

* ♻️ Remove unused methods
2026-09-02 12:58:52 +02:00
Alejandro Alonso
b9ddfc1596
⚡ Batch WASM shape upload to speed up page switches (#11443)
Upload structural shape attrs (base, children, blur, shadows,
flex, layout-item) via multi-shape `_set_shapes_batch` FFI in
chunks of 512, then apply host attrs with use-shape selection.
2026-09-02 10:32:52 +02:00
Alejandro Alonso
d1a54134bb
✨ Add text and @font-face to WASM SVG export (#11442)
* ✨ Add text and @font-face to WASM SVG export

Emit selectable <text> elements and inject @font-face rules that
reference the font URLs registered at load time. Text fill bypasses
save_layer wrappers that SkSVGDevice drops. Text strokes are deferred
to a follow-up PR.

* 🐛 Strip trailing commas from Skia SVG text positions

Skia's SVG backend appends a trailing comma to per-glyph x/y lists.
Firefox rejects the malformed attribute and drops glyph positioning,
making exported text vanish or mis-render.
2026-09-01 17:06:50 +02:00
Belén Albeza
de8d8ca401
🐛 Fix serialization of constraints (#11108) 2026-08-06 15:49:04 +02:00
Elena Torró
10a2c19f92
🔧 Improve text editor selection and tab conversion (#11071) 2026-08-06 09:43:02 +02:00
Elena Torró
14a6ea5c52
🔧 Support text style shortcuts (#11002) 2026-08-04 15:30:26 +02:00
Elena Torró
0fed63eeb3
🐛 Fix text replacement on selection and text offsets (#10983)
* 🐛 Fix text not being replaced when there is a selection

* 🐛 Fix text editor offsets on transformed text
2026-08-03 15:22:49 +02:00
Elena Torró
a722a33503
🐛 Fix gradients and text selection (#10941)
* 🐛 Add missing text fills

* ✨ Keep text leaf selected while editing
2026-07-30 12:11:53 +02:00
Belén Albeza
3e7fb07931
🐛 Fix undo crash after deleting a word (#10862) 2026-07-29 15:53:41 +02:00
Belén Albeza
704cd40182
🐛 Fix caret not mimicking text color (#10866) 2026-07-28 16:25:47 +02:00
Belén Albeza
4f46700c94
🐛 Fix text editor v3 blinks (#10654) 2026-07-24 09:14:59 +02:00
Elena Torró
ef3511e519
✨ Add basic individual strokes implementation (#10648) 2026-07-22 16:13:38 +02:00
Elena Torró
08e42687d1
♻️ Extract GPU-free RenderResources; add headless wasm exports (#10653) 2026-07-22 09:56:21 +02:00
Belén Albeza
2e32a8743e
♻️ Refactor rulers into ui modules (#10461)
* ♻️ Move rulers rendering to ui submodule

* ♻️ Refactor RulerState into UIState
2026-07-03 08:37:22 +02:00
Alejandro Alonso
b82ab0c830 Merge remote-tracking branch 'origin/staging' into develop 2026-07-02 13:52:25 +02:00
Belén Albeza
6d458c80a1
🐛 Fix font and variant dropdowns on mixed text styles (#10520)
* 🐛 Fix dropdown shown Mixed Font Families for same family with different variant

* 🐛 Fix variants dropdown appearing blank on mixed variants but same family

* ✨ Add playwright test for mixed font families/variants
2026-07-01 20:15:20 +02:00
Elena Torró
c8f586a197
🐛 Fix async text rendering on tiles (#10504) 2026-07-01 11:09:57 +02:00
Andrey Antukh
c4e72fd7f9 Merge remote-tracking branch 'origin/staging' into develop 2026-06-30 16:23:23 +02:00
Belén Albeza
e6a49adfbc
🐛 Fix crash on composition update when pressing Esc on a IME (#10479) 2026-06-30 13:55:06 +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
Aitor Moreno
2d4a24bf97
🐛 Fix stroke to path extra points (#10190)
* 🐛 Fix stroke to path extra points

* 🐛 Set evenodd when needed on stroke to path (#10446)

---------

Co-authored-by: Elena Torró <elenatorro@gmail.com>
2026-06-29 10:34:44 +02:00
Eva Marco
2a098e5b16
🎉 Add background blur (#10034)
* 🎉 Add background blur

* 🎉 Add test

* 🎉 Add background blur info to plugins API

* 🎉 Suport in wasm for both layer and background blur

* 🐛 Fix failing test

* ♻️ Fix comments

---------

Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
2026-06-16 19:46:03 +02:00
Belén Albeza
d1dd5d9016
🎉 Render guides in wasm (#10014)
* ✨ Remove guides from svg overlay

* 🎉 Draw guides in wasm

* 🎉 Serialize guides to wasm

* ✨ Store separate and sorted horizontal and vertical guides

* 🎉 Implement collision detection with guides

* 🎉 Right click on guides to change color or remove

* ✨ Implement dragging guides

* 🎉 Edit wasm guides by double clicking them

* 🎉 Implement changing mouse cursor on hovering a guide

* ✨ Show guide pill on hover

* 🎉 Implement removing guide on hovering + Del

* 🔧 Fix lint + fmt errors

* 🎉 Clip out outer board guide lines

* ♻️ Extract common code into guide-pill* component

* 🎉 Draw dotted lines on hovering board guides

* 🐛 Fix board rotation when it has guides

* 🎉 Make foreign guides not visible in focus mode
2026-06-16 09:19:58 +02:00
Elena Torró
60d3c81450
✨ Add wasm rulers (#9858)
* ✨ Add wasm rulers

* 🔧 Fix dpr on page zoom

Co-authored-by: Alejandro Alonso <alejandroalonsofernandez@gmail.com>
Co-authored-by: Elena Torro <elenatorro@gmail.com>

* 🔧 Change page-switch behavior to refresh rulers and keep blurred snapshot

* 🐛 Restore WASM rulers after WebGL context recovery

Co-Authored-By: Elena Torro <elenatorro@gmail.com>
Co-Authored-By: Alejandro Alonso <alejandroalonsofernandez@gmail.com>

---------

Co-authored-by: Alejandro Alonso <alejandroalonsofernandez@gmail.com>
2026-06-05 07:51:35 +02:00
Andrey Antukh
3858993a57 Merge remote-tracking branch 'origin/staging' into develop 2026-05-27 13:37:02 +02:00
Elena Torró
75c61f9211
🐛 Fix stroke rendering on drag (#9871) 2026-05-26 13:14:38 +02:00
Eva Marco
2c453e4a00
🎉 Add dash and gap inputs for dashed strokes (#9765)
* ✨ Add dash and gap customization for dashed strokes

Signed-off-by: eureka0928 <meobius123@gmail.com>

* ♻️ Change old numeric-inputs for new components

---------

Signed-off-by: eureka0928 <meobius123@gmail.com>
Signed-off-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: eureka0928 <meobius123@gmail.com>
Co-authored-by: Andrey Antukh <niwi@niwi.nz>
Co-authored-by: Alejandro Alonso <alejandro.alonso@kaleidos.net>
2026-05-21 13:52:01 +02:00
Dexterity
5156866f20
♻️ Make ShapeImageIds byte conversion fallible (#9283)
Co-authored-by: Belén Albeza <belen.albeza@kaleidos.net>
2026-05-21 12:03:05 +02:00
Aitor Moreno
64f73ef23b
♻️ Remove Mutex from mem buffer (#9479) 2026-05-14 12:57:10 +02:00
Aitor Moreno
a126379cc7 ♻️ Refactor State into DesignState 2026-05-12 12:14:38 +02:00
Aitor Moreno
4e98dfb99f
♻️ Refactor GpuState and RenderState
* ♻️ Refactor GpuState

* ♻️ Refactor RenderState

* 🔧 Tweak some _build_env options
2026-05-08 11:10:14 +02:00
Aitor Moreno
bc0f081371
♻️ Refactor text editor state (#9379) 2026-05-07 16:16:44 +02:00
Aitor Moreno
31b09be405 🎉 Add overtype mode to text editor 2026-04-28 11:10:22 +02:00
Elena Torro
f673b32567 🐛 Fix image loading callback 2026-04-22 14:00:49 +02:00