Commit Graph
45 Commits
Author SHA1 Message Date
BFlat 83054f771d Merge pull request #1971 from TrevorBrown-dev/fix/connector-end-gap
Boards: connectors attached to a side start 4 pt outside the border, as FigJam's do (#1916)
2026-11-07 09:37:24 +08:00
bflatastic 4e5f0f8c04 Merge main into #1900 2026-11-07 08:18:31 +07:00
NetterSilverandClaude Opus 5.5 39c5833818 Keep object snapping when Snap to Pixel Grid is on
Fixes #2029

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-06 04:10:50 +01:00
Dylan M. Taylor 972ccc6a17 fix(engine): count each id Shape Builder is given once (#1974) 2026-11-03 23:01:44 -05:00
Trevor BrownandClaude Fable 5.1 7be7c054eb M12: a connector joined to a layer inside a moving section is no snap target; the docs say which connectors share a run (#1916)
Review: a section moving carries its stickies and their connectors, so those connectors and
their labels are no snap targets for the section either (an end counts as carried when its node
or any ancestor is moving). The parity rows, the END_GAP doc and the test's doc no longer say
connectors from one side run together: elbows share their first run along the normal, curves
leave tangent to it, straight ones fan out from the same point.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-03 21:46:34 -05:00
Trevor BrownandClaude Fable 5.1 2dc7bed2a0 M12: connectors attached to a side start 4 pt outside the border (#1916)
A connector end attached to a side was routed from the middle of that side, so the stroke's
centre line started on the border and half its width plus the round cap lay on the fill, and
several connectors leaving one side piled up into a knot sticking into the card. FigJam starts
the line a few pixels outside the border and connectors leaving one side run together along the
side's normal before they fan out (the issue's screenshots). `board_route::END_GAP` (4 pt): an end
attached to a side (`RouteEnd::Box`) starts that far outside it along the side's normal; an end
placed by hand on the object (`RouteEnd::On`) keeps its exact point; a free end is unchanged.
The pinned route coordinates in the engine, format and UI tests move with it; the overlay's
attach points stay on the border. Docs: the Connectors rows, status line and history of
boards-parity.md.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-03 21:08:19 -05:00
Hamza 58af1a6237 M4, #1747: a text layer's name follows the characters typed into it 2026-11-03 12:41:29 +05:00
a579ba2d3c M8: a click with the Bend tool curves a sharp point, its handles along the line between its neighbours (#1875) (#1877)
* M8: a click with the Bend tool curves a sharp point, its handles along the line between its neighbours (#1875)

A release without movement on a sharp point between two segments runs ops::auto_smooth instead of cancelling; a curved point still sharpens, a drag still sets the handles by hand, an endpoint is left alone. Tests; docs.

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

* Vector edit hit test: the nearer of a handle's end and a point wins

A short path's handles end within the point's hit tolerance; a click on the point is on the point, one on the handle's end still takes the handle. Test short_handles_leave_their_point_clickable.

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

* Vector edit hit test: the nearer of a handle's end and a point is judged on the page

Candidates stay within the tolerance as before; ranking by page distance keeps a non-uniform scale from favouring the wrong one. Test a_handle_and_a_point_are_compared_on_the_page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Vector edit hit test: a handle wins a tie with a point, as before

A handle snapped onto another point stays selectable there. Test a_handle_on_another_point_is_still_the_handles (fails with the strict comparison).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-11-03 00:13:38 -05:00
9f09b7163c M2: a double-click on an auto layout edge sets Hug contents, with Alt Fill container (#1838) (#1867)
* M2: a double-click on an auto layout edge sets Hug contents, with Alt Fill container (#1838)

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

* Auto layout edges: a text child fills with Alt too, and still enters on a plain double-click

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-02 05:31:00 -05:00
e9eb7ce7f4 M4: max lines on auto-width text keep it auto width, hugging that many lines (#1864)
* M4: max lines on auto-width text keep it auto width, hugging that many lines

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

* M4: an auto-width text with max lines hugs the ellipsis on its last line, and the editing test expects auto width

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-02 05:29:59 -05:00
3358961033 M4: Backspace or Delete at a list item's start keeps its indentation level (#1862)
* M4: Backspace or Delete at a list item's start keeps its indentation level

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

* Lists: the level is kept by Backspace and Delete alone, and the tests read the text's own glyph

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-02 05:29:33 -05:00
9c3ec8c1d1 M4: typing 1) and a space starts a numbered list, as 1. does (#1842)
* M4: typing 1) and a space starts a numbered list, as 1. does

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

* Parity: the Text area row names the list creation characters

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

* Parity: the list creation characters without spaces inside the code spans

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-02 00:00:19 -05: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 2d784c363d M3: library analytics count insertions and show them over time (#1704)
Gap 10: a file counts the library instances it places, creates, pastes or duplicates and reports them like its detaches. The server counts what a file's totals grow past their highest in the current week and keeps a year of weeks (actions.json). library.usage {days} and Show usage give insertions and detachments over the past 30 days, 90 days or year. New variants are appended; an older server refuses them and Show usage says so.
2026-10-31 04:13:14 +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 e97961ba66 M12: stamps stick to the objects they land on, which are highlighted first (#1671)
* M12: stamps stick to the objects they land on

A stamp sticks to the topmost object under its centre (BoardItem::Stamp gains an optional on, left out when empty so older files open with stamps on nothing) and the board's normalize pass carries it when that object moves or turns, once when both move together. A stamp dragged onto another object sticks to that one, off everything to nothing; one whose object is deleted stays where it is. Gap 24.

* M12: the object a stamp will stick to is highlighted

While the stamp tool is over an object, or a stamp is dragged over one, the object it will stick to is outlined with the canvas's hover outline (Overlay::stamp_target, found by the same stamp_target as board.stamp and settle_stamps). A drag no longer drops what it moves into the auto layout of a sticky, shape with text or table: board objects take no drops; a stamp already inside its sticky moves with it once. Builds on #1671. Gap 24.
2026-10-31 00:25:38 +07:00
BFlat f0abe51a84 M6: plugin widgets have hover styles (gap 6) (#1634) 2026-10-30 17:33:04 +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 bc11197c21 M1: Enter or a double-click with the pen edits the vector with the Move tool (#1430) (#1590)
A double-click with the pen drew on: its second press added a point on the last one, and a double-click meant to edit a layer drew a stray vector on it. A press on the last anchor now adds nothing; a double-click ends the path (or, when it only started one, edits the layer under it) and Enter ends the path, both going on in vector edit mode with the Move tool. In edit mode the pen's Enter and double-click take the Move tool, the double-click taking back what its first press began. Escape still keeps the pen (#760).
2026-10-30 08:41:01 +07:00
bflatastic d60d973459 Merge main into #1516 2026-10-30 00:32:07 +07:00
bflatastic 86ab39b5cc Merge main into #1516 2026-10-29 23:58:44 +07:00
bflatastic c13c8d5e6a Merge main into #1498 2026-10-29 23:28:32 +07:00
BFlat d4e6ad5d81 M12: the connector tool previews the connector it will make, ends snapping to attach points (#1387) (#1526) 2026-10-29 18:29:48 +07:00
BFlat 7ba22fb2b6 M1: Crop image from the Object menu, the right-click menu and the image fill row (#1464) (#1501)
* M1: Crop image from the Object menu, the right-click menu and the image fill row (#1464)

* M1: crop mode shows the image dimmed outside the layer and heavier crop marks (#1464)

* M1: the handle overlay draws gradients and crops in their own helpers (#1464)
2026-10-29 15:07:21 +07:00
Ahmed Mahmoud f3017259ff Merge branch 'main' into feat/guide-snapping 2026-10-29 10:08:31 +03:00
Ahmed Mahmoud eedc66bddc M1: address guide snapping review findings
Hidden containers hide their whole branch from snapping, the 5 px reach is converted through the owner's scaled axis so a zoomed frame snaps at 5 screen pixels, and a click on a guide only selects it. Tests cover all three in guide_snap_tests.rs.

Signed-off-by: Ahmed Mahmoud <ahmedmhmouad41@gmail.com>
2026-10-29 09:47:11 +03:00
bflatastic 7158419698 Merge main into #1427 2026-10-29 13:42:33 +07:00
BFlat f3e7db0b2a Merge pull request #1460 from MortenHusted/fix/unknown-params
M0.9: commands, MCP tools and batch steps refuse names they don't take (#1413)
2026-10-29 13:40:58 +07:00
Ahmed Mahmoud 475a7da502 M1: guides snap to layer edges and centres when dragged
Dragging a guide, or moving one, now snaps to nearby layer edges and centres on the guide's own axis: within 5 screen pixels, gated by Snap to Objects and suspended with Cmd/Ctrl. Page guides see the page's top-level layers; frame guides see the frame's box, its layout grids and every visible layer inside it, in the frame's own space.

Snapping sits in the interaction path (canvas/guides.rs, not guide.add/guide.move) so the commands stay deterministic for the control channel and MCP. Tests: guide_snap_tests.rs in the engine and ui-egui crates; docs/ui-parity.md updated.

Signed-off-by: Ahmed Mahmoud <ahmedmhmouad41@gmail.com>
2026-10-29 09:00:47 +03:00
bflatastic 789fa33444 Merge main into #634 2026-10-29 12:25:06 +07:00
bflatastic 2f3dc8cccd Merge main into #1427
# Conflicts:
#	docs/roadmap.md
2026-10-29 11:54:42 +07:00
bflatastic f99f01d57b Merge main into #1460 2026-10-29 11:09:30 +07:00
bflatastic 9202b1032f Merge main into #1460 2026-10-29 10:38:06 +07:00
bflatastic 158d402ee7 Merge main into #1457 2026-10-29 09:55:12 +07:00
Nicek dd93a6a2a8 fix: 修复 PR #1457 的选择与拖拽评审问题
- 标签在选择边界外也参与收窄,Dev Mode 单击已选图层只选它
- 区分点击命中图层与描边空白处的拖拽回退目标
- 重排圆环按按下时的修饰键判断点击,画布外松手视为拖拽结束
- 修复 clippy unnecessary_get_then_check
2026-10-29 09:33:55 +08:00
Nicek ce93dc6ff5 feat: 混合模式悬停预览、多选层点击与图层属性预览 2026-10-29 09:33:54 +08:00
Filip StrajnarandClaude Opus 5.5 62c69ba7ab M8: normalising after a command runs under the panic guard (part of #1085)
Session::execute ran a command under guard::run but normalised what it
changed afterwards, outside it: a panic in normalisation escaped execute
instead of becoming EngineError::Internal with the edit rolled back. Inside
a batch it unwound through Session::grouped, which then never restored the
undo limit it had lifted or ended the group. Normalising is now the last
step of the guarded result chain, so a panic there fails the command like
one in the command itself (and a batch step fails like any other failed
step); live text previews normalise under the guard too.

The other half of #1085, restoring grouped()'s state when its closure
panics, is left alone: #1419 rewrites that function.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LvG6E2qDnjtMzfPVuEP7iH
2026-10-28 22:11:31 +01:00
Dylan M. Taylor 5e16683963 refactor(engine): simplify canvas and board handlers 2026-10-28 16:00:36 +00:00
Skyfuller1303 faa96926f4 M4: keep stroked whole-shape merges and bound region paints per file
Merging every region of one stroked shape is a no-op again: only fill
parts count, so a vector with several painted regions still merges.

The region paint import budget now covers the whole file instead of
resetting for each layer, so instances of a main with large region paint
stacks cannot multiply the work.
2026-10-28 11:59:53 +01:00
Skyfuller1303 c785e96b1c Merge remote-tracking branch 'origin/main' into feat/fig-vector-region-fills
# Conflicts:
#	crates/format/src/fig/export.rs
#	crates/format/src/fig/map.rs
2026-10-28 10:12:00 +01:00
MortenHustedandClaude Opus 5.5 64e648ae7e M0.9: command docs list every parameter their commands read
Before commands can refuse names their docs don't list (#1413), the docs
have to list what the code reads. These are the top-level names the run
code (and every helper it hands the params to) reads that its doc left
out, all checked against the code; nothing changes behaviour:

- creation: node.create (characters, points, closed), frame.create,
  shape.*, text.create and vector.create (index, name, select),
  image.place (name)
- aliases the code already took: comment.* (id | link), annotation.add
  and annotation.edit (label | text), text.set (characters | text),
  text.listStyle (style | list), fill.add and stroke.add (paint | color),
  proto.interaction.add/set (durationMs | duration_ms, delayMs | delay_ms)
- options: file.export and export.update (contentsOnly,
  includeBoundingBox, svgOutlineText, svgSimplifyStroke), comment.next
  (frame, resolved), layout.suggest (direction), variable.create
  (description), vector.edit.widthPoint (side), collab.share (key),
  collab.manageFile (access, label, link, name), fill.pattern (visible,
  blend)
- docs whose shape hid a name: proto.interaction.add (a stray `}` closed
  the object before delayMs; dest was only in a trailer),
  proto.interaction.set (patch fields in a second object), collab.join
  (a top-level comma inside the link examples), pref.uiScale (commas in
  the value's description)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 09:06:33 +01:00
Muamer MustaficandCursor 1b545e91b2 M1: the release decides copy or move when Alt changed after the last move (#1385)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-27 12:45:45 +01:00
Muamer MustaficandCursor a02c4abefc M1: Alt pressed during a drag turns the move into a copy (#1385)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-27 12:21:44 +01:00
aiongg 2e7790b58b refactor(engine): split canvas/tools.rs into tools/ 2026-10-26 20:17:48 +00:00
aiongg 094b01e501 refactor(engine): move tools, snap, distance, guides, drop, arc_handles, shape_handles, rotation_origin, al_keys, booleans, shapebuilder, textedit, textpreview, vectoredit to canvas/ 2026-10-26 20:17:47 +00:00