Commit Graph
209 Commits
Author SHA1 Message Date
AganzeFelicite fc37c808aa M7: a document dropped on the canvas opens as a new file (#916)
A .figcraft, .fig or file JSON dropped on the canvas ran file.import,
which added its pages to the file in front. It now opens as a file of
its own, as on the home screen, in the browser too. Images and SVG
still import at the drop point; File > Import still adds pages.

- pending_open::open_dropped queues a dropped file as file.open; the
  home screen and the canvas share it.
- Dragging only documents over the canvas says "Drop to open" (not in
  the browser, which names no file until the drop).
- The tab-switch import test drops an SVG: it pinned where an import
  lands, using a .figcraft only as a file to import.
2026-10-20 15:27:27 +02:00
bflatastic f979878d9e Merge main into fix/issue-892
# Conflicts:
#	ROADMAP.md
2026-10-20 06:09:17 +07:00
bflatastic 64efe12e6f Merge main into fix/issue-890
# Conflicts:
#	ROADMAP.md
#	docs/ui-parity.md
2026-10-20 05:58:56 +07:00
bflatastic 8a46478e81 Merge main into fix/issue-879
# Conflicts:
#	ROADMAP.md
2026-10-20 05:46:30 +07:00
bflatastic b82369201d M3: duplicating a main component makes an instance (#892)
Duplicate and Alt-drag copied a main component as a second main. Within
its file the copy is now an instance of it (components::copy_in_file,
shared by edit.duplicate and the Alt-drag copy), as Copy then Paste
already was. A variant duplicated in its set stays a new variant.
2026-10-20 05:44:46 +07:00
bflatastic e3b20b6540 Merge main into fix/issue-894
# Conflicts:
#	ROADMAP.md
2026-10-20 05:35:21 +07:00
bflatastic 05da6b2125 M1: node.get reports layer data (pluginData) (#879)
Layer data written with node.set {props: {pluginData}} was saved but no
command read it back. node.get (the command, the control method and MCP
get_node) and document.inspect now report pluginData as {key: value}
when a layer or page has any, so a tool keeping state in a file can
read it on its next run. Widget keys are included: the plugin rules
only stop plugins writing them, and widget.list already reports that
state.
2026-10-20 04:58:32 +07:00
bflatastic 5617c94167 M1: nudge while measuring; distances follow, handles hide (#894)
Alt+arrows were left out of the nudge keys, so with Alt held to measure
the arrows did nothing and the distances never changed. The arrows now
nudge with Alt held too (Shift: the big step); the hover measurement is
recomputed every frame, so the labels follow each nudge. While measuring
to another layer the selection's handles and size label hide; both
layers keep their outlines.
2026-10-20 04:58:31 +07:00
bflatastic 9ee0c7f572 M1: readable disabled and selected text (#890)
The dim text token (disabled buttons, placeholders, hints) was 2.1:1 on
the light theme's panels; disabled buttons also faded to 40% opacity,
and selected text was drawn in the dark green, 1.4:1 on the dark
theme's selection highlight. The dim text token is now at least 3:1
against panels, fields and selected rows in both themes while staying
dimmer than secondary text, a disabled button is grey with that text,
and selected text takes the theme's readable green. A test checks the
ratios with the colour contrast helper.
2026-10-20 04:57:20 +07:00
bflatastic ddde77db0a M9: release fonts: web build embeds craft-fonts, pin back to the Latin manifest (#885)
The browser has no system fonts, and since releases moved to the shared
workflow the web build was made without craft-fonts: no font list and no
text drawn. The Georgian change also re-pinned craft-fonts to its main,
which has no fonts/latin-manifest.txt, so the next release would fail
under CRAFT_FONTS_REQUIRED. Pin the Latin commit again until one commit
has both (craft-fonts#42); desktop Georgian uses an installed font.
2026-10-20 04:26:04 +07:00
BFlat 42242e3c8b M1: IME composition in text layers (#747) (#755)
- Text editing on the canvas now tells the platform it wants the IME on **every** frame of the
  edit. Before, it only did so on frames that brought input events.
- The candidate window opens at the caret. Before, it opened at the canvas corner: the desktop
  integration places it by `IMEOutput.rect`, which was the whole canvas.
- Keys that reach the editor while a composition is in progress belong to the IME. Enter, Escape,
  Backspace, arrows and shortcuts don't act on the text then, so Enter picking a candidate doesn't
  add a newline.
- New test `crates/ui-egui/src/ime_tests.rs`.
- Docs: the IME rows in `docs/ui-parity.md` and `ROADMAP.md` now say it hasn't been checked on a Mac.

Fixes #747.
2026-10-18 01:41:15 +07:00
BFlat c6d6433473 Docs: parity rows for the 2026-10-17 landings (#756)
Brings the progress docs up to date with what landed on 2026-10-17: community batches #727, #728, #733, #735, #736 and #753, plus #729, #745, #746 and #751. Docs only.
2026-10-17 21:56:26 +07:00
BFlat b8f97a5f4f M8: the control channel answers while the window is minimized (#720) (#751)
- A minimised or covered window now answers every control request that acts on the app's state
  (`engine.execute`, `document.inspect`, `node.get`, `ui.inspect`, `ui.pointer`, `ui.key`, `ui.type`,
  `ui.set`, `ui.menu.invoke`, `ui.render`, `window.list`, `window.focus`, `app.quit`), in that window's
  file, from the app's `logic` (no frame drawn). Before, only six methods were answered there.
- What only a drawn frame can answer (`ui.screenshot`, `ui.input`, `window.new`) is an error at once
  for a hidden window: `window 0 is minimised or hidden: `ui.screenshot` needs it on screen
  (`ui.render` draws the document without it)`. Before, these waited up to 60 s.
- `crates/ui-egui/src/control.rs`: the `NO_FRAME` allow-list becomes a short `NEEDS_FRAME` list;
  `drain_hidden` answers the rest and fails the frame-only ones at once.
- Docs: `docs/control-protocol.md` (hidden windows, `ui.screenshot`) and `docs/mcp.md` (attached
  mode while the window is minimised). ROADMAP progress log and revision history.

Part of #720.
2026-10-17 20:29:32 +07:00
BFlat fdd4eb5cad Docs: roadmap rows for the community fixes from #524 up (#727, #728, #733, #735, #736, #729) (#744)
Adds the progress rows for the community PRs landed today: batches #727, #728, #733, #735 and #736, plus #729. That's 55 PRs, and the batch-13 boolean-op crash guard.

- `ROADMAP.md` and `docs/roadmap.md`: the header's "Last updated/Change" line and one revision-history row each.
- Headline numbers are unchanged. The per-area rows for the `.fig` import and component overrides come with the #731/#732 PR.
2026-10-17 19:16:10 +07:00
BFlat 9e28e11954 M3: number variables in the text content picker (#629) (#725)
The Typography section's variable picker also lists number variables for a text layer's content (shown as the number), as the engine already accepted; other text fields keep their own types (#629).
2026-10-17 14:03:59 +07:00
BFlat 40c744da10 M1: convert frames to sections and sections to frames (#719) (#721)
Frames on the page or in a section convert to sections (object.convertToSection) and sections back to frames (object.convertToFrame), in place, one undo step each, from the canvas context menu; nested frames, instances, main components and sections holding sections are refused.
2026-10-17 13:03:49 +07:00
BFlat f40c600e67 Docs: roadmap rows for the community fixes from #478 up (#591, #598, #602) and #589 (#605) 2026-10-16 07:55:52 +07:00
bflatastic f948e08fbc Merge remote-tracking branch 'origin/main' into fix-601
# Conflicts:
#	ROADMAP.md
2026-10-16 05:04:09 +07:00
bflatastic 049f97a4c3 Merge branch 'pr-597' into batch/t7
# Conflicts:
#	ROADMAP.md
2026-10-16 03:46:48 +07:00
Skyfuller1303 b640069961 M7: drop files anywhere to open them
- Files dragged over the window show a "Drop to open" (home screen) or
  "Drop to import" (canvas) overlay.
- On the home screen each dropped file the app opens becomes a document of its
  own; any other file is turned away with a toast.
- On the canvas dropped files import as before, at the drop point.
- A small card ("Opening as a new file", "Adding to {document}") is on screen
  before a large file blocks the window; the files then open a frame later.
- A canvas import stays with the document and page that received the drop,
  keeps its placement, restores the page and selection chosen meanwhile, and is
  cancelled with a message if that document or page is gone.
- ui.inspect reports pendingOpens; every new string, including the cancel
  messages, goes through tr() with en, de and es entries.
2026-10-15 21:48:25 +02:00
Yarila f8458b70a8 Reduce blank canvas strips during panning with bounded overscan 2026-10-16 00:28:10 +05:00
bflatastic 46316c9895 Merge remote-tracking branch 'origin/main' into rb-568
# Conflicts:
#	ROADMAP.md
#	crates/text/src/fontdb_tests.rs
2026-10-16 02:26:29 +07:00
bflatastic 466fd249fe Merge branch 'pr-579' into batch/t5
# Conflicts:
#	docs/ui-parity.md
2026-10-16 01:36:18 +07:00
bflatastic 97b10b637d Merge branch 'pr-482' into batch/t5
# Conflicts:
#	docs/localization-parity.md
#	docs/mcp.md
#	docs/translating.md
2026-10-16 01:35:12 +07:00
bflatastic 9552e530be Swedish: command labels that only repeated the command id show in English
468 of the 588 command and menu rows had the command id as their text
("align.bottom" = "align.bottom"), which the app would have shown as is.
They are left out, so English shows until they are translated. Swedish
isn't in the complete list (that would make every new UI string need a
Swedish translation in the same change); the docs say what it covers.
2026-10-16 01:30:10 +07:00
Ivan Shishkanov 55db465d8e M7: scrub typography spacing from leading icons (#373) 2026-10-15 19:28:46 +03:00
IanHollow 5454bdc244 M7: skip special files when scanning system fonts
Require resolved font entries to be regular files before indexing them. Preserve normal font symlinks and cover named pipes in a bounded child-process regression.

Assisted-by: OpenAI Codex (GPT-6)
Signed-off-by: IanHollow <72767437+IanHollow@users.noreply.github.com>
2026-10-15 08:29:11 -07:00
Patrik LermonandClaude Opus 5.5 ee2640d18e Swedish interface translation (sv)
Adds Swedish (Svenska) as an interface language: 2165 strings, written from the English source.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 10:34:20 +02:00
bflatastic 8959030255 Merge #262: M7: follow system theme from the first frame (@VanTan666)
# Conflicts:
#	contributors/people.toml
#	docs/ui-parity.md
2026-10-15 15:10:11 +07:00
Marco Arnulfo e92792e3e8 M1: a press on a corner handle resizes instead of picking the corner-radius handle (fixes #292)
The corner-radius handle sits 10 px in from each corner and answers within 6 px, so a press on the inner half of the 8 px corner square picked it: the drag rounded the corners or, dragged outward, did nothing. Inside the drawn square the corner now resizes both sides (in ratio with Shift or the constrain-proportions link); further in the radius handle still answers.
2026-10-14 14:36:39 +02:00
VanTan 4964910cc9 M7: follow system theme from the first frame 2026-10-14 10:34:14 +03:00
bcedfeba10 M7: recent files as thumbnail cards on the home screen (#249)
The Recents section shows recent files as cards: the picture each file
carries (thumbnail.png in .figcraft and .fig, an image file itself) fitted
on its first page's colour, a divider, and a footer with the file's icon,
name and "Edited ... ago" (or "File not found"); the full path on hover.
Pictures are read off the frame's thread, only for cards in view, cached
by path and modification time, capped in bytes and pixels; missing,
damaged or oversized ones give a placeholder. They fade in, and cards
show a skeleton until their file has been looked at.

A sort menu (Alphabetical, Date created, Last modified; either order) and
a grid/list toggle sit above them. The list is a Name, Last modified and
Created table whose headers sort. View (per section) and sort are kept in
the preferences and set with ui.set {homeRecentsView, homeRecentsSort,
homeRecentsOrder}. A click selects, a double-click or Enter opens, arrow
keys move the selection; a right-click offers Open, Show in folder and
Remove from recents (file.openRecent {remove}).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: bflatastic <bombayapirat@gmail.com>
2026-10-14 12:00:20 +07:00
BFlat 52d013e1db Docs: roadmap entries for the community fixes in #221, #226, #228, #229, #231 to #238 and #240 to #242 (#246) 2026-10-14 11:35:04 +07:00
rcoenenandClaude Opus 5.5 6267ae5638 Vector edit mode: hover, hatch, select and drag filled regions (#243)
With the Move tool, the filled area under the pointer is hatched with "/" lines in the selection colour (1.4 px), clipped by the area's own fill rule so holes stay clear. The hatch goes when the pointer is 3.5 px from a point or 2 px from a segment (screen pixels at any zoom); between those clearances and the 6 px hit radius the area wins. Bezier handles, the transform box's handles and other edited layers' parts come first. Line spacing follows octave folding: 8 px at every power-of-two zoom, growing with the zoom and stepping back at sqrt(2) * 2^n. The pattern is anchored to the vector's origin, so it stays on the shape when panning, and several selected areas share one pattern. The last area listed wins where areas overlap, and only vectors with a visible fill have areas.

A click selects an area with its points; they move with it (drags, nudges, the Design panel) but keep the unselected look and show no handles. Shift adds an area or takes a selected one out (its own points go, points selected on their own or shared with another selected area stay). A press on a selected area drags everything selected, as one undo step; Escape puts it back. Drags show no hover hatch. Any other press without Shift drops the areas and the points selected only through them, and a click on an empty spot such as a hole clears them and stays in edit mode. Inside the transform box around points selected on their own, the box keeps its drag unless the area is already selected. Area indexes are dropped when the network's topology changes; moving points keeps them.

The network gets region walking, an inside test per region by its own fill rule, the region under a point, a region's points and a topology fingerprint (vector_regions.rs). The engine does hit testing and lays out and clips the hatch in screen space (vectoredit/regions.rs); the canvas only draws the lines. vector.edit.select takes regions, and vector.edit.state reports regions, regionVertices and regionHover. Tests: vector_regions_tests (doc) and regions_tests (engine), covering fill rules, overlap, curves, reversed segments, clearances at 100% and 400%, selection, drags, undo and cancel, and the hatch spacing at seven zoom levels, its direction, anchoring, coverage and clipping.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 11:17:17 +07:00
BFlat 7a9a67a8c3 Docs: roadmap entries for the community fixes in #211, #213, #214, #216 and #218 (#230)
* Docs: roadmap entries for the community fixes landed in #211, #213, #214, #216 and #218

Their ROADMAP.md lines were left out when the five PRs were rebased (all five edited the
same header line); the wording is the contributors' own.

* Docs: the header lists all five community fixes
2026-10-14 09:30:55 +07:00
fede9234 db55720c93 fix(engine): keep proportions when a linked width or height is typed (#222)
With a layer's proportions locked, setting only its width or only its height through node.set (the W and H fields) scales the other side by the layer's own ratio; setting both in one edit leaves them as typed, an unlinked layer is unchanged, and a zero side has no ratio to keep. Fixes #220.
2026-10-14 07:31:19 +07:00
Euisuh Jeong d30255bf62 fix(engine): preserve the underlying repeat command (#212)
edit.repeat no longer records itself as the last command, so repeating keeps applying the command it repeats instead of recursing, and it refuses when there is nothing to repeat. Fixes #181.
2026-10-14 03:26:16 +07:00
bflatastic e56856f401 M3: component playground: variant, boolean and text properties 2026-10-13 13:36:54 +07:00
bflatastic e02ad435b3 M2: auto layout: dragging an automatic gap's handle fixes the gap 2026-10-13 13:36:54 +07:00
bflatastic a399ff8934 M3: variables inside styles 2026-10-13 13:36:54 +07:00
bflatastic d7f76246d3 M3: effects on groups: inner shadows and background blur 2026-10-13 13:36:54 +07:00
bflatastic fca70a6063 M1: text made by commands in the app is readable on dark pages 2026-10-13 13:36:54 +07:00
bflatastic 2c35c7bd21 M1: frames and groups: one-step convert 2026-10-13 13:36:54 +07:00
bflatastic 5548284c65 M5: prototyping: delays on mouse triggers, manual overlay offsets 2026-10-13 13:36:54 +07:00
bflatastic a2a0f80252 M2: auto layout: wrapped-line alignment 2026-10-13 13:36:54 +07:00
bflatastic c910060f36 M7: .fig export: one-sided stroke widths as outlines, bound layer fields name their field; docs
A variable width is written only as the same reach on both sides of the path (a
point with unequal sides drew nothing elsewhere). A stroke wider on one side, on a
shape that shows nothing else, is written as a vector filled with the area it
paints, with the stroke itself kept as plugin data that the import puts back.
Bound layer and text fields also name the number of the field they set. Docs: text,
effect and grid styles and symmetric variable widths are checked; non-colour
bindings come across as values.
2026-10-13 10:05:39 +07:00
bflatastic 3efff16df2 M7: .fig export: stroke reach as a share of the weight; docs
Variable-width points say each side's reach as a share of the stroke weight (in
pixels they drew far too wide elsewhere). Progress docs: instance sizes, variables
with modes and shared styles in the .fig export, and what was checked by opening
written files in other software.
2026-10-13 10:05:39 +07:00
bflatastic 5be3012e9b M11: starter templates: docs and the essentials assessment 2026-10-13 03:01:19 +07:00
bflatastic 31815f1d13 M9: online files: crash recovery never drops an unsent edit
The recovery copy of one of your online files is no longer discarded when
the file reopens from its server. It is kept until the outcome is known:

- the server has everything in it: the copy goes;
- the copy has changes that never reached the server and nothing else
  changed there: they are applied on top as one edit and sent to everyone;
- anything else (others changed the file, the server forgot the client):
  the copy opens beside the file as "<name> (recovered copy)", with a toast;
- the server can't be reached: the copy opens as an unsaved document.

The note beside a copy is written after it and records who the app was in
the file and the server's last change it had. The app joins again as that
client (the first hello resumes; a new sync client numbers its batches
after the ones the server processed), so the layers it made are its own to
send. The old copy is removed only once this run has copied what it became.
collab.recoverFile; a tooltip on the tab mark; docs.
2026-10-13 01:41:14 +07:00
bflatastic 501e4a6737 M9: online files: the app remembers the files it put online
A file put on a server without the server's key stays its owner's after its
tab closes or the app quits. The app records it with the preferences (server,
id, name, owner token, the owner's links, what others join with; at most 200;
validated when read back) and never shows the token: not in command results,
ui.inspect, logs or debug output.

- Home > Online lists your online files: open one as its owner (a new tab, or
  its tab comes to the front), start and stop sharing, copy the code or link,
  rename on the server, remove from the server (confirmed), forget here. One
  request per server when the section opens; offline and gone states.
- collab.files, collab.openFile, collab.forgetFile; collab.manageFile takes
  its server and token from the record and gains share / unshare.
- Server: Admin owned (a compatible addition within protocol version 2) says
  what became of the files a list of owner tokens owns. Older servers leave
  the list as recorded.
- Tabs mark files in a live session (shared, or open only to you); toasts
  about a file that isn't in front name it; after a crash a recorded file
  reopens from its server as its owner.
- Draft catalogs (de, fr, ja, ko, pt-BR, zh-Hans) translate the 71 strings
  and 5 command labels they lacked and the new ones; es and es-419 follow.
- Docs: collab-protocol, server, mcp, control-protocol, parity pages, gaps,
  ROADMAP.
2026-10-13 01:41:14 +07:00