- The last line shown runs on into the word wrapped after it, so the ellipsis
fills the line ("Lorem consec…") instead of following the last whole word.
- An explicit height (node.set, transform.resize, a resize handle, fixed
sizing) on truncating text keeps it truncating (TextData::fix_size).
- Max lines on auto-height text hugs up to that many lines and ends in an
ellipsis; text.maxLines keeps auto height, Type settings show it as
truncating, .fig export writes textTruncation and import keeps auto height.
- node.get reports maxLines; text.fit measures every line.
Co-authored-by: Cursor <cursoragent@cursor.com>
Open and Revert to saved recorded the file's size and modification time after reading it: a change written in between would be stamped as already loaded and a later save would replace it. Taking the stamp first makes such a change refuse the next save instead (CodeRabbit on #1374).
With a file open in the desktop app, figcraft-cli could change it on disk;
the next Save (or auto-save) wrote the app's copy over it and said
"Saved", losing the CLI's changes.
Each tab now remembers its file's size and modification time when it
opens, reverts or saves it (the Stamp library files already use), and
Save and auto-save refuse while the file on disk differs, pointing to
Revert to saved or Save as. Scripts pass `file.save {overwrite: true}`.
Save as and saves to an explicit path are unchanged.
Branches of a file in a live session: create, edit, review, update from
main and merge while the file is in a session. The branch stays with its
author and makes ids in their own client space far above the main file's;
merging is one synced edit and one undo step; peers' edits merge against
the branch's own base and conflicts resolve as offline; a view link still
refuses merging; a branch made outside the session that adds layers is
refused with a reason. Branch history entries for creation and updates
from main. Fuzz and junk tests extended to export settings, guides and the
live case. Docs: gap 17, collaboration parity, ROADMAP, mcp, protocol.
* M8: widgets take text input and a property menu
Plugin API 4 (gap 6). A widget's text layer can be an editable field:
a double-click edits it like any text, and when editing ends the value
(cleaned, at most 1,000 characters) goes into the widget's state under
the field's name and its plugin redraws, as one undo step. A widget can
also carry a property menu of up to 8 actions, toggles and colour
choices, shown in a small bar over it while it's selected.
- widget.setText and widget.menu drive the same paths from scripts,
the CLI and MCP; widget.insert/list also report fields and menu
- descriptions, values and stored menus are validated as untrusted;
an API 3 plugin drawing a field or menu is refused, older hosts
refuse API 4 manifests; no new host imports
- SDK reads the new events; the Counter example gains a label field
and a menu (reset, steps of ten, a colour)
- tests for fields, menus, canvas editing, rollback, forged data and
live sync; junk sweep extended; labels in every command catalog
- docs: plugins, mcp, gaps, target-app-parity, ROADMAP, roadmap
* M8: widget property menu on egui 0.37
Id::new and Ui::id are deprecated in egui 0.37: the widget bar's area uses Id::unique and its buttons' interaction ids come from ui.scope_id().
* M11: a double-click a board object or a widget field takes, in a function of its own (Sonar)
Found while testing crates/engine/src/codegen.rs:122-150 (lengths_to_rem calls num, which rounds to 2 decimals, at :112)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>