M6: Dev Mode measurements kept on the canvas, a read-only layers panel (#1248)

* M6: Dev Mode measurements kept on the canvas, a read-only layers panel

Measurements placed between two layers, along a side or to the layer
around one (devMode.addMeasurement / removeMeasurement / measurements),
stored on the layer they start from (saved, synced, undoable; old files
open without them), drawn in Dev Mode as labelled redlines that follow
the layers and listed, placed and removed in the Inspect tab. In Dev Mode
the layers panel only selects and expands.

* M6: Dev Mode measurements: explicit imports, a measurement row of its own (Sonar)
This commit is contained in:
BFlat
2026-10-24 13:33:26 +07:00
committed by GitHub
parent 3476f4b1e7
commit e3e39bbc4d
48 changed files with 1005 additions and 48 deletions
+4 -2
View File
@@ -2,7 +2,7 @@
**Stage: alpha** · next: beta, ~26 points and ~162–292 h away (one Opus 5.5 agent; ~33–67 h wall-clock with 5 agents) **Stage: alpha** · next: beta, ~26 points and ~162–292 h away (one Opus 5.5 agent; ~33–67 h wall-clock with 5 agents)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-24 · **Change:** minor (boards: a board area row and progress entry; `fill`/`stroke` props accept `"none"` and `null` as no paint (#1036); community fixes in #507, #510, #529 and #530: auto-save, a text box by dragging, batch step references, removing someone from a live session, Russian and Turkish catalogs reviewed, Czech and Indonesian drafts, the Japanese draft completed, zoom previews, the web clipboard, Alt-drag snapping and Cmd+D repeat, recovery copies, crash and rollback fixes, colour emoji, `.fig` import fixes; earlier changes are in the revision history below) · **Target:** Figma Design (desktop app 126.10.7) > **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-24 · **Change:** minor (Dev Mode measurements kept on the canvas and a read-only layers panel; Dev Mode measurements kept on the canvas and a read-only layers panel; boards: a board area row and progress entry; community fixes in #507, #510, #529 and #530: auto-save, a text box by dragging, batch step references, removing someone from a live session, Russian and Turkish catalogs reviewed, Czech and Indonesian drafts, the Japanese draft completed, zoom previews, the web clipboard, Alt-drag snapping and Cmd+D repeat, recovery copies, crash and rollback fixes, colour emoji, `.fig` import fixes; earlier changes are in the revision history below) · **Target:** Figma Design (desktop app 126.10.7)
FigCraft is a clean-room, open-source, pure-Rust reimplementation of the Figma design workflow, FigCraft is a clean-room, open-source, pure-Rust reimplementation of the Figma design workflow,
native on macOS, Windows, Linux and FreeBSD and in the browser, fully drivable by agents through native on macOS, Windows, Linux and FreeBSD and in the browser, fully drivable by agents through
@@ -86,7 +86,7 @@ native speakers' reviews of the nine language drafts need people.
| Styles & variables | 95% | 76% | 2–4 | [components-parity](docs/components-parity.md) | | Styles & variables | 95% | 76% | 2–4 | [components-parity](docs/components-parity.md) |
| Prototyping | 92% | 74% | 2–5 | [prototyping-parity](docs/prototyping-parity.md) | | Prototyping | 92% | 74% | 2–5 | [prototyping-parity](docs/prototyping-parity.md) |
| Import/export | 86% | 58% | 5–10 | [file-format-parity](docs/file-format-parity.md) | | Import/export | 86% | 58% | 5–10 | [file-format-parity](docs/file-format-parity.md) |
| Dev Mode / inspect | 97% | 76% | 2–4 | [dev-mode-parity](docs/dev-mode-parity.md) | | Dev Mode / inspect | 98% | 78% | 1–2 | [dev-mode-parity](docs/dev-mode-parity.md) |
| Comments, versions, misc | 87% | 63% | 1–3 | [collaboration-parity](docs/collaboration-parity.md) | | Comments, versions, misc | 87% | 63% | 1–3 | [collaboration-parity](docs/collaboration-parity.md) |
| Multiplayer & sharing | 17% | 5% | 42–84 | [collaboration-parity](docs/collaboration-parity.md) | | Multiplayer & sharing | 17% | 5% | 42–84 | [collaboration-parity](docs/collaboration-parity.md) |
| Plugins & widgets | 60% | 35% | 8–26 | [plugins](docs/plugins.md) | | Plugins & widgets | 60% | 35% | 8–26 | [plugins](docs/plugins.md) |
@@ -132,6 +132,7 @@ Ranked; detail and milestones in [`docs/roadmap.md`](docs/roadmap.md).
## Progress log ## Progress log
- **2026-10-24** · Dev Mode tail (M6, 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. Dev Mode 98% / 78%.
- **2026-10-24** · Boards (M12): File ▸ New board makes a board, a file kind of its own saved in `.figcraft` and carried to people joining a live session. Sticky notes (8 colours, text that shrinks as the note grows, the author's name, Shift+Cmd/Ctrl+Enter for the next one), shapes with text (7 outlines), connectors that re-route when what they join moves (computed in the engine's normalize pass, so local edits, undo and others' edits agree), marker, highlighter and eraser, stamps with the placer's name on hover; a board toolbar, a contextual bar over the selection and the Design panel hidden until asked for; prototyping and Dev Mode refuse on boards. Design files and boards have their own marks (a pen nib, a note) on tabs, the home screen and the Open files list. Checked in the app, with two app instances in one live session, and through MCP; the web build compiles ([boards-parity](docs/boards-parity.md)). - **2026-10-24** · Boards (M12): File ▸ New board makes a board, a file kind of its own saved in `.figcraft` and carried to people joining a live session. Sticky notes (8 colours, text that shrinks as the note grows, the author's name, Shift+Cmd/Ctrl+Enter for the next one), shapes with text (7 outlines), connectors that re-route when what they join moves (computed in the engine's normalize pass, so local edits, undo and others' edits agree), marker, highlighter and eraser, stamps with the placer's name on hover; a board toolbar, a contextual bar over the selection and the Design panel hidden until asked for; prototyping and Dev Mode refuse on boards. Design files and boards have their own marks (a pen nib, a note) on tabs, the home screen and the Open files list. Checked in the app, with two app instances in one live session, and through MCP; the web build compiles ([boards-parity](docs/boards-parity.md)).
- **2026-10-15** · Auto-save (M1, #305): Preferences ▸ Save files automatically (`pref.autoSave`, off by default, desktop only) saves each open file that has a path 3 s after its last change, never during a gesture, a live text preview or while a version is shown; the file is encoded off the UI thread, a failed save says why and is tried again a minute later. Numbers unchanged. - **2026-10-15** · Auto-save (M1, #305): Preferences ▸ Save files automatically (`pref.autoSave`, off by default, desktop only) saves each open file that has a path 3 s after its last change, never during a gesture, a live text preview or while a version is shown; the file is encoded off the UI thread, a failed save says why and is tried again a minute later. Numbers unchanged.
- **2026-10-15** · A text box by dragging (M1, #363): a drag with the Text tool makes a fixed-size text box the size of the drag; a click still makes auto-width text. Numbers unchanged. - **2026-10-15** · A text box by dragging (M1, #363): a drag with the Text tool makes a fixed-size text box the size of the drag; a click still makes auto-width text. Numbers unchanged.
@@ -328,6 +329,7 @@ Ranked; detail and milestones in [`docs/roadmap.md`](docs/roadmap.md).
| Date | Change | Summary | | Date | Change | Summary |
|---|---|---| |---|---|---|
| 2026-10-24 | minor | Dev Mode tail (M6, 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. Dev Mode 97% → 98% breadth, 76% → 78% ready, 2–4 h → 1–2 h; headline numbers unchanged at this rounding |
| 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. Boards row added outside the weighted numbers; headline numbers unchanged at this rounding | | 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. Boards row added outside the weighted numbers; headline numbers unchanged at this rounding |
| 2026-10-15 | minor | Auto-save (#305, #296): Preferences ▸ Save files automatically (`pref.autoSave`, off by default; desktop only, the browser has no files to save to) saves each open file that has a path 3 s after its last change, so steady editing doesn't save every few seconds, and never during a gesture, a live text preview or while a version is shown. The file is encoded on a thread of its own and written as of that moment (edits made meanwhile stay unsaved); a failed save says why in a toast and is tried again a minute later, and version history checkpoints keep coming with it on. Numbers unchanged | | 2026-10-15 | minor | Auto-save (#305, #296): Preferences ▸ Save files automatically (`pref.autoSave`, off by default; desktop only, the browser has no files to save to) saves each open file that has a path 3 s after its last change, so steady editing doesn't save every few seconds, and never during a gesture, a live text preview or while a version is shown. The file is encoded on a thread of its own and written as of that moment (edits made meanwhile stay unsaved); a failed save says why in a toast and is tried again a minute later, and version history checkpoints keep coming with it on. Numbers unchanged |
| 2026-10-15 | minor | A text box by dragging (#363, #360): a drag with the Text tool makes a fixed-size text box the size of the drag (at least one line tall); a click, or a drag shorter than the drag threshold, still makes auto-width text. Numbers unchanged | | 2026-10-15 | minor | A text box by dragging (#363, #360): a drag with the Text tool makes a fixed-size text box the size of the drag (at least one line tall); a click, or a drag shorter than the drag threshold, still makes auto-width text. Numbers unchanged |
+1
View File
@@ -181,6 +181,7 @@ node_props! {
Annotations => annotations, Annotations => annotations,
CodeLinks => code_links, CodeLinks => code_links,
DevResources => dev_resources, DevResources => dev_resources,
Measurements => measurements,
Board => board, Board => board,
} }
+7 -5
View File
@@ -6,11 +6,12 @@
//! values, or a layer deleted on one side while the other side edited it or something inside it //! values, or a layer deleted on one side while the other side edited it or something inside it
//! (including adding layers to it or moving layers into or out of it). The layer lists whose items //! (including adding layers to it or moving layers into or out of it). The layer lists whose items
//! stand on their own (fills, strokes, effects, layout grids, export settings, guides, prototype //! stand on their own (fills, strokes, effects, layout grids, export settings, guides, prototype
//! interactions, accessibility annotations and Dev Mode resources) merge item by item: both sides //! interactions, accessibility annotations, Dev Mode resources and measurements) merge item by
//! changing different items of the list (or one adding items at its end) is no conflict, only the //! item: both sides changing different items of the list (or one adding items at its end) is no
//! same item changed both ways or the list shortened on a side while the other changed it. Nothing //! conflict, only the same item changed both ways or the list shortened on a side while the other
//! that conflicts is merged until it is resolved: each conflict is resolved for one side, and the //! changed it. Nothing that conflicts is merged until it is resolved: each conflict is resolved for
//! other side's changes to it are left out. Everything else from both sides is kept. //! one side, and the other side's changes to it are left out. Everything else from both sides is
//! kept.
use std::collections::{BTreeMap, BTreeSet}; use std::collections::{BTreeMap, BTreeSet};
use std::fmt; use std::fmt;
@@ -176,6 +177,7 @@ const PER_ITEM: &[Prop] = &[
// Not `CodeLinks`: there is one per language, and lining them up by position could leave two // Not `CodeLinks`: there is one per language, and lining them up by position could leave two
// for one language (the second hidden), so they merge whole. // for one language (the second hidden), so they merge whole.
Prop::DevResources, Prop::DevResources,
Prop::Measurements,
]; ];
/// Both sides' writes `b` and `m` of list property `key` merged item by item against the base /// Both sides' writes `b` and `m` of list property `key` merged item by item against the base
+6
View File
@@ -856,6 +856,12 @@ impl Document {
{ {
*component = *m; *component = *m;
} }
// A measurement copied with the layer it measures to measures to the copy; one to a
// layer left behind isn't copied.
c.measurements.retain_mut(|m| match m.to {
Some(t) => map.get(&t).map(|n| m.to = Some(*n)).is_some(),
None => true,
});
// A connector (or label) copied with what it joins joins the copies. // A connector (or label) copied with what it joins joins the copies.
if let Some(b) = c.board.as_mut() { if let Some(b) = c.board.as_mut() {
b.remap(map); b.remap(map);
+15
View File
@@ -237,6 +237,17 @@ pub struct DevResource {
pub name: String, pub name: String,
} }
/// A measurement kept on the canvas for developers, stored on the layer it starts from: to another
/// layer (`to`), or across one of the layer's own sides (`side`). Dev Mode draws it from the layers'
/// current bounds, so it follows them.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Measurement {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub to: Option<NodeId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub side: Option<crate::Side>,
}
/// A kind of annotation (Development, Accessibility…): its stable id, name and the colour its /// A kind of annotation (Development, Accessibility…): its stable id, name and the colour its
/// callouts are drawn in. /// callouts are drawn in.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@@ -518,6 +529,9 @@ pub struct Node {
/// Dev resources: links for developers (docs, code, tickets…), in the order they were added. /// Dev resources: links for developers (docs, code, tickets…), in the order they were added.
#[serde(default, skip_serializing_if = "Vec::is_empty")] #[serde(default, skip_serializing_if = "Vec::is_empty")]
pub dev_resources: Vec<DevResource>, pub dev_resources: Vec<DevResource>,
/// Dev Mode measurements that start from this layer, in the order they were added.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub measurements: Vec<Measurement>,
/// On a board: what this object is (a sticky note, a connector, a stamp…). /// On a board: what this object is (a sticky note, a connector, a stamp…).
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub board: Option<crate::BoardItem>, pub board: Option<crate::BoardItem>,
@@ -631,6 +645,7 @@ impl Node {
annotations: Vec::new(), annotations: Vec::new(),
code_links: Vec::new(), code_links: Vec::new(),
dev_resources: Vec::new(), dev_resources: Vec::new(),
measurements: Vec::new(),
board: None, board: None,
} }
} }
+1
View File
@@ -99,6 +99,7 @@ fn registry() -> &'static Registry {
list.extend(crate::code_connect::specs()); list.extend(crate::code_connect::specs());
list.extend(crate::annotations::specs()); list.extend(crate::annotations::specs());
list.extend(crate::dev_resources::specs()); list.extend(crate::dev_resources::specs());
list.extend(crate::dev_measurements::specs());
list.extend(crate::distance::specs()); list.extend(crate::distance::specs());
list.extend(crate::collab::specs()); list.extend(crate::collab::specs());
list.extend(crate::branches::specs()); list.extend(crate::branches::specs());
+5
View File
@@ -302,6 +302,8 @@ pub fn create_instance_at(doc: &mut Document, component: NodeId, parent: NodeId,
root.code_links.clear(); root.code_links.clear();
// The main's dev resources show on its instances without being copied. // The main's dev resources show on its instances without being copied.
root.dev_resources.clear(); root.dev_resources.clear();
// Measurements belong to the layer they were placed on.
root.measurements.clear();
// The main's interactions play on its instances in prototypes; the instance's own start empty. // The main's interactions play on its instances in prototypes; the instance's own start empty.
root.interactions.clear(); root.interactions.clear();
// So do the variable modes the main sets (see `Document::inherited_modes`). // So do the variable modes the main sets (see `Document::inherited_modes`).
@@ -720,6 +722,7 @@ fn sync_instance_depth(doc: &mut Document, inst: NodeId, depth: usize, spare: BT
let prev = old.get(&nid); let prev = old.get(&nid);
// Annotations belong to the layer they were added to, not to its copies. // Annotations belong to the layer they were added to, not to its copies.
n.annotations = prev.map(|o| o.annotations.clone()).unwrap_or_default(); n.annotations = prev.map(|o| o.annotations.clone()).unwrap_or_default();
n.measurements = prev.map(|o| o.measurements.clone()).unwrap_or_default();
if let Some(o) = prev { if let Some(o) = prev {
carry_overrides(o, &mut n, Carry::Values); carry_overrides(o, &mut n, Carry::Values);
keep_laid_out(doc, orig, o, &mut n); keep_laid_out(doc, orig, o, &mut n);
@@ -797,6 +800,7 @@ fn sync_instance_depth(doc: &mut Document, inst: NodeId, depth: usize, spare: BT
fresh.code_links.clear(); fresh.code_links.clear();
// An instance's own dev resources (its main's are listed with them, not copied). // An instance's own dev resources (its main's are listed with them, not copied).
fresh.dev_resources = root.dev_resources.clone(); fresh.dev_resources = root.dev_resources.clone();
fresh.measurements = root.measurements.clone();
fresh.locked = root.locked; fresh.locked = root.locked;
fresh.visible = root.visible; fresh.visible = root.visible;
fresh.var_modes = root.var_modes.clone(); fresh.var_modes = root.var_modes.clone();
@@ -1591,6 +1595,7 @@ fn copy_layers(doc: &mut Document, src: NodeId, parent: NodeId) -> Option<NodeId
n.overrides.clear(); n.overrides.clear();
n.prop_refs.clear(); n.prop_refs.clear();
n.annotations.clear(); n.annotations.clear();
n.measurements.clear();
insert_linked(doc, n).ok()?; insert_linked(doc, n).ok()?;
} }
map.get(&src).copied() map.get(&src).copied()
+222
View File
@@ -0,0 +1,222 @@
//! Measurements kept on the canvas (Dev Mode): a developer places one between two layers, or
//! across one side of a layer, and everyone who opens the file sees it. Each is stored on the layer
//! it starts from (`Node::measurements`: saved, synced, undoable) and drawn from the layers'
//! current bounds, so it follows them when they move or resize. One whose other layer was deleted
//! or moved to another page is neither drawn nor listed (undoing the deletion brings it back).
use figcraft_doc::{Document, Measurement, Node, NodeId, Side};
use figcraft_geom::{Point, Rect};
use serde_json::{Value, json};
use crate::cmd::CommandSpec;
use crate::distance::{between, measured};
use crate::params::{get, opt_id, opt_index};
use crate::snap::Guide;
use crate::{EngineError, Result, Session, bad, spec};
/// Most measurements starting from one layer.
pub const MAX_PER_LAYER: usize = 64;
/// Most measurements listed or drawn for one page.
pub const MAX_LISTED: usize = 2_000;
/// A measurement on a page: the layer it starts from, its index there, what it measures and the
/// lines it draws (page space).
#[derive(Clone, Debug, PartialEq)]
pub struct Placed {
pub from: NodeId,
pub index: usize,
pub measurement: Measurement,
pub lines: Vec<Guide>,
}
impl Placed {
/// Whether it starts or ends at layer `id`.
pub fn touches(&self, id: NodeId) -> bool {
self.from == id || self.measurement.to == Some(id)
}
/// The distances it shows, as on its labels.
pub fn labels(&self) -> Vec<&str> {
self.lines.iter().filter_map(|g| g.label.as_deref()).collect()
}
}
/// The line along side `side` of box `b`, labelled with that side's length.
fn side_line(b: Rect, side: Side) -> Vec<Guide> {
let (p, q) = match side {
Side::Top => (Point::new(b.x0, b.y0), Point::new(b.x1, b.y0)),
Side::Bottom => (Point::new(b.x0, b.y1), Point::new(b.x1, b.y1)),
Side::Left => (Point::new(b.x0, b.y0), Point::new(b.x0, b.y1)),
Side::Right => (Point::new(b.x1, b.y0), Point::new(b.x1, b.y1)),
Side::Auto => return Vec::new(),
};
let mut out = Vec::new();
measured(&mut out, p, q);
out
}
/// The lines measurement `m` from layer `from` draws, in page space (none when a layer is gone).
pub fn lines(doc: &Document, from: NodeId, m: Measurement) -> Vec<Guide> {
let Some(a) = doc.world_bounds(from) else { return Vec::new() };
match (m.to, m.side) {
(Some(to), _) => doc.world_bounds(to).map(|b| between(a, b)).unwrap_or_default(),
(None, Some(side)) => side_line(a, side),
(None, None) => Vec::new(),
}
}
/// The measurements on `page` whose layers are both there, in layer order (bounded).
pub fn on_page(doc: &Document, page: NodeId) -> Vec<Placed> {
let mut out = Vec::new();
let mut stack = vec![page];
let mut visited = 0usize;
while let Some(id) = stack.pop() {
visited += 1;
if out.len() >= MAX_LISTED || visited > 200_000 {
break;
}
let Some(n) = doc.node(id) else { continue };
for (index, m) in n.measurements.iter().enumerate() {
if m.to.is_some_and(|t| t == id || doc.page_of(t) != Some(page)) {
continue;
}
out.push(Placed { from: id, index, measurement: *m, lines: lines(doc, id, *m) });
}
stack.extend(n.children.iter().rev().copied());
}
out
}
fn placed_json(doc: &Document, p: &Placed) -> Value {
let name = |id: NodeId| doc.node(id).map(|n| n.name.clone()).unwrap_or_default();
let mut j = json!({"from": p.from, "fromName": name(p.from), "index": p.index, "labels": p.labels(), "lines": p.lines});
if let Some(t) = p.measurement.to {
j["to"] = json!(t);
j["toName"] = json!(name(t));
}
if let Some(s) = p.measurement.side {
j["side"] = json!(s);
}
j
}
/// The layer a command starts from: `from`, else the first selected layer; never a page.
fn from_layer(cmd: &str, s: &Session, p: &Value) -> Result<NodeId> {
let id = match opt_id(cmd, p, "from")? {
Some(id) => id,
None => s.selection().first().copied().ok_or_else(|| bad(cmd, "select a layer or pass `from`"))?,
};
let n = s.doc()?.node(id).ok_or(EngineError::NoNode(id))?;
if n.is_page() {
return Err(bad(cmd, "measure from a layer, not a page"));
}
Ok(id)
}
/// The `side` parameter: top, right, bottom or left.
fn side_param(cmd: &str, p: &Value) -> Result<Option<Side>> {
match get(p, "side") {
None | Some(Value::Null) => Ok(None),
Some(Value::String(v)) => Side::from_id(v).filter(|s| *s != Side::Auto).map(Some).ok_or_else(|| bad(cmd, "side: top, right, bottom or left")),
Some(_) => Err(bad(cmd, "side: top, right, bottom or left")),
}
}
fn add(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "devMode.addMeasurement";
let from = from_layer(cmd, s, p)?;
let (to, side) = (opt_id(cmd, p, "to")?, side_param(cmd, p)?);
let doc = s.doc()?;
let n = doc.node(from).ok_or(EngineError::NoNode(from))?;
let to = match (to, side) {
(Some(_), Some(_)) => return Err(bad(cmd, "pass `to` or `side`, not both")),
(Some(t), None) => Some(t),
(None, Some(_)) => None,
// Neither: to the layer around it.
(None, None) => Some(
n.parent
.filter(|q| doc.node(*q).is_some_and(|q| !q.is_page()))
.ok_or_else(|| bad(cmd, "pass `to` or `side` (the layer is at the top of its page)"))?,
),
};
if let Some(t) = to {
let target = doc.node(t).ok_or(EngineError::NoNode(t))?;
if t == from {
return Err(bad(cmd, "measure to another layer, or across a `side` of this one"));
}
if target.is_page() || doc.page_of(t) != doc.page_of(from) {
return Err(bad(cmd, "measure to a layer on the same page"));
}
}
let m = Measurement { to, side };
// The same two layers measured from the other end are the same measurement.
let back = to.and_then(|t| doc.node(t)).is_some_and(|t| t.measurements.contains(&Measurement { to: Some(from), side: None }));
if n.measurements.contains(&m) || back {
return Err(bad(cmd, "that measurement is already there"));
}
if n.measurements.len() >= MAX_PER_LAYER {
return Err(bad(cmd, format!("a layer starts at most {MAX_PER_LAYER} measurements")));
}
let drawn = lines(doc, from, m);
let n = s.doc_mut()?.get_mut(from)?;
n.measurements.push(m);
Ok(json!({"from": from, "index": n.measurements.len() - 1, "lines": drawn}))
}
fn remove(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "devMode.removeMeasurement";
let from = from_layer(cmd, s, p)?;
let index = opt_index(cmd, p, "index")?;
let n = s.doc_mut()?.get_mut(from)?;
let count = n.measurements.len();
if count == 0 {
return Err(bad(cmd, "no measurement starts from this layer"));
}
let removed = match index {
Some(i) if i < count => {
n.measurements.remove(i);
1
}
Some(i) => return Err(bad(cmd, format!("no measurement {i} (the layer starts {count})"))),
None => std::mem::take(&mut n.measurements).len(),
};
Ok(json!({"removed": removed}))
}
fn list(s: &mut Session, p: &Value) -> Result<Value> {
let cmd = "devMode.measurements";
let doc = s.doc()?;
let page = match opt_id(cmd, p, "page")? {
Some(pg) if doc.node(pg).is_some_and(Node::is_page) => pg,
Some(_) => return Err(bad(cmd, "`page` must be a page id")),
None => s.page()?,
};
let only = opt_id(cmd, p, "id")?;
Ok(Value::Array(on_page(doc, page).iter().filter(|m| only.is_none_or(|id| m.touches(id))).map(|m| placed_json(doc, m)).collect()))
}
pub fn specs() -> Vec<CommandSpec> {
vec![
spec!(
"devMode.addMeasurement",
"Add measurement",
"{from? (default the first selected layer), to? (a layer on the same page), side? (top|right|bottom|left)} keeps a measurement on the canvas: between `from` and `to`, across one side of `from`, or (neither) to the layer around it; Dev Mode draws it and it follows the layers → {from, index, lines}",
true,
add
),
spec!(
"devMode.removeMeasurement",
"Remove measurement",
"{from? (default the first selected layer), index? (omit: every measurement starting from the layer)} → {removed}",
true,
remove
),
spec!(
"devMode.measurements",
"List measurements",
"{page? (default the current page), id? (only those from or to this layer)} → [{from, fromName, index, to?, toName?, side?, labels, lines: [{x0, y0, x1, y1, label, dashed}]}] (page space)",
false,
list
),
]
}
+204
View File
@@ -0,0 +1,204 @@
//! Measurements kept on the canvas: placed between two layers, across a side or to the layer
//! around one, following the layers, removed, undoable, copied with what they measure, refused on
//! bad input, synced, saved (old files without them still open) and allowed in Dev Mode.
use figcraft_collab::{Change, Prop, Replica};
use figcraft_doc::NodeId;
use serde_json::{Value, json};
use crate::Session;
use crate::params::id_of;
fn ok(s: &mut Session, cmd: &str, p: Value) -> Value {
s.execute(cmd, &p).unwrap_or_else(|e| panic!("{cmd} {p}: {e}"))
}
fn rect(s: &mut Session, x: f64, w: f64, parent: Option<NodeId>) -> NodeId {
let mut p = json!({"x": x, "y": 20, "width": w, "height": 40});
if let Some(parent) = parent {
p["parent"] = json!(parent);
}
id_of(&ok(s, "shape.rectangle", p)["id"]).unwrap()
}
/// A frame with two rectangles 30 apart: (frame, a, b).
fn setup() -> (Session, NodeId, NodeId, NodeId) {
let mut s = Session::new();
let f = id_of(&ok(&mut s, "frame.create", json!({"x": 0, "y": 0, "width": 300, "height": 100}))["id"]).unwrap();
let a = rect(&mut s, 20.0, 50.0, Some(f));
let b = rect(&mut s, 100.0, 60.0, Some(f));
(s, f, a, b)
}
fn labels(s: &mut Session, id: Option<NodeId>) -> Vec<Vec<String>> {
let p = id.map_or(json!({}), |i| json!({"id": i}));
ok(s, "devMode.measurements", p)
.as_array()
.unwrap()
.iter()
.map(|m| m["labels"].as_array().unwrap().iter().map(|l| l.as_str().unwrap().to_string()).collect())
.collect()
}
#[test]
fn measurements_are_placed_follow_their_layers_and_are_removed() {
let (mut s, f, a, b) = setup();
let m = ok(&mut s, "devMode.addMeasurement", json!({"from": a, "to": b}));
assert_eq!((&m["from"], &m["index"]), (&json!(a), &json!(0)));
assert_eq!(m["lines"][0]["label"], "30", "the gap between them");
// Across a side: the side's length; with neither, to the frame around it.
ok(&mut s, "devMode.addMeasurement", json!({"from": b, "side": "top"}));
ok(&mut s, "select.set", json!({"ids": [a]}));
ok(&mut s, "devMode.addMeasurement", json!({}));
assert_eq!(labels(&mut s, None), [vec!["30"], vec!["20", "230", "20", "40"], vec!["60"]]);
let listed = ok(&mut s, "devMode.measurements", json!({"id": b}));
assert_eq!(listed.as_array().unwrap().len(), 2, "from or to the layer");
assert_eq!((&listed[1]["side"], &listed[1]["to"]), (&json!("top"), &Value::Null));
assert_eq!(listed[0]["toName"], s.doc().unwrap().node(b).unwrap().name.as_str());
// They follow the layers.
ok(&mut s, "node.set", json!({"id": b, "props": {"x": 110, "width": 80}}));
assert_eq!(labels(&mut s, Some(b)), [vec!["40"], vec!["80"]]);
// Undo takes back an edit, and a placement.
ok(&mut s, "edit.undo", json!({}));
assert_eq!(labels(&mut s, Some(b)), [vec!["30"], vec!["60"]]);
ok(&mut s, "edit.undo", json!({}));
assert_eq!(labels(&mut s, None).len(), 2);
ok(&mut s, "edit.redo", json!({}));
// A deleted layer takes its measurements off the canvas; undo brings them back.
ok(&mut s, "edit.delete", json!({"ids": [b]}));
assert_eq!(labels(&mut s, None), [vec!["20", "230", "20", "40"]]);
ok(&mut s, "edit.undo", json!({}));
assert_eq!(labels(&mut s, None).len(), 3);
// Removed one by one, or all of a layer's.
assert_eq!(ok(&mut s, "devMode.removeMeasurement", json!({"from": a, "index": 1}))["removed"], 1);
assert_eq!(labels(&mut s, None), [vec!["30"], vec!["60"]]);
ok(&mut s, "devMode.addMeasurement", json!({"from": a, "side": "left"}));
assert_eq!(ok(&mut s, "devMode.removeMeasurement", json!({"from": a}))["removed"], 2);
assert!(s.execute("devMode.removeMeasurement", &json!({"from": a})).is_err(), "nothing left");
assert_eq!(labels(&mut s, None), [vec!["60"]]);
let _ = f;
}
#[test]
fn bad_measurements_are_refused() {
let (mut s, f, a, b) = setup();
let page = s.page().unwrap();
let other = id_of(&ok(&mut s, "page.add", json!({}))["id"]).unwrap();
let far = rect(&mut s, 0.0, 10.0, Some(other));
ok(&mut s, "page.select", json!({"id": page}));
let top = rect(&mut s, 400.0, 10.0, None);
ok(&mut s, "select.none", json!({}));
for p in [
json!({"from": a, "to": a}),
json!({"from": a, "to": page}),
json!({"from": a, "to": far}),
json!({"from": a, "to": 99_999}),
json!({"from": page, "side": "top"}),
json!({"from": a, "to": b, "side": "top"}),
json!({"from": a, "side": "middle"}),
json!({"from": a, "side": "auto"}),
json!({"from": a, "side": 3}),
json!({"from": "x", "side": "top"}),
json!({"from": top}),
json!({"side": "top"}),
] {
assert!(s.execute("devMode.addMeasurement", &p).is_err(), "{p}");
}
ok(&mut s, "devMode.addMeasurement", json!({"from": a, "to": b}));
assert!(s.execute("devMode.addMeasurement", &json!({"from": a, "to": b})).is_err(), "already there");
assert!(s.execute("devMode.addMeasurement", &json!({"from": b, "to": a})).is_err(), "the same two layers");
assert!(s.execute("devMode.removeMeasurement", &json!({"from": a, "index": 5})).is_err());
assert!(s.execute("devMode.measurements", &json!({"page": a})).is_err());
assert_eq!(ok(&mut s, "devMode.measurements", json!({"page": other})), json!([]));
// A layer starts a bounded number.
for _ in 1..crate::dev_measurements::MAX_PER_LAYER {
let t = rect(&mut s, 200.0, 5.0, Some(f));
ok(&mut s, "devMode.addMeasurement", json!({"from": a, "to": t}));
}
assert!(s.execute("devMode.addMeasurement", &json!({"from": a, "side": "top"})).is_err(), "capped");
assert_eq!(s.doc().unwrap().node(a).unwrap().measurements.len(), crate::dev_measurements::MAX_PER_LAYER);
}
#[test]
fn copies_measure_what_was_copied_with_them() {
let (mut s, f, a, b) = setup();
ok(&mut s, "devMode.addMeasurement", json!({"from": a, "to": b}));
ok(&mut s, "devMode.addMeasurement", json!({"from": a, "side": "bottom"}));
// The frame copied: the copy of `a` measures to the copy of `b`.
ok(&mut s, "edit.duplicate", json!({"ids": [f]}));
let copy = s.selection()[0];
let doc = s.doc().unwrap();
let kids = doc.node(copy).unwrap().children.clone();
let ca = doc.node(kids[0]).unwrap();
assert_eq!(ca.measurements.len(), 2);
assert_eq!(ca.measurements[0].to, Some(kids[1]));
// `a` alone copied: only its side measurement comes along.
ok(&mut s, "edit.duplicate", json!({"ids": [a]}));
let lone = s.selection()[0];
let m = &s.doc().unwrap().node(lone).unwrap().measurements;
assert_eq!((m.len(), m[0].to, m[0].side), (1, None, Some(figcraft_doc::Side::Bottom)));
}
#[test]
fn measurements_sync_save_and_old_files_open() {
let (mut s, _, a, b) = setup();
let before = s.doc().unwrap().clone();
ok(&mut s, "devMode.addMeasurement", json!({"from": a, "to": b}));
ok(&mut s, "devMode.addMeasurement", json!({"from": b, "side": "right"}));
// A change collaborators receive.
let d = figcraft_collab::diff(&Replica::new(before), s.doc().unwrap()).unwrap();
assert!(d.changes.iter().any(|c| matches!(c, Change::Set { id, prop: Prop::Measurements, .. } if *id == a)), "{:?}", d.changes);
// What agents inspect.
assert_eq!(crate::inspect::node_json(s.doc().unwrap(), b, 0)["measurements"], json!([{"side": "right"}]));
// Saved and opened again.
let doc = s.doc().unwrap();
let bytes = figcraft_format::save_bytes(doc, None).unwrap();
let (back, _) = figcraft_format::load_bytes(&bytes).unwrap();
assert_eq!(back.node(a).unwrap().measurements, doc.node(a).unwrap().measurements);
assert_eq!(back.node(b).unwrap().measurements, doc.node(b).unwrap().measurements);
// A file from before measurements (no such field) opens with none.
fn strip(v: &mut Value) {
match v {
Value::Object(o) => {
o.remove("measurements");
o.values_mut().for_each(strip);
}
Value::Array(a) => a.iter_mut().for_each(strip),
_ => {}
}
}
let mut old = figcraft_format::to_json(doc).unwrap();
assert!(old.to_string().contains("\"measurements\""));
strip(&mut old);
let (back, _) = figcraft_format::from_json(&old.to_string()).unwrap();
assert!(back.node(a).unwrap().measurements.is_empty() && back.node(b).unwrap().name == doc.node(b).unwrap().name);
}
#[test]
fn dev_mode_places_measurements() {
use crate::devinspect::dev_mode_allows;
for id in ["devMode.addMeasurement", "devMode.removeMeasurement", "devMode.measurements"] {
assert!(dev_mode_allows(id), "{id}");
}
}
#[test]
fn instances_keep_their_own_measurements_across_main_edits() {
let mut s = Session::new();
let f = id_of(&ok(&mut s, "frame.create", json!({"x": 0, "y": 0, "width": 100, "height": 40}))["id"]).unwrap();
let r = rect(&mut s, 5.0, 10.0, Some(f));
ok(&mut s, "devMode.addMeasurement", json!({"from": r, "side": "top"}));
let main = id_of(&ok(&mut s, "component.create", json!({"ids": [f]}))["id"]).unwrap();
let inst = id_of(&ok(&mut s, "instance.create", json!({"component": main, "x": 200, "y": 0}))["id"]).unwrap();
let inner = s.doc().unwrap().node(inst).unwrap().children[0];
// The main's measurements aren't copied onto its instances.
assert!(s.doc().unwrap().node(inner).unwrap().measurements.is_empty());
ok(&mut s, "devMode.addMeasurement", json!({"from": inner}));
ok(&mut s, "devMode.addMeasurement", json!({"from": inst, "side": "left"}));
ok(&mut s, "node.set", json!({"id": r, "props": {"opacity": 0.5}}));
ok(&mut s, "node.set", json!({"id": main, "props": {"width": 120}}));
let doc = s.doc().unwrap();
assert_eq!(doc.node(inner).unwrap().measurements[0].to, Some(inst), "kept across a main edit");
assert_eq!(doc.node(inst).unwrap().measurements.len(), 1);
assert_eq!(doc.node(r).unwrap().measurements.len(), 1);
}
+4 -4
View File
@@ -219,11 +219,11 @@ pub struct DevItem {
} }
/// Whether Dev Mode runs command `id` from the menus and shortcuts: commands that don't edit the /// Whether Dev Mode runs command `id` from the menus and shortcuts: commands that don't edit the
/// document, and Dev Mode's own edits (annotations, statuses, dev resources, code connections, /// document, and Dev Mode's own edits (annotations, statuses, dev resources, measurements, code
/// comments). Choosing a tool other than moving around and commenting is refused too, and so is /// connections, comments). Choosing a tool other than moving around and commenting is refused too,
/// Revert to saved, which isn't an undo step but throws the design's unsaved edits away. /// and so is Revert to saved, which isn't an undo step but throws the design's unsaved edits away.
pub fn dev_mode_allows(id: &str) -> bool { pub fn dev_mode_allows(id: &str) -> bool {
const OWN: [&str; 5] = ["annotation.", "dev.", "devResource.", "codeConnect.", "comment."]; const OWN: [&str; 6] = ["annotation.", "dev.", "devMode.", "devResource.", "codeConnect.", "comment."];
match id.strip_prefix("tool.") { match id.strip_prefix("tool.") {
Some(t) => matches!(t, "move" | "hand" | "comment"), Some(t) => matches!(t, "move" | "hand" | "comment"),
None if id == "file.revert" => false, None if id == "file.revert" => false,
+1 -1
View File
@@ -34,7 +34,7 @@ pub fn label(d: f64) -> String {
} }
/// A labelled distance line, unless it has no length. /// A labelled distance line, unless it has no length.
fn measured(out: &mut Vec<Guide>, a: Point, b: Point) { pub(crate) fn measured(out: &mut Vec<Guide>, a: Point, b: Point) {
let d = (b - a).hypot(); let d = (b - a).hypot();
if d > EPS && d.is_finite() { if d > EPS && d.is_finite() {
out.push(Guide { label: Some(label(d)), ..Guide::new(a, b) }); out.push(Guide { label: Some(label(d)), ..Guide::new(a, b) });
+3
View File
@@ -144,6 +144,9 @@ pub fn node_json(doc: &Document, id: NodeId, depth: usize) -> Value {
if !n.code_links.is_empty() { if !n.code_links.is_empty() {
o.insert("codeLinks".into(), camel(serde_json::to_value(&n.code_links).unwrap_or(Value::Null))); o.insert("codeLinks".into(), camel(serde_json::to_value(&n.code_links).unwrap_or(Value::Null)));
} }
if !n.measurements.is_empty() {
o.insert("measurements".into(), serde_json::to_value(&n.measurements).unwrap_or(Value::Null));
}
let dev = crate::dev_resources::list_json(doc, n.id); let dev = crate::dev_resources::list_json(doc, n.id);
if dev.as_array().is_some_and(|a| !a.is_empty()) { if dev.as_array().is_some_and(|a| !a.is_empty()) {
o.insert("devResources".into(), dev); o.insert("devResources".into(), dev);
+3
View File
@@ -23,6 +23,7 @@ pub mod codegen;
pub mod collab; pub mod collab;
pub mod component_codegen; pub mod component_codegen;
pub mod components; pub mod components;
pub mod dev_measurements;
pub mod dev_resources; pub mod dev_resources;
pub mod devcompare; pub mod devcompare;
pub mod devinspect; pub mod devinspect;
@@ -1657,6 +1658,8 @@ mod dark_page_text_tests;
#[cfg(test)] #[cfg(test)]
mod design_paste_tests; mod design_paste_tests;
#[cfg(test)] #[cfg(test)]
mod dev_measurements_tests;
#[cfg(test)]
mod dev_resources_tests; mod dev_resources_tests;
#[cfg(test)] #[cfg(test)]
mod devcompare_tests; mod devcompare_tests;
+4
View File
@@ -395,6 +395,10 @@ fn every_command_survives_junk_params() {
// Links (dev resources) and indexes as junk. // Links (dev resources) and indexes as junk.
json!({"id": 2, "url": "javascript:alert(1)", "index": 1e308, "name": {"x": 1}}), json!({"id": 2, "url": "javascript:alert(1)", "index": 1e308, "name": {"x": 1}}),
json!({"id": 2, "url": "https://\u{0}x", "index": -1, "name": ""}), json!({"id": 2, "url": "https://\u{0}x", "index": -1, "name": ""}),
// Dev Mode measurements: layers, sides, pages and indexes as junk.
json!({"from": "x", "to": -1, "side": [1], "index": 1e308, "page": {"x": 1}}),
json!({"from": 2, "to": 2, "side": "middle", "index": -1, "page": 2, "id": "nope"}),
json!({"from": 2, "to": 1, "side": "auto"}),
// Widgets: plugins, targets and fuel as junk. // Widgets: plugins, targets and fuel as junk.
json!({"id": 1, "plugin": {"x": 1}, "target": [1], "fuel": -1e308, "x": 1e308}), json!({"id": 1, "plugin": {"x": 1}, "target": [1], "fuel": -1e308, "x": 1e308}),
// Plugin directories: ids, versions, servers and confirmations as junk. // Plugin directories: ids, versions, servers and confirmations as junk.
+7 -2
View File
@@ -1134,8 +1134,13 @@ impl<'d> Exporter<'d> {
if !n.guides.is_empty() { if !n.guides.is_empty() {
self.warn("ruler guides were left out"); self.warn("ruler guides were left out");
} }
if !n.annotations.is_empty() || !n.dev_resources.is_empty() || !n.code_links.is_empty() || !n.dev_status.is_none() { if !n.annotations.is_empty()
self.warn("Dev Mode annotations, statuses, resources and code links were left out"); || !n.dev_resources.is_empty()
|| !n.code_links.is_empty()
|| !n.measurements.is_empty()
|| !n.dev_status.is_none()
{
self.warn("Dev Mode annotations, statuses, resources, measurements and code links were left out");
} }
if !n.plugin_data.is_empty() { if !n.plugin_data.is_empty() {
self.warn("plugin data was left out"); self.warn("plugin data was left out");
+13
View File
@@ -55,6 +55,7 @@ status = "draft — needs native review"
"Add library file…" = "লাইব্রেরি ফাইল যোগ করুন..." "Add library file…" = "লাইব্রেরি ফাইল যোগ করুন..."
"Add max height" = "সর্বোচ্চ উচ্চতা যোগ করুন" "Add max height" = "সর্বোচ্চ উচ্চতা যোগ করুন"
"Add max width" = "সর্বোচ্চ প্রস্থ যোগ করুন" "Add max width" = "সর্বোচ্চ প্রস্থ যোগ করুন"
"Add measurement" = "মাপ যোগ করুন"
"Add min height" = "সর্বনিম্ন উচ্চতা যোগ করুন" "Add min height" = "সর্বনিম্ন উচ্চতা যোগ করুন"
"Add min width" = "সর্বনিম্ন প্রস্থ যোগ করুন" "Add min width" = "সর্বনিম্ন প্রস্থ যোগ করুন"
"Add mode" = "মোড যোগ করুন" "Add mode" = "মোড যোগ করুন"
@@ -94,6 +95,10 @@ status = "draft — needs native review"
"All supported properties" = "সমর্থিত সব প্রপার্টি" "All supported properties" = "সমর্থিত সব প্রপার্টি"
"All the fonts this file uses are installed" = "এই ফাইলে ব্যবহৃত সব ফন্ট ইনস্টল করা আছে" "All the fonts this file uses are installed" = "এই ফাইলে ব্যবহৃত সব ফন্ট ইনস্টল করা আছে"
"All variables" = "সব ভেরিয়েবল" "All variables" = "সব ভেরিয়েবল"
"Along the bottom" = "নিচের কিনারা বরাবর"
"Along the left" = "বাম কিনারা বরাবর"
"Along the right" = "ডান কিনারা বরাবর"
"Along the top" = "উপরের কিনারা বরাবর"
"Alpha" = "আলফা" "Alpha" = "আলফা"
"Alphabetical" = "বর্ণানুক্রমিক" "Alphabetical" = "বর্ণানুক্রমিক"
"Alt+drag" = "Alt+ড্র্যাগ" "Alt+drag" = "Alt+ড্র্যাগ"
@@ -158,6 +163,7 @@ status = "draft — needs native review"
"Basics" = "মৌলিক বিষয়" "Basics" = "মৌলিক বিষয়"
"Before" = "আগে" "Before" = "আগে"
"Bend" = "বাঁক" "Bend" = "বাঁক"
"Between the selected layers" = "নির্বাচিত লেয়ারগুলোর মাঝে"
"Bevel" = "বেভেল" "Bevel" = "বেভেল"
"Binary assets added" = "বাইনারি অ্যাসেট যোগ করা হয়েছে" "Binary assets added" = "বাইনারি অ্যাসেট যোগ করা হয়েছে"
"Binary assets removed" = "বাইনারি অ্যাসেট সরানো হয়েছে" "Binary assets removed" = "বাইনারি অ্যাসেট সরানো হয়েছে"
@@ -683,6 +689,7 @@ status = "draft — needs native review"
"Jun" = "জুন" "Jun" = "জুন"
"Just now" = "এইমাত্র" "Just now" = "এইমাত্র"
"Justify" = "উভয়মুখী সারিবদ্ধকরণ (Justify)" "Justify" = "উভয়মুখী সারিবদ্ধকরণ (Justify)"
"Keep a measurement on the canvas for developers" = "ডেভেলপারদের জন্য ক্যানভাসে একটি মাপ রাখুন"
"Keep appearance" = "চেহারা অপরিবর্তিত রাখুন" "Keep appearance" = "চেহারা অপরিবর্তিত রাখুন"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "চেহারা অপরিবর্তিত রাখলে প্রতিটি রঙের মান {profile}-এ রূপান্তরিত হয় যাতে রঙগুলো একই দেখায়। মান অপরিবর্তিত রাখলে সংখ্যাগুলো যেমন আছে তেমনই থাকে, ফলে রঙ পরিবর্তিত হয়।" "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "চেহারা অপরিবর্তিত রাখলে প্রতিটি রঙের মান {profile}-এ রূপান্তরিত হয় যাতে রঙগুলো একই দেখায়। মান অপরিবর্তিত রাখলে সংখ্যাগুলো যেমন আছে তেমনই থাকে, ফলে রঙ পরিবর্তিত হয়।"
"Keep tool selected after use" = "ব্যবহারের পরেও টুল নির্বাচিত রাখুন" "Keep tool selected after use" = "ব্যবহারের পরেও টুল নির্বাচিত রাখুন"
@@ -772,6 +779,7 @@ status = "draft — needs native review"
"Maximize" = "বড় করুন" "Maximize" = "বড় করুন"
"May" = "মে" "May" = "মে"
"Measure to the selection" = "নির্বাচন পর্যন্ত পরিমাপ করুন" "Measure to the selection" = "নির্বাচন পর্যন্ত পরিমাপ করুন"
"Measurements" = "মাপ"
"Merge into main" = "প্রধান ফাইলের সাথে মার্জ করুন" "Merge into main" = "প্রধান ফাইলের সাথে মার্জ করুন"
"Merge regions" = "অঞ্চলগুলো মার্জ করুন" "Merge regions" = "অঞ্চলগুলো মার্জ করুন"
"Merge the branch into the main file (one undo step) and archive it" = "ব্রাঞ্চটিকে প্রধান ফাইলের সাথে মার্জ করুন (একটি আনডু ধাপ) এবং আর্কাইভ করুন" "Merge the branch into the main file (one undo step) and archive it" = "ব্রাঞ্চটিকে প্রধান ফাইলের সাথে মার্জ করুন (একটি আনডু ধাপ) এবং আর্কাইভ করুন"
@@ -1087,6 +1095,7 @@ status = "draft — needs native review"
"Remove guide" = "গাইড সরান" "Remove guide" = "গাইড সরান"
"Remove interaction" = "ইন্টারঅ্যাকশন সরান" "Remove interaction" = "ইন্টারঅ্যাকশন সরান"
"Remove library from this file" = "এই ফাইল থেকে লাইব্রেরি সরান" "Remove library from this file" = "এই ফাইল থেকে লাইব্রেরি সরান"
"Remove measurement" = "মাপ সরান"
"Remove min and max" = "সর্বনিম্ন এবং সর্বোচ্চ সরান" "Remove min and max" = "সর্বনিম্ন এবং সর্বোচ্চ সরান"
"Remove min and max height" = "সর্বনিম্ন এবং সর্বোচ্চ উচ্চতা সরান" "Remove min and max height" = "সর্বনিম্ন এবং সর্বোচ্চ উচ্চতা সরান"
"Remove min and max width" = "সর্বনিম্ন এবং সর্বোচ্চ প্রস্থ সরান" "Remove min and max width" = "সর্বনিম্ন এবং সর্বোচ্চ প্রস্থ সরান"
@@ -1420,6 +1429,7 @@ status = "draft — needs native review"
"Title" = "শিরোনাম" "Title" = "শিরোনাম"
"Title case" = "টাইটেল কেস" "Title case" = "টাইটেল কেস"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "আপনার টিমের সাথে এই লাইব্রেরি শেয়ার করতে, ফাইলটি একটি সার্ভারে প্রকাশ করুন (Share ▸ Publish to server…), এরপর সেখানে লাইব্রেরি প্রকাশ করুন।" "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "আপনার টিমের সাথে এই লাইব্রেরি শেয়ার করতে, ফাইলটি একটি সার্ভারে প্রকাশ করুন (Share ▸ Publish to server…), এরপর সেখানে লাইব্রেরি প্রকাশ করুন।"
"To the layer around it" = "চারপাশের লেয়ার পর্যন্ত"
"Toggle" = "টগল" "Toggle" = "টগল"
"Toggle light/dark theme" = "লাইট/ডার্ক থিম টগল করুন" "Toggle light/dark theme" = "লাইট/ডার্ক থিম টগল করুন"
"Toggle visibility" = "দৃশ্যমানতা টগল করুন" "Toggle visibility" = "দৃশ্যমানতা টগল করুন"
@@ -1776,6 +1786,9 @@ status = "draft — needs native review"
"board.shape" = "লেখাসহ আকৃতি" "board.shape" = "লেখাসহ আকৃতি"
"board.stamp" = "স্ট্যাম্প" "board.stamp" = "স্ট্যাম্প"
"board.sticky" = "স্টিকি নোট" "board.sticky" = "স্টিকি নোট"
"devMode.addMeasurement" = "মাপ যোগ করুন"
"devMode.measurements" = "মাপের তালিকা"
"devMode.removeMeasurement" = "মাপ সরান"
"file.newBoard" = "নতুন বোর্ড" "file.newBoard" = "নতুন বোর্ড"
"tool.connector" = "সংযোগকারী" "tool.connector" = "সংযোগকারী"
"tool.eraser" = "ইরেজার" "tool.eraser" = "ইরেজার"
+13
View File
@@ -51,6 +51,7 @@ status = "draft — needs native review"
"Add library file…" = "Přidat soubor knihovny…" "Add library file…" = "Přidat soubor knihovny…"
"Add max height" = "Přidat maximální výšku" "Add max height" = "Přidat maximální výšku"
"Add max width" = "Přidat maximální šířku" "Add max width" = "Přidat maximální šířku"
"Add measurement" = "Přidat měření"
"Add min height" = "Přidat minimální výšku" "Add min height" = "Přidat minimální výšku"
"Add min width" = "Přidat minimální šířku" "Add min width" = "Přidat minimální šířku"
"Add mode" = "Přidat režim" "Add mode" = "Přidat režim"
@@ -88,6 +89,10 @@ status = "draft — needs native review"
"All fonts" = "Všechna písma" "All fonts" = "Všechna písma"
"All supported properties" = "Všechny podporované vlastnosti" "All supported properties" = "Všechny podporované vlastnosti"
"All variables" = "Všechny proměnné" "All variables" = "Všechny proměnné"
"Along the bottom" = "Podél dolní hrany"
"Along the left" = "Podél levé hrany"
"Along the right" = "Podél pravé hrany"
"Along the top" = "Podél horní hrany"
"Alpha" = "Alfa" "Alpha" = "Alfa"
"Alphabetical" = "Abecedně" "Alphabetical" = "Abecedně"
"Alt+drag" = "Alt+tažení" "Alt+drag" = "Alt+tažení"
@@ -152,6 +157,7 @@ status = "draft — needs native review"
"Basics" = "Základy" "Basics" = "Základy"
"Before" = "Před" "Before" = "Před"
"Bend" = "Ohnout" "Bend" = "Ohnout"
"Between the selected layers" = "Mezi vybranými vrstvami"
"Bevel" = "Zkosený" "Bevel" = "Zkosený"
"Binary assets added" = "Přidané binární soubory" "Binary assets added" = "Přidané binární soubory"
"Binary assets removed" = "Odstraněné binární soubory" "Binary assets removed" = "Odstraněné binární soubory"
@@ -668,6 +674,7 @@ status = "draft — needs native review"
"Jun" = "června" "Jun" = "června"
"Just now" = "Právě teď" "Just now" = "Právě teď"
"Justify" = "Do bloku" "Justify" = "Do bloku"
"Keep a measurement on the canvas for developers" = "Ponechat měření na plátně pro vývojáře"
"Keep appearance" = "Zachovat vzhled" "Keep appearance" = "Zachovat vzhled"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Zachovat vzhled převede všechny hodnoty barev do profilu {profile}, aby barvy vypadaly stejně. Zachovat hodnoty ponechá čísla beze změny, takže se barvy posunou." "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Zachovat vzhled převede všechny hodnoty barev do profilu {profile}, aby barvy vypadaly stejně. Zachovat hodnoty ponechá čísla beze změny, takže se barvy posunou."
"Keep tool selected after use" = "Po použití ponechat nástroj vybraný" "Keep tool selected after use" = "Po použití ponechat nástroj vybraný"
@@ -757,6 +764,7 @@ status = "draft — needs native review"
"Maximize" = "Maximalizovat" "Maximize" = "Maximalizovat"
"May" = "května" "May" = "května"
"Measure to the selection" = "Měřit k výběru" "Measure to the selection" = "Měřit k výběru"
"Measurements" = "Měření"
"Merge into main" = "Sloučit do hlavního souboru" "Merge into main" = "Sloučit do hlavního souboru"
"Merge regions" = "Sloučit oblasti" "Merge regions" = "Sloučit oblasti"
"Merge the branch into the main file (one undo step) and archive it" = "Sloučit větev do hlavního souboru (jeden krok zpět) a archivovat ji" "Merge the branch into the main file (one undo step) and archive it" = "Sloučit větev do hlavního souboru (jeden krok zpět) a archivovat ji"
@@ -1066,6 +1074,7 @@ status = "draft — needs native review"
"Remove guide" = "Odebrat vodítko" "Remove guide" = "Odebrat vodítko"
"Remove interaction" = "Odebrat interakci" "Remove interaction" = "Odebrat interakci"
"Remove library from this file" = "Odebrat knihovnu z tohoto souboru" "Remove library from this file" = "Odebrat knihovnu z tohoto souboru"
"Remove measurement" = "Odebrat měření"
"Remove min and max" = "Odebrat minimum a maximum" "Remove min and max" = "Odebrat minimum a maximum"
"Remove min and max height" = "Odebrat minimální a maximální výšku" "Remove min and max height" = "Odebrat minimální a maximální výšku"
"Remove min and max width" = "Odebrat minimální a maximální šířku" "Remove min and max width" = "Odebrat minimální a maximální šířku"
@@ -1391,6 +1400,7 @@ status = "draft — needs native review"
"Title" = "Název" "Title" = "Název"
"Title case" = "Velká počáteční písmena slov" "Title case" = "Velká počáteční písmena slov"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Chcete-li sdílet tuto knihovnu s týmem, publikujte soubor na server (Sdílet ▸ Publikovat na server…) a pak tam publikujte knihovnu." "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Chcete-li sdílet tuto knihovnu s týmem, publikujte soubor na server (Sdílet ▸ Publikovat na server…) a pak tam publikujte knihovnu."
"To the layer around it" = "K vrstvě, která ji obsahuje"
"Toggle" = "Přepnout" "Toggle" = "Přepnout"
"Toggle light/dark theme" = "Přepnout světlý/tmavý motiv" "Toggle light/dark theme" = "Přepnout světlý/tmavý motiv"
"Toggle visibility" = "Přepnout viditelnost" "Toggle visibility" = "Přepnout viditelnost"
@@ -1840,6 +1850,9 @@ status = "draft — needs native review"
"dev.focus" = "Zaměřený pohled" "dev.focus" = "Zaměřený pohled"
"dev.resolveChange" = "Vyřešit změnu" "dev.resolveChange" = "Vyřešit změnu"
"dev.unfocus" = "Opustit zaměřený pohled" "dev.unfocus" = "Opustit zaměřený pohled"
"devMode.addMeasurement" = "Přidat měření"
"devMode.measurements" = "Vypsat měření"
"devMode.removeMeasurement" = "Odebrat měření"
"devResource.add" = "Přidat zdroj pro vývojáře" "devResource.add" = "Přidat zdroj pro vývojáře"
"devResource.edit" = "Upravit zdroj pro vývojáře" "devResource.edit" = "Upravit zdroj pro vývojáře"
"devResource.list" = "Vypsat zdroje pro vývojáře" "devResource.list" = "Vypsat zdroje pro vývojáře"
+13
View File
@@ -52,6 +52,7 @@ status = "draft — needs native review"
"Add library file…" = "Bibliotheksdatei hinzufügen…" "Add library file…" = "Bibliotheksdatei hinzufügen…"
"Add max height" = "Max. Höhe hinzufügen" "Add max height" = "Max. Höhe hinzufügen"
"Add max width" = "Max. Breite hinzufügen" "Add max width" = "Max. Breite hinzufügen"
"Add measurement" = "Messung hinzufügen"
"Add min height" = "Min. Höhe hinzufügen" "Add min height" = "Min. Höhe hinzufügen"
"Add min width" = "Min. Breite hinzufügen" "Add min width" = "Min. Breite hinzufügen"
"Add mode" = "Modus hinzufügen" "Add mode" = "Modus hinzufügen"
@@ -90,6 +91,10 @@ status = "draft — needs native review"
"All fonts" = "Alle Schriften" "All fonts" = "Alle Schriften"
"All supported properties" = "Alle unterstützten Eigenschaften" "All supported properties" = "Alle unterstützten Eigenschaften"
"All variables" = "Alle Variablen" "All variables" = "Alle Variablen"
"Along the bottom" = "Entlang der Unterkante"
"Along the left" = "Entlang der linken Kante"
"Along the right" = "Entlang der rechten Kante"
"Along the top" = "Entlang der Oberkante"
"Alpha" = "Alpha" "Alpha" = "Alpha"
"Alt+drag" = "Alt+Ziehen" "Alt+drag" = "Alt+Ziehen"
"An invite link that opens FigCraft and joins" = "Ein Einladungslink, der FigCraft öffnet und beitritt" "An invite link that opens FigCraft and joins" = "Ein Einladungslink, der FigCraft öffnet und beitritt"
@@ -149,6 +154,7 @@ status = "draft — needs native review"
"Basics" = "Grundlagen" "Basics" = "Grundlagen"
"Before" = "Vorher" "Before" = "Vorher"
"Bend" = "Biegen" "Bend" = "Biegen"
"Between the selected layers" = "Zwischen den ausgewählten Ebenen"
"Bevel" = "Abgeflacht" "Bevel" = "Abgeflacht"
"Binary assets added" = "Binärdateien hinzugefügt" "Binary assets added" = "Binärdateien hinzugefügt"
"Binary assets removed" = "Binärdateien entfernt" "Binary assets removed" = "Binärdateien entfernt"
@@ -652,6 +658,7 @@ status = "draft — needs native review"
"Join|stroke" = "Ecke" "Join|stroke" = "Ecke"
"Just now" = "Gerade eben" "Just now" = "Gerade eben"
"Justify" = "Blocksatz" "Justify" = "Blocksatz"
"Keep a measurement on the canvas for developers" = "Eine Messung für Entwickler auf der Arbeitsfläche behalten"
"Keep appearance" = "Aussehen beibehalten" "Keep appearance" = "Aussehen beibehalten"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "„Aussehen beibehalten“ wandelt jeden Farbwert in {profile} um, damit die Farben gleich aussehen. „Werte beibehalten“ lässt die Zahlen unverändert, sodass sich die Farben verschieben." "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "„Aussehen beibehalten“ wandelt jeden Farbwert in {profile} um, damit die Farben gleich aussehen. „Werte beibehalten“ lässt die Zahlen unverändert, sodass sich die Farben verschieben."
"Keep tool selected after use" = "Werkzeug nach Verwendung ausgewählt lassen" "Keep tool selected after use" = "Werkzeug nach Verwendung ausgewählt lassen"
@@ -734,6 +741,7 @@ status = "draft — needs native review"
"Max lines" = "Max. Zeilen" "Max lines" = "Max. Zeilen"
"Maximize" = "Maximieren" "Maximize" = "Maximieren"
"Measure to the selection" = "Bis zur Auswahl messen" "Measure to the selection" = "Bis zur Auswahl messen"
"Measurements" = "Messungen"
"Merge into main" = "In Hauptdatei zusammenführen" "Merge into main" = "In Hauptdatei zusammenführen"
"Merge regions" = "Bereiche vereinen" "Merge regions" = "Bereiche vereinen"
"Merge the branch into the main file (one undo step) and archive it" = "Führt den Branch in die Hauptdatei zusammen (ein Rückgängig-Schritt) und archiviert ihn" "Merge the branch into the main file (one undo step) and archive it" = "Führt den Branch in die Hauptdatei zusammen (ein Rückgängig-Schritt) und archiviert ihn"
@@ -1034,6 +1042,7 @@ status = "draft — needs native review"
"Remove guide" = "Hilfslinie entfernen" "Remove guide" = "Hilfslinie entfernen"
"Remove interaction" = "Interaktion entfernen" "Remove interaction" = "Interaktion entfernen"
"Remove library from this file" = "Bibliothek aus dieser Datei entfernen" "Remove library from this file" = "Bibliothek aus dieser Datei entfernen"
"Remove measurement" = "Messung entfernen"
"Remove min and max" = "Min. und Max. entfernen" "Remove min and max" = "Min. und Max. entfernen"
"Remove min and max height" = "Min. und max. Höhe entfernen" "Remove min and max height" = "Min. und max. Höhe entfernen"
"Remove min and max width" = "Min. und max. Breite entfernen" "Remove min and max width" = "Min. und max. Breite entfernen"
@@ -1352,6 +1361,7 @@ status = "draft — needs native review"
"Title" = "Titel" "Title" = "Titel"
"Title case" = "Großschreibung am Wortanfang" "Title case" = "Großschreibung am Wortanfang"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Um diese Bibliothek mit Ihrem Team zu teilen, veröffentlichen Sie die Datei auf einem Server (Teilen ▸ Auf Server veröffentlichen…) und veröffentlichen Sie dort dann die Bibliothek." "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Um diese Bibliothek mit Ihrem Team zu teilen, veröffentlichen Sie die Datei auf einem Server (Teilen ▸ Auf Server veröffentlichen…) und veröffentlichen Sie dort dann die Bibliothek."
"To the layer around it" = "Bis zur umgebenden Ebene"
"Toggle" = "Umschalten" "Toggle" = "Umschalten"
"Toggle light/dark theme" = "Zwischen hellem und dunklem Design wechseln" "Toggle light/dark theme" = "Zwischen hellem und dunklem Design wechseln"
"Toggle visibility" = "Sichtbarkeit umschalten" "Toggle visibility" = "Sichtbarkeit umschalten"
@@ -1795,6 +1805,9 @@ status = "draft — needs native review"
"dev.focus" = "Fokusansicht" "dev.focus" = "Fokusansicht"
"dev.resolveChange" = "Änderung erledigen" "dev.resolveChange" = "Änderung erledigen"
"dev.unfocus" = "Fokusansicht verlassen" "dev.unfocus" = "Fokusansicht verlassen"
"devMode.addMeasurement" = "Messung hinzufügen"
"devMode.measurements" = "Messungen auflisten"
"devMode.removeMeasurement" = "Messung entfernen"
"devResource.add" = "Entwicklerressource hinzufügen" "devResource.add" = "Entwicklerressource hinzufügen"
"devResource.edit" = "Entwicklerressource bearbeiten" "devResource.edit" = "Entwicklerressource bearbeiten"
"devResource.list" = "Entwicklerressourcen auflisten" "devResource.list" = "Entwicklerressourcen auflisten"
+10
View File
@@ -54,6 +54,7 @@ name = "English"
"Add library file…" = "Add library file…" "Add library file…" = "Add library file…"
"Add max height" = "Add max height" "Add max height" = "Add max height"
"Add max width" = "Add max width" "Add max width" = "Add max width"
"Add measurement" = "Add measurement"
"Add min height" = "Add min height" "Add min height" = "Add min height"
"Add min width" = "Add min width" "Add min width" = "Add min width"
"Add mode" = "Add mode" "Add mode" = "Add mode"
@@ -93,6 +94,10 @@ name = "English"
"All supported properties" = "All supported properties" "All supported properties" = "All supported properties"
"All the fonts this file uses are installed" = "All the fonts this file uses are installed" "All the fonts this file uses are installed" = "All the fonts this file uses are installed"
"All variables" = "All variables" "All variables" = "All variables"
"Along the bottom" = "Along the bottom"
"Along the left" = "Along the left"
"Along the right" = "Along the right"
"Along the top" = "Along the top"
"Alpha" = "Alpha" "Alpha" = "Alpha"
"Alphabetical" = "Alphabetical" "Alphabetical" = "Alphabetical"
"Alt+drag" = "Alt+drag" "Alt+drag" = "Alt+drag"
@@ -157,6 +162,7 @@ name = "English"
"Basics" = "Basics" "Basics" = "Basics"
"Before" = "Before" "Before" = "Before"
"Bend" = "Bend" "Bend" = "Bend"
"Between the selected layers" = "Between the selected layers"
"Bevel" = "Bevel" "Bevel" = "Bevel"
"Binary assets added" = "Binary assets added" "Binary assets added" = "Binary assets added"
"Binary assets removed" = "Binary assets removed" "Binary assets removed" = "Binary assets removed"
@@ -688,6 +694,7 @@ name = "English"
"Jun" = "Jun" "Jun" = "Jun"
"Just now" = "Just now" "Just now" = "Just now"
"Justify" = "Justify" "Justify" = "Justify"
"Keep a measurement on the canvas for developers" = "Keep a measurement on the canvas for developers"
"Keep appearance" = "Keep appearance" "Keep appearance" = "Keep appearance"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift."
"Keep tool selected after use" = "Keep tool selected after use" "Keep tool selected after use" = "Keep tool selected after use"
@@ -778,6 +785,7 @@ name = "English"
"Maximize" = "Maximize" "Maximize" = "Maximize"
"May" = "May" "May" = "May"
"Measure to the selection" = "Measure to the selection" "Measure to the selection" = "Measure to the selection"
"Measurements" = "Measurements"
"Merge into main" = "Merge into main" "Merge into main" = "Merge into main"
"Merge regions" = "Merge regions" "Merge regions" = "Merge regions"
"Merge the branch into the main file (one undo step) and archive it" = "Merge the branch into the main file (one undo step) and archive it" "Merge the branch into the main file (one undo step) and archive it" = "Merge the branch into the main file (one undo step) and archive it"
@@ -1097,6 +1105,7 @@ name = "English"
"Remove guide" = "Remove guide" "Remove guide" = "Remove guide"
"Remove interaction" = "Remove interaction" "Remove interaction" = "Remove interaction"
"Remove library from this file" = "Remove library from this file" "Remove library from this file" = "Remove library from this file"
"Remove measurement" = "Remove measurement"
"Remove min and max" = "Remove min and max" "Remove min and max" = "Remove min and max"
"Remove min and max height" = "Remove min and max height" "Remove min and max height" = "Remove min and max height"
"Remove min and max width" = "Remove min and max width" "Remove min and max width" = "Remove min and max width"
@@ -1439,6 +1448,7 @@ name = "English"
"Title" = "Title" "Title" = "Title"
"Title case" = "Title case" "Title case" = "Title case"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there."
"To the layer around it" = "To the layer around it"
"Toggle" = "Toggle" "Toggle" = "Toggle"
"Toggle light/dark theme" = "Toggle light/dark theme" "Toggle light/dark theme" = "Toggle light/dark theme"
"Toggle visibility" = "Toggle visibility" "Toggle visibility" = "Toggle visibility"
+2
View File
@@ -35,6 +35,7 @@ status = "draft — needs native review"
"Add library file…" = "Agregar archivo de biblioteca…" "Add library file…" = "Agregar archivo de biblioteca…"
"Add max height" = "Agregar alto máximo" "Add max height" = "Agregar alto máximo"
"Add max width" = "Agregar ancho máximo" "Add max width" = "Agregar ancho máximo"
"Add measurement" = "Agregar medición"
"Add min height" = "Agregar alto mínimo" "Add min height" = "Agregar alto mínimo"
"Add min width" = "Agregar ancho mínimo" "Add min width" = "Agregar ancho mínimo"
"Add mode" = "Agregar modo" "Add mode" = "Agregar modo"
@@ -95,6 +96,7 @@ status = "draft — needs native review"
"comment.add" = "Agregar comentario" "comment.add" = "Agregar comentario"
"component.addProperty" = "Agregar propiedad de componente" "component.addProperty" = "Agregar propiedad de componente"
"component.addVariant" = "Agregar variante" "component.addVariant" = "Agregar variante"
"devMode.addMeasurement" = "Agregar medición"
"devResource.add" = "Agregar recurso de desarrollo" "devResource.add" = "Agregar recurso de desarrollo"
"effect.add" = "Agregar efecto" "effect.add" = "Agregar efecto"
"export.add" = "Agregar configuración de exportación" "export.add" = "Agregar configuración de exportación"
+13
View File
@@ -8,7 +8,13 @@ name = "Español"
status = "draft — needs native review" status = "draft — needs native review"
[ui] [ui]
"Add measurement" = "Añadir medición"
"Along the bottom" = "A lo largo del borde inferior"
"Along the left" = "A lo largo del borde izquierdo"
"Along the right" = "A lo largo del borde derecho"
"Along the top" = "A lo largo del borde superior"
"Arrow at the end" = "Flecha al final" "Arrow at the end" = "Flecha al final"
"Between the selected layers" = "Entre las capas seleccionadas"
"Check" = "Visto" "Check" = "Visto"
"Chevron" = "Cheurón" "Chevron" = "Cheurón"
"Compatibility notes" = "Notas de compatibilidad" "Compatibility notes" = "Notas de compatibilidad"
@@ -689,6 +695,7 @@ status = "draft — needs native review"
"Jun" = "jun" "Jun" = "jun"
"Just now" = "Justo ahora" "Just now" = "Justo ahora"
"Justify" = "Justificar" "Justify" = "Justificar"
"Keep a measurement on the canvas for developers" = "Mantén una medición en el lienzo para desarrolladores"
"Keep appearance" = "Mantener apariencia" "Keep appearance" = "Mantener apariencia"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Mantener apariencia convierte cada valor de color a {profile} para que los colores se vean igual. Mantener valores deja los números como están, así que los colores cambian." "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Mantener apariencia convierte cada valor de color a {profile} para que los colores se vean igual. Mantener valores deja los números como están, así que los colores cambian."
"Keep tool selected after use" = "Mantener la herramienta tras usarla" "Keep tool selected after use" = "Mantener la herramienta tras usarla"
@@ -779,6 +786,7 @@ status = "draft — needs native review"
"Maximize" = "Maximizar" "Maximize" = "Maximizar"
"May" = "may" "May" = "may"
"Measure to the selection" = "Medir hasta la selección" "Measure to the selection" = "Medir hasta la selección"
"Measurements" = "Mediciones"
"Merge into main" = "Fusionar en la principal" "Merge into main" = "Fusionar en la principal"
"Merge regions" = "Fusionar regiones" "Merge regions" = "Fusionar regiones"
"Merge the branch into the main file (one undo step) and archive it" = "Fusiona la rama en el archivo principal (un paso de deshacer) y la archiva" "Merge the branch into the main file (one undo step) and archive it" = "Fusiona la rama en el archivo principal (un paso de deshacer) y la archiva"
@@ -1098,6 +1106,7 @@ status = "draft — needs native review"
"Remove guide" = "Quitar guía" "Remove guide" = "Quitar guía"
"Remove interaction" = "Quitar interacción" "Remove interaction" = "Quitar interacción"
"Remove library from this file" = "Quitar biblioteca de este archivo" "Remove library from this file" = "Quitar biblioteca de este archivo"
"Remove measurement" = "Quitar medición"
"Remove min and max" = "Quitar mín. y máx." "Remove min and max" = "Quitar mín. y máx."
"Remove min and max height" = "Quitar alto mín. y máx." "Remove min and max height" = "Quitar alto mín. y máx."
"Remove min and max width" = "Quitar ancho mín. y máx." "Remove min and max width" = "Quitar ancho mín. y máx."
@@ -1440,6 +1449,7 @@ status = "draft — needs native review"
"Title" = "Título" "Title" = "Título"
"Title case" = "Tipo título" "Title case" = "Tipo título"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Para compartir esta biblioteca con tu equipo, publica el archivo en un servidor (Compartir ▸ Publicar en servidor…) y luego publica allí la biblioteca." "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Para compartir esta biblioteca con tu equipo, publica el archivo en un servidor (Compartir ▸ Publicar en servidor…) y luego publica allí la biblioteca."
"To the layer around it" = "Hasta la capa que la contiene"
"Toggle" = "Activar/desactivar" "Toggle" = "Activar/desactivar"
"Toggle light/dark theme" = "Cambiar tema claro/oscuro" "Toggle light/dark theme" = "Cambiar tema claro/oscuro"
"Toggle visibility" = "Mostrar/ocultar" "Toggle visibility" = "Mostrar/ocultar"
@@ -1904,6 +1914,9 @@ status = "draft — needs native review"
"dev.focus" = "Vista de enfoque" "dev.focus" = "Vista de enfoque"
"dev.resolveChange" = "Resolver cambio" "dev.resolveChange" = "Resolver cambio"
"dev.unfocus" = "Salir de la vista de enfoque" "dev.unfocus" = "Salir de la vista de enfoque"
"devMode.addMeasurement" = "Añadir medición"
"devMode.measurements" = "Listar mediciones"
"devMode.removeMeasurement" = "Quitar medición"
"devResource.add" = "Añadir recurso de desarrollo" "devResource.add" = "Añadir recurso de desarrollo"
"devResource.edit" = "Editar recurso de desarrollo" "devResource.edit" = "Editar recurso de desarrollo"
"devResource.list" = "Listar recursos de desarrollo" "devResource.list" = "Listar recursos de desarrollo"
+13
View File
@@ -56,6 +56,7 @@ status = "draft — needs native review"
"Add library file…" = "Lisää kirjastotiedosto…" "Add library file…" = "Lisää kirjastotiedosto…"
"Add max height" = "Lisää enimmäiskorkeus" "Add max height" = "Lisää enimmäiskorkeus"
"Add max width" = "Lisää enimmäisleveys" "Add max width" = "Lisää enimmäisleveys"
"Add measurement" = "Lisää mittaus"
"Add min height" = "Lisää vähimmäiskorkeus" "Add min height" = "Lisää vähimmäiskorkeus"
"Add min width" = "Lisää vähimmäisleveys" "Add min width" = "Lisää vähimmäisleveys"
"Add mode" = "Lisää tila" "Add mode" = "Lisää tila"
@@ -95,6 +96,10 @@ status = "draft — needs native review"
"All supported properties" = "Kaikki tuetut ominaisuudet" "All supported properties" = "Kaikki tuetut ominaisuudet"
"All the fonts this file uses are installed" = "Kaikki tämän tiedoston käyttämät fontit on asennettu" "All the fonts this file uses are installed" = "Kaikki tämän tiedoston käyttämät fontit on asennettu"
"All variables" = "Kaikki muuttujat" "All variables" = "Kaikki muuttujat"
"Along the bottom" = "Alareunaa pitkin"
"Along the left" = "Vasenta reunaa pitkin"
"Along the right" = "Oikeaa reunaa pitkin"
"Along the top" = "Yläreunaa pitkin"
"Alpha" = "Alfa" "Alpha" = "Alfa"
"Alphabetical" = "Aakkosjärjestys" "Alphabetical" = "Aakkosjärjestys"
"Alt+drag" = "Alt+drag" "Alt+drag" = "Alt+drag"
@@ -159,6 +164,7 @@ status = "draft — needs native review"
"Basics" = "Perusteet" "Basics" = "Perusteet"
"Before" = "Ennen" "Before" = "Ennen"
"Bend" = "Taivutus" "Bend" = "Taivutus"
"Between the selected layers" = "Valittujen tasojen välillä"
"Bevel" = "Viiste" "Bevel" = "Viiste"
"Binary assets added" = "Lisätyt binääriresurssit" "Binary assets added" = "Lisätyt binääriresurssit"
"Binary assets removed" = "Poistetut binääriresurssit" "Binary assets removed" = "Poistetut binääriresurssit"
@@ -685,6 +691,7 @@ status = "draft — needs native review"
"Jun" = "kesäk." "Jun" = "kesäk."
"Just now" = "Juuri nyt" "Just now" = "Juuri nyt"
"Justify" = "Tasaa molemmat reunat" "Justify" = "Tasaa molemmat reunat"
"Keep a measurement on the canvas for developers" = "Säilytä mittaus piirtoalueella kehittäjille"
"Keep appearance" = "Säilytä ulkoasu" "Keep appearance" = "Säilytä ulkoasu"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Säilytä ulkoasu muuntaa kaikki väriarvot profiiliin {profile}, jotta värit näyttävät samoilta. Säilytä arvot jättää lukuarvot ennalleen, jolloin värit muuttuvat." "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Säilytä ulkoasu muuntaa kaikki väriarvot profiiliin {profile}, jotta värit näyttävät samoilta. Säilytä arvot jättää lukuarvot ennalleen, jolloin värit muuttuvat."
"Keep tool selected after use" = "Pidä työkalu valittuna käytön jälkeen" "Keep tool selected after use" = "Pidä työkalu valittuna käytön jälkeen"
@@ -774,6 +781,7 @@ status = "draft — needs native review"
"Maximize" = "Suurenna" "Maximize" = "Suurenna"
"May" = "toukok." "May" = "toukok."
"Measure to the selection" = "Mittaa valintaan" "Measure to the selection" = "Mittaa valintaan"
"Measurements" = "Mittaukset"
"Merge into main" = "Yhdistä päähaaraan" "Merge into main" = "Yhdistä päähaaraan"
"Merge regions" = "Yhdistä alueet" "Merge regions" = "Yhdistä alueet"
"Merge the branch into the main file (one undo step) and archive it" = "Yhdistä haara päätiedostoon (yksi kumottava toiminto) ja arkistoi se" "Merge the branch into the main file (one undo step) and archive it" = "Yhdistä haara päätiedostoon (yksi kumottava toiminto) ja arkistoi se"
@@ -1091,6 +1099,7 @@ status = "draft — needs native review"
"Remove guide" = "Poista apuviiva" "Remove guide" = "Poista apuviiva"
"Remove interaction" = "Poista vuorovaikutus" "Remove interaction" = "Poista vuorovaikutus"
"Remove library from this file" = "Poista kirjasto tästä tiedostosta" "Remove library from this file" = "Poista kirjasto tästä tiedostosta"
"Remove measurement" = "Poista mittaus"
"Remove min and max" = "Poista vähimmäis- ja enimmäisrajat" "Remove min and max" = "Poista vähimmäis- ja enimmäisrajat"
"Remove min and max height" = "Poista korkeuden vähimmäis- ja enimmäisrajat" "Remove min and max height" = "Poista korkeuden vähimmäis- ja enimmäisrajat"
"Remove min and max width" = "Poista leveyden vähimmäis- ja enimmäisrajat" "Remove min and max width" = "Poista leveyden vähimmäis- ja enimmäisrajat"
@@ -1425,6 +1434,7 @@ status = "draft — needs native review"
"Title" = "Otsikko" "Title" = "Otsikko"
"Title case" = "Sanojen isot alkukirjaimet" "Title case" = "Sanojen isot alkukirjaimet"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Jaa tämä kirjasto tiimillesi julkaisemalla tiedosto palvelimelle (Jaa ▸ Julkaise palvelimelle…) ja julkaisemalla kirjasto siellä." "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Jaa tämä kirjasto tiimillesi julkaisemalla tiedosto palvelimelle (Jaa ▸ Julkaise palvelimelle…) ja julkaisemalla kirjasto siellä."
"To the layer around it" = "Ympäröivään tasoon"
"Toggle" = "Vaihda tila" "Toggle" = "Vaihda tila"
"Toggle light/dark theme" = "Vaihda vaalea/tumma teema" "Toggle light/dark theme" = "Vaihda vaalea/tumma teema"
"Toggle visibility" = "Vaihda näkyvyys" "Toggle visibility" = "Vaihda näkyvyys"
@@ -1883,6 +1893,9 @@ status = "draft — needs native review"
"dev.focus" = "Kohdistusnäkymä" "dev.focus" = "Kohdistusnäkymä"
"dev.resolveChange" = "Ratkaise muutos" "dev.resolveChange" = "Ratkaise muutos"
"dev.unfocus" = "Poistu kohdistusnäkymästä" "dev.unfocus" = "Poistu kohdistusnäkymästä"
"devMode.addMeasurement" = "Lisää mittaus"
"devMode.measurements" = "Luettele mittaukset"
"devMode.removeMeasurement" = "Poista mittaus"
"devResource.add" = "Lisää kehittäjäresurssi" "devResource.add" = "Lisää kehittäjäresurssi"
"devResource.edit" = "Muokkaa kehittäjäresurssia" "devResource.edit" = "Muokkaa kehittäjäresurssia"
"devResource.list" = "Luettele kehittäjäresurssit" "devResource.list" = "Luettele kehittäjäresurssit"
+13
View File
@@ -55,6 +55,7 @@ status = "draft — needs native review"
"Add library file…" = "Ajouter un fichier de bibliothèque…" "Add library file…" = "Ajouter un fichier de bibliothèque…"
"Add max height" = "Ajouter une hauteur max." "Add max height" = "Ajouter une hauteur max."
"Add max width" = "Ajouter une largeur max." "Add max width" = "Ajouter une largeur max."
"Add measurement" = "Ajouter une mesure"
"Add min height" = "Ajouter une hauteur min." "Add min height" = "Ajouter une hauteur min."
"Add min width" = "Ajouter une largeur min." "Add min width" = "Ajouter une largeur min."
"Add mode" = "Ajouter un mode" "Add mode" = "Ajouter un mode"
@@ -93,6 +94,10 @@ status = "draft — needs native review"
"All fonts" = "Toutes les polices" "All fonts" = "Toutes les polices"
"All supported properties" = "Toutes les propriétés prises en charge" "All supported properties" = "Toutes les propriétés prises en charge"
"All variables" = "Toutes les variables" "All variables" = "Toutes les variables"
"Along the bottom" = "Le long du bord inférieur"
"Along the left" = "Le long du bord gauche"
"Along the right" = "Le long du bord droit"
"Along the top" = "Le long du bord supérieur"
"Alpha" = "Alpha" "Alpha" = "Alpha"
"Alphabetical" = "Alphabétique" "Alphabetical" = "Alphabétique"
"Alt+drag" = "Alt+glisser" "Alt+drag" = "Alt+glisser"
@@ -157,6 +162,7 @@ status = "draft — needs native review"
"Basics" = "Essentiel" "Basics" = "Essentiel"
"Before" = "Avant" "Before" = "Avant"
"Bend" = "Courber" "Bend" = "Courber"
"Between the selected layers" = "Entre les calques sélectionnés"
"Bevel" = "Biseau" "Bevel" = "Biseau"
"Binary assets added" = "Ressources binaires ajoutées" "Binary assets added" = "Ressources binaires ajoutées"
"Binary assets removed" = "Ressources binaires supprimées" "Binary assets removed" = "Ressources binaires supprimées"
@@ -685,6 +691,7 @@ status = "draft — needs native review"
"Jun" = "juin" "Jun" = "juin"
"Just now" = "À l’instant" "Just now" = "À l’instant"
"Justify" = "Justifier" "Justify" = "Justifier"
"Keep a measurement on the canvas for developers" = "Garder une mesure sur le canevas pour les développeurs"
"Keep appearance" = "Conserver l’apparence" "Keep appearance" = "Conserver l’apparence"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Conserver l’apparence convertit chaque valeur de couleur en {profile} pour que les couleurs restent identiques. Conserver les valeurs laisse les nombres tels quels, si bien que les couleurs changent." "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Conserver l’apparence convertit chaque valeur de couleur en {profile} pour que les couleurs restent identiques. Conserver les valeurs laisse les nombres tels quels, si bien que les couleurs changent."
"Keep tool selected after use" = "Garder l’outil sélectionné après usage" "Keep tool selected after use" = "Garder l’outil sélectionné après usage"
@@ -774,6 +781,7 @@ status = "draft — needs native review"
"Maximize" = "Agrandir" "Maximize" = "Agrandir"
"May" = "mai" "May" = "mai"
"Measure to the selection" = "Mesurer jusqu’à la sélection" "Measure to the selection" = "Mesurer jusqu’à la sélection"
"Measurements" = "Mesures"
"Merge into main" = "Fusionner dans le principal" "Merge into main" = "Fusionner dans le principal"
"Merge regions" = "Fusionner les régions" "Merge regions" = "Fusionner les régions"
"Merge the branch into the main file (one undo step) and archive it" = "Fusionne la branche dans le fichier principal (une seule étape d’annulation) et l’archive" "Merge the branch into the main file (one undo step) and archive it" = "Fusionne la branche dans le fichier principal (une seule étape d’annulation) et l’archive"
@@ -1091,6 +1099,7 @@ status = "draft — needs native review"
"Remove guide" = "Retirer le repère" "Remove guide" = "Retirer le repère"
"Remove interaction" = "Retirer l’interaction" "Remove interaction" = "Retirer l’interaction"
"Remove library from this file" = "Retirer la bibliothèque de ce fichier" "Remove library from this file" = "Retirer la bibliothèque de ce fichier"
"Remove measurement" = "Retirer la mesure"
"Remove min and max" = "Retirer min. et max." "Remove min and max" = "Retirer min. et max."
"Remove min and max height" = "Retirer la hauteur min. et max." "Remove min and max height" = "Retirer la hauteur min. et max."
"Remove min and max width" = "Retirer la largeur min. et max." "Remove min and max width" = "Retirer la largeur min. et max."
@@ -1426,6 +1435,7 @@ status = "draft — needs native review"
"Title" = "Titre" "Title" = "Titre"
"Title case" = "Casse de titre" "Title case" = "Casse de titre"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Pour partager cette bibliothèque avec votre équipe, publiez le fichier sur un serveur (Partager ▸ Publier sur un serveur…), puis publiez-y la bibliothèque." "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Pour partager cette bibliothèque avec votre équipe, publiez le fichier sur un serveur (Partager ▸ Publier sur un serveur…), puis publiez-y la bibliothèque."
"To the layer around it" = "Jusqu’au calque qui le contient"
"Toggle" = "Activer/désactiver" "Toggle" = "Activer/désactiver"
"Toggle light/dark theme" = "Basculer entre thème clair et sombre" "Toggle light/dark theme" = "Basculer entre thème clair et sombre"
"Toggle visibility" = "Afficher/masquer" "Toggle visibility" = "Afficher/masquer"
@@ -1884,6 +1894,9 @@ status = "draft — needs native review"
"dev.focus" = "Vue ciblée" "dev.focus" = "Vue ciblée"
"dev.resolveChange" = "Résoudre la modification" "dev.resolveChange" = "Résoudre la modification"
"dev.unfocus" = "Quitter la vue ciblée" "dev.unfocus" = "Quitter la vue ciblée"
"devMode.addMeasurement" = "Ajouter une mesure"
"devMode.measurements" = "Lister les mesures"
"devMode.removeMeasurement" = "Retirer la mesure"
"devResource.add" = "Ajouter une ressource de développement" "devResource.add" = "Ajouter une ressource de développement"
"devResource.edit" = "Modifier la ressource de développement" "devResource.edit" = "Modifier la ressource de développement"
"devResource.list" = "Lister les ressources de développement" "devResource.list" = "Lister les ressources de développement"
+13
View File
@@ -55,6 +55,7 @@ status = "draft — needs native review"
"Add library file…" = "Tambah file pustaka…" "Add library file…" = "Tambah file pustaka…"
"Add max height" = "Tambah tinggi maks" "Add max height" = "Tambah tinggi maks"
"Add max width" = "Tambah lebar maks" "Add max width" = "Tambah lebar maks"
"Add measurement" = "Tambah pengukuran"
"Add min height" = "Tambah tinggi min" "Add min height" = "Tambah tinggi min"
"Add min width" = "Tambah lebar min" "Add min width" = "Tambah lebar min"
"Add mode" = "Tambah mode" "Add mode" = "Tambah mode"
@@ -92,6 +93,10 @@ status = "draft — needs native review"
"All fonts" = "Semua font" "All fonts" = "Semua font"
"All supported properties" = "Semua properti yang didukung" "All supported properties" = "Semua properti yang didukung"
"All variables" = "Semua variabel" "All variables" = "Semua variabel"
"Along the bottom" = "Sepanjang sisi bawah"
"Along the left" = "Sepanjang sisi kiri"
"Along the right" = "Sepanjang sisi kanan"
"Along the top" = "Sepanjang sisi atas"
"Alpha" = "Alfa" "Alpha" = "Alfa"
"Alphabetical" = "Alfabetis" "Alphabetical" = "Alfabetis"
"Alt+drag" = "Alt+seret" "Alt+drag" = "Alt+seret"
@@ -156,6 +161,7 @@ status = "draft — needs native review"
"Basics" = "Dasar" "Basics" = "Dasar"
"Before" = "Sebelum" "Before" = "Sebelum"
"Bend" = "Lengkungan" "Bend" = "Lengkungan"
"Between the selected layers" = "Di antara layer yang dipilih"
"Bevel" = "Bevel" "Bevel" = "Bevel"
"Binary assets added" = "Aset biner ditambahkan" "Binary assets added" = "Aset biner ditambahkan"
"Binary assets removed" = "Aset biner dihapus" "Binary assets removed" = "Aset biner dihapus"
@@ -672,6 +678,7 @@ status = "draft — needs native review"
"Jun" = "Jun" "Jun" = "Jun"
"Just now" = "Baru saja" "Just now" = "Baru saja"
"Justify" = "Rata kiri-kanan" "Justify" = "Rata kiri-kanan"
"Keep a measurement on the canvas for developers" = "Simpan pengukuran di kanvas untuk developer"
"Keep appearance" = "Pertahankan tampilan" "Keep appearance" = "Pertahankan tampilan"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Pertahankan tampilan mengonversi setiap nilai warna ke {profile} agar warna terlihat sama. Pertahankan nilai membiarkan angkanya apa adanya, sehingga warna bergeser." "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Pertahankan tampilan mengonversi setiap nilai warna ke {profile} agar warna terlihat sama. Pertahankan nilai membiarkan angkanya apa adanya, sehingga warna bergeser."
"Keep tool selected after use" = "Tetap pilih alat setelah dipakai" "Keep tool selected after use" = "Tetap pilih alat setelah dipakai"
@@ -761,6 +768,7 @@ status = "draft — needs native review"
"Maximize" = "Maksimalkan" "Maximize" = "Maksimalkan"
"May" = "Mei" "May" = "Mei"
"Measure to the selection" = "Ukur ke pilihan" "Measure to the selection" = "Ukur ke pilihan"
"Measurements" = "Pengukuran"
"Merge into main" = "Merge ke main" "Merge into main" = "Merge ke main"
"Merge regions" = "Gabungkan area" "Merge regions" = "Gabungkan area"
"Merge the branch into the main file (one undo step) and archive it" = "Merge branch ke file utama (satu langkah urungkan) lalu arsipkan" "Merge the branch into the main file (one undo step) and archive it" = "Merge branch ke file utama (satu langkah urungkan) lalu arsipkan"
@@ -1070,6 +1078,7 @@ status = "draft — needs native review"
"Remove guide" = "Hapus garis bantu" "Remove guide" = "Hapus garis bantu"
"Remove interaction" = "Hapus interaksi" "Remove interaction" = "Hapus interaksi"
"Remove library from this file" = "Hapus pustaka dari file ini" "Remove library from this file" = "Hapus pustaka dari file ini"
"Remove measurement" = "Hapus pengukuran"
"Remove min and max" = "Hapus min dan maks" "Remove min and max" = "Hapus min dan maks"
"Remove min and max height" = "Hapus tinggi min dan maks" "Remove min and max height" = "Hapus tinggi min dan maks"
"Remove min and max width" = "Hapus lebar min dan maks" "Remove min and max width" = "Hapus lebar min dan maks"
@@ -1395,6 +1404,7 @@ status = "draft — needs native review"
"Title" = "Judul" "Title" = "Judul"
"Title case" = "Huruf Awal Kapital" "Title case" = "Huruf Awal Kapital"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Untuk membagikan pustaka ini kepada tim Anda, publikasikan file ke server (Bagikan ▸ Publikasikan ke server…), lalu publikasikan pustaka di sana." "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Untuk membagikan pustaka ini kepada tim Anda, publikasikan file ke server (Bagikan ▸ Publikasikan ke server…), lalu publikasikan pustaka di sana."
"To the layer around it" = "Ke layer yang membungkusnya"
"Toggle" = "Alihkan" "Toggle" = "Alihkan"
"Toggle light/dark theme" = "Alihkan tema terang/gelap" "Toggle light/dark theme" = "Alihkan tema terang/gelap"
"Toggle visibility" = "Alihkan visibilitas" "Toggle visibility" = "Alihkan visibilitas"
@@ -1844,6 +1854,9 @@ status = "draft — needs native review"
"dev.focus" = "Tampilan fokus" "dev.focus" = "Tampilan fokus"
"dev.resolveChange" = "Selesaikan perubahan" "dev.resolveChange" = "Selesaikan perubahan"
"dev.unfocus" = "Keluar dari tampilan fokus" "dev.unfocus" = "Keluar dari tampilan fokus"
"devMode.addMeasurement" = "Tambah pengukuran"
"devMode.measurements" = "Daftar pengukuran"
"devMode.removeMeasurement" = "Hapus pengukuran"
"devResource.add" = "Tambah sumber daya dev" "devResource.add" = "Tambah sumber daya dev"
"devResource.edit" = "Edit sumber daya dev" "devResource.edit" = "Edit sumber daya dev"
"devResource.list" = "Daftar sumber daya dev" "devResource.list" = "Daftar sumber daya dev"
+13
View File
@@ -57,6 +57,7 @@ status = "draft — needs native review"
"Add library file…" = "ライブラリファイルを追加…" "Add library file…" = "ライブラリファイルを追加…"
"Add max height" = "最大の高さを追加" "Add max height" = "最大の高さを追加"
"Add max width" = "最大の幅を追加" "Add max width" = "最大の幅を追加"
"Add measurement" = "測定を追加"
"Add min height" = "最小の高さを追加" "Add min height" = "最小の高さを追加"
"Add min width" = "最小の幅を追加" "Add min width" = "最小の幅を追加"
"Add mode" = "モードを追加" "Add mode" = "モードを追加"
@@ -94,6 +95,10 @@ status = "draft — needs native review"
"All fonts" = "すべてのフォント" "All fonts" = "すべてのフォント"
"All supported properties" = "サポートされているすべてのプロパティ" "All supported properties" = "サポートされているすべてのプロパティ"
"All variables" = "すべての変数" "All variables" = "すべての変数"
"Along the bottom" = "下辺に沿って"
"Along the left" = "左辺に沿って"
"Along the right" = "右辺に沿って"
"Along the top" = "上辺に沿って"
"Alpha" = "アルファ" "Alpha" = "アルファ"
"Alphabetical" = "名前順" "Alphabetical" = "名前順"
"Alt+drag" = "Alt+ドラッグ" "Alt+drag" = "Alt+ドラッグ"
@@ -158,6 +163,7 @@ status = "draft — needs native review"
"Basics" = "基本" "Basics" = "基本"
"Before" = "変更前" "Before" = "変更前"
"Bend" = "曲げ" "Bend" = "曲げ"
"Between the selected layers" = "選択したレイヤーの間"
"Bevel" = "ベベル" "Bevel" = "ベベル"
"Binary assets added" = "追加されたバイナリアセット" "Binary assets added" = "追加されたバイナリアセット"
"Binary assets removed" = "削除されたバイナリアセット" "Binary assets removed" = "削除されたバイナリアセット"
@@ -679,6 +685,7 @@ status = "draft — needs native review"
"Jun" = "6月" "Jun" = "6月"
"Just now" = "たった今" "Just now" = "たった今"
"Justify" = "両端揃え" "Justify" = "両端揃え"
"Keep a measurement on the canvas for developers" = "開発者向けに測定をキャンバスに残す"
"Keep appearance" = "見た目を保持" "Keep appearance" = "見た目を保持"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "「見た目を保持」はすべてのカラー値を {profile} に変換し、色の見た目を保つ。「値を保持」は数値をそのまま残すため、色が変わる。" "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "「見た目を保持」はすべてのカラー値を {profile} に変換し、色の見た目を保つ。「値を保持」は数値をそのまま残すため、色が変わる。"
"Keep tool selected after use" = "使用後もツールを選択したままにする" "Keep tool selected after use" = "使用後もツールを選択したままにする"
@@ -768,6 +775,7 @@ status = "draft — needs native review"
"Maximize" = "最大化" "Maximize" = "最大化"
"May" = "5月" "May" = "5月"
"Measure to the selection" = "選択範囲までの距離を測定" "Measure to the selection" = "選択範囲までの距離を測定"
"Measurements" = "測定"
"Merge into main" = "メインにマージ" "Merge into main" = "メインにマージ"
"Merge regions" = "領域を結合" "Merge regions" = "領域を結合"
"Merge the branch into the main file (one undo step) and archive it" = "ブランチをメインファイルにマージし(1 回の取り消し単位)、アーカイブする" "Merge the branch into the main file (one undo step) and archive it" = "ブランチをメインファイルにマージし(1 回の取り消し単位)、アーカイブする"
@@ -1078,6 +1086,7 @@ status = "draft — needs native review"
"Remove guide" = "ガイドを削除" "Remove guide" = "ガイドを削除"
"Remove interaction" = "インタラクションを削除" "Remove interaction" = "インタラクションを削除"
"Remove library from this file" = "このファイルからライブラリを削除" "Remove library from this file" = "このファイルからライブラリを削除"
"Remove measurement" = "測定を削除"
"Remove min and max" = "最小と最大を削除" "Remove min and max" = "最小と最大を削除"
"Remove min and max height" = "最小と最大の高さを削除" "Remove min and max height" = "最小と最大の高さを削除"
"Remove min and max width" = "最小と最大の幅を削除" "Remove min and max width" = "最小と最大の幅を削除"
@@ -1409,6 +1418,7 @@ status = "draft — needs native review"
"Title" = "タイトル" "Title" = "タイトル"
"Title case" = "タイトルケース" "Title case" = "タイトルケース"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "このライブラリをチームと共有するには、ファイルをサーバーに公開し(共有 ▸ サーバーに公開…)、そこでライブラリを公開する。" "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "このライブラリをチームと共有するには、ファイルをサーバーに公開し(共有 ▸ サーバーに公開…)、そこでライブラリを公開する。"
"To the layer around it" = "親レイヤーまで"
"Toggle" = "切り替え" "Toggle" = "切り替え"
"Toggle light/dark theme" = "ライト/ダークテーマを切り替え" "Toggle light/dark theme" = "ライト/ダークテーマを切り替え"
"Toggle visibility" = "表示を切り替え" "Toggle visibility" = "表示を切り替え"
@@ -1864,6 +1874,9 @@ status = "draft — needs native review"
"dev.focus" = "フォーカスビュー" "dev.focus" = "フォーカスビュー"
"dev.resolveChange" = "変更を解決" "dev.resolveChange" = "変更を解決"
"dev.unfocus" = "フォーカスビューを終了" "dev.unfocus" = "フォーカスビューを終了"
"devMode.addMeasurement" = "測定を追加"
"devMode.measurements" = "測定を一覧表示"
"devMode.removeMeasurement" = "測定を削除"
"devResource.add" = "開発リソースを追加" "devResource.add" = "開発リソースを追加"
"devResource.edit" = "開発リソースを編集" "devResource.edit" = "開発リソースを編集"
"devResource.list" = "開発リソースを一覧表示" "devResource.list" = "開発リソースを一覧表示"
+13
View File
@@ -57,6 +57,7 @@ status = "draft — reviewed by a native speaker, awaiting a maintainer's approv
"Add library file…" = "ბიბლიოთეკის ფაილის დამატება…" "Add library file…" = "ბიბლიოთეკის ფაილის დამატება…"
"Add max height" = "მაქსიმალური სიმაღლის დამატება" "Add max height" = "მაქსიმალური სიმაღლის დამატება"
"Add max width" = "მაქსიმალური სიგანის დამატება" "Add max width" = "მაქსიმალური სიგანის დამატება"
"Add measurement" = "გაზომვის დამატება"
"Add min height" = "მინიმალური სიმაღლის დამატება" "Add min height" = "მინიმალური სიმაღლის დამატება"
"Add min width" = "მინიმალური სიგანის დამატება" "Add min width" = "მინიმალური სიგანის დამატება"
"Add mode" = "რეჟიმის დამატება" "Add mode" = "რეჟიმის დამატება"
@@ -96,6 +97,10 @@ status = "draft — reviewed by a native speaker, awaiting a maintainer's approv
"All supported properties" = "ყველა მხარდაჭერილი თვისება" "All supported properties" = "ყველა მხარდაჭერილი თვისება"
"All the fonts this file uses are installed" = "ამ ფაილის ყველა შრიფტი დაინსტალირებულია" "All the fonts this file uses are installed" = "ამ ფაილის ყველა შრიფტი დაინსტალირებულია"
"All variables" = "ყველა ცვლადი" "All variables" = "ყველა ცვლადი"
"Along the bottom" = "ქვედა კიდის გასწვრივ"
"Along the left" = "მარცხენა კიდის გასწვრივ"
"Along the right" = "მარჯვენა კიდის გასწვრივ"
"Along the top" = "ზედა კიდის გასწვრივ"
"Alpha" = "ალფა" "Alpha" = "ალფა"
"Alphabetical" = "ანბანის მიხედვით" "Alphabetical" = "ანბანის მიხედვით"
"Alt+drag" = "Alt+გადათრევა" "Alt+drag" = "Alt+გადათრევა"
@@ -160,6 +165,7 @@ status = "draft — reviewed by a native speaker, awaiting a maintainer's approv
"Basics" = "საფუძვლები" "Basics" = "საფუძვლები"
"Before" = "მანამდე" "Before" = "მანამდე"
"Bend" = "მოხრა" "Bend" = "მოხრა"
"Between the selected layers" = "მონიშნულ შრეებს შორის"
"Bevel" = "წაკვეთილი" "Bevel" = "წაკვეთილი"
"Binary assets added" = "დამატებული ბინარული ფაილები" "Binary assets added" = "დამატებული ბინარული ფაილები"
"Binary assets removed" = "წაშლილი ბინარული ფაილები" "Binary assets removed" = "წაშლილი ბინარული ფაილები"
@@ -685,6 +691,7 @@ status = "draft — reviewed by a native speaker, awaiting a maintainer's approv
"Jun" = "ივნ" "Jun" = "ივნ"
"Just now" = "ახლახან" "Just now" = "ახლახან"
"Justify" = "ორმხრივი სწორება" "Justify" = "ორმხრივი სწორება"
"Keep a measurement on the canvas for developers" = "გაზომვის შენახვა ტილოზე დეველოპერებისთვის"
"Keep appearance" = "იერსახის შენარჩუნება" "Keep appearance" = "იერსახის შენარჩუნება"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "„იერსახის შენარჩუნება“ ყველა ფერის მნიშვნელობას {profile}-ში გარდაქმნის, რომ ფერები იგივენაირად გამოჩნდეს. „მნიშვნელობების შენარჩუნება“ რიცხვებს უცვლელად ტოვებს, ამიტომ ფერები იცვლება." "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "„იერსახის შენარჩუნება“ ყველა ფერის მნიშვნელობას {profile}-ში გარდაქმნის, რომ ფერები იგივენაირად გამოჩნდეს. „მნიშვნელობების შენარჩუნება“ რიცხვებს უცვლელად ტოვებს, ამიტომ ფერები იცვლება."
"Keep tool selected after use" = "ხელსაწყოს არჩეულად დატოვება გამოყენების შემდეგ" "Keep tool selected after use" = "ხელსაწყოს არჩეულად დატოვება გამოყენების შემდეგ"
@@ -774,6 +781,7 @@ status = "draft — reviewed by a native speaker, awaiting a maintainer's approv
"Maximize" = "გაშლა" "Maximize" = "გაშლა"
"May" = "მაი" "May" = "მაი"
"Measure to the selection" = "მონიშნულამდე გაზომვა" "Measure to the selection" = "მონიშნულამდე გაზომვა"
"Measurements" = "გაზომვები"
"Merge into main" = "მთავარში შერწყმა" "Merge into main" = "მთავარში შერწყმა"
"Merge regions" = "არეების შერწყმა" "Merge regions" = "არეების შერწყმა"
"Merge the branch into the main file (one undo step) and archive it" = "განშტოების მთავარ ფაილთან შერწყმა (დაბრუნების ერთი ნაბიჯი) და მისი დაარქივება" "Merge the branch into the main file (one undo step) and archive it" = "განშტოების მთავარ ფაილთან შერწყმა (დაბრუნების ერთი ნაბიჯი) და მისი დაარქივება"
@@ -1089,6 +1097,7 @@ status = "draft — reviewed by a native speaker, awaiting a maintainer's approv
"Remove guide" = "დამხმარე ხაზის მოცილება" "Remove guide" = "დამხმარე ხაზის მოცილება"
"Remove interaction" = "ურთიერთქმედების წაშლა" "Remove interaction" = "ურთიერთქმედების წაშლა"
"Remove library from this file" = "ბიბლიოთეკის ამ ფაილიდან ამოშლა" "Remove library from this file" = "ბიბლიოთეკის ამ ფაილიდან ამოშლა"
"Remove measurement" = "გაზომვის წაშლა"
"Remove min and max" = "მინ. და მაქს. მოხსნა" "Remove min and max" = "მინ. და მაქს. მოხსნა"
"Remove min and max height" = "მინ. და მაქს. სიმაღლის მოხსნა" "Remove min and max height" = "მინ. და მაქს. სიმაღლის მოხსნა"
"Remove min and max width" = "მინ. და მაქს. სიგანის მოხსნა" "Remove min and max width" = "მინ. და მაქს. სიგანის მოხსნა"
@@ -1422,6 +1431,7 @@ status = "draft — reviewed by a native speaker, awaiting a maintainer's approv
"Title" = "დასახელება" "Title" = "დასახელება"
"Title case" = "სათაურის რეგისტრი" "Title case" = "სათაურის რეგისტრი"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "ამ ბიბლიოთეკის გუნდთან გასაზიარებლად გამოაქვეყნეთ ფაილი სერვერზე (გაზიარება ▸ სერვერზე გამოქვეყნება…), შემდეგ იქ გამოაქვეყნეთ ბიბლიოთეკა." "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "ამ ბიბლიოთეკის გუნდთან გასაზიარებლად გამოაქვეყნეთ ფაილი სერვერზე (გაზიარება ▸ სერვერზე გამოქვეყნება…), შემდეგ იქ გამოაქვეყნეთ ბიბლიოთეკა."
"To the layer around it" = "გარშემო მყოფ შრემდე"
"Toggle" = "გადართვა" "Toggle" = "გადართვა"
"Toggle light/dark theme" = "ღია/ბნელი თემის გადართვა" "Toggle light/dark theme" = "ღია/ბნელი თემის გადართვა"
"Toggle visibility" = "ხილვადობის ჩართვა/გამორთვა" "Toggle visibility" = "ხილვადობის ჩართვა/გამორთვა"
@@ -1879,6 +1889,9 @@ status = "draft — reviewed by a native speaker, awaiting a maintainer's approv
"dev.focus" = "ფოკუსის ხედი" "dev.focus" = "ფოკუსის ხედი"
"dev.resolveChange" = "ცვლილების გადაწყვეტა" "dev.resolveChange" = "ცვლილების გადაწყვეტა"
"dev.unfocus" = "ფოკუსის ხედიდან გასვლა" "dev.unfocus" = "ფოკუსის ხედიდან გასვლა"
"devMode.addMeasurement" = "გაზომვის დამატება"
"devMode.measurements" = "გაზომვების სია"
"devMode.removeMeasurement" = "გაზომვის წაშლა"
"devResource.add" = "დეველოპერის რესურსის დამატება" "devResource.add" = "დეველოპერის რესურსის დამატება"
"devResource.edit" = "დეველოპერის რესურსის რედაქტირება" "devResource.edit" = "დეველოპერის რესურსის რედაქტირება"
"devResource.list" = "დეველოპერის რესურსების სია" "devResource.list" = "დეველოპერის რესურსების სია"
+13
View File
@@ -53,6 +53,7 @@ status = "draft — needs native review"
"Add library file…" = "라이브러리 파일 추가…" "Add library file…" = "라이브러리 파일 추가…"
"Add max height" = "최대 높이 추가" "Add max height" = "최대 높이 추가"
"Add max width" = "최대 너비 추가" "Add max width" = "최대 너비 추가"
"Add measurement" = "측정 추가"
"Add min height" = "최소 높이 추가" "Add min height" = "최소 높이 추가"
"Add min width" = "최소 너비 추가" "Add min width" = "최소 너비 추가"
"Add mode" = "모드 추가" "Add mode" = "모드 추가"
@@ -90,6 +91,10 @@ status = "draft — needs native review"
"All fonts" = "모든 글꼴" "All fonts" = "모든 글꼴"
"All supported properties" = "지원되는 모든 속성" "All supported properties" = "지원되는 모든 속성"
"All variables" = "모든 변수" "All variables" = "모든 변수"
"Along the bottom" = "아래쪽 가장자리를 따라"
"Along the left" = "왼쪽 가장자리를 따라"
"Along the right" = "오른쪽 가장자리를 따라"
"Along the top" = "위쪽 가장자리를 따라"
"Alpha" = "알파" "Alpha" = "알파"
"Alt+drag" = "Alt+드래그" "Alt+drag" = "Alt+드래그"
"An invite link that opens FigCraft and joins" = "FigCraft를 열고 참여하는 초대 링크" "An invite link that opens FigCraft and joins" = "FigCraft를 열고 참여하는 초대 링크"
@@ -149,6 +154,7 @@ status = "draft — needs native review"
"Basics" = "기본" "Basics" = "기본"
"Before" = "변경 전" "Before" = "변경 전"
"Bend" = "구부리기" "Bend" = "구부리기"
"Between the selected layers" = "선택한 레이어 사이"
"Bevel" = "베벨" "Bevel" = "베벨"
"Binary assets added" = "추가된 바이너리 에셋" "Binary assets added" = "추가된 바이너리 에셋"
"Binary assets removed" = "제거된 바이너리 에셋" "Binary assets removed" = "제거된 바이너리 에셋"
@@ -648,6 +654,7 @@ status = "draft — needs native review"
"Join|stroke" = "선 연결" "Join|stroke" = "선 연결"
"Just now" = "방금" "Just now" = "방금"
"Justify" = "양쪽 정렬" "Justify" = "양쪽 정렬"
"Keep a measurement on the canvas for developers" = "개발자를 위해 캔버스에 측정 유지"
"Keep appearance" = "모양 유지" "Keep appearance" = "모양 유지"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "\"모양 유지\"는 모든 색상 값을 {profile}(으)로 변환해 색이 똑같아 보이게 함. \"값 유지\"는 숫자를 그대로 두므로 색이 달라짐." "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "\"모양 유지\"는 모든 색상 값을 {profile}(으)로 변환해 색이 똑같아 보이게 함. \"값 유지\"는 숫자를 그대로 두므로 색이 달라짐."
"Keep tool selected after use" = "사용 후에도 도구 선택 유지" "Keep tool selected after use" = "사용 후에도 도구 선택 유지"
@@ -730,6 +737,7 @@ status = "draft — needs native review"
"Max lines" = "최대 줄 수" "Max lines" = "최대 줄 수"
"Maximize" = "최대화" "Maximize" = "최대화"
"Measure to the selection" = "선택 항목까지 측정" "Measure to the selection" = "선택 항목까지 측정"
"Measurements" = "측정"
"Merge into main" = "메인에 병합" "Merge into main" = "메인에 병합"
"Merge regions" = "영역 병합" "Merge regions" = "영역 병합"
"Merge the branch into the main file (one undo step) and archive it" = "브랜치를 메인 파일에 병합(실행 취소 한 번)하고 보관" "Merge the branch into the main file (one undo step) and archive it" = "브랜치를 메인 파일에 병합(실행 취소 한 번)하고 보관"
@@ -1028,6 +1036,7 @@ status = "draft — needs native review"
"Remove guide" = "안내선 제거" "Remove guide" = "안내선 제거"
"Remove interaction" = "인터랙션 제거" "Remove interaction" = "인터랙션 제거"
"Remove library from this file" = "이 파일에서 라이브러리 제거" "Remove library from this file" = "이 파일에서 라이브러리 제거"
"Remove measurement" = "측정 제거"
"Remove min and max" = "최소 및 최대 제거" "Remove min and max" = "최소 및 최대 제거"
"Remove min and max height" = "최소 및 최대 높이 제거" "Remove min and max height" = "최소 및 최대 높이 제거"
"Remove min and max width" = "최소 및 최대 너비 제거" "Remove min and max width" = "최소 및 최대 너비 제거"
@@ -1343,6 +1352,7 @@ status = "draft — needs native review"
"Title" = "제목" "Title" = "제목"
"Title case" = "제목 대소문자" "Title case" = "제목 대소문자"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "이 라이브러리를 팀과 공유하려면 파일을 서버에 게시(공유 ▸ 서버에 게시…)한 다음 그곳에서 라이브러리를 게시하세요." "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "이 라이브러리를 팀과 공유하려면 파일을 서버에 게시(공유 ▸ 서버에 게시…)한 다음 그곳에서 라이브러리를 게시하세요."
"To the layer around it" = "감싸는 레이어까지"
"Toggle" = "전환" "Toggle" = "전환"
"Toggle light/dark theme" = "라이트/다크 테마 전환" "Toggle light/dark theme" = "라이트/다크 테마 전환"
"Toggle visibility" = "표시 여부 전환" "Toggle visibility" = "표시 여부 전환"
@@ -1786,6 +1796,9 @@ status = "draft — needs native review"
"dev.focus" = "포커스 보기" "dev.focus" = "포커스 보기"
"dev.resolveChange" = "변경 사항 해결" "dev.resolveChange" = "변경 사항 해결"
"dev.unfocus" = "포커스 보기 종료" "dev.unfocus" = "포커스 보기 종료"
"devMode.addMeasurement" = "측정 추가"
"devMode.measurements" = "측정 목록"
"devMode.removeMeasurement" = "측정 제거"
"devResource.add" = "개발 리소스 추가" "devResource.add" = "개발 리소스 추가"
"devResource.edit" = "개발 리소스 편집" "devResource.edit" = "개발 리소스 편집"
"devResource.list" = "개발 리소스 목록" "devResource.list" = "개발 리소스 목록"
+13
View File
@@ -56,6 +56,7 @@ status = "draft — needs native review"
"Add library file…" = "Bibliotheekbestand toevoegen…" "Add library file…" = "Bibliotheekbestand toevoegen…"
"Add max height" = "Maximale hoogte toevoegen" "Add max height" = "Maximale hoogte toevoegen"
"Add max width" = "Maximale breedte toevoegen" "Add max width" = "Maximale breedte toevoegen"
"Add measurement" = "Meting toevoegen"
"Add min height" = "Minimale hoogte toevoegen" "Add min height" = "Minimale hoogte toevoegen"
"Add min width" = "Minimale breedte toevoegen" "Add min width" = "Minimale breedte toevoegen"
"Add mode" = "Modus toevoegen" "Add mode" = "Modus toevoegen"
@@ -95,6 +96,10 @@ status = "draft — needs native review"
"All supported properties" = "Alle ondersteunde eigenschappen" "All supported properties" = "Alle ondersteunde eigenschappen"
"All the fonts this file uses are installed" = "Alle lettertypen die dit bestand gebruikt zijn geïnstalleerd" "All the fonts this file uses are installed" = "Alle lettertypen die dit bestand gebruikt zijn geïnstalleerd"
"All variables" = "Alle variabelen" "All variables" = "Alle variabelen"
"Along the bottom" = "Langs de onderkant"
"Along the left" = "Langs de linkerkant"
"Along the right" = "Langs de rechterkant"
"Along the top" = "Langs de bovenkant"
"Alpha" = "Alpha" "Alpha" = "Alpha"
"Alphabetical" = "Alfabetisch" "Alphabetical" = "Alfabetisch"
"Alt+drag" = "Alt+drag" "Alt+drag" = "Alt+drag"
@@ -159,6 +164,7 @@ status = "draft — needs native review"
"Basics" = "Basis" "Basics" = "Basis"
"Before" = "Voor" "Before" = "Voor"
"Bend" = "Buiging" "Bend" = "Buiging"
"Between the selected layers" = "Tussen de geselecteerde lagen"
"Bevel" = "Afgeschuind" "Bevel" = "Afgeschuind"
"Binary assets added" = "Binaire assets toegevoegd" "Binary assets added" = "Binaire assets toegevoegd"
"Binary assets removed" = "Verwijderde binaire assets" "Binary assets removed" = "Verwijderde binaire assets"
@@ -685,6 +691,7 @@ status = "draft — needs native review"
"Jun" = "jun." "Jun" = "jun."
"Just now" = "Zojuist" "Just now" = "Zojuist"
"Justify" = "Uitvullen" "Justify" = "Uitvullen"
"Keep a measurement on the canvas for developers" = "Een meting voor ontwikkelaars op het canvas houden"
"Keep appearance" = "Uiterlijk behouden" "Keep appearance" = "Uiterlijk behouden"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Kleur behouden converteert elke kleurwaarde naar {profile}, zodat kleuren er hetzelfde uitzien. Waarden behouden laat de getallen staan, waardoor kleuren verschuiven." "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Kleur behouden converteert elke kleurwaarde naar {profile}, zodat kleuren er hetzelfde uitzien. Waarden behouden laat de getallen staan, waardoor kleuren verschuiven."
"Keep tool selected after use" = "Tool na gebruik geselecteerd houden" "Keep tool selected after use" = "Tool na gebruik geselecteerd houden"
@@ -774,6 +781,7 @@ status = "draft — needs native review"
"Maximize" = "Maximaliseren" "Maximize" = "Maximaliseren"
"May" = "mei" "May" = "mei"
"Measure to the selection" = "Meten tot de selectie" "Measure to the selection" = "Meten tot de selectie"
"Measurements" = "Metingen"
"Merge into main" = "Samenvoegen in main" "Merge into main" = "Samenvoegen in main"
"Merge regions" = "Gebieden samenvoegen" "Merge regions" = "Gebieden samenvoegen"
"Merge the branch into the main file (one undo step) and archive it" = "Voeg de branch samen met het hoofdbestand (één keer ongedaan maken) en archiveer hem" "Merge the branch into the main file (one undo step) and archive it" = "Voeg de branch samen met het hoofdbestand (één keer ongedaan maken) en archiveer hem"
@@ -1091,6 +1099,7 @@ status = "draft — needs native review"
"Remove guide" = "Hulplijn verwijderen" "Remove guide" = "Hulplijn verwijderen"
"Remove interaction" = "Interactie verwijderen" "Remove interaction" = "Interactie verwijderen"
"Remove library from this file" = "Bibliotheek uit dit bestand verwijderen" "Remove library from this file" = "Bibliotheek uit dit bestand verwijderen"
"Remove measurement" = "Meting verwijderen"
"Remove min and max" = "Min en max verwijderen" "Remove min and max" = "Min en max verwijderen"
"Remove min and max height" = "Minimale en maximale hoogte verwijderen" "Remove min and max height" = "Minimale en maximale hoogte verwijderen"
"Remove min and max width" = "Minimale en maximale breedte verwijderen" "Remove min and max width" = "Minimale en maximale breedte verwijderen"
@@ -1425,6 +1434,7 @@ status = "draft — needs native review"
"Title" = "Titel" "Title" = "Titel"
"Title case" = "Hoofdletter per woord" "Title case" = "Hoofdletter per woord"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Om deze bibliotheek met je team te delen, publiceer het bestand naar een server (Delen ▸ Publiceren naar server…), publiceer daarna de bibliotheek." "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Om deze bibliotheek met je team te delen, publiceer het bestand naar een server (Delen ▸ Publiceren naar server…), publiceer daarna de bibliotheek."
"To the layer around it" = "Tot de laag eromheen"
"Toggle" = "Omschakelen" "Toggle" = "Omschakelen"
"Toggle light/dark theme" = "Wisselen tussen licht/donker thema" "Toggle light/dark theme" = "Wisselen tussen licht/donker thema"
"Toggle visibility" = "Zichtbaarheid omschakelen" "Toggle visibility" = "Zichtbaarheid omschakelen"
@@ -1882,6 +1892,9 @@ status = "draft — needs native review"
"dev.focus" = "Focusweergave" "dev.focus" = "Focusweergave"
"dev.resolveChange" = "Wijziging oplossen" "dev.resolveChange" = "Wijziging oplossen"
"dev.unfocus" = "Focusweergave verlaten" "dev.unfocus" = "Focusweergave verlaten"
"devMode.addMeasurement" = "Meting toevoegen"
"devMode.measurements" = "Metingen weergeven"
"devMode.removeMeasurement" = "Meting verwijderen"
"devResource.add" = "Dev-resource toevoegen" "devResource.add" = "Dev-resource toevoegen"
"devResource.edit" = "Dev-resource bewerken" "devResource.edit" = "Dev-resource bewerken"
"devResource.list" = "Dev-resources weergeven" "devResource.list" = "Dev-resources weergeven"
+13
View File
@@ -53,6 +53,7 @@ status = "draft — needs native review"
"Add library file…" = "Dodaj plik biblioteki…" "Add library file…" = "Dodaj plik biblioteki…"
"Add max height" = "Dodaj maksymalną wysokość" "Add max height" = "Dodaj maksymalną wysokość"
"Add max width" = "Dodaj maksymalną szerokość" "Add max width" = "Dodaj maksymalną szerokość"
"Add measurement" = "Dodaj pomiar"
"Add min height" = "Dodaj minimalną wysokość" "Add min height" = "Dodaj minimalną wysokość"
"Add min width" = "Dodaj minimalną szerokość" "Add min width" = "Dodaj minimalną szerokość"
"Add mode" = "Dodaj tryb" "Add mode" = "Dodaj tryb"
@@ -92,6 +93,10 @@ status = "draft — needs native review"
"All supported properties" = "Wszystkie obsługiwane właściwości" "All supported properties" = "Wszystkie obsługiwane właściwości"
"All the fonts this file uses are installed" = "Wszystkie czcionki używane w tym pliku są zainstalowane" "All the fonts this file uses are installed" = "Wszystkie czcionki używane w tym pliku są zainstalowane"
"All variables" = "Wszystkie zmienne" "All variables" = "Wszystkie zmienne"
"Along the bottom" = "Wzdłuż dolnej krawędzi"
"Along the left" = "Wzdłuż lewej krawędzi"
"Along the right" = "Wzdłuż prawej krawędzi"
"Along the top" = "Wzdłuż górnej krawędzi"
"Alpha" = "Alfa" "Alpha" = "Alfa"
"Alphabetical" = "Alfabetycznie" "Alphabetical" = "Alfabetycznie"
"Alt+drag" = "Alt+przeciągnięcie" "Alt+drag" = "Alt+przeciągnięcie"
@@ -156,6 +161,7 @@ status = "draft — needs native review"
"Basics" = "Podstawy" "Basics" = "Podstawy"
"Before" = "Przed" "Before" = "Przed"
"Bend" = "Wygnij" "Bend" = "Wygnij"
"Between the selected layers" = "Między zaznaczonymi warstwami"
"Bevel" = "Ścięte" "Bevel" = "Ścięte"
"Binary assets added" = "Dodane pliki binarne" "Binary assets added" = "Dodane pliki binarne"
"Binary assets removed" = "Usunięte pliki binarne" "Binary assets removed" = "Usunięte pliki binarne"
@@ -682,6 +688,7 @@ status = "draft — needs native review"
"Jun" = "Cze" "Jun" = "Cze"
"Just now" = "Przed chwilą" "Just now" = "Przed chwilą"
"Justify" = "Wyjustuj" "Justify" = "Wyjustuj"
"Keep a measurement on the canvas for developers" = "Zachowaj pomiar na płótnie dla programistów"
"Keep appearance" = "Zachowaj wygląd" "Keep appearance" = "Zachowaj wygląd"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Zachowanie wyglądu konwertuje każdą wartość koloru na {profile}, aby kolory wyglądały tak samo. Zachowanie wartości pozostawia liczby bez zmian, co może zmienić odcienie barw." "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Zachowanie wyglądu konwertuje każdą wartość koloru na {profile}, aby kolory wyglądały tak samo. Zachowanie wartości pozostawia liczby bez zmian, co może zmienić odcienie barw."
"Keep tool selected after use" = "Pozostaw narzędzie wybrane po użyciu" "Keep tool selected after use" = "Pozostaw narzędzie wybrane po użyciu"
@@ -771,6 +778,7 @@ status = "draft — needs native review"
"Maximize" = "Maksymalizuj" "Maximize" = "Maksymalizuj"
"May" = "maja" "May" = "maja"
"Measure to the selection" = "Zmierz odległość do zaznaczenia" "Measure to the selection" = "Zmierz odległość do zaznaczenia"
"Measurements" = "Pomiary"
"Merge into main" = "Scal z gałęzią główną" "Merge into main" = "Scal z gałęzią główną"
"Merge regions" = "Scal obszary" "Merge regions" = "Scal obszary"
"Merge the branch into the main file (one undo step) and archive it" = "Scal gałąź z plikiem głównym (jeden krok cofnięcia) i zarchiwizuj ją" "Merge the branch into the main file (one undo step) and archive it" = "Scal gałąź z plikiem głównym (jeden krok cofnięcia) i zarchiwizuj ją"
@@ -1087,6 +1095,7 @@ status = "draft — needs native review"
"Remove guide" = "Usuń linię pomocniczą" "Remove guide" = "Usuń linię pomocniczą"
"Remove interaction" = "Usuń interakcję" "Remove interaction" = "Usuń interakcję"
"Remove library from this file" = "Usuń bibliotekę z tego pliku" "Remove library from this file" = "Usuń bibliotekę z tego pliku"
"Remove measurement" = "Usuń pomiar"
"Remove min and max" = "Usuń min. i maks." "Remove min and max" = "Usuń min. i maks."
"Remove min and max height" = "Usuń minimalną i maksymalną wysokość" "Remove min and max height" = "Usuń minimalną i maksymalną wysokość"
"Remove min and max width" = "Usuń minimalną i maksymalną szerokość" "Remove min and max width" = "Usuń minimalną i maksymalną szerokość"
@@ -1420,6 +1429,7 @@ status = "draft — needs native review"
"Title" = "Tytuł" "Title" = "Tytuł"
"Title case" = "Jak W Tytule" "Title case" = "Jak W Tytule"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Aby udostępnić tę bibliotekę zespołowi, opublikuj plik na serwerze (Udostępnij ▸ Publikuj na serwerze…), a następnie opublikuj tam bibliotekę." "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Aby udostępnić tę bibliotekę zespołowi, opublikuj plik na serwerze (Udostępnij ▸ Publikuj na serwerze…), a następnie opublikuj tam bibliotekę."
"To the layer around it" = "Do warstwy nadrzędnej"
"Toggle" = "Przełącz" "Toggle" = "Przełącz"
"Toggle light/dark theme" = "Przełącz motyw jasny/ciemny" "Toggle light/dark theme" = "Przełącz motyw jasny/ciemny"
"Toggle visibility" = "Przełącz widoczność" "Toggle visibility" = "Przełącz widoczność"
@@ -1878,6 +1888,9 @@ status = "draft — needs native review"
"dev.focus" = "Widok skupienia" "dev.focus" = "Widok skupienia"
"dev.resolveChange" = "Rozwiąż zmianę" "dev.resolveChange" = "Rozwiąż zmianę"
"dev.unfocus" = "Wyjdź z widoku skupienia" "dev.unfocus" = "Wyjdź z widoku skupienia"
"devMode.addMeasurement" = "Dodaj pomiar"
"devMode.measurements" = "Lista pomiarów"
"devMode.removeMeasurement" = "Usuń pomiar"
"devResource.add" = "Dodaj zasób programistyczny" "devResource.add" = "Dodaj zasób programistyczny"
"devResource.edit" = "Edytuj zasób programistyczny" "devResource.edit" = "Edytuj zasób programistyczny"
"devResource.list" = "Lista zasobów programistycznych" "devResource.list" = "Lista zasobów programistycznych"
+13
View File
@@ -51,6 +51,7 @@ status = "draft — needs native review"
"Add library file…" = "Adicionar arquivo de biblioteca…" "Add library file…" = "Adicionar arquivo de biblioteca…"
"Add max height" = "Adicionar altura máx." "Add max height" = "Adicionar altura máx."
"Add max width" = "Adicionar largura máx." "Add max width" = "Adicionar largura máx."
"Add measurement" = "Adicionar medição"
"Add min height" = "Adicionar altura mín." "Add min height" = "Adicionar altura mín."
"Add min width" = "Adicionar largura mín." "Add min width" = "Adicionar largura mín."
"Add mode" = "Adicionar modo" "Add mode" = "Adicionar modo"
@@ -88,6 +89,10 @@ status = "draft — needs native review"
"All fonts" = "Todas as fontes" "All fonts" = "Todas as fontes"
"All supported properties" = "Todas as propriedades compatíveis" "All supported properties" = "Todas as propriedades compatíveis"
"All variables" = "Todas as variáveis" "All variables" = "Todas as variáveis"
"Along the bottom" = "Ao longo da borda inferior"
"Along the left" = "Ao longo da borda esquerda"
"Along the right" = "Ao longo da borda direita"
"Along the top" = "Ao longo da borda superior"
"Alpha" = "Alfa" "Alpha" = "Alfa"
"Alt+drag" = "Alt+arrastar" "Alt+drag" = "Alt+arrastar"
"An invite link that opens FigCraft and joins" = "Um link de convite que abre o FigCraft e entra na sessão" "An invite link that opens FigCraft and joins" = "Um link de convite que abre o FigCraft e entra na sessão"
@@ -147,6 +152,7 @@ status = "draft — needs native review"
"Basics" = "Básico" "Basics" = "Básico"
"Before" = "Antes" "Before" = "Antes"
"Bend" = "Curvar" "Bend" = "Curvar"
"Between the selected layers" = "Entre as camadas selecionadas"
"Bevel" = "Chanfro" "Bevel" = "Chanfro"
"Binary assets added" = "Arquivos binários adicionados" "Binary assets added" = "Arquivos binários adicionados"
"Binary assets removed" = "Arquivos binários removidos" "Binary assets removed" = "Arquivos binários removidos"
@@ -647,6 +653,7 @@ status = "draft — needs native review"
"Join|stroke" = "Junção" "Join|stroke" = "Junção"
"Just now" = "Agora mesmo" "Just now" = "Agora mesmo"
"Justify" = "Justificar" "Justify" = "Justificar"
"Keep a measurement on the canvas for developers" = "Manter uma medição no canvas para desenvolvedores"
"Keep appearance" = "Manter aparência" "Keep appearance" = "Manter aparência"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Manter aparência converte cada valor de cor para {profile} para que as cores pareçam iguais. Manter valores deixa os números como estão, então as cores mudam." "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Manter aparência converte cada valor de cor para {profile} para que as cores pareçam iguais. Manter valores deixa os números como estão, então as cores mudam."
"Keep tool selected after use" = "Manter a ferramenta selecionada após o uso" "Keep tool selected after use" = "Manter a ferramenta selecionada após o uso"
@@ -729,6 +736,7 @@ status = "draft — needs native review"
"Max lines" = "Máx. de linhas" "Max lines" = "Máx. de linhas"
"Maximize" = "Maximizar" "Maximize" = "Maximizar"
"Measure to the selection" = "Medir até a seleção" "Measure to the selection" = "Medir até a seleção"
"Measurements" = "Medições"
"Merge into main" = "Mesclar no principal" "Merge into main" = "Mesclar no principal"
"Merge regions" = "Mesclar regiões" "Merge regions" = "Mesclar regiões"
"Merge the branch into the main file (one undo step) and archive it" = "Mescla a branch no arquivo principal (uma etapa de desfazer) e a arquiva" "Merge the branch into the main file (one undo step) and archive it" = "Mescla a branch no arquivo principal (uma etapa de desfazer) e a arquiva"
@@ -1027,6 +1035,7 @@ status = "draft — needs native review"
"Remove guide" = "Remover guia" "Remove guide" = "Remover guia"
"Remove interaction" = "Remover interação" "Remove interaction" = "Remover interação"
"Remove library from this file" = "Remover biblioteca deste arquivo" "Remove library from this file" = "Remover biblioteca deste arquivo"
"Remove measurement" = "Remover medição"
"Remove min and max" = "Remover mín. e máx." "Remove min and max" = "Remover mín. e máx."
"Remove min and max height" = "Remover altura mín. e máx." "Remove min and max height" = "Remover altura mín. e máx."
"Remove min and max width" = "Remover largura mín. e máx." "Remove min and max width" = "Remover largura mín. e máx."
@@ -1342,6 +1351,7 @@ status = "draft — needs native review"
"Title" = "Título" "Title" = "Título"
"Title case" = "Iniciais maiúsculas" "Title case" = "Iniciais maiúsculas"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Para compartilhar esta biblioteca com sua equipe, publique o arquivo em um servidor (Compartilhar ▸ Publicar no servidor…) e depois publique a biblioteca lá." "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Para compartilhar esta biblioteca com sua equipe, publique o arquivo em um servidor (Compartilhar ▸ Publicar no servidor…) e depois publique a biblioteca lá."
"To the layer around it" = "Até a camada que a contém"
"Toggle" = "Alternar" "Toggle" = "Alternar"
"Toggle light/dark theme" = "Alternar tema claro/escuro" "Toggle light/dark theme" = "Alternar tema claro/escuro"
"Toggle visibility" = "Alternar visibilidade" "Toggle visibility" = "Alternar visibilidade"
@@ -1899,6 +1909,9 @@ status = "draft — needs native review"
"dev.focus" = "Visualização em foco" "dev.focus" = "Visualização em foco"
"dev.resolveChange" = "Resolver alteração" "dev.resolveChange" = "Resolver alteração"
"dev.unfocus" = "Sair da visualização em foco" "dev.unfocus" = "Sair da visualização em foco"
"devMode.addMeasurement" = "Adicionar medição"
"devMode.measurements" = "Listar medições"
"devMode.removeMeasurement" = "Remover medição"
"devResource.add" = "Adicionar recurso de desenvolvimento" "devResource.add" = "Adicionar recurso de desenvolvimento"
"devResource.edit" = "Editar recurso de desenvolvimento" "devResource.edit" = "Editar recurso de desenvolvimento"
"devResource.list" = "Listar recursos de desenvolvimento" "devResource.list" = "Listar recursos de desenvolvimento"
+13
View File
@@ -54,6 +54,7 @@ name = "Русский"
"Add library file…" = "Добавить файл библиотеки…" "Add library file…" = "Добавить файл библиотеки…"
"Add max height" = "Добавить максимальную высоту" "Add max height" = "Добавить максимальную высоту"
"Add max width" = "Добавить максимальную ширину" "Add max width" = "Добавить максимальную ширину"
"Add measurement" = "Добавить измерение"
"Add min height" = "Добавить минимальную высоту" "Add min height" = "Добавить минимальную высоту"
"Add min width" = "Добавить минимальную ширину" "Add min width" = "Добавить минимальную ширину"
"Add mode" = "Добавить режим" "Add mode" = "Добавить режим"
@@ -93,6 +94,10 @@ name = "Русский"
"All supported properties" = "Все поддерживаемые свойства" "All supported properties" = "Все поддерживаемые свойства"
"All the fonts this file uses are installed" = "Все шрифты, которые использует этот файл, установлены" "All the fonts this file uses are installed" = "Все шрифты, которые использует этот файл, установлены"
"All variables" = "Все переменные" "All variables" = "Все переменные"
"Along the bottom" = "Вдоль нижнего края"
"Along the left" = "Вдоль левого края"
"Along the right" = "Вдоль правого края"
"Along the top" = "Вдоль верхнего края"
"Alpha" = "Альфа" "Alpha" = "Альфа"
"Alphabetical" = "По алфавиту" "Alphabetical" = "По алфавиту"
"Alt+drag" = "Alt+перетаскивание" "Alt+drag" = "Alt+перетаскивание"
@@ -157,6 +162,7 @@ name = "Русский"
"Basics" = "Основы" "Basics" = "Основы"
"Before" = "До" "Before" = "До"
"Bend" = "Изгиб" "Bend" = "Изгиб"
"Between the selected layers" = "Между выбранными слоями"
"Bevel" = "Скошенное" "Bevel" = "Скошенное"
"Binary assets added" = "Добавлено бинарных ресурсов" "Binary assets added" = "Добавлено бинарных ресурсов"
"Binary assets removed" = "Удалено бинарных ресурсов" "Binary assets removed" = "Удалено бинарных ресурсов"
@@ -686,6 +692,7 @@ name = "Русский"
"Jun" = "июн." "Jun" = "июн."
"Just now" = "Только что" "Just now" = "Только что"
"Justify" = "По ширине" "Justify" = "По ширине"
"Keep a measurement on the canvas for developers" = "Оставить измерение на холсте для разработчиков"
"Keep appearance" = "Сохранить внешний вид" "Keep appearance" = "Сохранить внешний вид"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "«Сохранить внешний вид» преобразует все значения цветов в {profile}, сохраняя их вид. «Сохранить значения» оставляет числа без изменений, поэтому цвета меняются." "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "«Сохранить внешний вид» преобразует все значения цветов в {profile}, сохраняя их вид. «Сохранить значения» оставляет числа без изменений, поэтому цвета меняются."
"Keep tool selected after use" = "Оставлять инструмент выбранным после использования" "Keep tool selected after use" = "Оставлять инструмент выбранным после использования"
@@ -776,6 +783,7 @@ name = "Русский"
"Maximize" = "Развернуть окно" "Maximize" = "Развернуть окно"
"May" = "май" "May" = "май"
"Measure to the selection" = "Измерить до выделения" "Measure to the selection" = "Измерить до выделения"
"Measurements" = "Измерения"
"Merge into main" = "Объединить с основной" "Merge into main" = "Объединить с основной"
"Merge regions" = "Объединить области" "Merge regions" = "Объединить области"
"Merge the branch into the main file (one undo step) and archive it" = "Объединить ветку с основным файлом (один шаг отмены) и архивировать её" "Merge the branch into the main file (one undo step) and archive it" = "Объединить ветку с основным файлом (один шаг отмены) и архивировать её"
@@ -1094,6 +1102,7 @@ name = "Русский"
"Remove guide" = "Удалить направляющую" "Remove guide" = "Удалить направляющую"
"Remove interaction" = "Удалить взаимодействие" "Remove interaction" = "Удалить взаимодействие"
"Remove library from this file" = "Удалить библиотеку из этого файла" "Remove library from this file" = "Удалить библиотеку из этого файла"
"Remove measurement" = "Удалить измерение"
"Remove min and max" = "Убрать минимум и максимум" "Remove min and max" = "Убрать минимум и максимум"
"Remove min and max height" = "Убрать минимальную и максимальную высоту" "Remove min and max height" = "Убрать минимальную и максимальную высоту"
"Remove min and max width" = "Убрать минимальную и максимальную ширину" "Remove min and max width" = "Убрать минимальную и максимальную ширину"
@@ -1433,6 +1442,7 @@ name = "Русский"
"Title" = "Заголовок" "Title" = "Заголовок"
"Title case" = "Каждое Слово С Прописной" "Title case" = "Каждое Слово С Прописной"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Чтобы поделиться библиотекой с командой, опубликуйте файл на сервере («Поделиться» ▸ «Опубликовать на сервере…»), затем опубликуйте там библиотеку." "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Чтобы поделиться библиотекой с командой, опубликуйте файл на сервере («Поделиться» ▸ «Опубликовать на сервере…»), затем опубликуйте там библиотеку."
"To the layer around it" = "До родительского слоя"
"Toggle" = "Переключить" "Toggle" = "Переключить"
"Toggle light/dark theme" = "Переключить светлую/тёмную тему" "Toggle light/dark theme" = "Переключить светлую/тёмную тему"
"Toggle visibility" = "Переключить видимость" "Toggle visibility" = "Переключить видимость"
@@ -1895,6 +1905,9 @@ name = "Русский"
"dev.focus" = "Режим фокуса" "dev.focus" = "Режим фокуса"
"dev.resolveChange" = "Разрешить изменение" "dev.resolveChange" = "Разрешить изменение"
"dev.unfocus" = "Выйти из режима фокуса" "dev.unfocus" = "Выйти из режима фокуса"
"devMode.addMeasurement" = "Добавить измерение"
"devMode.measurements" = "Список измерений"
"devMode.removeMeasurement" = "Удалить измерение"
"devResource.add" = "Добавить ресурс для разработки" "devResource.add" = "Добавить ресурс для разработки"
"devResource.edit" = "Изменить ресурс для разработки" "devResource.edit" = "Изменить ресурс для разработки"
"devResource.list" = "Список ресурсов для разработки" "devResource.list" = "Список ресурсов для разработки"
+13
View File
@@ -54,6 +54,7 @@ name = "Svenska"
"Add library file…" = "Lägg till biblioteksfil…" "Add library file…" = "Lägg till biblioteksfil…"
"Add max height" = "Lägg till maxhöjd" "Add max height" = "Lägg till maxhöjd"
"Add max width" = "Lägg till maxbredd" "Add max width" = "Lägg till maxbredd"
"Add measurement" = "Lägg till mått"
"Add min height" = "Lägg till minhöjd" "Add min height" = "Lägg till minhöjd"
"Add min width" = "Lägg till minbredd" "Add min width" = "Lägg till minbredd"
"Add mode" = "Lägg till läge" "Add mode" = "Lägg till läge"
@@ -92,6 +93,10 @@ name = "Svenska"
"All fonts" = "Alla typsnitt" "All fonts" = "Alla typsnitt"
"All supported properties" = "Alla egenskaper som stöds" "All supported properties" = "Alla egenskaper som stöds"
"All variables" = "Alla variabler" "All variables" = "Alla variabler"
"Along the bottom" = "Längs nederkanten"
"Along the left" = "Längs vänsterkanten"
"Along the right" = "Längs högerkanten"
"Along the top" = "Längs överkanten"
"Alpha" = "Alfa" "Alpha" = "Alfa"
"Alphabetical" = "I bokstavsordning" "Alphabetical" = "I bokstavsordning"
"Alt+drag" = "Alt+dra" "Alt+drag" = "Alt+dra"
@@ -156,6 +161,7 @@ name = "Svenska"
"Basics" = "Grunder" "Basics" = "Grunder"
"Before" = "Före" "Before" = "Före"
"Bend" = "Böj" "Bend" = "Böj"
"Between the selected layers" = "Mellan de markerade lagren"
"Bevel" = "Fasning" "Bevel" = "Fasning"
"Binary assets added" = "Binära resurser tillagda" "Binary assets added" = "Binära resurser tillagda"
"Binary assets removed" = "Binära resurser borttagna" "Binary assets removed" = "Binära resurser borttagna"
@@ -679,6 +685,7 @@ name = "Svenska"
"Jun" = "jun" "Jun" = "jun"
"Just now" = "Nyss" "Just now" = "Nyss"
"Justify" = "Marginaljustera" "Justify" = "Marginaljustera"
"Keep a measurement on the canvas for developers" = "Behåll ett mått på arbetsytan för utvecklare"
"Keep appearance" = "Behåll utseende" "Keep appearance" = "Behåll utseende"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Behåll utseende konverterar varje färgvärde till {profile} så att färgerna ser likadana ut. Behåll värden lämnar siffrorna som de är, så färgerna förskjuts." "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Behåll utseende konverterar varje färgvärde till {profile} så att färgerna ser likadana ut. Behåll värden lämnar siffrorna som de är, så färgerna förskjuts."
"Keep tool selected after use" = "Behåll verktyget markerat efter användning" "Keep tool selected after use" = "Behåll verktyget markerat efter användning"
@@ -768,6 +775,7 @@ name = "Svenska"
"Maximize" = "Maximera" "Maximize" = "Maximera"
"May" = "Maj" "May" = "Maj"
"Measure to the selection" = "Mät avståndet till markeringen" "Measure to the selection" = "Mät avståndet till markeringen"
"Measurements" = "Mått"
"Merge into main" = "Sammanfoga med main" "Merge into main" = "Sammanfoga med main"
"Merge regions" = "Sammanfoga regioner" "Merge regions" = "Sammanfoga regioner"
"Merge the branch into the main file (one undo step) and archive it" = "Sammanfoga grenen med huvudfilen (ett ångrasteg) och arkivera den" "Merge the branch into the main file (one undo step) and archive it" = "Sammanfoga grenen med huvudfilen (ett ångrasteg) och arkivera den"
@@ -1079,6 +1087,7 @@ name = "Svenska"
"Remove guide" = "Ta bort stödlinje" "Remove guide" = "Ta bort stödlinje"
"Remove interaction" = "Ta bort interaktion" "Remove interaction" = "Ta bort interaktion"
"Remove library from this file" = "Ta bort biblioteket från den här filen" "Remove library from this file" = "Ta bort biblioteket från den här filen"
"Remove measurement" = "Ta bort mått"
"Remove min and max" = "Ta bort min och max" "Remove min and max" = "Ta bort min och max"
"Remove min and max height" = "Ta bort min- och maxhöjd" "Remove min and max height" = "Ta bort min- och maxhöjd"
"Remove min and max width" = "Ta bort min- och maxbredd" "Remove min and max width" = "Ta bort min- och maxbredd"
@@ -1410,6 +1419,7 @@ name = "Svenska"
"Title" = "Titel" "Title" = "Titel"
"Title case" = "Inledande versaler" "Title case" = "Inledande versaler"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Om du vill dela biblioteket med ditt team publicerar du filen på en server (Dela ▸ Publicera på server…) och publicerar sedan biblioteket där." "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Om du vill dela biblioteket med ditt team publicerar du filen på en server (Dela ▸ Publicera på server…) och publicerar sedan biblioteket där."
"To the layer around it" = "Till lagret runt det"
"Toggle" = "Visa/dölj" "Toggle" = "Visa/dölj"
"Toggle light/dark theme" = "Växla mellan ljust och mörkt tema" "Toggle light/dark theme" = "Växla mellan ljust och mörkt tema"
"Toggle visibility" = "Visa/dölj" "Toggle visibility" = "Visa/dölj"
@@ -1864,6 +1874,9 @@ name = "Svenska"
"dev.focus" = "Fokusvy" "dev.focus" = "Fokusvy"
"dev.resolveChange" = "Lös ändring" "dev.resolveChange" = "Lös ändring"
"dev.unfocus" = "Lämna fokusvy" "dev.unfocus" = "Lämna fokusvy"
"devMode.addMeasurement" = "Lägg till mått"
"devMode.measurements" = "Lista mått"
"devMode.removeMeasurement" = "Ta bort mått"
"devResource.add" = "Lägg till utvecklarresurs" "devResource.add" = "Lägg till utvecklarresurs"
"devResource.edit" = "Redigera utvecklarresurs" "devResource.edit" = "Redigera utvecklarresurs"
"devResource.list" = "Lista utvecklarresurser" "devResource.list" = "Lista utvecklarresurser"
+13
View File
@@ -55,6 +55,7 @@ name = "Türkçe"
"Add library file…" = "Kitaplık dosyası ekle…" "Add library file…" = "Kitaplık dosyası ekle…"
"Add max height" = "En büyük yükseklik ekle" "Add max height" = "En büyük yükseklik ekle"
"Add max width" = "En büyük genişlik ekle" "Add max width" = "En büyük genişlik ekle"
"Add measurement" = "Ölçüm ekle"
"Add min height" = "En küçük yükseklik ekle" "Add min height" = "En küçük yükseklik ekle"
"Add min width" = "En küçük genişlik ekle" "Add min width" = "En küçük genişlik ekle"
"Add mode" = "Mod ekle" "Add mode" = "Mod ekle"
@@ -92,6 +93,10 @@ name = "Türkçe"
"All fonts" = "Tüm yazı tipleri" "All fonts" = "Tüm yazı tipleri"
"All supported properties" = "Desteklenen tüm özellikler" "All supported properties" = "Desteklenen tüm özellikler"
"All variables" = "Tüm değişkenler" "All variables" = "Tüm değişkenler"
"Along the bottom" = "Alt kenar boyunca"
"Along the left" = "Sol kenar boyunca"
"Along the right" = "Sağ kenar boyunca"
"Along the top" = "Üst kenar boyunca"
"Alpha" = "Alfa" "Alpha" = "Alfa"
"Alphabetical" = "Alfabetik" "Alphabetical" = "Alfabetik"
"Alt+drag" = "Alt+sürükle" "Alt+drag" = "Alt+sürükle"
@@ -156,6 +161,7 @@ name = "Türkçe"
"Basics" = "Temel bilgiler" "Basics" = "Temel bilgiler"
"Before" = "Önce" "Before" = "Önce"
"Bend" = "Bük" "Bend" = "Bük"
"Between the selected layers" = "Seçili katmanlar arasında"
"Bevel" = "Eğimli" "Bevel" = "Eğimli"
"Binary assets added" = "Eklenen ikili varlıklar" "Binary assets added" = "Eklenen ikili varlıklar"
"Binary assets removed" = "Kaldırılan ikili varlıklar" "Binary assets removed" = "Kaldırılan ikili varlıklar"
@@ -672,6 +678,7 @@ name = "Türkçe"
"Jun" = "Haz" "Jun" = "Haz"
"Just now" = "Az önce" "Just now" = "Az önce"
"Justify" = "İki yana yasla" "Justify" = "İki yana yasla"
"Keep a measurement on the canvas for developers" = "Geliştiriciler için tuvalde bir ölçüm tut"
"Keep appearance" = "Görünümü koru" "Keep appearance" = "Görünümü koru"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Görünümü koru, renkler aynı görünsün diye her renk değerini {profile} profiline dönüştürür. Değerleri koru, sayıları olduğu gibi bırakır; bu yüzden renkler kayar." "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "Görünümü koru, renkler aynı görünsün diye her renk değerini {profile} profiline dönüştürür. Değerleri koru, sayıları olduğu gibi bırakır; bu yüzden renkler kayar."
"Keep tool selected after use" = "Kullanımdan sonra aracı seçili tut" "Keep tool selected after use" = "Kullanımdan sonra aracı seçili tut"
@@ -761,6 +768,7 @@ name = "Türkçe"
"Maximize" = "Ekranı kapla" "Maximize" = "Ekranı kapla"
"May" = "May" "May" = "May"
"Measure to the selection" = "Seçime kadar ölç" "Measure to the selection" = "Seçime kadar ölç"
"Measurements" = "Ölçümler"
"Merge into main" = "Ana dosyaya birleştir" "Merge into main" = "Ana dosyaya birleştir"
"Merge regions" = "Bölgeleri birleştir" "Merge regions" = "Bölgeleri birleştir"
"Merge the branch into the main file (one undo step) and archive it" = "Dalı ana dosyaya birleştir (tek geri alma adımı) ve arşivle" "Merge the branch into the main file (one undo step) and archive it" = "Dalı ana dosyaya birleştir (tek geri alma adımı) ve arşivle"
@@ -1070,6 +1078,7 @@ name = "Türkçe"
"Remove guide" = "Kılavuzu kaldır" "Remove guide" = "Kılavuzu kaldır"
"Remove interaction" = "Etkileşimi kaldır" "Remove interaction" = "Etkileşimi kaldır"
"Remove library from this file" = "Kitaplığı bu dosyadan kaldır" "Remove library from this file" = "Kitaplığı bu dosyadan kaldır"
"Remove measurement" = "Ölçümü kaldır"
"Remove min and max" = "En küçük ve en büyüğü kaldır" "Remove min and max" = "En küçük ve en büyüğü kaldır"
"Remove min and max height" = "En küçük ve en büyük yüksekliği kaldır" "Remove min and max height" = "En küçük ve en büyük yüksekliği kaldır"
"Remove min and max width" = "En küçük ve en büyük genişliği kaldır" "Remove min and max width" = "En küçük ve en büyük genişliği kaldır"
@@ -1395,6 +1404,7 @@ name = "Türkçe"
"Title" = "Başlık" "Title" = "Başlık"
"Title case" = "Başlık biçimi" "Title case" = "Başlık biçimi"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Bu kitaplığı ekibinizle paylaşmak için dosyayı bir sunucuya yayımlayın (Paylaş ▸ Sunucuya yayımla…), sonra kitaplığı orada yayımlayın." "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "Bu kitaplığı ekibinizle paylaşmak için dosyayı bir sunucuya yayımlayın (Paylaş ▸ Sunucuya yayımla…), sonra kitaplığı orada yayımlayın."
"To the layer around it" = "Çevreleyen katmana kadar"
"Toggle" = "Aç/Kapat" "Toggle" = "Aç/Kapat"
"Toggle light/dark theme" = "Açık/koyu temayı değiştir" "Toggle light/dark theme" = "Açık/koyu temayı değiştir"
"Toggle visibility" = "Görünürlüğü değiştir" "Toggle visibility" = "Görünürlüğü değiştir"
@@ -1844,6 +1854,9 @@ name = "Türkçe"
"dev.focus" = "Odak görünümü" "dev.focus" = "Odak görünümü"
"dev.resolveChange" = "Değişikliği çöz" "dev.resolveChange" = "Değişikliği çöz"
"dev.unfocus" = "Odak görünümünden çık" "dev.unfocus" = "Odak görünümünden çık"
"devMode.addMeasurement" = "Ölçüm ekle"
"devMode.measurements" = "Ölçümleri listele"
"devMode.removeMeasurement" = "Ölçümü kaldır"
"devResource.add" = "Geliştirici kaynağı ekle" "devResource.add" = "Geliştirici kaynağı ekle"
"devResource.edit" = "Geliştirici kaynağını düzenle" "devResource.edit" = "Geliştirici kaynağını düzenle"
"devResource.list" = "Geliştirici kaynaklarını listele" "devResource.list" = "Geliştirici kaynaklarını listele"
+13
View File
@@ -54,6 +54,7 @@ status = "draft — needs native review"
"Add library file…" = "添加库文件…" "Add library file…" = "添加库文件…"
"Add max height" = "添加最大高度" "Add max height" = "添加最大高度"
"Add max width" = "添加最大宽度" "Add max width" = "添加最大宽度"
"Add measurement" = "添加测量"
"Add min height" = "添加最小高度" "Add min height" = "添加最小高度"
"Add min width" = "添加最小宽度" "Add min width" = "添加最小宽度"
"Add mode" = "添加模式" "Add mode" = "添加模式"
@@ -91,6 +92,10 @@ status = "draft — needs native review"
"All fonts" = "全部字体" "All fonts" = "全部字体"
"All supported properties" = "所有支持的属性" "All supported properties" = "所有支持的属性"
"All variables" = "全部变量" "All variables" = "全部变量"
"Along the bottom" = "沿底边"
"Along the left" = "沿左边"
"Along the right" = "沿右边"
"Along the top" = "沿顶边"
"Alpha" = "Alpha" "Alpha" = "Alpha"
"Alt+drag" = "Alt+拖动" "Alt+drag" = "Alt+拖动"
"An invite link that opens FigCraft and joins" = "可打开 FigCraft 并加入的邀请链接" "An invite link that opens FigCraft and joins" = "可打开 FigCraft 并加入的邀请链接"
@@ -150,6 +155,7 @@ status = "draft — needs native review"
"Basics" = "基础" "Basics" = "基础"
"Before" = "之前" "Before" = "之前"
"Bend" = "弯曲" "Bend" = "弯曲"
"Between the selected layers" = "所选图层之间"
"Bevel" = "斜角" "Bevel" = "斜角"
"Binary assets added" = "新增的二进制资源" "Binary assets added" = "新增的二进制资源"
"Binary assets removed" = "删除的二进制资源" "Binary assets removed" = "删除的二进制资源"
@@ -649,6 +655,7 @@ status = "draft — needs native review"
"Join|stroke" = "转角" "Join|stroke" = "转角"
"Just now" = "刚刚" "Just now" = "刚刚"
"Justify" = "两端对齐" "Justify" = "两端对齐"
"Keep a measurement on the canvas for developers" = "为开发者在画布上保留测量"
"Keep appearance" = "保持外观" "Keep appearance" = "保持外观"
"Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "“保持外观”会将每个颜色值转换为 {profile},使颜色看起来不变。“保持数值”则保留数值不变,颜色会有偏移。" "Keep appearance converts every color value to {profile} so colors look the same. Keep values leaves the numbers as they are, so colors shift." = "“保持外观”会将每个颜色值转换为 {profile},使颜色看起来不变。“保持数值”则保留数值不变,颜色会有偏移。"
"Keep tool selected after use" = "使用后保持工具选中" "Keep tool selected after use" = "使用后保持工具选中"
@@ -731,6 +738,7 @@ status = "draft — needs native review"
"Max lines" = "最大行数" "Max lines" = "最大行数"
"Maximize" = "最大化" "Maximize" = "最大化"
"Measure to the selection" = "测量到所选项的距离" "Measure to the selection" = "测量到所选项的距离"
"Measurements" = "测量"
"Merge into main" = "合并到主文件" "Merge into main" = "合并到主文件"
"Merge regions" = "合并区域" "Merge regions" = "合并区域"
"Merge the branch into the main file (one undo step) and archive it" = "将分支合并到主文件(一步撤销)并归档" "Merge the branch into the main file (one undo step) and archive it" = "将分支合并到主文件(一步撤销)并归档"
@@ -1029,6 +1037,7 @@ status = "draft — needs native review"
"Remove guide" = "移除参考线" "Remove guide" = "移除参考线"
"Remove interaction" = "移除交互" "Remove interaction" = "移除交互"
"Remove library from this file" = "从此文件中移除库" "Remove library from this file" = "从此文件中移除库"
"Remove measurement" = "移除测量"
"Remove min and max" = "移除最小值和最大值" "Remove min and max" = "移除最小值和最大值"
"Remove min and max height" = "移除最小和最大高度" "Remove min and max height" = "移除最小和最大高度"
"Remove min and max width" = "移除最小和最大宽度" "Remove min and max width" = "移除最小和最大宽度"
@@ -1344,6 +1353,7 @@ status = "draft — needs native review"
"Title" = "标题" "Title" = "标题"
"Title case" = "首字母大写" "Title case" = "首字母大写"
"To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "要与团队共享此库,请先将文件发布到服务器(分享 ▸ 发布到服务器…),然后在那里发布库。" "To share this library with your team, publish the file to a server (Share ▸ Publish to server…), then publish the library there." = "要与团队共享此库,请先将文件发布到服务器(分享 ▸ 发布到服务器…),然后在那里发布库。"
"To the layer around it" = "到外层图层"
"Toggle" = "切换显示" "Toggle" = "切换显示"
"Toggle light/dark theme" = "切换浅色/深色主题" "Toggle light/dark theme" = "切换浅色/深色主题"
"Toggle visibility" = "切换可见性" "Toggle visibility" = "切换可见性"
@@ -1787,6 +1797,9 @@ status = "draft — needs native review"
"dev.focus" = "聚焦视图" "dev.focus" = "聚焦视图"
"dev.resolveChange" = "解决更改" "dev.resolveChange" = "解决更改"
"dev.unfocus" = "退出聚焦视图" "dev.unfocus" = "退出聚焦视图"
"devMode.addMeasurement" = "添加测量"
"devMode.measurements" = "列出测量"
"devMode.removeMeasurement" = "移除测量"
"devResource.add" = "添加开发资源" "devResource.add" = "添加开发资源"
"devResource.edit" = "编辑开发资源" "devResource.edit" = "编辑开发资源"
"devResource.list" = "列出开发资源" "devResource.list" = "列出开发资源"
+1
View File
@@ -244,6 +244,7 @@ pub fn show(app: &mut FigApp, ui: &mut Ui, worker: &mut Worker) {
let image_rect = presented_image_rect(tl, v, zoom, ppp, pixel_preview); let image_rect = presented_image_rect(tl, v, zoom, ppp, pixel_preview);
painter.image(tex.id(), image_rect, Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)), Color32::WHITE); painter.image(tex.id(), image_rect, Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)), Color32::WHITE);
} }
crate::dev_mode::refresh_measurements(app);
app.session.tool.labels = overlays(app, &painter, &t); app.session.tool.labels = overlays(app, &painter, &t);
crate::frame_labels::rename_field(app, ui); crate::frame_labels::rename_field(app, ui);
// The zoom tool's area. // The zoom tool's area.
+128 -2
View File
@@ -3,22 +3,25 @@
//! categories and the filter of the callouts, the handoff status of the selected frames (with the //! categories and the filter of the callouts, the handoff status of the selected frames (with the
//! focus view and Compare changes), the list of frames and sections that are ready for dev, the //! focus view and Compare changes), the list of frames and sections that are ready for dev, the
//! focus view's bar, Connect to code (a component's link to its code component), dev resources //! focus view's bar, Connect to code (a component's link to its code component), dev resources
//! (links for developers) and the plugins that only read the document. //! (links for developers), measurements kept on the canvas and the plugins that only read the
//! document.
use egui::{Align2, Color32, CornerRadius, Id, Rect, Sense, Stroke, StrokeKind, Ui, pos2, vec2}; use egui::{Align2, Color32, CornerRadius, Id, Rect, Sense, Stroke, StrokeKind, Ui, pos2, vec2};
use figcraft_doc::{Annotation, AnnotationCategory, CodeLink, DevStatus, Document, NodeId}; use figcraft_doc::{Annotation, AnnotationCategory, CodeLink, DevStatus, Document, NodeId};
use figcraft_engine::annotations::{self, PINNABLE, Pinnable}; use figcraft_engine::annotations::{self, PINNABLE, Pinnable};
use figcraft_engine::code_connect; use figcraft_engine::code_connect;
use figcraft_engine::codegen::{CodeLang, code_for}; use figcraft_engine::codegen::{CodeLang, code_for};
use figcraft_engine::dev_measurements::{self, Placed};
use figcraft_engine::devinspect::{self, DevItem, Prop}; use figcraft_engine::devinspect::{self, DevItem, Prop};
use figcraft_engine::plugins::Access; use figcraft_engine::plugins::Access;
use figcraft_engine::snap::Guide;
use figcraft_engine::{components, dev_resources}; use figcraft_engine::{components, dev_resources};
use serde_json::json; use serde_json::json;
use crate::a11y::Tip; use crate::a11y::Tip;
use crate::i18n::{tr, tr_noop, trf, trn}; use crate::i18n::{tr, tr_noop, trf, trn};
use crate::icons::{self, Icon}; use crate::icons::{self, Icon};
use crate::right::{menu_check, menu_item_with, menu_popup, prop_row, row}; use crate::right::{menu_check, menu_item, menu_item_with, menu_popup, prop_row, row};
use crate::theme::{ACCENT, Tokens, doc_c32, regular, semibold}; use crate::theme::{ACCENT, Tokens, doc_c32, regular, semibold};
use crate::widgets::{self, DropLead, PAD}; use crate::widgets::{self, DropLead, PAD};
use crate::{FigApp, RightTab}; use crate::{FigApp, RightTab};
@@ -94,6 +97,9 @@ pub struct DevUi {
pub compare: Option<crate::dev_compare::CompareView>, pub compare: Option<crate::dev_compare::CompareView>,
annotated: Vec<NodeId>, annotated: Vec<NodeId>,
annotated_key: Option<(u64, u64, NodeId)>, annotated_key: Option<(u64, u64, NodeId)>,
/// The measurements kept on the current page, with their lines.
measured: Vec<Placed>,
measured_key: Option<(u64, u64, NodeId)>,
ready: Vec<DevItem>, ready: Vec<DevItem>,
ready_key: Option<(u64, u64, usize)>, ready_key: Option<(u64, u64, usize)>,
/// Every ready-for-dev frame (the focus view's previous / next). /// Every ready-for-dev frame (the focus view's previous / next).
@@ -190,6 +196,35 @@ pub fn draw(app: &mut FigApp, painter: &egui::Painter) {
} }
} }
/// Bring the current page's measurements up to date (when the document, its revision or the page
/// changed).
pub fn refresh_measurements(app: &mut FigApp) {
let Ok(ds) = app.session.ds() else { return };
let key = (ds.uid, ds.revision, ds.page);
if app.ui.dev.measured_key != Some(key) {
app.ui.dev.measured = dev_measurements::on_page(&ds.doc, ds.page);
app.ui.dev.measured_key = Some(key);
}
}
/// The lines of the measurements kept on the canvas, drawn with the hover measurements (Inspect
/// tab only; both layers shown; the focus view's frame only).
pub fn saved_lines(app: &FigApp) -> impl Iterator<Item = &Guide> {
let ds = app.session.ds().ok().filter(|_| active(app));
let focus = ds.and_then(figcraft_engine::DocState::focused);
app.ui
.dev
.measured
.iter()
.filter(move |m| {
ds.is_some_and(|ds| {
let shown = |id: NodeId| ds.doc.node(id).is_some_and(|n| n.visible);
shown(m.from) && m.measurement.to.is_none_or(shown) && focus.is_none_or(|f| ds.doc.is_ancestor(f, m.from))
})
})
.flat_map(|m| &m.lines)
}
/// One annotation as a card: its category, its note (up to six lines) and its pinned properties /// One annotation as a card: its category, its note (up to six lines) and its pinned properties
/// with their values. /// with their values.
struct Callout { struct Callout {
@@ -690,6 +725,97 @@ pub fn dev_resources_section(app: &mut FigApp, ui: &mut Ui, doc: &Document, id:
ui.add_space(4.0); ui.add_space(4.0);
} }
/// The menu label of measuring along `side`.
fn side_label(side: figcraft_doc::Side) -> &'static str {
use figcraft_doc::Side;
match side {
Side::Top => tr("Along the top"),
Side::Right => tr("Along the right"),
Side::Bottom => tr("Along the bottom"),
Side::Left | Side::Auto => tr("Along the left"),
}
}
/// Measurements kept on the canvas: those from or to the selected layer (with nothing selected,
/// every one on the page), each with its distances; a click selects the layer it starts from and
/// − removes it. With a layer selected, a menu places a new one: between two selected layers, to
/// the layer around it or along one of its sides.
pub fn measurements_section(app: &mut FigApp, ui: &mut Ui, doc: &Document, sel: &[NodeId]) {
use figcraft_doc::Side;
refresh_measurements(app);
let t = Tokens::get(ui.ctx());
let first = sel.first().copied().filter(|id| doc.node(*id).is_some_and(|n| !n.is_page()));
let rows: Vec<&Placed> = app.ui.dev.measured.iter().filter(|m| first.is_none_or(|id| m.touches(id))).collect();
if first.is_none() && rows.is_empty() {
return;
}
let mut action: Option<(&str, serde_json::Value)> = None;
widgets::separator(ui);
widgets::section_header(ui, tr("Measurements"), &[], rows.is_empty());
for m in &rows {
if let Some(a) = measurement_row(ui, &t, doc, m) {
action = Some(a);
}
}
if let Some(id) = first {
let around = doc.node(id).and_then(|n| n.parent).and_then(|p| doc.node(p)).is_some_and(|p| !p.is_page());
let w = ui.available_width() - PAD * 2.0;
row(ui, 32.0, |ui| {
let resp = widgets::dropdown(ui, tr("Add measurement"), w, Some(Icon::Measure))
.on_hover_text(tr("Keep a measurement on the canvas for developers"));
menu_popup(&resp).show(|ui| {
if let [a, b, ..] = sel
&& menu_item(ui, tr("Between the selected layers"), "")
{
action = Some(("devMode.addMeasurement", json!({"from": a, "to": b})));
}
if around && menu_item(ui, tr("To the layer around it"), "") {
action = Some(("devMode.addMeasurement", json!({"from": id})));
}
for side in [Side::Top, Side::Right, Side::Bottom, Side::Left] {
if menu_item(ui, side_label(side), "") {
action = Some(("devMode.addMeasurement", json!({"from": id, "side": side})));
}
}
});
});
}
if let Some((cmd, params)) = action {
app.run(cmd, params);
}
ui.add_space(4.0);
}
/// One measurement in the Inspect tab, on two lines: what it measures, and its distances below. A
/// click selects the layer it starts from, − removes it: the command to run.
fn measurement_row(ui: &mut Ui, t: &Tokens, doc: &Document, m: &Placed) -> Option<(&'static str, serde_json::Value)> {
let name = |id: NodeId| doc.node(id).map_or(String::new(), |n| n.name.clone());
let mut action = None;
let (r, resp) = ui.allocate_exact_size(vec2(ui.available_width(), 36.0), Sense::click());
if resp.hovered() {
ui.painter().rect_filled(r.shrink2(vec2(PAD / 2.0, 1.0)), CornerRadius::same(4), t.hover);
}
icons::paint(ui.painter(), Rect::from_center_size(pos2(r.min.x + PAD + 7.0, r.center().y), vec2(14.0, 14.0)), Icon::Measure, t.icon_secondary);
let what = match (m.measurement.to, m.measurement.side) {
(Some(to), _) => format!("{} → {}", name(m.from), name(to)),
(None, side) => format!("{} · {}", name(m.from), side.map_or("", side_label)),
};
let value = m.labels().join(" · ");
let del = Rect::from_center_size(pos2(r.max.x - PAD - 4.0, r.center().y), vec2(22.0, 22.0));
let x = r.min.x + PAD + 22.0;
let text_w = del.min.x - 6.0 - x;
ui.painter().text(pos2(x, r.center().y - 7.0), Align2::LEFT_CENTER, widgets::elide(ui, &what, text_w), regular(11.0), t.text);
ui.painter().text(pos2(x, r.center().y + 7.0), Align2::LEFT_CENTER, widgets::elide(ui, &value, text_w), regular(10.0), t.text_secondary);
crate::a11y::button(&resp, &format!("{what}: {value}"));
if resp.clicked() {
action = Some(("select.set", json!({"ids": [m.from]})));
}
if widgets::icon_button_at(ui, Id::new(("measure_del", m.from, m.index)), del, Icon::Minus, tr("Remove measurement")) {
action = Some(("devMode.removeMeasurement", json!({"from": m.from, "index": m.index})));
}
action
}
/// The category of annotation `a` as a dropdown: the one picked (`Value::Null` for none). /// The category of annotation `a` as a dropdown: the one picked (`Value::Null` for none).
fn category_menu(ui: &mut Ui, doc: &Document, id: Id, a: &Annotation) -> Option<serde_json::Value> { fn category_menu(ui: &mut Ui, doc: &Document, id: Id, a: &Annotation) -> Option<serde_json::Value> {
let current = annotations::category_of(doc, a); let current = annotations::category_of(doc, a);
+78
View File
@@ -108,3 +108,81 @@ fn dev_mode_pastes_only_a_comments_link() {
crate::menus::paste(&mut app, None, None); crate::menus::paste(&mut app, None, None);
assert_eq!(app.ui.comments.open, Some(c), "the link opens its thread"); assert_eq!(app.ui.comments.open, Some(c), "the link opens its thread");
} }
#[test]
fn dev_mode_layers_panel_selects_and_expands_but_edits_nothing() {
use egui::{Event, Pos2};
let mut s = Session::new();
let f = id(&s.execute("frame.create", &json!({"x": 0, "y": 0, "width": 300, "height": 400, "name": "Login"})).unwrap());
let a = id(&s.execute("shape.rectangle", &json!({"x": 20, "y": 30, "width": 120, "height": 40, "parent": f})).unwrap());
let b = id(&s.execute("shape.rectangle", &json!({"x": 20, "y": 90, "width": 120, "height": 40, "parent": f})).unwrap());
s.execute("select.none", &json!({})).unwrap();
let mut u = crate::layers_drag_tests::Ui::new(s);
u.app.ui.right_tab = crate::RightTab::Inspect;
u.app.ui.expanded.insert(f);
u.frame(vec![]);
let order = |u: &crate::layers_drag_tests::Ui| u.app.session.doc().unwrap().node(f).unwrap().children.clone();
let before = order(&u);
let (ra, rb) = (u.row_at(a), u.row_at(b));
let click = |u: &mut crate::layers_drag_tests::Ui, p: Pos2| {
u.button(p, true);
u.button(p, false);
};
let rest = |u: &mut crate::layers_drag_tests::Ui| {
for _ in 0..30 {
u.frame(vec![]);
}
};
// A click selects.
click(&mut u, ra);
assert_eq!(u.app.session.selection(), [a]);
// A double click doesn't rename, and says why.
click(&mut u, ra);
assert!(u.app.ui.rename.is_none(), "no rename");
assert!(u.app.ui.toast.as_ref().is_some_and(|(m, _)| m.contains("Shift+D")));
rest(&mut u);
// The eye and padlock spots don't hide or lock (the row is selected instead).
let (eye, padlock) = (272.0, 250.0);
for x in [eye, padlock] {
u.frame(vec![Event::PointerMoved(Pos2::new(x, rb.y))]);
click(&mut u, Pos2::new(x, rb.y));
rest(&mut u);
}
let doc = u.app.session.doc().unwrap();
assert!(doc.node(b).unwrap().visible && !doc.node(b).unwrap().locked, "not hidden or locked");
assert_eq!(u.app.session.selection(), [b]);
// Dragging a row doesn't reorder.
u.drag_to(ra, rb + egui::vec2(0.0, 6.0));
u.button(rb + egui::vec2(0.0, 6.0), false);
rest(&mut u);
assert_eq!(order(&u), before, "not reordered");
// Back in the design, the same double click renames.
u.app.ui.right_tab = crate::RightTab::Design;
u.frame(vec![]);
click(&mut u, ra);
click(&mut u, ra);
assert!(u.app.ui.rename.is_some_and(|r| r.id == a));
}
#[test]
fn measurements_kept_on_the_canvas_draw_in_dev_mode_and_follow_their_layers() {
let mut s = Session::new();
let f = id(&s.execute("frame.create", &json!({"x": 0, "y": 0, "width": 300, "height": 400})).unwrap());
let a = id(&s.execute("shape.rectangle", &json!({"x": 20, "y": 30, "width": 120, "height": 40, "parent": f})).unwrap());
let b = id(&s.execute("shape.rectangle", &json!({"x": 20, "y": 90, "width": 120, "height": 40, "parent": f})).unwrap());
let mut u = Ui::new(s);
u.run("devMode.addMeasurement", json!({"from": a, "to": b}));
let labels = |u: &Ui| crate::dev_mode::saved_lines(&u.app).filter_map(|g| g.label.clone()).collect::<Vec<_>>();
u.frame(vec![]);
assert!(labels(&u).is_empty(), "the design doesn't show them");
u.app.ui.right_tab = crate::RightTab::Inspect;
u.frame(vec![]);
assert_eq!(labels(&u), ["20"]);
u.run("node.set", json!({"id": b, "props": {"y": 110}}));
u.frame(vec![]);
assert_eq!(labels(&u), ["40"], "it follows the layer");
// Hidden layers' measurements aren't drawn.
u.run("node.set", json!({"id": b, "props": {"visible": false}}));
u.frame(vec![]);
assert!(labels(&u).is_empty());
}
+9
View File
@@ -209,6 +209,8 @@ icons! {
Connector, Connector,
Shapes, Shapes,
Stamp, Stamp,
/// A measurement: a dimension line with arrowheads between two end ticks.
Measure,
} }
/// Points along a circular arc from angle `a0` to `a1` (radians, y down: increasing is clockwise). /// Points along a circular arc from angle `a0` to `a1` (radians, y down: increasing is clockwise).
@@ -1180,6 +1182,13 @@ fn draw(p: &P, icon: Icon) {
p.circle(8.0, 8.0, 6.0); p.circle(8.0, 8.0, 6.0);
p.poly(&[(5.0, 8.3), (7.2, 10.5), (11.0, 6.0)], false); p.poly(&[(5.0, 8.3), (7.2, 10.5), (11.0, 6.0)], false);
} }
Measure => {
p.line(2.5, 4.0, 2.5, 12.0);
p.line(13.5, 4.0, 13.5, 12.0);
p.line(3.0, 8.0, 13.0, 8.0);
p.poly(&[(5.5, 5.5), (3.0, 8.0), (5.5, 10.5)], false);
p.poly(&[(10.5, 5.5), (13.0, 8.0), (10.5, 10.5)], false);
}
Logo => { Logo => {
// FigCraft's own mark: the curled tail of the chameleon on the app icon. One bold // FigCraft's own mark: the curled tail of the chameleon on the app icon. One bold
// stroke: a shaft along the bottom, then three half turns, each tighter than the last. // stroke: a shaft along the bottom, then three half turns, each tighter than the last.
+17 -7
View File
@@ -704,6 +704,11 @@ fn search_field(ui: &mut Ui, t: &Tokens, w: f32, id: Id, hint: &str, text: &mut
fn layers(app: &mut FigApp, ui: &mut Ui) { fn layers(app: &mut FigApp, ui: &mut Ui) {
let t = Tokens::get(ui.ctx()); let t = Tokens::get(ui.ctx());
// Dev Mode only inspects: rows select and expand, but don't rename, reorder, hide or lock.
let dev = app.ui.right_tab == crate::RightTab::Inspect;
if dev {
app.ui.rename = None;
}
let w = ui.available_width(); let w = ui.available_width();
let (hr, _) = ui.allocate_exact_size(vec2(w, 40.0), Sense::hover()); let (hr, _) = ui.allocate_exact_size(vec2(w, 40.0), Sense::hover());
ui.painter().text(pos2(hr.min.x + 16.0, hr.center().y), Align2::LEFT_CENTER, tr("Layers"), semibold(11.0), t.text); ui.painter().text(pos2(hr.min.x + 16.0, hr.center().y), Align2::LEFT_CENTER, tr("Layers"), semibold(11.0), t.text);
@@ -825,7 +830,7 @@ fn layers(app: &mut FigApp, ui: &mut Ui) {
let ui = &mut band_ui; let ui = &mut band_ui;
for row in rows.iter().take(last).skip(first) { for row in rows.iter().take(last).skip(first) {
let Some(n) = doc.node(row.id) else { continue }; let Some(n) = doc.node(row.id) else { continue };
let (r, resp) = ui.allocate_exact_size(vec2(w, ROW), Sense::click_and_drag()); let (r, resp) = ui.allocate_exact_size(vec2(w, ROW), if dev { Sense::click() } else { Sense::click_and_drag() });
let selected = sel.contains(&row.id); let selected = sel.contains(&row.id);
a11y::layer_row(&mut app.ui.nav, &resp); a11y::layer_row(&mut app.ui.nav, &resp);
if sel.last() == Some(&row.id) { if sel.last() == Some(&row.id) {
@@ -923,14 +928,17 @@ fn layers(app: &mut FigApp, ui: &mut Ui) {
// Lock / eye on hover (or when set); a layer in a locked one shows a faint padlock. // Lock / eye on hover (or when set); a layer in a locked one shows a faint padlock.
let hidden = swept(false).unwrap_or(!n.visible); let hidden = swept(false).unwrap_or(!n.visible);
let locked = swept(true).unwrap_or(n.locked); let locked = swept(true).unwrap_or(n.locked);
let show_lock = over || locked || row.locked_above; // In Dev Mode they only show what is set.
let show_eye = over || hidden; let show_lock = (over && !dev) || locked || row.locked_above;
let show_eye = (over && !dev) || hidden;
let er = Rect::from_center_size(pos2(rr.max.x - 14.0, r.center().y), vec2(20.0, 20.0)); let er = Rect::from_center_size(pos2(rr.max.x - 14.0, r.center().y), vec2(20.0, 20.0));
let lr = Rect::from_center_size(pos2(rr.max.x - 36.0, r.center().y), vec2(20.0, 20.0)); let lr = Rect::from_center_size(pos2(rr.max.x - 36.0, r.center().y), vec2(20.0, 20.0));
if show_eye { if show_eye {
icons::paint(ui.painter(), er.shrink(3.0), if hidden { Icon::EyeOff } else { Icon::Eye }, t.icon_secondary);
}
if show_eye && !dev {
let eresp = ui.interact(er, ui.id().with(("eye", row.id)), Sense::click_and_drag()); let eresp = ui.interact(er, ui.id().with(("eye", row.id)), Sense::click_and_drag());
a11y::toggle(&eresp, tr("Hidden"), hidden); a11y::toggle(&eresp, tr("Hidden"), hidden);
icons::paint(ui.painter(), er.shrink(3.0), if hidden { Icon::EyeOff } else { Icon::Eye }, t.icon_secondary);
if eresp.clicked() { if eresp.clicked() {
vis = Some(row.id); vis = Some(row.id);
} }
@@ -939,10 +947,12 @@ fn layers(app: &mut FigApp, ui: &mut Ui) {
} }
} }
if show_lock { if show_lock {
let lresp = ui.interact(lr, ui.id().with(("lock", row.id)), Sense::click_and_drag());
a11y::toggle(&lresp, tr("Locked"), locked || row.locked_above);
let col = if locked || !row.locked_above { t.icon_secondary } else { t.text_tertiary }; let col = if locked || !row.locked_above { t.icon_secondary } else { t.text_tertiary };
icons::paint(ui.painter(), lr.shrink(3.0), if locked || row.locked_above { Icon::Lock } else { Icon::Unlock }, col); icons::paint(ui.painter(), lr.shrink(3.0), if locked || row.locked_above { Icon::Lock } else { Icon::Unlock }, col);
}
if show_lock && !dev {
let lresp = ui.interact(lr, ui.id().with(("lock", row.id)), Sense::click_and_drag());
a11y::toggle(&lresp, tr("Locked"), locked || row.locked_above);
if lresp.clicked() { if lresp.clicked() {
lock = Some(row.id); lock = Some(row.id);
} }
@@ -953,7 +963,7 @@ fn layers(app: &mut FigApp, ui: &mut Ui) {
if resp.clicked() { if resp.clicked() {
clicked = Some((row.id, ui.input(|i| i.modifiers))); clicked = Some((row.id, ui.input(|i| i.modifiers)));
} }
if resp.double_clicked() { if resp.double_clicked() && !crate::menus::dev_mode_refuses(app, "object.rename") {
app.ui.rename = Some(Rename { id: row.id, start: true }); app.ui.rename = Some(Rename { id: row.id, start: true });
} }
if resp.secondary_clicked() { if resp.secondary_clicked() {
+4 -3
View File
@@ -12,8 +12,9 @@ const GAP: f32 = 4.0;
/// Padding inside a label's pill. /// Padding inside a label's pill.
const PAD: Vec2 = vec2(4.0, 2.0); const PAD: Vec2 = vec2(4.0, 2.0);
/// Draw the tool overlay's guides, measurement labels and measured box; labels keep clear of /// Draw the tool overlay's guides, measurement labels and measured box, and in Dev Mode the
/// `avoid` (the selection's size label). /// measurements kept on the canvas; labels keep clear of `avoid` (the selection's size label) and
/// of one another.
pub fn draw(app: &FigApp, painter: &egui::Painter, t: &Tokens, avoid: Option<Rect>) { pub fn draw(app: &FigApp, painter: &egui::Painter, t: &Tokens, avoid: Option<Rect>) {
let ov = &app.session.tool.overlay; let ov = &app.session.tool.overlay;
let line = Stroke::new(1.0, t.guide); let line = Stroke::new(1.0, t.guide);
@@ -21,7 +22,7 @@ pub fn draw(app: &FigApp, painter: &egui::Painter, t: &Tokens, avoid: Option<Rec
painter.rect_stroke(Rect::from_two_pos(app.to_screen(x0, y0), app.to_screen(x1, y1)), CornerRadius::ZERO, line, StrokeKind::Middle); painter.rect_stroke(Rect::from_two_pos(app.to_screen(x0, y0), app.to_screen(x1, y1)), CornerRadius::ZERO, line, StrokeKind::Middle);
} }
let mut labels: Vec<(Pos2, Pos2, String)> = Vec::new(); let mut labels: Vec<(Pos2, Pos2, String)> = Vec::new();
for g in &ov.guides { for g in ov.guides.iter().chain(crate::dev_mode::saved_lines(app)) {
let (a, b) = (app.to_screen(g.x0, g.y0), app.to_screen(g.x1, g.y1)); let (a, b) = (app.to_screen(g.x0, g.y0), app.to_screen(g.x1, g.y1));
if g.x0 == g.x1 && g.y0 == g.y1 { if g.x0 == g.x1 && g.y0 == g.y1 {
for d in [vec2(4.0, 4.0), vec2(4.0, -4.0)] { for d in [vec2(4.0, 4.0), vec2(4.0, -4.0)] {
+2 -2
View File
@@ -109,8 +109,8 @@ fn clipboard_png(app: &mut FigApp) -> Option<(Vec<u8>, u32, u32)> {
/// Dev Mode (the Inspect tab) refuses command `id` from the menus, shortcuts and clipboard: it /// Dev Mode (the Inspect tab) refuses command `id` from the menus, shortcuts and clipboard: it
/// edits the design ([`figcraft_engine::devinspect::dev_mode_allows`]). Says so when it does. /// edits the design ([`figcraft_engine::devinspect::dev_mode_allows`]). Says so when it does.
/// shortcut: the layers panel (rename, reorder, hide, lock) and files dropped on the canvas still /// shortcut: files dropped on the canvas still edit in Dev Mode; route them through here if people
/// edit in Dev Mode; route them through here if people trip over it. /// trip over it.
pub fn dev_mode_refuses(app: &mut FigApp, id: &str) -> bool { pub fn dev_mode_refuses(app: &mut FigApp, id: &str) -> bool {
let refused = app.ui.right_tab == crate::RightTab::Inspect && !figcraft_engine::devinspect::dev_mode_allows(id); let refused = app.ui.right_tab == crate::RightTab::Inspect && !figcraft_engine::devinspect::dev_mode_allows(id);
if refused { if refused {
+2
View File
@@ -2243,6 +2243,7 @@ fn inspect(app: &mut FigApp, ui: &mut Ui) {
widgets::note(ui, tr("Hover other layers to measure the distance"), t.text_secondary); widgets::note(ui, tr("Hover other layers to measure the distance"), t.text_secondary);
crate::dev_mode::ready_list(app, ui); crate::dev_mode::ready_list(app, ui);
crate::dev_mode::categories_section(app, ui); crate::dev_mode::categories_section(app, ui);
crate::dev_mode::measurements_section(app, ui, &doc, &sel);
crate::dev_mode::plugins_section(app, ui); crate::dev_mode::plugins_section(app, ui);
return; return;
}; };
@@ -2251,6 +2252,7 @@ fn inspect(app: &mut FigApp, ui: &mut Ui) {
crate::dev_mode::status(app, ui, &doc, &sel); crate::dev_mode::status(app, ui, &doc, &sel);
crate::dev_mode::annotations_section(app, ui, &doc, first); crate::dev_mode::annotations_section(app, ui, &doc, first);
crate::dev_mode::dev_resources_section(app, ui, &doc, first); crate::dev_mode::dev_resources_section(app, ui, &doc, first);
crate::dev_mode::measurements_section(app, ui, &doc, &sel);
use figcraft_engine::devinspect::ColorFormat; use figcraft_engine::devinspect::ColorFormat;
let fmt_id = Id::new("inspect_color_format"); let fmt_id = Id::new("inspect_color_format");
let fmt = ui.data(|d| d.get_temp::<usize>(fmt_id)).and_then(|i| ColorFormat::ALL.get(i).copied()).unwrap_or_default(); let fmt = ui.data(|d| d.get_temp::<usize>(fmt_id)).and_then(|i| ColorFormat::ALL.get(i).copied()).unwrap_or_default();
+2 -1
View File
@@ -131,7 +131,8 @@ component/instance links, overrides, `slot` (a slot layer's property name), boun
`devStatus` (handoff status), `devChanged` (changed since it was marked ready for dev), `devStatus` (handoff status), `devChanged` (changed since it was marked ready for dev),
`annotations` (Dev Mode notes: `[{label, properties, category?}]`), `codeLinks` (a component's `annotations` (Dev Mode notes: `[{label, properties, category?}]`), `codeLinks` (a component's
links to code: `[{language, importPath, name, template}]`), `devResources` (links for developers, links to code: `[{language, importPath, name, template}]`), `devResources` (links for developers,
its own and inherited: `[{name, url, index?, from?}]`), `pluginData` (layer data written with its own and inherited: `[{name, url, index?, from?}]`), `measurements` (Dev Mode measurements
starting from the layer: `[{to} | {side}]`), `pluginData` (layer data written with
`node.set {props: {pluginData: {key: value}}}`, pages included: `{key: value}`, values as strings; `node.set {props: {pluginData: {key: value}}}`, pages included: `{key: value}`, values as strings;
only when there is some). The auto layout props only when there is some). The auto layout props
that `node.set` accepts are listed in [mcp.md](mcp.md#conventions-agents-must-know). that `node.set` accepts are listed in [mcp.md](mcp.md#conventions-agents-must-know).
+8 -7
View File
@@ -1,16 +1,16 @@
# Dev Mode parity # Dev Mode parity
> **Last reviewed:** 2026-10-15 · **Last updated:** 2026-10-15 · **Change:** minor (import lines in generated layer trees; compare with any saved version; Dev Mode behaviour suite: an inspect-only canvas, Changed in the ready list, statuses only on frames and sections; plugin panels from the Inspect tab) · **Target:** Figma Design Dev Mode (desktop app 126.10.7) > **Last reviewed:** 2026-10-24 · **Last updated:** 2026-10-24 · **Change:** minor (measurements kept on the canvas; a read-only layers panel in Dev Mode; earlier: import lines in generated layer trees; compare with any saved version; Dev Mode behaviour suite: an inspect-only canvas, Changed in the ready list, statuses only on frames and sections; plugin panels from the Inspect tab) · **Target:** Figma Design Dev Mode (desktop app 126.10.7)
**Breadth ~97%, ready ~76% (estimated).** The inspect surface is broad, components connect **Breadth ~98%, ready ~78% (estimated).** The inspect surface is broad, components connect
to the code components of a repository (with their imports, also inside generated layer trees), to the code components of a repository (with their imports, also inside generated layer trees),
changes compare with any saved version and the canvas only inspects in Dev Mode. Left: measurements changes compare with any saved version, the canvas and the layers panel only inspect in Dev Mode,
kept on the canvas, a read-only layers panel in Dev Mode, publishing mappings from the code and measurements stay on the canvas for everyone. Left: publishing mappings from the code
repository. Remaining: 2–4 h. repository, third-party integrations. Remaining: 1–2 h.
| Feature | Status | Notes | | Feature | Status | Notes |
|---|---|---| |---|---|---|
| Dev Mode toggle (Shift+D), Inspect panel | done | the canvas only inspects: presses select, pan and comment but never move, resize, draw or edit text and vectors; menus and shortcuts refuse design edits with a note (Dev Mode's annotations, statuses, dev resources, code connections and comments still work); a drawing tool chosen in the toolbar goes back to the design. The layers panel and dropped files still edit (ours to tighten) | | Dev Mode toggle (Shift+D), Inspect panel | done | the canvas only inspects: presses select, pan and comment but never move, resize, draw or edit text and vectors; menus and shortcuts refuse design edits with a note (Dev Mode's annotations, statuses, dev resources, measurements, code connections and comments still work); a drawing tool chosen in the toolbar goes back to the design. The layers panel only selects and expands (no renaming, reordering, hiding or locking; a double click says why); files dropped on the canvas still edit (ours to tighten) |
| Properties with their styles and variables; redlines on hover | done | positions from the frame around the layer (groups aren't frames), rotation, radius (CSS corner order), opacity, blend; colours in hex, RGB, CSS, HSL or HSB (`inspect.properties {colorFormat}`, the Inspect tab's Color format) | | Properties with their styles and variables; redlines on hover | done | positions from the frame around the layer (groups aren't frames), rotation, radius (CSS corner order), opacity, blend; colours in hex, RGB, CSS, HSL or HSB (`inspect.properties {colorFormat}`, the Inspect tab's Color format) |
| Code: CSS, iOS (SwiftUI), Android (Compose) | done | plus HTML, React and Leptos (ours; Leptos: `#[component]` functions with `#[prop]` defaults, variant enums and `view!` markup); components take properties as parameters, instances are calls; CSS in px or rem (`inspect.code {unit, rootFontSize}`), with rotation, blend modes, gradient directions and kinds (linear, radial, conic), text alignment, case and decoration | | Code: CSS, iOS (SwiftUI), Android (Compose) | done | plus HTML, React and Leptos (ours; Leptos: `#[component]` functions with `#[prop]` defaults, variant enums and `view!` markup); components take properties as parameters, instances are calls; CSS in px or rem (`inspect.code {unit, rootFontSize}`), with rotation, blend modes, gradient directions and kinds (linear, radial, conic), text alignment, case and decoration |
| Assets list with export | done | layers with export settings, icons (small, vector shapes only: SVG) and images (PNG); the icon rule is ours | | Assets list with export | done | layers with export settings, icons (small, vector shapes only: SVG) and images (PNG); the icon rule is ours |
@@ -20,7 +20,7 @@ repository. Remaining: 2–4 h.
| Compare changes (before/after side by side or overlaid, property diffs) | done | against the snapshot taken when a frame was first changed after it was marked ready, or any saved version (`dev.compare {version}`; the view's Compare with menu); a frame that isn't ready for dev compares with versions | | Compare changes (before/after side by side or overlaid, property diffs) | done | against the snapshot taken when a frame was first changed after it was marked ready, or any saved version (`dev.compare {version}`; the view's Compare with menu); a frame that isn't ready for dev compares with versions |
| Code connect (map components to repository code) | done | per language (React, Leptos, SwiftUI, Compose): import path, name and a template with `{property}` and `{property ? "a" : "b"}` placeholders; instances (also inside generated layer trees) show the snippet with their values; edited in the Inspect panel with a live preview; `figcraft.connect.json` export/import; generated layer trees bring the import lines of the connected components they call (also in definitions, slots and swapped-in instances) once each at the top, per language. No CLI publishing mappings from the code repository | | Code connect (map components to repository code) | done | per language (React, Leptos, SwiftUI, Compose): import path, name and a template with `{property}` and `{property ? "a" : "b"}` placeholders; instances (also inside generated layer trees) show the snippet with their values; edited in the Inspect panel with a live preview; `figcraft.connect.json` export/import; generated layer trees bring the import lines of the connected components they call (also in definitions, slots and swapped-in instances) once each at the top, per language. No CLI publishing mappings from the code repository |
| Plugins in Dev Mode (Jira, GitHub, etc.) | partial | FigCraft's own WASM plugins that only read run from the Inspect tab, with their panels (callbacks can only read); no third-party integrations | | Plugins in Dev Mode (Jira, GitHub, etc.) | partial | FigCraft's own WASM plugins that only read run from the Inspect tab, with their panels (callbacks can only read); no third-party integrations |
| Measurements (redlines on hover, Alt in the design) | partial | distances between the selection and the hovered layer, its parent or a guide, auto layout padding and gaps (Shift+Alt); measurements kept on the canvas for others aren't there | | Measurements (redlines on hover, Alt in the design) | done | distances between the selection and the hovered layer, its parent or a guide, auto layout padding and gaps (Shift+Alt); measurements kept on the canvas (`devMode.addMeasurement` between two layers, along a side or to the layer around it; stored on the layer, saved, synced, undoable) drawn in Dev Mode as labelled redlines that follow the layers, listed, placed and removed in the Inspect tab; a measurement whose other layer was deleted isn't drawn until an undo brings it back |
| VS Code extension | out of scope | FigCraft's MCP server and control channel already serve design data, code and connected snippets to agents and editors | | VS Code extension | out of scope | FigCraft's MCP server and control channel already serve design data, code and connected snippets to agents and editors |
| Dev resources links on layers | done | a URL and a name per link on any layer (`devResource.*`, saved, synced, undoable); instances list their main component's (and its set's) read-only; URLs checked as http/https and never fetched (no page titles: a link without a name is named after its URL); the Inspect tab lists, adds, edits and removes them; `get_node` returns them | | Dev resources links on layers | done | a URL and a name per link on any layer (`devResource.*`, saved, synced, undoable); instances list their main component's (and its set's) read-only; URLs checked as http/https and never fetched (no page titles: a link without a name is named after its URL); the Inspect tab lists, adds, edits and removes them; `get_node` returns them |
@@ -28,6 +28,7 @@ repository. Remaining: 2–4 h.
| Date | Change | Summary | | Date | Change | Summary |
|---|---|---| |---|---|---|
| 2026-10-24 | minor | 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. Breadth 97% → 98%, ready 76% → 78%, 2–4 h → 1–2 h |
| 2026-10-15 | minor | Dev Mode tail (gap 16): generated layer trees (React, Leptos, SwiftUI, Compose) start with the import lines of the connected components they call, once each, also those in a component's definition, in slots an instance fills and placed by a connected template's instance swaps; Compare changes takes any saved version as the before (`dev.compare {version}`, the view's Compare with menu; frames that aren't ready for dev compare with versions). Behaviour suite (5 tests; 4 failed before the fixes): the Dev Mode canvas only inspects (no moving, resizing, drawing, text or vector editing; menus, shortcuts, nudges, opacity keys and paste refuse design edits with a note; toolbar drawing tools go back to the design), the ready list filters and badges Changed items, statuses are refused on layers inside frames. Read-only plugins run from the Inspect tab show their panels (the runtime's panels, reused). Breadth 96% → 97% (measurements kept on the canvas and the read-only layers panel counted as missing), ready 72% → 76%, 0–1 h → 2–4 h | | 2026-10-15 | minor | Dev Mode tail (gap 16): generated layer trees (React, Leptos, SwiftUI, Compose) start with the import lines of the connected components they call, once each, also those in a component's definition, in slots an instance fills and placed by a connected template's instance swaps; Compare changes takes any saved version as the before (`dev.compare {version}`, the view's Compare with menu; frames that aren't ready for dev compare with versions). Behaviour suite (5 tests; 4 failed before the fixes): the Dev Mode canvas only inspects (no moving, resizing, drawing, text or vector editing; menus, shortcuts, nudges, opacity keys and paste refuse design edits with a note; toolbar drawing tools go back to the design), the ready list filters and badges Changed items, statuses are refused on layers inside frames. Read-only plugins run from the Inspect tab show their panels (the runtime's panels, reused). Breadth 96% → 97% (measurements kept on the canvas and the read-only layers panel counted as missing), ready 72% → 76%, 0–1 h → 2–4 h |
| 2026-10-12 | minor | Dev resources (gap 16): links for developers (a URL and a name) on any layer: `devResource.add` / `edit` / `remove` / `list`, undoable, saved and synced; an instance also lists its main component's (and that component's set's) links read-only with where they come from, and layers inside an instance show their main layer's; URLs are checked as web links (http or https, a host, no spaces, at most 2,048 characters) and never fetched (a link added without a name is named after its URL), opened only when clicked; the Inspect tab lists, adds, edits and removes them; `get_node` and `inspect_document` return them as `devResources`. The editor extension stays out of scope (the MCP server serves agents and editors). Dev Mode breadth 93% → 96%, ready 70% → 72%, 1–3 h → 0–1 h; feature breadth (~82%) and ready for real work (~51%) unchanged at this rounding; full parity ~304–593 h → ~303–591 h | | 2026-10-12 | minor | Dev resources (gap 16): links for developers (a URL and a name) on any layer: `devResource.add` / `edit` / `remove` / `list`, undoable, saved and synced; an instance also lists its main component's (and that component's set's) links read-only with where they come from, and layers inside an instance show their main layer's; URLs are checked as web links (http or https, a host, no spaces, at most 2,048 characters) and never fetched (a link added without a name is named after its URL), opened only when clicked; the Inspect tab lists, adds, edits and removes them; `get_node` and `inspect_document` return them as `devResources`. The editor extension stays out of scope (the MCP server serves agents and editors). Dev Mode breadth 93% → 96%, ready 70% → 72%, 1–3 h → 0–1 h; feature breadth (~82%) and ready for real work (~51%) unchanged at this rounding; full parity ~304–593 h → ~303–591 h |
| 2026-10-11 | minor | Export and inspect behaviour suite written from the public documentation (139 cases in 19 tests; 47 failed before the fixes) and its fixes: an export covers what the layer paints, so a drop shadow adds room only where it falls, strokes on boxes and ellipses reach their real width (they added up to four times as much), hidden layers take no room and a mask group exports the mask's area; a slice exports everything under it; slashes in layer names export into folders; Copy as PNG copies the whole selection. Added: the export settings' contents only and include bounding box, and for SVG outline text (off writes `<text>`), unique id attributes and simplified strokes (stroke attributes where exact); Inspect colour formats (hex, RGB, CSS, HSL, HSB), CSS in rem, a Blend row, positions measured from the frame around a layer (groups aren't frames), CSS for rotation, blend modes, gradient directions and kinds, text alignment, case and decoration; icons and images in the asset list (`file.exportLayers {assets}`). Dev Mode breadth 90% → 93%, ready 64% → 70%, hours unchanged (1–3, dev resources); feature breadth and ready for real work unchanged at this rounding | | 2026-10-11 | minor | Export and inspect behaviour suite written from the public documentation (139 cases in 19 tests; 47 failed before the fixes) and its fixes: an export covers what the layer paints, so a drop shadow adds room only where it falls, strokes on boxes and ellipses reach their real width (they added up to four times as much), hidden layers take no room and a mask group exports the mask's area; a slice exports everything under it; slashes in layer names export into folders; Copy as PNG copies the whole selection. Added: the export settings' contents only and include bounding box, and for SVG outline text (off writes `<text>`), unique id attributes and simplified strokes (stroke attributes where exact); Inspect colour formats (hex, RGB, CSS, HSL, HSB), CSS in rem, a Blend row, positions measured from the frame around a layer (groups aren't frames), CSS for rotation, blend modes, gradient directions and kinds, text alignment, case and decoration; icons and images in the asset list (`file.exportLayers {assets}`). Dev Mode breadth 90% → 93%, ready 64% → 70%, hours unchanged (1–3, dev resources); feature breadth and ready for real work unchanged at this rounding |
+2 -2
View File
@@ -20,7 +20,7 @@ and prototype interactions are still unchecked there.
| `.fig` (Figma Design, `com.figma.document`, `application/x-figma`) | native (cloud; "Save local copy") | **yes, best effort** (`file.open`, `file.import`, drop, CLI, MCP) | **experimental** ([below](#export-to-fig-experimental)) | `.fig` files open (best effort): the zip, its deflate/zstd chunks and the kiwi schema are decoded generically and mapped by field name onto the REST importer, so pages, the layer tree, geometry, paints (styles resolved), strokes, effects, text with styled runs, vectors as editable vector networks (points, curved segments, filled areas with their winding, per-point corner radius and handle mirroring, end caps; outlines when the network is missing or damaged), auto layout (grid included; a flow that records no primary-axis sizing hugs on that axis, an instance that records no auto layout has its main's), constraints, ellipse arcs, boolean operations, components, sets (with their descriptions), instances (expanded from their main with overrides, derived layout and property values; the size and resizing their layers override stay overrides), styles, variables with modes and bindings, prototyping (set-variable, set-variable-mode and conditional actions included) and image fills from the archive come across; sticky notes, shapes with text, connectors, tables, code blocks, slides and similar layers come in as the closest frames, text layers and vectors, with a warning; anything else is a warning. Since 2026-10-17 (community batches #727, #728): an instance's own identity and the cached library components a file carries, overrides applied by key and order, local text values and instance overrides over a shared text style, grid track sizes (fixed, content-sized, fill), slot content, labels of connectors saved with their drawn path (a static snapshot; the ends aren't live), skip ink, paragraph options and hanging lists, list start numbers, vertical trim, per-point stroke joins, editable text on a path, image adjustments and images carried in the message. Budgets come from the file's own size (one value per compressed byte, 20M–128M, lower ceilings on 32-bit targets; the layer cap follows), so a 15.4 MB synthetic file with 300,000 layers opens whole; the file's bytes stay out of the command journal; an import report lists what was left out or changed once the file opens. Not read: elbowed or curved connectors without a saved path (drawn straight) and labels of connectors without a saved snapshot, table cells beyond their own layers, filled areas with their own fills, variables bound to fields the editor can't bind, video fills. Tested on fixtures generated in code (`crates/format/src/fig/tests.rs`, `tail_tests.rs` and the per-feature `*_tests.rs` beside them, fuzzed) and by reading back what the export writes; no real-world files yet | read tail 5–10, write: checks against real files 5–15 | | `.fig` (Figma Design, `com.figma.document`, `application/x-figma`) | native (cloud; "Save local copy") | **yes, best effort** (`file.open`, `file.import`, drop, CLI, MCP) | **experimental** ([below](#export-to-fig-experimental)) | `.fig` files open (best effort): the zip, its deflate/zstd chunks and the kiwi schema are decoded generically and mapped by field name onto the REST importer, so pages, the layer tree, geometry, paints (styles resolved), strokes, effects, text with styled runs, vectors as editable vector networks (points, curved segments, filled areas with their winding, per-point corner radius and handle mirroring, end caps; outlines when the network is missing or damaged), auto layout (grid included; a flow that records no primary-axis sizing hugs on that axis, an instance that records no auto layout has its main's), constraints, ellipse arcs, boolean operations, components, sets (with their descriptions), instances (expanded from their main with overrides, derived layout and property values; the size and resizing their layers override stay overrides), styles, variables with modes and bindings, prototyping (set-variable, set-variable-mode and conditional actions included) and image fills from the archive come across; sticky notes, shapes with text, connectors, tables, code blocks, slides and similar layers come in as the closest frames, text layers and vectors, with a warning; anything else is a warning. Since 2026-10-17 (community batches #727, #728): an instance's own identity and the cached library components a file carries, overrides applied by key and order, local text values and instance overrides over a shared text style, grid track sizes (fixed, content-sized, fill), slot content, labels of connectors saved with their drawn path (a static snapshot; the ends aren't live), skip ink, paragraph options and hanging lists, list start numbers, vertical trim, per-point stroke joins, editable text on a path, image adjustments and images carried in the message. Budgets come from the file's own size (one value per compressed byte, 20M–128M, lower ceilings on 32-bit targets; the layer cap follows), so a 15.4 MB synthetic file with 300,000 layers opens whole; the file's bytes stay out of the command journal; an import report lists what was left out or changed once the file opens. Not read: elbowed or curved connectors without a saved path (drawn straight) and labels of connectors without a saved snapshot, table cells beyond their own layers, filled areas with their own fills, variables bound to fields the editor can't bind, video fills. Tested on fixtures generated in code (`crates/format/src/fig/tests.rs`, `tail_tests.rs` and the per-feature `*_tests.rs` beside them, fuzzed) and by reading back what the export writes; no real-world files yet | read tail 5–10, write: checks against real files 5–15 |
| `.jam` (whiteboards), `.deck` (Slides) | native to other Figma products | no | no | `.jam`: not read or written (boards are FigCraft files, see [boards-parity](boards-parity.md)); `.deck` out of scope | — | | `.jam` (whiteboards), `.deck` (Slides) | native to other Figma products | no | no | `.jam`: not read or written (boards are FigCraft files, see [boards-parity](boards-parity.md)); `.deck` out of scope | — |
| REST API file JSON (`GET /v1/files/:key`) + local variables JSON + `images/` folder | the API's output | **yes** (`file.open`, `file.import`, CLI) | no | pages, layer tree, geometry, paints, strokes, effects, text runs, vectors, auto layout (sizing modes left out read as hug, as documented; grid track sizes from `gridColumnsSizing` / `gridRowsSizing`: px, fr, auto, `minmax(0, 1fr)`, `repeat()`), constraints, grids, components, sets, instances with overrides (sizes and resizing included), component properties, slots (`SLOT` layers bound to their slot property; an instance's slot keeps its content as the instance's own unless its property value is the main's default content; slot limits and preferred components are warnings), styles, prototyping (incl. conditionals and variable modes), variables with modes, aliases, scopes and bindings, image adjustments (the same tint sign as `.fig`); the import report after opening; warnings for what's skipped (video triggers and actions, modifier keys). Unit tests in `crates/format/src/rest/*_tests.rs` | — | | REST API file JSON (`GET /v1/files/:key`) + local variables JSON + `images/` folder | the API's output | **yes** (`file.open`, `file.import`, CLI) | no | pages, layer tree, geometry, paints, strokes, effects, text runs, vectors, auto layout (sizing modes left out read as hug, as documented; grid track sizes from `gridColumnsSizing` / `gridRowsSizing`: px, fr, auto, `minmax(0, 1fr)`, `repeat()`), constraints, grids, components, sets, instances with overrides (sizes and resizing included), component properties, slots (`SLOT` layers bound to their slot property; an instance's slot keeps its content as the instance's own unless its property value is the main's default content; slot limits and preferred components are warnings), styles, prototyping (incl. conditionals and variable modes), variables with modes, aliases, scopes and bindings, image adjustments (the same tint sign as `.fig`); the import report after opening; warnings for what's skipped (video triggers and actions, modifier keys). Unit tests in `crates/format/src/rest/*_tests.rs` | — |
| `.figcraft` (ours: zip of `document.json` + images, versions inside) | — | yes | yes | streamed JSON, 50k layers save ~0.09 s / load ~0.16 s (`cargo xtask bench`); old files keep opening (`large_tests.rs`); saves write a unique staging file and rename it into place (#284); optional auto-save of files with a path (`pref.autoSave`, #305); `kind: "board"` marks a board (left out for design files, so files from before boards are design files; the home screen reads it for the file's mark); board objects keep their `board` metadata on the layer | — | | `.figcraft` (ours: zip of `document.json` + images, versions inside) | — | yes | yes | streamed JSON, 50k layers save ~0.09 s / load ~0.16 s (`cargo xtask bench`); old files keep opening (`large_tests.rs`); saves write a unique staging file and rename it into place (#284); optional auto-save of files with a path (`pref.autoSave`, #305); `kind: "board"` marks a board (left out for design files, so files from before boards are design files; the home screen reads it for the file's mark); board objects keep their `board` metadata on the layer; Dev Mode measurements are kept on the layer they start from (`measurements: [{to} | {side}]`, left out when there are none, so older files open with none) | — |
| Local library files | team libraries in the cloud | yes (enable a `.figcraft` as a library) | yes (publish) | per-asset versions, updates with overrides kept | see [collaboration-parity](collaboration-parity.md) | | Local library files | team libraries in the cloud | yes (enable a `.figcraft` as a library) | yes (publish) | per-asset versions, updates with overrides kept | see [collaboration-parity](collaboration-parity.md) |
## Import (place / paste / drop) ## Import (place / paste / drop)
@@ -115,7 +115,7 @@ each.
properties, arc fields, font weight: written as the values they give), text styles on part of a properties, arc fields, font weight: written as the values they give), text styles on part of a
text (written as values), variables' publishing, code syntax and inheritance settings, extended text (written as values), variables' publishing, code syntax and inheritance settings, extended
collections (written as collections of their own), comments, Dev Mode annotations, statuses, collections (written as collections of their own), comments, Dev Mode annotations, statuses,
resources and code links, plugin data, ruler resources, measurements and code links, plugin data, ruler
guides, prototype device settings, slot properties and slot content, brush guides, prototype device settings, slot properties and slot content, brush
strokes (written plain), strokes wider on one side of their path (written as filled outlines, or strokes (written plain), strokes wider on one side of their path (written as filled outlines, or
with the width on both sides when the shape has a fill too), pattern fills, WebP export settings (written as PNG), with the width on both sides when the shape has a fill too), pattern fills, WebP export settings (written as PNG),
+13 -7
View File
@@ -1,6 +1,6 @@
# Where FigCraft falls short of Figma # Where FigCraft falls short of Figma
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-24 · **Change:** minor (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-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)
Every known shortfall against Figma, one entry each, ranked by how much it stops a Figma user from 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 doing real work in FigCraft. Agents: pick work from the top unless the owner says otherwise. The
@@ -29,7 +29,7 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe
| 13 | Vector tail: none left (boolean hit testing on the result landed 2026-10-11) | Feature | — | [target-app-parity](target-app-parity.md) | | 13 | Vector tail: none left (boolean hit testing on the result landed 2026-10-11) | Feature | — | [target-app-parity](target-app-parity.md) |
| 14 | Variables: extended collections checked on real libraries (the tail landed 2026-10-12) | Feature | 0–1 | [components-parity](components-parity.md) | | 14 | Variables: extended collections checked on real libraries (the tail landed 2026-10-12) | Feature | 0–1 | [components-parity](components-parity.md) |
| 15 | Prototyping: media triggers and actions; controllers on macOS, Linux and FreeBSD (controller buttons on key/gamepad triggers, widgets in prototypes landed) | Feature | 2–5 | [prototyping-parity](prototyping-parity.md) | | 15 | Prototyping: media triggers and actions; controllers on macOS, Linux and FreeBSD (controller buttons on key/gamepad triggers, widgets in prototypes landed) | Feature | 2–5 | [prototyping-parity](prototyping-parity.md) |
| 16 | Dev Mode: measurements kept on the canvas, a read-only layers panel, publishing mappings from the code repository; the editor extension is left to the MCP server (import lines, compare with versions and an inspect-only canvas landed 2026-10-15) | Feature | 2–4 | [dev-mode-parity](dev-mode-parity.md) | | 16 | Dev Mode: publishing mappings from the code repository; the editor extension is left to the MCP server (measurements kept on the canvas and a read-only layers panel landed 2026-10-24) | Feature | 1–2 | [dev-mode-parity](dev-mode-parity.md) |
| 17 | Branching: branches in live sessions, lists other than fills, strokes and effects merged whole (a branch's own version history and per-item list merges landed 2026-10-12) | Feature | 1–2 | [collaboration-parity](collaboration-parity.md) | | 17 | Branching: branches in live sessions, lists other than fills, strokes and effects merged whole (a branch's own version history and per-item list merges landed 2026-10-12) | Feature | 1–2 | [collaboration-parity](collaboration-parity.md) |
| 18 | Display P3 on screen: blocked upstream (egui-wgpu picks an 8-bit sRGB surface); gamut warning and soft-proof sRGB landed, as did several windows | Hardware | 4–8 | [hardware-parity](hardware-parity.md) | | 18 | Display P3 on screen: blocked upstream (egui-wgpu picks an 8-bit sRGB surface); gamut warning and soft-proof sRGB landed, as did several windows | Hardware | 4–8 | [hardware-parity](hardware-parity.md) |
| 19 | AI features | AI | 30–60 | [target-app-parity](target-app-parity.md) | | 19 | AI features | AI | 30–60 | [target-app-parity](target-app-parity.md) |
@@ -386,11 +386,16 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe
ready list filters and badges Changed items (`inspect.readyForDev {status: changed}`); statuses ready list filters and badges Changed items (`inspect.readyForDev {status: changed}`); statuses
are refused on layers inside frames. Read-only plugins run from the Inspect tab show their are refused on layers inside frames. Read-only plugins run from the Inspect tab show their
panels. panels.
- **Missing:** measurements kept on the canvas for others (only hover redlines); the layers panel - **Landed (2026-10-24):** measurements kept on the canvas: between two layers, along one side of
and files dropped on the canvas still edit in Dev Mode; publishing mappings from the code a layer or to the layer around it (`devMode.addMeasurement` / `removeMeasurement` /
repository with a CLI. An editor extension is out of scope: the MCP server already serves design `measurements`), stored on the layer they start from (saved, synced, undoable; old files open
data, code and the connected snippets to agents and editors. without them), drawn in Dev Mode as labelled redlines that follow the layers, and listed, placed
- **Estimate:** 2–4 h. and removed in the Inspect tab. The layers panel is read-only in Dev Mode (rows select and
expand; no renaming, reordering, hiding or locking).
- **Missing:** files dropped on the canvas still edit in Dev Mode; 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.
- **Estimate:** 1–2 h.
### 17. Branching and merging ### 17. Branching and merging
- **Landed (2026-10-11):** branches of a file (`branch.*`): File ▸ Create branch… (or the Branches - **Landed (2026-10-11):** branches of a file (`branch.*`): File ▸ Create branch… (or the Branches
@@ -471,6 +476,7 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe
| Date | Change | Summary | | Date | Change | Summary |
|---|---|---| |---|---|---|
| 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-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 | | 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 |
| 2026-10-17 | minor | The 2026-10-17 landings. Gap 1: a new Landed entry (community batches #727 and #728, then #745: instance identity and cached library components, overrides by key, text style precedence, grid track sizes, slot content, labels of connectors saved with their path, skip ink, paragraph options, list start numbers, vertical trim, per-point stroke joins, text on a path, image adjustments, images in the message, instance layers' size and resizing overrides, Hug where nothing is recorded; budgets from the file's size; the import report; a `.fig` copy from Save as; paste from another design app), its Missing list cut to what is still left, and the wrong "whether other apps open its files is unknown" replaced by what was checked; all on synthetic files, so the beta blocker and hours stand. Gap 2: server limits, log recovery and web join links in the evidence. Gap 4: menus, the Actions palette and the editor fixes (#733, #735, #736, #729), then Selection colors and smart-selection spacing (#753) landed, none compared side by side. Gap 5: catalog counts re-measured (2,272 entries; drafts at 95–99%; Indonesian, Czech, Turkish and Swedish listed). Gap 8: the Linux and BSD clipboard changes and Wayland window controls, not run there by the maintainers. Ranking and hours unchanged | | 2026-10-17 | minor | The 2026-10-17 landings. Gap 1: a new Landed entry (community batches #727 and #728, then #745: instance identity and cached library components, overrides by key, text style precedence, grid track sizes, slot content, labels of connectors saved with their path, skip ink, paragraph options, list start numbers, vertical trim, per-point stroke joins, text on a path, image adjustments, images in the message, instance layers' size and resizing overrides, Hug where nothing is recorded; budgets from the file's size; the import report; a `.fig` copy from Save as; paste from another design app), its Missing list cut to what is still left, and the wrong "whether other apps open its files is unknown" replaced by what was checked; all on synthetic files, so the beta blocker and hours stand. Gap 2: server limits, log recovery and web join links in the evidence. Gap 4: menus, the Actions palette and the editor fixes (#733, #735, #736, #729), then Selection colors and smart-selection spacing (#753) landed, none compared side by side. Gap 5: catalog counts re-measured (2,272 entries; drafts at 95–99%; Indonesian, Czech, Turkish and Swedish listed). Gap 8: the Linux and BSD clipboard changes and Wayland window controls, not run there by the maintainers. Ranking and hours unchanged |
+18 -2
View File
@@ -1392,6 +1392,22 @@ permissions: without it nothing is downloaded); `plugin.directory.publish {path
are clipped to 200 characters; a layer holds at most 32 links. Links are data: never fetched or are clipped to 200 characters; a layer holds at most 32 links. Links are data: never fetched or
opened by the engine (the app opens one when it is clicked). `get_node` and `inspect_document` opened by the engine (the app opens one when it is clicked). `get_node` and `inspect_document`
list them as `devResources`; the Inspect tab lists, adds, edits and removes them. list them as `devResources`; the Inspect tab lists, adds, edits and removes them.
- **Measurements kept on the canvas:** distances a developer leaves on the canvas for everyone.
`devMode.addMeasurement {from? (default the first selected layer), to?, side?}` → `{from, index,
lines}` places one between `from` and `to` (a layer on the same page), along one side of `from`
(`side`: `top`, `right`, `bottom` or `left`: that side's length) or, with neither, to the layer
around `from`; `devMode.removeMeasurement {from?, index?}` → `{removed}` (no index: every one
starting from the layer); both undoable. `devMode.measurements {page?, id?}` → `[{from, fromName,
index, to?, toName?, side?, labels, lines: [{x0, y0, x1, y1, label, dashed}]}]` lists those on a
page (default the current one; `id`: only those from or to that layer), with their lines in page
space measured from the layers' current bounds, so they follow the layers. A measurement is
stored on the layer it starts from (`get_node` lists them as `measurements: [{to} | {side}]`),
saved with the file and synced; the same two layers are measured once (either way round), a
layer starts at most 64. One whose other layer was deleted or moved to another page isn't listed
or drawn (an undo brings it back); a copy keeps the side measurements and those to layers copied
with it. In the app, Dev Mode draws them with the hover redlines, and the Inspect tab lists them
(with nothing selected, every one on the page), selects a measurement's layer on a click, removes
one with −, and places new ones from its Add measurement menu.
- **Annotations:** notes for developers on any layer, each with optional pinned properties that - **Annotations:** notes for developers on any layer, each with optional pinned properties that
Dev Mode shows with their current values (and the style or variable behind them). Dev Mode shows with their current values (and the style or variable behind them).
`annotation.add {id?, label?, properties?}` (a note, pinned properties or both; returns `{id, `annotation.add {id?, label?, properties?}` (a note, pinned properties or both; returns `{id,
@@ -1428,8 +1444,8 @@ permissions: without it nothing is downloaded); `plugin.directory.publish {path
button to open it in the focus view). In the app's Dev Mode the canvas only inspects: presses button to open it in the focus view). In the app's Dev Mode the canvas only inspects: presses
select (and pan, and comment), never move, resize, draw or edit text and vectors, and the menus select (and pan, and comment), never move, resize, draw or edit text and vectors, and the menus
and shortcuts refuse commands that edit the design (Dev Mode's own annotations, statuses, dev and shortcuts refuse commands that edit the design (Dev Mode's own annotations, statuses, dev
resources, code connections and comments still work); choosing a drawing tool in the toolbar resources, measurements, code connections and comments still work); choosing a drawing tool in
goes back to the design. Commands sent over MCP or the control channel aren't affected. the toolbar goes back to the design, and the layers panel only selects and expands. Commands sent over MCP or the control channel aren't affected.
- **Compare changes:** when a layer marked Ready for dev first changes (it turns "Changed"), the - **Compare changes:** when a layer marked Ready for dev first changes (it turns "Changed"), the
file keeps a copy of it and everything in it as it was just before that edit (saved in the file keeps a copy of it and everything in it as it was just before that edit (saved in the
`.figcraft` file only while there is a change; resolving the change, marking it again, another `.figcraft` file only while there is a change; resolving the change, marking it again, another
+1 -1
View File
@@ -110,7 +110,7 @@ Breadth: does it exist. Ready: depth, correctness and fidelity for real projects
| 12 | Styles & variables | 5 | 95% | 76% | Real-library QA (variables inside styles, behaviour suite from the documentation, style folders and order, mode order, code syntax, extended collections with their tail (new library variables as updates, chains through libraries) and variables import/export landed) ([components-parity](components-parity.md)) | 2–4 | | 12 | Styles & variables | 5 | 95% | 76% | Real-library QA (variables inside styles, behaviour suite from the documentation, style folders and order, mode order, code syntax, extended collections with their tail (new library variables as updates, chains through libraries) and variables import/export landed) ([components-parity](components-parity.md)) | 2–4 |
| 13 | Prototyping | 6 | 92% | 74% | Video triggers and media actions, controllers on macOS, Linux and FreeBSD, real prototype QA (behaviour suite from the documentation, delays on mouse triggers, manual overlay offsets, key combinations, controller buttons on key/gamepad triggers (Windows and the browser), widgets reacting in prototypes, interactive components playing their variants, drag-driven animations, physical springs landed) ([prototyping-parity](prototyping-parity.md)) | 2–5 | | 13 | Prototyping | 6 | 92% | 74% | Video triggers and media actions, controllers on macOS, Linux and FreeBSD, real prototype QA (behaviour suite from the documentation, delays on mouse triggers, manual overlay offsets, key combinations, controller buttons on key/gamepad triggers (Windows and the browser), widgets reacting in prototypes, interactive components playing their variants, drag-driven animations, physical springs landed) ([prototyping-parity](prototyping-parity.md)) | 2–5 |
| 14 | Import/export | 6 | 86% | 58% | `.fig` import's tail (best-effort import, editable vector networks, stand-ins for non-design layers and variable prototype actions landed, then (2026-10-17) grid tracks, slots, labels of connectors saved with their path, per-point joins, text on a path, image adjustments, budgets for large files, an import report and paste from another design app; all on synthetic files, no real-file checks yet), checking the rest of the experimental `.fig` export (landed; written files open and draw in other software) on effects, images, prototypes and real files, HEIC/video import, CMYK output intent ([file-format-parity](file-format-parity.md)) | 5–10 | | 14 | Import/export | 6 | 86% | 58% | `.fig` import's tail (best-effort import, editable vector networks, stand-ins for non-design layers and variable prototype actions landed, then (2026-10-17) grid tracks, slots, labels of connectors saved with their path, per-point joins, text on a path, image adjustments, budgets for large files, an import report and paste from another design app; all on synthetic files, no real-file checks yet), checking the rest of the experimental `.fig` export (landed; written files open and draw in other software) on effects, images, prototypes and real files, HEIC/video import, CMYK output intent ([file-format-parity](file-format-parity.md)) | 5–10 |
| 15 | Dev Mode / inspect | 2 | 96% | 72% | Import lines for connected instances inside generated layer trees; an editor extension is left to the MCP server (inspect behaviour suite with colour formats, rem and icon and image assets, code connect, plugins in Dev Mode and dev resources links landed) ([dev-mode-parity](dev-mode-parity.md)) | 0–1 | | 15 | Dev Mode / inspect | 2 | 98% | 78% | Publishing mappings from the code repository; an editor extension is left to the MCP server (inspect behaviour suite with colour formats, rem and icon and image assets, code connect, plugins in Dev Mode, dev resources links, import lines, compare with versions, an inspect-only canvas and layers panel and measurements kept on the canvas landed) ([dev-mode-parity](dev-mode-parity.md)) | 1–2 |
| 16 | Comments, versions, find, misc | 3 | 87% | 63% | Comments are local only; comparing two versions; branches in live sessions; text runs and other lists besides fills, strokes and effects merge whole (branches with review and merge, their own version history and per-item list merges, and a comments and version history behaviour suite from the documentation landed) | 1–3 | | 16 | Comments, versions, find, misc | 3 | 87% | 63% | Comments are local only; comparing two versions; branches in live sessions; text runs and other lists besides fills, strokes and effects merge whole (branches with review and merge, their own version history and per-item list merges, and a comments and version history behaviour suite from the documentation landed) | 1–3 |
| 17 | Multiplayer & sharing | 8 | 17% | 5% | Core landed (`crates/collab`: ids, change sets, server ordering, convergence tested); live sessions (preview): WebSocket transport, hosting from the desktop app, joining from the app or the browser, invite links that open the app, cursors, selections and follow, cursor chat and spotlight, per-person undo, offline resume; a self-hosted `figcraft-server` with persistence and share links to edit or view, which can be open to the public (owner tokens, limits per address, expiry, moderation). Left: accounts and teams, audio ([collaboration-parity](collaboration-parity.md)) | 42–84 | | 17 | Multiplayer & sharing | 8 | 17% | 5% | Core landed (`crates/collab`: ids, change sets, server ordering, convergence tested); live sessions (preview): WebSocket transport, hosting from the desktop app, joining from the app or the browser, invite links that open the app, cursors, selections and follow, cursor chat and spotlight, per-person undo, offline resume; a self-hosted `figcraft-server` with persistence and share links to edit or view, which can be open to the public (owner tokens, limits per address, expiry, moderation). Left: accounts and teams, audio ([collaboration-parity](collaboration-parity.md)) | 42–84 |
| 18 | Plugins & widgets | 4 | 60% | 35% | Own sandboxed WASM plugins landed (#101: manifest, permissions, menu, Run last, Manage, SDK), with log byte/count limits checked before allocation and host forwarding ([logging limits](plugins.md#logging-limits)), plugin panels called back per button or changed value and per-plugin settings (ABI v2), then widgets (API 3: plugin-drawn layers on the canvas with state kept in the document, redrawn on clicks; a Counter example), panels side by side and a colour picker for plugin colour fields, then a plugin directory on `figcraft-server` (publish with the server key, browse, install after a permissions confirmation, hash-checked downloads, updates by version); layer data written with `node.set {props: {pluginData}}` reads back through `node.get` (#879). Left: a public catalogue, publisher accounts and signatures, ratings, background update checks, the directory in the browser; widgets take clicks only (no text input, property menus, hover or timers) and draw frames, text, shapes and images only; Figma's plugins are JavaScript and don't run (a JS engine would be an owner decision) ([plugins.md](plugins.md)) | 8–26 | | 18 | Plugins & widgets | 4 | 60% | 35% | Own sandboxed WASM plugins landed (#101: manifest, permissions, menu, Run last, Manage, SDK), with log byte/count limits checked before allocation and host forwarding ([logging limits](plugins.md#logging-limits)), plugin panels called back per button or changed value and per-plugin settings (ABI v2), then widgets (API 3: plugin-drawn layers on the canvas with state kept in the document, redrawn on clicks; a Counter example), panels side by side and a colour picker for plugin colour fields, then a plugin directory on `figcraft-server` (publish with the server key, browse, install after a permissions confirmation, hash-checked downloads, updates by version); layer data written with `node.set {props: {pluginData}}` reads back through `node.get` (#879). Left: a public catalogue, publisher accounts and signatures, ratings, background update checks, the directory in the browser; widgets take clicks only (no text input, property menus, hover or timers) and draw frames, text, shapes and images only; Figma's plugins are JavaScript and don't run (a JS engine would be an owner decision) ([plugins.md](plugins.md)) | 8–26 |