Commit Graph
440 Commits
Author SHA1 Message Date
bflatastic 02f5a3520a Merge main into #1759 2026-11-07 11:19:38 +07:00
BFlat 7c869057c4 Merge pull request #2020 from TrevorBrown-dev/feat/suggest-auto-layout-1823
M2: Suggest auto layout adds nested auto layout frames by rows and columns and keeps the placement (#1823)
2026-11-07 11:57:49 +08:00
BFlat 390d72cf92 Merge pull request #1884 from ljkx/fix/weight-steps-family-styles
M4: Increase and decrease font weight step through the styles the family has (#1831)
2026-11-07 11:53:05 +08:00
Trevor BrownandClaude Fable 5.1 ea1ae05d16 Merge upstream/main into feat/suggest-auto-layout-1823
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-06 22:39:35 -05:00
Trevor BrownandClaude Fable 5.1 4dae256074 Merge upstream/main into feat/suggest-auto-layout-1823
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-06 21:39:54 -05:00
bflatastic 748a75cdd3 Merge main into #2050 2026-11-07 09:20:52 +07:00
bflatastic cd3c3a2fd4 Merge main into #2050 2026-11-07 09:10:30 +07:00
bflatastic 5aaec67e20 Merge main into #1869 2026-11-07 09:07:56 +07:00
BFlat 11b2ddfaf4 Merge pull request #1958 from MortenHusted/fix/import-variable-id-collisions
M3: importing a file keeps its own variables and styles when their ids are already taken
2026-11-07 10:03:52 +08:00
bflatastic a93ce619ee Merge main into #2020 2026-11-07 08:58:24 +07:00
bflatastic 32f2f119f9 Merge main into #2050 2026-11-07 08:57:25 +07:00
BFlat 7522c39ba5 Merge pull request #1760 from Silvertree2010/fix/1745-pen-tool-undo-cmd-z-bug
Make undo while drawing with the pen remove only the last anchor
2026-11-07 09:50:33 +08:00
BFlat 5762212ced Merge pull request #2006 from JonRC/fix/jpeg-quality-srgb-icc
M6: JPG exports take a quality and embed the sRGB profile (#2004)
2026-11-07 09:47:15 +08:00
Filip StrajnarandClaude Opus 5.5 20f1219fbc M1: Set default properties gives new shapes and text the layer's properties (#2028)
Edit ▸ Set default properties put the selected layer's properties into the Paste properties
clipboard and did nothing else: a rectangle drawn afterwards was still the default grey, and what
Copy properties had copied was replaced.

The defaults are now kept apart (`Session::default_props`: one set for shapes, set from any layer
but text, and one for text). Rectangles, ellipses, polygons and stars drawn or clicked with their
tools take the shape defaults: fills, strokes and their settings, effects, opacity, blend and
corners (only where the shape has corners), through `apply_copied_props`, the rules Paste
properties already used (now shared). New text from the Text tool takes a text layer's fills and
font and is laid out at that size. Frames, sections, slices, lines, arrows, vectors and layers made
by commands keep their own look; the help center is silent on those. `edit.setDefaultProperties`
answers `{for: shapes|text, properties}`.

Tests: `default_props_tests::new_shapes_drawn_with_the_tools_take_the_default_properties`,
`setting_defaults_leaves_the_copied_properties_alone`,
`new_text_takes_a_text_layers_defaults_and_shapes_keep_theirs`. Docs: ui-parity.md, ROADMAP.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LvG6E2LxvJ6MuFmiLA6Zqm
2026-11-06 12:55:21 +01:00
Trevor BrownandClaude Fable 5.1 4c793faf51 M2: Suggest auto layout adds nested auto layout frames by rows and columns and keeps the placement (#1823)
layout.suggest used to run layout.add and made one row of everything. It now splits a frame's layers into rows or columns by the gaps between them, wraps each band of several layers in a hugging frame without fill and repeats inside it, so the design keeps its placement where the gaps are even. The frame keeps its size, and uneven gaps and layers that overlap along both axes come back as warnings.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-05 11:06:33 -05:00
JonRC a55a728c4c M6: JPG exports take a quality and embed the sRGB profile (#2004)
file.export always encoded JPG at quality 90, and an sRGB document's JPGs
carried no colour profile, so viewers could only assume sRGB. PNG exports
always get an sRGB chunk, and Display P3 JPGs already embedded their ICC
profile.

- ExportSetting gets a quality (1 to 100, default 90, saved only when it
  isn't 90), parsed with the other export options: file.export, export.add,
  export.update, file.exportLayers and MCP export take it.
- encode_export_with takes the quality; encode_export keeps quality 90.
  Every JPG now embeds the document's profile, sRGB included.
2026-11-05 00:09:19 -03:00
JonRC 5244b0075d fix(engine): an unknown stroke cap lists the caps node.set takes (#2005)
strokeCap, startCap and endCap answered only "unknown stroke cap". They now
name the key and every cap, as strokeAlign, strokeJoin and dashCap do.
2026-11-04 23:53:43 -03:00
MortenHustedandClaude Opus 5.5 024b7fe452 M3: importing a file keeps its own variables and styles when their ids are already taken
`file.import` of a FigCraft file kept the document's style, variable or
collection for every id both files used, and the pasted layers kept
those ids. Ids are numbered per file, so two files made apart share
them, and the imported layers were bound to the document's unrelated
variables, modes and styles.

`append_pages` now gives the incoming file new ids first (`Renames`):
- an incoming style, variable or collection is shared, as before, only
  when the document has the same under its id and everything it names
  is shared too (the same file imported again); a collection and its
  variables are shared only together; library copies keep their ids;
- every other id the document already uses gets a new one from its
  allocators, which now take ids to avoid (the incoming file's own);
- layers' style links, bindings, explicit modes and prototype actions,
  styles' bindings, variables' collections, aliases, calculations and
  extensions, and the renamed collections' mode ids follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 20:14:20 +01:00
ljkx 6365e4339b M4: Increase and decrease font weight step through the styles the family has (#1831)
text.weightUp/weightDown stepped the CSS weight by 100 and stored a canonical
name ("Medium", "Semi Bold", ...) whether the family had that face or not, so
the first press on Arial Regular changed nothing visible and the stored style
named a face no font provides. They now move to the nearest heavier or lighter
style of the family with the same slant and width (Narrow stays Narrow), and
stay put at either end. A family that isn't installed keeps the old 100 steps.
2026-11-02 20:32:37 +01:00
Trevor BrownandClaude Fable 5.1 41ee4584cb Merge upstream/main into fix/place-crosshair
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 06:02:48 -05:00
1f0acd2bbd M4: Alt+Enter while editing text opens the link at the caret (text.openLink) (#1860)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-02 05:31:21 -05:00
Trevor BrownandClaude Fable 5.1 367f188b34 M6: the arrows show and move a placement crosshair with a shape or frame tool chosen, and Enter places there (#1839)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-02 04:39:07 -05:00
a953a04e33 Shortcuts: Cmd/Ctrl+Shift+Alt+= / - / 0 make the interface larger, smaller and default-sized (#1846)
* Shortcuts: Cmd/Ctrl+Shift+Alt+= / - / 0 make the interface larger, smaller and default-sized

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Interface size keys: live while editing text too, and the new size draws on the next frame

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* A preference set during a frame repaints this window too, so the interface size applies on the next frame

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Control channel: a preference changed by a hidden window's command repaints every window

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-01 23:59:08 -05:00
NetterSilverandClaude Opus 5.5 0e6fcf9bf7 Make undo while drawing with the pen remove only the last anchor
Fixes #1745

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 12:03:45 +01:00
dotCooCoo afc5e13972 Export: each export setting chooses a color profile
Each export setting has a color profile: Same as file (the default, the
document's profile when it exports), sRGB or Display P3. PNG, JPG and
WebP settings list it in their advanced settings, and export.add,
export.update, file.export, file.exportLayers (for every setting) and
the MCP export tool take it as colorProfile. The rendered pixels are
converted to the chosen profile (out-of-gamut colors clip) and the file
is tagged as before: an sRGB chunk for sRGB PNG, the generated ICC
profile (and cICP in PNG) for Display P3; sRGB JPG and WebP stay
untagged. A setting keeps its choice when its format changes, and
settings saved without one read as Same as file.

SVG, PDF and Android XML show no choice and stay sRGB; PDF has no
output intent yet.
2026-10-31 03:07:17 -07:00
BFlat 12e9b409ed M12: a connector end on any part of an object with Cmd/Ctrl (#1733) 2026-10-31 10:27:56 +07:00
BFlat 6b28ef57dc M4: Dash cap setting for Dash and Custom strokes (#1728) 2026-10-31 09:41:39 +07:00
BFlat 1c5c9e04c3 M11: widgets draw lines and SVG pictures (plugin API 6) (#1727)
A widget description can have a line, 100 long and black and 1 thick unless told
otherwise, and an SVG that comes in as locked layers, scaled to fit a size and
within the widget's 256 layers. The Counter example uses both.
2026-10-31 08:13:08 +07:00
BFlat 3837b08a71 M8: a damaged image is refused with a reason instead of showing as an empty box (#1724)
* M8: a damaged image is refused with a reason instead of showing as an empty box (#1474)

* M8: replacing an image refuses a damaged file too
2026-10-31 07:22:44 +07:00
BFlat b462a48346 M12: a connector's path can be reshaped by dragging its path handles (#1710) (#1717) 2026-10-31 04:53:36 +07:00
BFlat fd8d49fc95 M11: a widget opens its plugin's panel (plugin API 5) (#1684)
* M11: a widget opens its plugin's panel (plugin API 5)

Gap 6: a widget callback's ui_show opens the plugin's panel and keeps the module loaded; the panel's callbacks get the widget's id and state and redraw it in the same undo step. Older plugins' ui_show from a widget still opens nothing. The Counter example sets its count from a panel.

* M11: open_panel takes its opener as one value
2026-10-31 03:24:32 +07:00
BFlat b377c70a8c M4: the eyedropper with Cmd/Ctrl+Shift makes a colour style or variable (#1674)
* M4: the eyedropper with Cmd/Ctrl+Shift makes a colour style or variable of what it picks

* M4: the Create from color window's collection picker is its own function
2026-10-31 02:09:08 +07:00
BFlat 9f4d1ce75b M12: board votes say who voted for what, and earlier votes' results are kept (#1666)
* M12: an ended vote's results say who voted for each object

* M12: a board keeps earlier votes' results, listed in Results and deleted one at a time

* M12: the vote Results popover is drawn by small helpers, and its stepper, rows and Clear are tested
2026-10-31 01:19:03 +07:00
Patrick Wozniak 2d23f95673 M9: Home ▸ Online shows the server's team list, and its files open by links without a secret (desktop and browser) 2026-10-30 17:10:12 +01:00
BFlat b3ca228a75 M5: skipping the Open link notice by command is saved at once (#1667)
proto.linkNotice from the Actions palette, the control channel or MCP changed the preference in memory only until the app next saved its preferences. It now sets Session::prefs_unsaved, which the app saves at the end of the frame like installed plugins and live-session tokens; the notice's own Don't show this again goes the same way. Gap 15.
2026-10-30 23:05:57 +07:00
BFlat 4f5a25cc82 M12: spreadsheet cells paste onto a board as a table (#1663) 2026-10-30 22:32:44 +07:00
BFlat 27920c5e26 M8: a drag moving many layers normalises only what sits around them, not each layer's bindings, sizing, text and instance checks (#1571) (#1656) 2026-10-30 20:53:03 +07:00
BFlat 01b07af55a M6: a pasted image the clipboard doesn't carry shows as missing and is named (#1648)
* M6: a pasted image the clipboard doesn't carry shows as missing and is named (#1646)

The placeholder an import stores for an image it lacks is now striped, not flat grey; the fill keeps the image's name; .fig and paste give a fixed, translated note per image; edit.pasteDesignLayers returns missingImages, the paste's message names those layers, and a paste with notes opens the import report.

* M6: the missing-image placeholder's stripes stay clean when scaled, and a .fig gives one note (#1646)
2026-10-30 19:34:24 +07:00
BFlat e4a7e89f39 M3: typed and stored variable, collection and mode ids find exactly that object (#1613) (#1635)
Builds on #1618. Variables::get and Variables::collection take an exact id before a name; stored mode ids go through Collection::mode_by_id (expressions, the prototype's mode switch); font family variables and the colour picker, prototype editor and panels read stored ids exactly. Ported from fix/stored-ids-exact.
2026-10-30 18:00:34 +07:00
BFlat f0abe51a84 M6: plugin widgets have hover styles (gap 6) (#1634) 2026-10-30 17:33:04 +07:00
BFlat 10c66678c3 Merge pull request #1641 from ChristopherLinnett/feat/1280-color-calculations
M3: colour variables can be calculated from others, faded or mixed per mode (#1280)
2026-10-30 16:16:23 +07:00
BFlat 99440fa9ff M8: a drag that only moves the selection shifts what was worked out at its start (#1571) (#1640)
The engine notes when a drag has only moved the selected layers by one offset (Interaction::moved_by); the canvas keeps the outlines, box and slots from the drag's start document and shifts them, the selection spacing and the Design panel's values (but the position fields) come from that document, and the Export section's values are kept. Dragging 648 layers: UI pass 2.10 to 1.44 ms a frame.
2026-10-30 16:13:55 +07:00
BFlat 34fce9bfcf M12: create the next board shape already connected (#1631)
* M12: create the next board shape already connected (#1597)

A selected sticky or shape shows a + beside each side; hovering one previews where the next goes, clicking makes one of the same kind, size and colour there, joined by a connector, and starts typing in it, in one undo step. board.addConnected {id?, side?, edit?} does it from commands.

* M12: the connected-shape handle and command in every full catalog (#1597)

* M12: Shift+Cmd/Ctrl+Enter on a board shape adds the next one already connected (#1597)
2026-10-30 15:59:13 +07:00
bflatastic 68450a6a0e Merge main into #1641 2026-10-30 15:57:31 +07:00
BFlat c1451fecd5 M4: an imported file finds an installed family under the other names its fonts give it (#1292) (#1628)
Fonts name a family by its typographic name and by a legacy name per weight; fonts without typographic names are installed under the legacy ones only. FontDb::resolve finds either from the other, and matches names across case, spaces, hyphens and underscores; every lookup (import, paste, REST import, rendering, missing fonts, the font report) uses it. A weight-number style goes by that weight, and .fig and REST imports wait for the installed fonts to be indexed.
2026-10-30 15:44:09 +07:00
Christopher Linnett bc76665c65 M3: colour variables can be calculated from others, faded or mixed per mode (#1280)
A colour variable's value in a mode can now be {"alpha": {"variable", "opacity"}} - that
variable's colour with its alpha replaced - or {"mix": {"from", "to", "t"}}, a per-channel blend
of two colour variables or literals over the stored values (in the document's colour profile),
alpha included. Calculations resolve lazily like aliases, so bound layers, mode switches,
instances and undo follow their sources; they compose, and a loop, or a calculation that reads
more than 32 variables in its collection's modes (other collections in their default modes), is
refused when it is set. Operands are stored by id and read by exact id, as stored references
are. Files keep them; REST, CSS, Penpot and .fig exports write the colour they show in each mode;
deleting a source leaves its readers on the colour they showed; library import and swap
re-point their operands. The Variables panel shows them with their formula and can swap one for
an alias or detach it to its colour.
2026-10-30 16:25:26 +08:00
bflatastic e5a5da9e62 Merge main into #1609 2026-10-30 14:35:19 +07:00
bflatastic 991ea891b5 Merge main into #1609 2026-10-30 13:56:15 +07:00
bflatastic 966f823551 Merge main into #1420 2026-10-30 13:52:53 +07:00
BFlat c47177498d M8: every file a command reads by path is a regular file within a cap (#1114) (#1625) 2026-10-30 13:48:26 +07:00