mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-25 19:06:47 -06:00
* Prototype player: no clicks reach the prototype behind the link notice * Selection colors: scopes filter the variable menu; the command binds as variable.bind does * Style picker: color variables scoped to every selected layer's fill or stroke * Corner button: named Corner smoothing on polygons and stars * docs/gaps.md: known gaps from the #1311 review * cargo fmt * Style picker: clippy
This commit is contained in:
@@ -248,8 +248,8 @@ fn unbind_slot(n: &mut figcraft_doc::Node, fills: bool, index: usize) -> bool {
|
||||
}
|
||||
|
||||
/// Why `variable` can't be applied to `targets` (`selection.bindColorVariable`), if it can't: a
|
||||
/// variable that isn't a color, a gradient, or a slot outside the variable's scopes (a variable
|
||||
/// hidden from pickers has none).
|
||||
/// variable that isn't a color, or a gradient. Scopes don't refuse, as with `variable.bind`; they
|
||||
/// only filter what the menu offers ([`in_scope`]).
|
||||
pub fn bind_refusal(doc: &Document, targets: &[PaintTarget], variable: &str) -> Option<String> {
|
||||
let Some(var) = doc.variables.get(variable) else { return Some("no such variable".into()) };
|
||||
if var.kind != VariableType::Color {
|
||||
@@ -264,15 +264,21 @@ pub fn bind_refusal(doc: &Document, targets: &[PaintTarget], variable: &str) ->
|
||||
if !crate::cmd::variables::field_takes(doc, target.id, &field, var.kind) {
|
||||
return Some(format!("`{field}` doesn't take a color variable"));
|
||||
}
|
||||
if let Some(scope) = crate::cmd::variables::field_scope(doc, target.id, &field)
|
||||
&& !var.in_scope(scope)
|
||||
{
|
||||
return Some(format!("the variable isn't scoped to {scope}"));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Whether `variable`'s scopes cover every slot of `targets` (a variable hidden from pickers has
|
||||
/// none), so the Selection colors menu offers it there.
|
||||
pub fn in_scope(doc: &Document, targets: &[PaintTarget], variable: &str) -> bool {
|
||||
let Some(var) = doc.variables.get(variable) else { return false };
|
||||
targets.iter().all(|target| {
|
||||
let list = if target.fills { BoundList::Fills } else { BoundList::Strokes };
|
||||
let field = format!("{}/{}/color", list.prefix(), target.index);
|
||||
crate::cmd::variables::field_scope(doc, target.id, &field).is_none_or(|scope| var.in_scope(scope))
|
||||
})
|
||||
}
|
||||
|
||||
fn bind_variable(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
const CMD: &str = "selection.bindColorVariable";
|
||||
let targets = checked_targets(s, p, CMD)?;
|
||||
|
||||
@@ -399,7 +399,7 @@ fn color_variables_refuse_gradients_other_types_and_unknown_names() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn color_variables_apply_only_within_their_scopes() {
|
||||
fn scopes_filter_the_menu_but_the_command_binds_anyway() {
|
||||
let mut s = Session::new();
|
||||
let a = rectangle(&mut s, "#ff0000");
|
||||
run(&mut s, "stroke.add", json!({"id":a,"color":"#ff0000"}));
|
||||
@@ -410,11 +410,14 @@ fn color_variables_apply_only_within_their_scopes() {
|
||||
let red = group(&s, &[a], "FF0000");
|
||||
let fill_slot: Vec<_> = red.targets.iter().filter(|t| t.fills).cloned().collect();
|
||||
let stroke_slot: Vec<_> = red.targets.iter().filter(|t| !t.fills).cloned().collect();
|
||||
for (targets, variable) in [(&red.targets, "Line"), (&fill_slot, "Line"), (&stroke_slot, "Hidden")] {
|
||||
assert!(s.execute("selection.bindColorVariable", &json!({"targets":targets,"variable":variable})).is_err());
|
||||
}
|
||||
assert!(s.doc().unwrap().node(a).unwrap().bound.is_empty());
|
||||
let doc = s.doc().unwrap();
|
||||
let offered = |targets: &[figcraft_engine::selection_colors::PaintTarget], v: &str| figcraft_engine::selection_colors::in_scope(doc, targets, v);
|
||||
assert!(offered(&stroke_slot, "Line"));
|
||||
assert!(!offered(&red.targets, "Line") && !offered(&fill_slot, "Line") && !offered(&stroke_slot, "Hidden"));
|
||||
run(&mut s, "selection.bindColorVariable", json!({"targets":stroke_slot,"variable":"Line"}));
|
||||
assert_eq!(s.doc().unwrap().node(a).unwrap().strokes[0].solid_color().unwrap().to_hex(), "00FF00");
|
||||
assert_eq!(fill(&s, a), "FF0000");
|
||||
// As with `variable.bind`, the command itself binds outside the scopes.
|
||||
run(&mut s, "selection.bindColorVariable", json!({"targets":fill_slot,"variable":"Line"}));
|
||||
assert_eq!(fill(&s, a), "00FF00");
|
||||
}
|
||||
|
||||
@@ -100,7 +100,8 @@ fn polygons_and_stars_take_corner_smoothing() {
|
||||
s.execute("node.set", &json!({"ids": [id], "props": {"cornerRadius": 8}})).unwrap();
|
||||
s.execute("select.set", &json!({"ids": [id]})).unwrap();
|
||||
let mut u = Ui::new(s);
|
||||
let t = named_button(&mut u, "Individual corners");
|
||||
// With no corners of their own, the button is named for what it opens.
|
||||
let t = named_button(&mut u, "Corner smoothing");
|
||||
click(&mut u, t);
|
||||
assert!((0..4).all(|k| u.ctx.read_response(Id::new(("corner", k)).with("field")).is_none()), "{cmd}: one radius, no corner fields");
|
||||
let smoothing = u.ctx.read_response(Id::new("smoothing").with("field")).expect("the smoothing field shows").rect.center();
|
||||
|
||||
@@ -2220,10 +2220,10 @@ fn player(p: &mut Presenter, ui: &mut Ui, rect: Rect, now: f64, chrome: Chrome)
|
||||
let resp = ui.interact(area, ui.id().with("present_area"), Sense::click_and_drag());
|
||||
let over = resp.contains_pointer();
|
||||
if let Some(l) = layout(p, area, chrome) {
|
||||
// The pane only takes the keyboard while the pointer is over it, and neither takes it
|
||||
// while a link notice shows (Escape there cancels the notice).
|
||||
// The pane only takes the keyboard while the pointer is over it. While a link notice shows
|
||||
// the prototype takes neither keys nor the pointer (Escape there cancels the notice).
|
||||
let keys = chrome.keys && p.link_notice.is_none() && (!chrome.pane || over);
|
||||
input(p, &ctx, &l, over, keys, chrome.pane, now);
|
||||
input(p, &ctx, &l, over && p.link_notice.is_none(), keys, chrome.pane, now);
|
||||
}
|
||||
let Some(l) = layout(p, area, chrome) else { return action };
|
||||
p.shown = Some((p.current, l.place, l.clip.intersect(area)));
|
||||
|
||||
@@ -191,3 +191,35 @@ fn a_prototype_cannot_open_a_script_or_handler_url() {
|
||||
assert!(opens_after_a_delay(url).is_empty(), "{url:?} must never reach the opener");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_prototype_takes_no_clicks_while_the_notice_shows() {
|
||||
let mut s = Session::new();
|
||||
let a = id(&ex(&mut s, "frame.create", json!({"x": 0, "y": 0, "width": 300, "height": 400, "name": "A"})));
|
||||
let b = id(&ex(&mut s, "frame.create", json!({"x": 400, "y": 0, "width": 300, "height": 400, "name": "B"})));
|
||||
let layer = id(&ex(&mut s, "shape.rectangle", json!({"x": 10, "y": 10, "width": 30, "height": 30, "parent": a})));
|
||||
let link = json!({"type": "openUrl", "url": "https://example.com/a"});
|
||||
ex(&mut s, "proto.interaction.add", json!({"id": layer, "trigger": {"type": "afterDelay", "ms": 100}, "action": link}));
|
||||
let to_b = json!({"type": "navigate", "dest": b});
|
||||
ex(&mut s, "proto.interaction.add", json!({"id": a, "trigger": {"type": "onClick"}, "action": to_b, "transition": {"type": "instant"}}));
|
||||
let mut u = Harness::new(s);
|
||||
u.app.services = Box::new(Opened::default());
|
||||
u.run("proto.present", json!({"start": a}));
|
||||
u.frames(40, Vec::new);
|
||||
assert_eq!(notice(&u), "https://example.com/a");
|
||||
// On the frame, above the notice.
|
||||
let at = egui::pos2(700.0, 290.0);
|
||||
u.button(at, true);
|
||||
u.button(at, false);
|
||||
u.frames(5, Vec::new);
|
||||
assert_eq!(u.app.inspect()["presenter"]["current"], json!(a), "the click behind the notice did not navigate");
|
||||
assert_eq!(notice(&u), "https://example.com/a", "the notice still waits");
|
||||
let shapes = crate::live_tests::flat(&u.frame_output(vec![]).shapes);
|
||||
crate::live_tests::text_center(&shapes, "Open link");
|
||||
u.key(egui::pos2(700.0, 450.0), egui::Key::Escape);
|
||||
assert!(notice(&u).is_null());
|
||||
u.button(at, true);
|
||||
u.button(at, false);
|
||||
u.frames(5, Vec::new);
|
||||
assert_eq!(u.app.inspect()["presenter"]["current"], json!(b), "with the notice gone the same click navigates");
|
||||
}
|
||||
|
||||
@@ -876,7 +876,9 @@ fn appearance(app: &mut FigApp, ui: &mut Ui, doc: &Document, sel: &[NodeId]) {
|
||||
let radius_of = |n: &Node| n.corners.uniform().unwrap_or_else(|| n.corners.radii.iter().copied().fold(0.0, f64::max));
|
||||
app.field_of(&ids, "cornerRadius", e, radius_of, |v| json!(v.max(0.0)));
|
||||
let open = ui.data(|d| d.get_temp::<bool>(Id::new("indep_corners"))).unwrap_or(false);
|
||||
if smooth_ok && icons::button(ui, Icon::IndependentCorners, 24.0, open, tr("Individual corners")).clicked() {
|
||||
// Polygons and stars have no corners of their own: the button opens only Corner smoothing.
|
||||
let tip = if indep_ok { tr("Individual corners") } else { tr("Corner smoothing") };
|
||||
if smooth_ok && icons::button(ui, Icon::IndependentCorners, 24.0, open, tip).clicked() {
|
||||
ui.data_mut(|d| d.insert_temp(Id::new("indep_corners"), !open));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::sync::Arc;
|
||||
use egui::{Id, Ui};
|
||||
use figcraft_color::Color;
|
||||
use figcraft_doc::{Document, NodeId, StyleKind, VariableType};
|
||||
use figcraft_engine::selection_colors::{PaintTarget, SelectionColor, bind_refusal, collect};
|
||||
use figcraft_engine::selection_colors::{PaintTarget, SelectionColor, bind_refusal, collect, in_scope};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::a11y::Tip;
|
||||
@@ -133,7 +133,11 @@ pub fn section(app: &mut FigApp, ui: &mut Ui, doc: &Arc<Document>, ids: &[NodeId
|
||||
.variables
|
||||
.variables
|
||||
.iter()
|
||||
.filter(|v| v.kind == VariableType::Color && bind_refusal(doc, &group.targets, &v.id).is_none())
|
||||
.filter(|v| {
|
||||
v.kind == VariableType::Color
|
||||
&& bind_refusal(doc, &group.targets, &v.id).is_none()
|
||||
&& in_scope(doc, &group.targets, &v.id)
|
||||
})
|
||||
.collect();
|
||||
if !variables.is_empty() {
|
||||
ui.separator();
|
||||
|
||||
@@ -555,10 +555,10 @@ fn listing(app: &FigApp, ui: &mut Ui, doc: &Document, p: &mut Picker, out: &mut
|
||||
}
|
||||
}
|
||||
let colors = if slot.paints() {
|
||||
// Variables scoped to the selection's fill (or stroke), as the Design panel's pickers offer.
|
||||
let scope = p.ids.first().and_then(|id| figcraft_engine::cmd::variables::field_scope(doc, *id, slot.key()));
|
||||
// Variables scoped to every selected layer's fill (or stroke), as the Design panel's pickers offer.
|
||||
let scopes: Vec<_> = p.ids.iter().filter_map(|id| figcraft_engine::cmd::variables::field_scope(doc, *id, slot.key())).collect();
|
||||
crate::variable_ui::groups(doc, &libs, |_, v| {
|
||||
v.kind == VariableType::Color && (q.is_empty() || v.name.to_lowercase().contains(&q)) && scope.is_none_or(|s| v.in_scope(s))
|
||||
v.kind == VariableType::Color && (q.is_empty() || v.name.to_lowercase().contains(&q)) && scopes.iter().all(|s| v.in_scope(s))
|
||||
})
|
||||
} else {
|
||||
Vec::new()
|
||||
|
||||
@@ -633,4 +633,19 @@ mod scope_tests {
|
||||
check(&texts, shown, hidden, &format!("{slot:?}"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_mixed_selections_fill_picker_offers_variables_scoped_to_every_layer() {
|
||||
let mut s = session();
|
||||
let r = s.selection()[0];
|
||||
let f = s.doc().unwrap().nodes().find(|n| matches!(n.kind, figcraft_doc::NodeKind::Frame)).unwrap().id;
|
||||
run(&mut s, "variable.create", json!({"collection": "Tokens", "name": "both fills", "type": "color", "values": {"Base": "#FFFF00"}}));
|
||||
run(&mut s, "variable.update", json!({"variable": "both fills", "scopes": ["FRAME_FILL", "SHAPE_FILL"]}));
|
||||
// The frame first: its scope alone would offer "frame fill" for the rectangle too.
|
||||
run(&mut s, "select.set", json!({"ids": [f, r]}));
|
||||
let mut u = TestUi::new(s);
|
||||
crate::style_picker::open_for_selection(&mut u.app, StyleSlot::Fill, None);
|
||||
u.frame(vec![]);
|
||||
check(&drawn(&mut u), &["both fills"], &["frame fill", "shape fill"], "frame and rectangle");
|
||||
}
|
||||
}
|
||||
|
||||
+12
-4
@@ -1,6 +1,6 @@
|
||||
# Where FigCraft falls short of Figma
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-24 · **Change:** minor (gap 16: measurements kept on the canvas, a read-only layers panel in Dev Mode; gap 24: boards and their tail; Dev Mode tail: import lines in layer trees, compare with versions, an inspect-only canvas; gap 5: reviewed Turkish, Czech and Indonesian drafts, the Japanese draft completed; reviewed Russian catalog and integer plurals; `.fig` export: instance sizes, variables with modes, shared styles, variable-width strokes; sharing with a join code; text box sizes measured against documents re-saved by other software; live sessions per document; .fig export checked by opening written files in other software; experimental .fig export; keyboard shortcut parity suite and bindings; Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; `.fig` import: editable vector networks, more layer kinds, variable prototype actions; `.fig` import, best effort: gap 1 down to its tail; Windows QA pass: everyday workflows on Windows 11, eight fixes incl. unsaved work kept after a crash or a forced quit; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings: gap 14 down to its tail; the 2026-10-17 landings: `.fig` import fidelity, budgets for large files, the import report, a `.fig` copy from Save as and paste from another design app (gap 1); menus, the Actions palette, editor fixes, Selection colors and smart-selection spacing (gap 4); catalog counts re-measured (gap 5); bounded canvas pan overscan; reviewed Russian catalog and integer plurals; `.fig` export: instance sizes, variables with modes, shared styles, variable-width strokes; sharing with a join code; text box sizes measured against documents re-saved by other software; live sessions per document; .fig export checked by opening written files in other software; experimental .fig export; keyboard shortcut parity suite and bindings; Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; `.fig` import: editable vector networks, more layer kinds, variable prototype actions; `.fig` import, best effort: gap 1 down to its tail; Windows QA pass: everyday workflows on Windows 11, eight fixes incl. unsaved work kept after a crash or a forced quit; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings: gap 14 down to its tail) · **Target:** Figma Design (desktop app 126.10.7)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-25 · **Change:** minor (known gaps from the #1311 review in gaps 4, 14, 15 and 16; gap 16: measurements kept on the canvas, a read-only layers panel in Dev Mode; gap 24: boards and their tail; Dev Mode tail: import lines in layer trees, compare with versions, an inspect-only canvas; gap 5: reviewed Turkish, Czech and Indonesian drafts, the Japanese draft completed; reviewed Russian catalog and integer plurals; `.fig` export: instance sizes, variables with modes, shared styles, variable-width strokes; sharing with a join code; text box sizes measured against documents re-saved by other software; live sessions per document; .fig export checked by opening written files in other software; experimental .fig export; keyboard shortcut parity suite and bindings; Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; `.fig` import: editable vector networks, more layer kinds, variable prototype actions; `.fig` import, best effort: gap 1 down to its tail; Windows QA pass: everyday workflows on Windows 11, eight fixes incl. unsaved work kept after a crash or a forced quit; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings: gap 14 down to its tail; the 2026-10-17 landings: `.fig` import fidelity, budgets for large files, the import report, a `.fig` copy from Save as and paste from another design app (gap 1); menus, the Actions palette, editor fixes, Selection colors and smart-selection spacing (gap 4); catalog counts re-measured (gap 5); bounded canvas pan overscan; reviewed Russian catalog and integer plurals; `.fig` export: instance sizes, variables with modes, shared styles, variable-width strokes; sharing with a join code; text box sizes measured against documents re-saved by other software; live sessions per document; .fig export checked by opening written files in other software; experimental .fig export; keyboard shortcut parity suite and bindings; Simplified Chinese draft catalog; Latin American Spanish overlay; prototyping tail: gamepad triggers, widgets in prototypes, mode lookups; plugin directory on figcraft-server; Japanese and Korean draft catalogs and CJK UI fonts; plugin widgets and the plugin panel tail; French, German and Brazilian Portuguese draft catalogs; cursor chat and spotlight in live sessions; layers and assets panels behaviour suite and fixes; vector multi-edit and edit tail; dev resources links; extended collections tail; branch version history and finer list merges; comments and version history behaviour suite and fixes; canvas navigation, guides and grids behaviour suite and fixes; `.fig` import: editable vector networks, more layer kinds, variable prototype actions; `.fig` import, best effort: gap 1 down to its tail; Windows QA pass: everyday workflows on Windows 11, eight fixes incl. unsaved work kept after a crash or a forced quit; Display P3: gamut warning and soft-proof (true P3 blocked upstream); several windows; localization layer with a draft Spanish catalog; hosting figcraft-server: wss in the desktop app, server releases, deployment guide; plugin panels and settings; canvas fixes: boolean hit testing on the result, group drags, frame outlines and slices; code connect; stretch and scatter brushes, pen pressure; text on a path and pattern fills; team libraries on the server and swap library; branches: create, update from main, review and merge; animated GIF fills and TIFF images; Open recent, outlines for the selection and mask outlines: menu breadth 100%; extended collections, variables import/export, gradient stop and variant bindings: gap 14 down to its tail) · **Target:** Figma Design (desktop app 126.10.7)
|
||||
|
||||
Every known shortfall against Figma, one entry each, ranked by how much it stops a Figma user from
|
||||
doing real work in FigCraft. Agents: pick work from the top unless the owner says otherwise. The
|
||||
@@ -152,7 +152,9 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe
|
||||
|
||||
### 4. Interaction fidelity not compared tool by tool
|
||||
- **Missing:** a side-by-side pass of every tool and panel: cursors, modifier keys while dragging,
|
||||
hover states, popovers, menus, snapping thresholds.
|
||||
hover states, popovers, menus, snapping thresholds. Known: the Assets panel's export preview
|
||||
follows a slice's first export setting, so a second setting with the other Ignore overlapping
|
||||
layers value previews differently from what it exports.
|
||||
- **Landed (2026-10-11):** behaviour suites written from the public documentation for auto layout,
|
||||
selection and transform, text, and canvas navigation with rulers, guides, layout grids, outline
|
||||
mode and measuring, then (2026-10-12) comments and version history, and the layers and Assets
|
||||
@@ -342,7 +344,8 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe
|
||||
collection (a brand theme extending a core one) can be extended again, its values flowing down
|
||||
the chain as each file accepts updates, every level's overrides kept.
|
||||
- **Missing:** checked with synthetic files only; extending a collection of the same file isn't
|
||||
offered (extending takes library collections).
|
||||
offered (extending takes library collections). A paint style's color offers variables scoped to
|
||||
any fill or stroke: FigCraft's own rule, since the help center doesn't say which scopes it takes.
|
||||
- **Estimate:** 0–1 h.
|
||||
|
||||
### 15. Prototyping: media triggers and actions, controllers off Windows
|
||||
@@ -357,6 +360,9 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe
|
||||
importer warns and skips them, `crates/format/src/rest/proto.rs`); controllers on macOS, Linux
|
||||
and FreeBSD (the usual Rust controller library links libudev on Linux; a pure-Rust reader per
|
||||
platform, or an owner decision on that dependency); PlayStation and Switch button labels.
|
||||
`proto.linkNotice` from the Actions palette or the control channel changes the preference in
|
||||
memory only; it reaches the preferences file with the next preferences save (the notice's own
|
||||
"Don't show this again" saves at once).
|
||||
- **Estimate:** 2–5 h, the media part after video fills (gap 12).
|
||||
|
||||
### 16. Dev Mode: the tail after dev resources
|
||||
@@ -398,7 +404,8 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe
|
||||
the Pages list only opens pages).
|
||||
- **Missing:** publishing mappings from the code repository with a CLI. An editor extension is out
|
||||
of scope: the MCP server already serves design data, code and the connected snippets to agents
|
||||
and editors.
|
||||
and editors. The file name field in the left panel still renames the file in Dev Mode
|
||||
(`file.rename`); whether Figma allows that in Dev Mode needs checking before it changes.
|
||||
- **Estimate:** 1–2 h.
|
||||
|
||||
### 17. Branching and merging
|
||||
@@ -480,6 +487,7 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-25 | minor | Known gaps from the #1311 review: gap 4, the export preview follows a slice's first export setting; gap 14, the scopes a paint style's color takes are FigCraft's own rule; gap 15, `proto.linkNotice` from the palette or control channel isn't saved until the next preferences save; gap 16, the file name field still renames in Dev Mode (to check against Figma first). Ranking and hours unchanged |
|
||||
| 2026-10-24 | minor | Gap 16: Measurements kept on the canvas (gap 16): `devMode.addMeasurement {from?, to?, side?}` places one between two layers, along one side of a layer or (neither) to the layer around it, stored on the layer it starts from (saved in `.figcraft`, synced, undoable; old files open without them); Dev Mode draws them as labelled redlines that follow the layers, the Inspect tab lists them (those of the selected layer, or every one on the page), places and removes them; `devMode.removeMeasurement` and `devMode.measurements` remove and list them; a copy keeps those to layers copied with it. The layers panel is read-only in Dev Mode: rows select and expand, but don't rename, reorder, hide or lock. Left: publishing mappings with a CLI; 2–4 h → 1–2 h |
|
||||
| 2026-10-24 | minor | Boards (M12): a file kind of its own (`file.newBoard`, saved in `.figcraft`, carried to live-session joiners) with sticky notes, shapes with text, connectors that re-route when what they join moves (an engine recompute, the same for local edits, undo and others' edits), marker, highlighter and eraser, stamps with the placer's name, a board toolbar and a contextual bar; prototyping and Dev Mode refuse on boards; design files and boards have their own marks on tabs and file rows. New gap 24 (the board tail, 30–60 h); the ranking above it is unchanged |
|
||||
| 2026-10-15 | minor | Gap 5: Turkish reviewed by a native speaker (#263), Czech (#469) and Indonesian (#477) drafts, the Japanese draft caught up (#288); Indonesian is no longer missing; counts re-measured (2,177 entries) and corrected: only Spanish is held to completeness, Russian included. Estimate unchanged at this rounding |
|
||||
|
||||
+2
-1
@@ -1627,7 +1627,8 @@ Pass the returned target list to:
|
||||
to those solid fill and stroke slots, as `variable.bind` does with `fills/<i>/color` or
|
||||
`strokes/<i>/color`. A color style on the list and an earlier binding of the slot are removed.
|
||||
Text ranges with fills of their own take the variable's value in the layer's modes, without a
|
||||
binding. Gradients, other variable types and slots outside the variable's scopes are refused.
|
||||
binding. Gradients and other variable types are refused; scopes only filter what the Selection
|
||||
colors menu offers, as for `variable.bind`.
|
||||
- `selection.detachColor {targets}` to unlink the color style and the color variable of those
|
||||
slots, keeping their values.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user