Merge remote-tracking branch 'upstream/main' into feat-4936-spell-check

Language per character rows in docs/type-parity.md: spelling is no longer listed as not yet done, and the hyphenation note names the languages that still lack it.
This commit is contained in:
HassanKhan
2026-11-09 11:42:44 +05:00
53 changed files with 790 additions and 124 deletions
+3
View File
@@ -5175,6 +5175,9 @@ fn arb_ai_token() -> impl Strategy<Value = String> {
"-9 /Real (CornerRadius) ,",
"1 /Bool (Absolute) ,",
"-0.5 /Real ,",
"0.5 /Real ,",
"1 /Real ,",
"1e308 /Real ,",
"1.5 /Real ,",
"-2 /Real ,",
"; (BSLeftWidthsArray) ,",
+1 -1
View File
@@ -1308,7 +1308,7 @@ impl<'a> Reader<'a> {
self.drawn_looks += self.pattern_looks.values().sum::<usize>();
if self.drawn_looks > 0 {
warnings.push(format!(
"objects with several fills or strokes, effects or brushes came in as their drawn look ({}): their appearance isn't editable yet",
"objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({}): their appearance isn't editable yet",
self.drawn_looks
));
}
+35 -1
View File
@@ -752,6 +752,24 @@ impl<'a> Reader<'a> {
{
return;
}
// A variable-width stroke's look that draws nothing but copies read from `%_` lines: an
// empty group, which [`styled`] takes the stroke for.
let drawn;
let look = if !hidden
&& slot_of(object.name.as_deref()).is_none()
&& matches!(look.kind, NodeKind::Group { .. })
&& self.styles.get(&style).is_some_and(|st| st.uneven)
&& !draws(look, &self.hidden_ids)
{
let mut copy = look.clone();
if let Some(children) = copy.children_mut() {
children.clear();
}
drawn = copy;
&drawn
} else {
look
};
// A style read here: the object, painted as it says.
if let Some(mut appearance) = self.styles.get(&style).and_then(|st| {
styled(
@@ -1067,7 +1085,8 @@ fn styled(object: &Node, look: &Node, st: &crate::import::ai::style::ArtStyle, p
// A symbol instance draws its symbol's art: it keeps its drawn look.
if !st.plain
|| matches!(object.kind, NodeKind::SymbolInstance { .. })
|| (!slot && !st.simple && !stacked)
// A look with no outline in it to keep takes a variable-width stroke as the object's own.
|| (!slot && !st.simple && !stacked && (!st.uneven || draws(look, &HashSet::new())))
|| (object.is_container() && !slot && !matches!(object.kind, NodeKind::Compound { .. }) && !st.items.is_empty())
|| (st.pathfinder
&& (slot || !st.items.is_empty() || object.is_compound_shape() || !matches!(object.kind, NodeKind::Group { clip: false, .. })))
@@ -1275,6 +1294,21 @@ fn strip(n: &mut Node, hidden: &HashSet<NodeId>) {
}
}
/// Whether `n` holds anything that draws, apart from the children read from `%_` lines (`hidden`):
/// a path or a compound path with a paint of its own, or a node other than a group or those.
fn draws(n: &Node, hidden: &HashSet<NodeId>) -> bool {
if hidden.contains(&n.id) {
return false;
}
let paints = n.appearance.items.iter().any(|i| i.visible() && !i.paint().is_none());
match (&n.kind, n.children()) {
(NodeKind::Compound { .. }, Some(children)) => paints && children.iter().any(|c| c.visible && !hidden.contains(&c.id)),
(NodeKind::Group { .. }, Some(children)) => children.iter().any(|c| c.visible && draws(c, hidden)),
(NodeKind::Path { .. }, _) => paints,
_ => true,
}
}
/// Whether ASCII85 data in comment lines, up to its `~>`, follows the entry keyed `key` of a
/// dictionary of type `ty`: the `/ASCII85Decode` of a `/Binary` dictionary or a text document, or a
/// non-native art object's (`/ForeignObject`'s) `/Data`.
+21 -3
View File
@@ -174,13 +174,16 @@ const PATHFINDER_OPTIONS: [(&str, Kind, f64); 12] = [
pub(super) struct ArtStyle {
/// The style reads as [`Self::items`] and [`Self::effects`].
pub plain: bool,
/// It is one fill and one stroke at most, in solid colours or the object's own gradient, with
/// It is one fill and one stroke at most, none of them [`Self::uneven`], in solid colours or the object's own gradient, with
/// no transparency or effects of their own, and only Drop Shadow, Inner Glow, Outer Glow,
/// Feather, Round Corners, Scribble, Convert to Shape, the Path effects (Offset Path, Outline Object, Outline Stroke),
/// Bas Relief read from its settings block and some Distort & Transform effects ([`any_object_takes`]) on the object
/// (and Pathfinder effects when it has no fills or strokes): what any object is read with. Type takes the other plain styles too (its drawn look
/// can't be retyped), paths and compound paths [`Self::stacked`] ones; other objects keep their drawn look, which is the app's own rendering.
pub simple: bool,
/// It would be [`Self::simple`] but for a Stroke Style filter's variable width written as one
/// centered array ([`centered_widths`]): an object whose drawn look is empty takes it with its profile.
pub uneven: bool,
/// It is several fills and strokes in solid colours or gradients (strokes without a brush),
/// each with its own visibility, opacity and blend mode but no effects of its own, under only
/// the effects any object takes that don't move the outline: what a path or compound path is
@@ -1601,7 +1604,7 @@ fn style(filters: &[Filter]) -> ArtStyle {
st.plain = b.plain;
let bas_relief = bas_relief_read(filters);
let count = |stroke: bool| st.items.iter().filter(|i| matches!(i, StyleItem::Stroke { .. }) == stroke).count();
st.simple = st.plain
let simple = st.plain
&& count(true) <= 1
&& count(false) <= 1
&& st.items.iter().all(|i| matches!(i, StyleItem::Fill(_) | StyleItem::Stroke { .. } | StyleItem::Gradient { .. }))
@@ -1612,7 +1615,11 @@ fn style(filters: &[Filter]) -> ArtStyle {
&& (!st.moves_outline() || st.effects.len() == 1)
// Pathfinder effects on a group with no fills or strokes of its own (`styled` checks the group).
&& (!st.pathfinder || st.items.is_empty());
st.stacked = st.plain
// A stroke with one centered width array comes in as the drawn look, which holds the outline as the file draws it.
let centered = filters.iter().any(centered_widths);
(st.simple, st.uneven) = (simple && !centered, simple && centered);
st.stacked = !centered
&& st.plain
&& !st.pathfinder
&& !st.moves_outline()
&& st.items.iter().all(|i| matches!(i, StyleItem::Fill(_) | StyleItem::Gradient { .. } | StyleItem::Stroke { brush: None, .. }))
@@ -1621,6 +1628,17 @@ fn style(filters: &[Filter]) -> ArtStyle {
st
}
/// Whether `f` is a painted Stroke Style filter with a variable width written as one centered array,
/// `BSCenteredWidthsArray`. A Stroke Offset filter's profile and one written as each side's widths
/// (`BSLeftWidthsArray` and `BSRightWidthsArray`, as VectorCraft saves it) are not.
fn centered_widths(f: &Filter) -> bool {
let each_side = f
.arrays
.get("BSVariableWidthTValuesArray")
.is_some_and(|at| ["BSLeftWidthsArray", "BSRightWidthsArray"].iter().all(|key| f.arrays.get(*key).is_some_and(|w| w.len() == at.len())));
f.name == STROKE && f.paints.0 && !each_side && width_profile(&f.arrays).is_some()
}
mod adapter;
#[cfg(test)]
mod tests;
+51 -12
View File
@@ -5,20 +5,59 @@ fn paint_op(f: &mut Filter, op: &str, vals: &[V]) {
super::paint_op(f, op, vals, &mut Names::default());
}
#[test]
fn a_variable_width_stroke_keeps_its_profile() {
let data = b"/KnownStyle :\n(Tapered) /Name ,\n/ActiveStyle :\n/CompoundFilter :\n(Chain Style Filter) 0 0 /Filter ,\n1 /Visible ,\n\
/// The style `Tapered`: a 30 pt stroke whose beautiful-strokes dictionary holds `arrays`.
fn width_style(arrays: &str) -> ArtStyle {
let data = format!(
"/KnownStyle :\n(Tapered) /Name ,\n/ActiveStyle :\n/CompoundFilter :\n(Chain Style Filter) 0 0 /Filter ,\n1 /Visible ,\n\
/CompoundFilter :\n(Stack Style Filter) 0 0 /Filter ,\n1 /Visible ,\n/BasicFilter :\n(Stroke Style Filter) 0 0 /Filter ,\n1 /Visible ,\n\
2 /FillOrStroke ,\n/Dictionary : /NotRecorded ,\n/StrokeStyle : 0 R\n0 0 0 0 0 0 1 XA\n0 J 0 j 30 w 10 M []0 d\n0 XR\n1 0 Xd\n/Def ;\n (StrokeStyle) ,\n\
/Dictionary : /NotRecorded ,\n2 /Int (BSWidthSource) ,\n/Array :\n0 /Real ,\n1 /Real ,\n; (BSVariableWidthTValuesArray) ,\n\
/Array :\n1 /Real ,\n0.1 /Real ,\n; (BSCenteredWidthsArray) ,\n; (kAIBeautifulStrokesParamsDictKey) ,\n; /Dict ;\n /Part ,\n\
/BasicFilter :\n(Conduit Filter) 0 0 /Filter ,\n1 /Visible ,\n;\n /Part ,\n;\n";
let styles = art_styles(data);
let style = &styles["Tapered"];
assert!(style.plain);
let [StyleItem::Stroke { width, profile, .. }] = style.items.as_slice() else { panic!("{:?}", style.items) };
assert_eq!(*width, 30.0);
assert_eq!(profile.as_ref().map(|p| p.points.clone()), Some(vec![(0.0, 1.0, 1.0), (1.0, 0.1, 0.1)]));
/Dictionary : /NotRecorded ,\n2 /Int (BSWidthSource) ,\n{arrays}; (kAIBeautifulStrokesParamsDictKey) ,\n; /Dict ;\n /Part ,\n\
/BasicFilter :\n(Conduit Filter) 0 0 /Filter ,\n1 /Visible ,\n;\n /Part ,\n;\n"
);
art_styles(data.as_bytes()).remove("Tapered").unwrap_or_default()
}
/// A beautiful-strokes array `key` of `values`.
fn bs_array(key: &str, values: &[&str]) -> String {
let items: String = values.iter().map(|v| format!("{v} /Real ,\n")).collect();
format!("/Array :\n{items}; ({key}) ,\n")
}
#[test]
fn a_centered_width_array_reads_as_uneven_with_its_profile() {
// GUARDS: the style counted as `simple`, and any object read with it dropped its drawn look.
let t = |v: &[&str]| bs_array("BSVariableWidthTValuesArray", v);
let w = |v: &[&str]| bs_array("BSCenteredWidthsArray", v);
for (arrays, points) in [
(t(&["0", "0.5", "1"]) + &w(&["0", "1", "0"]), vec![(0.0, 0.0, 0.0), (0.5, 1.0, 1.0), (1.0, 0.0, 0.0)]),
(t(&["0", "1"]) + &w(&["1", "0.1"]), vec![(0.0, 1.0, 1.0), (1.0, 0.1, 0.1)]),
] {
let style = width_style(&arrays);
assert!(style.plain && !style.simple && style.uneven, "{arrays:?}: {style:?}");
let [StyleItem::Stroke { width, profile, .. }] = style.items.as_slice() else { panic!("{:?}", style.items) };
assert_eq!((*width, profile.as_ref().map(|p| p.points.clone())), (30.0, Some(points)), "{arrays:?}");
}
}
#[test]
fn a_uniform_or_unreadable_width_profile_stays_a_live_stroke() {
let t = |v: &[&str]| bs_array("BSVariableWidthTValuesArray", v);
let w = |v: &[&str]| bs_array("BSCenteredWidthsArray", v);
for arrays in [
String::new(),
t(&["0", "1"]) + &w(&["1", "1"]),
// No widths, widths of another length, widths that aren't numbers.
t(&["0", "0.5", "1"]),
t(&["0", "0.5", "1"]) + &w(&["0", "1"]),
t(&["0", "1"]) + "/Array :\n(x) /String ,\n(y) /String ,\n; (BSCenteredWidthsArray) ,\n",
// Positions that aren't numbers.
t(&["nan", "nan"]) + &w(&["0", "1"]),
] {
let style = width_style(&arrays);
assert!(style.plain && style.simple, "{arrays:?}: {style:?}");
let [StyleItem::Stroke { width, profile, .. }] = style.items.as_slice() else { panic!("{arrays:?}: {:?}", style.items) };
assert_eq!((*width, profile), (30.0, &None), "{arrays:?}");
}
}
#[test]
+113
View File
@@ -6452,6 +6452,31 @@ fn an_object_with_a_feather_style_comes_in_with_a_live_effect() {
assert!(!r.warnings.iter().any(|w| w.contains("drawn look")), "{:?}", r.warnings);
}
/// The art style `Tapered`: a 4 pt stroke with width points `widths` (position, width) along it,
/// their widths written as one centered array.
fn width_profile_style(widths: &[(f64, f64)]) -> String {
width_arrays_style(widths, &["BSCenteredWidthsArray"])
}
/// The art style `Tapered`: a 4 pt stroke with width points `widths` (position, width) along it,
/// their widths written under each of `keys`.
fn width_arrays_style(widths: &[(f64, f64)], keys: &[&str]) -> String {
let array = |values: Vec<f64>| values.iter().map(|v| format!("{v} /Real ,\n")).collect::<String>();
let width_arrays: String = keys.iter().map(|key| format!("/Array :\n{}; ({key}) ,\n", array(widths.iter().map(|w| w.1).collect()))).collect();
format!(
"%AI9_BeginArtStyles\n\
/KnownStyle :\n(Tapered) /Name ,\n/ActiveStyle :\n/CompoundFilter :\n(Chain Style Filter) 0 0 /Filter ,\n1 /Visible ,\n\
/CompoundFilter :\n(Stack Style Filter) 0 0 /Filter ,\n1 /Visible ,\n\
/BasicFilter :\n(Stroke Style Filter) 0 0 /Filter ,\n1 /Visible ,\n2 /FillOrStroke ,\n/Dictionary : /NotRecorded ,\n\
/StrokeStyle : 0 R\n0 0 0 0 0 0 1 XA\n0 J 0 j 4 w 10 M []0 d\n0 XR\n1 0 Xd\n/Def ;\n (StrokeStyle) ,\n\
/Dictionary : /NotRecorded ,\n2 /Int (BSWidthSource) ,\n/Array :\n{}; (BSVariableWidthTValuesArray) ,\n{width_arrays}\
; (kAIBeautifulStrokesParamsDictKey) ,\n; /Dict ;\n /Part ,\n\
/BasicFilter :\n(Conduit Filter) 0 0 /Filter ,\n1 /Visible ,\n;\n /Part ,\n;\n /Part ,\n;\n\
%AI9_EndArtStyles\n",
array(widths.iter().map(|w| w.0).collect()),
)
}
/// An art style section with style `PF`: an Effect › Pathfinder filter with `Command` `command`
/// and `Mix` `mix` in the object's chain, over a fill and stroke that paint nothing.
fn pathfinder_styles(command: u32, mix: f64) -> String {
@@ -6482,6 +6507,73 @@ fn pathfinder_group() -> String {
)
}
#[test]
fn a_variable_width_stroke_comes_in_as_its_drawn_outline() {
// GUARDS: the object came in as a live stroke drawn from its profile, and the outline in the file was dropped.
let hidden_line = "0 5 m\n10 5 L\n".replace('\n', "\n%_");
let fills = format!("{RED}{}f\n", rect_path(30.0, 0.0, 40.0, 10.0));
let art_ = format!("{fills}0 Ae\nu\n{RED}{}f\nU\n%_{hidden_line}S\n1 (Tapered) XW\n{fills}", rect_path(0.0, 3.0, 10.0, 7.0));
let lens = width_profile_style(&[(0.0, 0.0), (0.5, 1.0), (1.0, 0.0)]);
let r = read_ok(&format!("{lens}{}", layer(&art_)));
let a = art(&r.doc);
assert_eq!(a.len(), 3, "{a:#?}");
let look = &a[1];
assert!(matches!(look.kind, NodeKind::Group { .. }), "{look:#?}");
let [outline] = look.children().unwrap().as_slice() else { panic!("{look:#?}") };
assert!(matches!(outline.kind, NodeKind::Path { .. }) && outline.appearance.fill().is_some(), "{outline:#?}");
assert!(outline.appearance.stroke().is_none_or(|s| s.profile.is_none()), "{outline:#?}");
assert!([&a[0], &a[2]].iter().all(|n| matches!(n.kind, NodeKind::Path { .. })), "the objects beside it stay in place");
assert!(r.warnings.iter().any(|w| w.contains("drawn look (1)")), "{:?}", r.warnings);
// A uniform profile is a plain stroke: the object comes in as itself.
let uniform = width_profile_style(&[(0.0, 1.0), (1.0, 1.0)]);
let r = read_ok(&format!("{uniform}{}", layer(&art_)));
let a = art(&r.doc);
assert_eq!(a.len(), 3, "{a:#?}");
assert!(matches!(a[1].kind, NodeKind::Path { .. }), "{:#?}", a[1]);
assert_eq!(a[1].appearance.stroke().map(|s| (s.width, s.profile.is_none())), Some((4.0, true)), "{:#?}", a[1]);
assert!(!r.warnings.iter().any(|w| w.contains("drawn look")), "{:?}", r.warnings);
}
/// Only a Stroke Style filter's widths written as one centered array come in as the drawn look.
/// A Stroke Offset stroke and widths written for each side, as VectorCraft saves them, stay live.
#[test]
fn only_centered_widths_on_a_plain_stroke_come_in_as_the_drawn_outline() {
// GUARDS: every stroke with a profile went to its drawn look, so a Stroke Offset stroke and the
// app's own files lost their live profile.
let hidden_line = "0 5 m\n10 5 L\n".replace('\n', "\n%_");
let art_ = format!("0 Ae\nu\n{RED}{}f\nU\n%_{hidden_line}S\n1 (Tapered) XW\n", rect_path(0.0, 3.0, 10.0, 7.0));
let lens = [(0.0, 0.0), (0.5, 1.0), (1.0, 0.0)];
let r = read_ok(&format!("{}{}", width_profile_style(&lens), layer(&art_)));
let a = art(&r.doc);
let [look] = a.as_slice() else { panic!("{a:#?}") };
assert!(matches!(look.kind, NodeKind::Group { .. }), "{look:#?}");
assert!(r.warnings.iter().any(|w| w.contains("drawn look (1)")), "{:?}", r.warnings);
// One side's array alone doesn't replace the centered one, so the widths are still centered.
let r = read_ok(&format!("{}{}", width_arrays_style(&lens, &["BSCenteredWidthsArray", "BSLeftWidthsArray"]), layer(&art_)));
let a = art(&r.doc);
let [look] = a.as_slice() else { panic!("{a:#?}") };
assert!(matches!(look.kind, NodeKind::Group { .. }), "{look:#?}");
let sides = width_arrays_style(&lens, &["BSLeftWidthsArray", "BSRightWidthsArray"]);
let profile = "/Dictionary : /NotRecorded ,\n/Array :\n0 /Real ,\n0.5 /Real ,\n1 /Real ,\n; (BSVariableWidthTValuesArray) ,\n\
/Array :\n0 /Real ,\n1 /Real ,\n0 /Real ,\n; (BSCenteredWidthsArray) ,\n; (kAIBeautifulStrokesParamsDictKey) ,\n";
let offset = format!(
"%AI9_BeginArtStyles\n{}%AI9_EndArtStyles\n",
offset_stroke_style_with("Tapered", &format!("0 /Bool (StrokeOffsetInside) ,\n{profile}"), OFFSET_STROKE)
);
for (why, style) in [("each side's widths", sides), ("a Stroke Offset stroke", offset)] {
let r = read_ok(&format!("{style}{}", layer(&art_)));
let a = art(&r.doc);
let [object] = a.as_slice() else { panic!("{why}: {a:#?}") };
assert!(matches!(object.kind, NodeKind::Path { .. }), "{why}: {object:#?}");
assert!(object.appearance.stroke().is_some_and(|s| s.profile.is_some()), "{why}: {object:#?}");
assert!(!r.warnings.iter().any(|w| w.contains("drawn look")), "{why}: {:?}", r.warnings);
}
}
#[test]
fn a_group_with_a_pathfinder_effect_keeps_its_members_and_the_effect() {
// GUARDS: the style was not read, and the group came in as its drawn look: one path, with no effect.
@@ -6498,6 +6590,27 @@ fn a_group_with_a_pathfinder_effect_keeps_its_members_and_the_effect() {
assert!(!r.warnings.iter().any(|w| w.contains("drawn look")), "{:?}", r.warnings);
}
#[test]
fn a_variable_width_stroke_with_an_empty_look_comes_in_as_itself() {
// GUARDS: with no outline in its look to keep, the object was dropped.
let hidden_line = "0 5 m\n10 5 L\n".replace('\n', "\n%_");
let hidden_fill = format!("{}f\n", rect_path(0.0, 3.0, 10.0, 7.0)).replace('\n', "\n%_");
let lens = width_profile_style(&[(0.0, 0.0), (0.5, 1.0), (1.0, 0.0)]);
// An empty look, ones whose group or compound path holds only a copy read from `%_` lines, and
// ones that hold only a path painted with nothing or a hidden painted path.
let unpainted = format!("u\n{}n\nU\n", rect_path(0.0, 3.0, 10.0, 7.0));
let invisible = format!("u\n1 Xw\n{RED}{}f\n0 Xw\nU\n", rect_path(0.0, 3.0, 10.0, 7.0));
for look in ["u\nU\n".to_string(), format!("u\nu\n%_{hidden_fill}\nU\nU\n"), format!("u\n*u\n%_{hidden_fill}\n*U\nU\n"), unpainted, invisible] {
let art_ = format!("{look}%_{hidden_line}S\n1 (Tapered) XW\n");
let r = read_ok(&format!("{lens}{}", layer(&art_)));
let a = art(&r.doc);
let [object] = a.as_slice() else { panic!("{look:?}: {a:#?}") };
assert!(matches!(object.kind, NodeKind::Path { .. }), "{look:?}: {object:#?}");
assert_eq!(object.appearance.stroke().map(|s| (s.width, s.profile.is_some())), Some((4.0, true)), "{look:?}: {object:#?}");
assert!(!r.warnings.iter().any(|w| w.contains("drawn look")), "{look:?}: {:?}", r.warnings);
}
}
#[test]
fn each_pathfinder_operation_on_a_group_draws_as_vectorcraft_draws_it() {
// GUARDS: only Add (`Command` 0) was read; the other operations came in as the drawn look's one path.
+30
View File
@@ -5774,6 +5774,36 @@ fn type_with_convert_to_shape_comes_in_as_type_with_it_live() {
);
}
#[test]
fn type_with_a_variable_width_stroke_keeps_it_live() {
// GUARDS: other objects with a variable-width stroke come in as their drawn look; type keeps the
// stroke, since its drawn look can't be retyped.
use vectorcraft_doc::AppearanceItem;
let doc = text_document(&[("Bang\r", 0, (8200.0, 8180.0), (0.0, 0.0), 0.0)]);
let style = "%AI9_BeginArtStyles\n/KnownStyle :\n(Tapered) /Name ,\n/ActiveStyle :\n/CompoundFilter :\n(Chain Style Filter) 0 0 /Filter ,\n1 /Visible ,\n\
/CompoundFilter :\n(Stack Style Filter) 0 0 /Filter ,\n1 /Visible ,\n/BasicFilter :\n(Stroke Style Filter) 0 0 /Filter ,\n1 /Visible ,\n\
2 /FillOrStroke ,\n/Dictionary : /NotRecorded ,\n/StrokeStyle : 0 R\n0 0 0 1 0 0 0 XA\n0 J 0 j 2 w 10 M []0 d\n0 XR\n1 0 Xd\n/Def ;\n (StrokeStyle) ,\n\
/Dictionary : /NotRecorded ,\n/Array :\n0 /Real ,\n0.5 /Real ,\n1 /Real ,\n; (BSVariableWidthTValuesArray) ,\n\
/Array :\n0 /Real ,\n1 /Real ,\n0 /Real ,\n; (BSCenteredWidthsArray) ,\n; (kAIBeautifulStrokesParamsDictKey) ,\n; /Dict ;\n /Part ,\n\
/BasicFilter :\n(Conduit Filter) 0 0 /Filter ,\n1 /Visible ,\n;\n /Part ,\n;\n /Part ,\n;\n%AI9_EndArtStyles\n";
let drawing = "0 0 0 1 k\n54 56 m\n100 56 L\n100 80 L\n54 80 L\nf";
let look = format!("u\n/AI11Text :\n1 /FreeUndo ,\n0 /FrameIndex ,\n0 /StoryIndex ,\n;\n{drawing}\nU\n9 () XW\n");
let own: String = text_object(0).lines().map(|l| format!("%_{l}\n")).collect();
let art = native_with_text(&(style.to_string() + &layer("Art", true, &format!("{look}{own}1 (Tapered) XW\n"))), &doc);
let (d, notes) = crate::ai_alone(&private(&art)).unwrap();
let mut texts = vec![];
d.walk(|n| {
if let NodeKind::Text(t) = &n.kind {
texts.push((t.plain_text(), n.appearance.clone()));
}
});
let [(text, a)] = texts.as_slice() else { panic!("{texts:?} {notes:?}") };
assert_eq!(text, "Bang");
let [AppearanceItem::Stroke(stroke)] = a.items.as_slice() else { panic!("{a:?}") };
assert_eq!(stroke.profile.as_ref().map(|p| p.points.clone()), Some(vec![(0.0, 0.0, 0.0), (0.5, 1.0, 1.0), (1.0, 0.0, 0.0)]));
assert!(!notes.iter().any(|w| w.contains("drawn look")), "{notes:?}");
}
#[test]
fn point_type_without_a_matrix_is_placed_by_its_layout() {
// GUARDS: Illustrator 24 leaves an unturned, unscaled point type's matrix out of its frame; such
+2 -1
View File
@@ -561,7 +561,8 @@ impl VectorcraftApp {
tools_float::clear_canvas_dock_zone(&ctx);
}
// Beside the canvas, docked Tools draws inside the canvas panel (`toolbar::show_beside_canvas`).
if self.ui.toolbar && self.ui.screen_mode < 3 && (!self.ui.tools_beside_canvas || self.ui.toolbar_pos.is_some()) {
// So does Tools docked on the right, left of the panels.
if self.ui.toolbar && self.ui.screen_mode < 3 && ((!self.ui.tools_beside_canvas && !self.ui.toolbar_right) || self.ui.toolbar_pos.is_some()) {
toolbar::show(self, ui);
} else if !self.ui.toolbar && self.ui.screen_mode < 3 && self.ui.tab_hid.is_none() {
// Window › Tools hides the Tools panel alone; Tab hides the toolbars made with New
@@ -358,6 +358,7 @@ fn details(app: &mut VectorcraftApp, ui: &mut egui::Ui, d: &mut Dialog, b: &mut
let top = ui.cursor().top();
ui.label(egui::RichText::new(tl!("PRESET DETAILS")).font(theme::semibold(11.0)).color(t.text_dim));
ui.add_space(8.0);
form::caption(ui, tl!("Document Name"));
ui.horizontal(|ui| {
form::text(ui, d, "name", DETAILS - 50.0);
save_button(app, ui, d);
@@ -876,6 +877,44 @@ mod tests {
assert!(selected(&out), "the turned preset stays chosen: {:?}", field(&app, "preset"));
}
/// The document's name field has a caption of its own under the Preset Details heading, and
/// the column stays inside the dialog's height with Save Preset and Advanced Options open.
#[test]
fn the_name_field_is_captioned_and_the_column_fits() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
app.run("file.newDialog", json!({})).unwrap();
set(&mut app, "advanced", json!(true));
set(&mut app, super::SAVING, json!(true));
let ctx = egui::Context::default();
crate::theme::install_fonts(&ctx);
let mut time = 0.0;
let mut run = |app: &mut VectorcraftApp| {
time += 0.5;
let screen_rect = Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1400.0, 1000.0)));
let raw = egui::RawInput { screen_rect, time: Some(time), ..Default::default() };
let mut out = ctx.run_ui(raw, |ui| crate::dialogs::show(app, ui.ctx()));
out.textures_delta.clear();
out
};
run(&mut app);
let out = run(&mut app);
fn walk(s: &egui::Shape, out: &mut Vec<(String, egui::Rect)>) {
match s {
egui::Shape::Text(t) => out.push((t.galley.text().to_string(), t.visual_bounding_rect())),
egui::Shape::Vec(v) => v.iter().for_each(|s| walk(s, out)),
_ => {}
}
}
let mut texts = vec![];
out.shapes.iter().for_each(|c| walk(&c.shape, &mut texts));
let at = |name: &str| texts.iter().find(|(t, _)| t == name).map(|(_, r)| *r).unwrap_or_else(|| panic!("{name} is missing: {texts:?}"));
let (heading, caption, value) = (at("PRESET DETAILS"), at("Document Name"), at("Untitled-1"));
assert!(heading.bottom() <= caption.top() && caption.bottom() <= value.top(), "{heading:?} {caption:?} {value:?}");
assert!(value.bottom() <= at("Preset Name").top(), "the preset's own name field follows");
let used = at("Create").bottom() - heading.top();
assert!(used <= super::HEIGHT, "the column used {used}");
}
#[test]
fn a_preset_fills_the_details_and_create_makes_the_document() {
let mut app = VectorcraftApp::new(Session::new(), Default::default());
+1 -1
View File
@@ -467,7 +467,7 @@ pub(crate) fn drawer(app: &mut VectorcraftApp, ctx: &egui::Context) {
let screen = ctx.content_rect();
let id = drawer_id();
let size = ctx.memory(|m| m.area_rect(id)).map_or(vec2(WIDTH, 200.0), |r| r.size());
let x = (bar.right() + GAP).min(screen.right() - size.x - GAP).max(screen.left());
let x = crate::toolbar::beside(bar, size.x, GAP, crate::toolbar::room(app, ctx).shrink(GAP));
// Never above the toolbar's top: higher up are the window's title bar (and on macOS its
// buttons) and the bars under it.
let top = bar.top().max(screen.top() + GAP);
+3 -2
View File
@@ -3928,7 +3928,7 @@ axis Both И двете
Dock to Bottom Закотвяне долу
Dock Control Bar Закотвяне на контролната лента
Drag to float the toolbar Плъзнете, за да направите лентата с инструменти плаваща
Drag to the window's left edge to dock Плъзнете до левия ръб на прозореца за закотвяне
Drag to the window's left or right edge to dock Плъзнете до левия или десния ръб на прозореца за закотвяне
Float Tool Group Плаваща група инструменти
@msg no library `{id}` няма библиотека `{id}`
@msg select some type first първо изберете текст
@@ -4618,7 +4618,7 @@ axis Both И двете
@msg its objects are nested too deeply обектите му са вложени твърде дълбоко
@msg its section ending with `%{end}` doesn't end разделът му, завършващ на `%{end}`, няма край
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) четките, които VectorCraft все още не може да прочете, бяха пропуснати от панела „Четки“ ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet обектите с множество запълвания или щрихове, ефекти или четки се появиха във вид на изрисувания си изглед ({_1}): тяхната поява все още не подлежи на редактиране
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet обектите с множество запълвания или щрихове, щрихове с променлива ширина, ефекти или четки се появиха във вид на изрисувания си изглед ({_1}): тяхната поява все още не подлежи на редактиране
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding името `{name}` не е в UTF-8 и е прочетено като {label}: ако изглежда грешно, отворете файла с друго legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding името `{name}` не е в UTF-8, а части от него не са валидни и в {label}: отворете файла с друго legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) още имена, които не са в UTF-8, са прочетени като {label} ({count})
@@ -6426,3 +6426,4 @@ breakText Glyphs Глифи
# Align to Pixel Grid
Align to Pixel Grid Подравняване към пикселната мрежа
@msg side is left or right, not `{s}` side е left или right, а не `{s}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -1269,7 +1269,7 @@ lighting Light Svjetlo
X: X:
Y: Y:
Drag to float the toolbar Prevucite da odvojite traku s alatima
Drag to the window's left edge to dock Prevucite do lijeve ivice prozora za sidrenje
Drag to the window's left or right edge to dock Prevucite do lijeve ili desne ivice prozora za sidrenje
Crystallize Kristalizuj
Add Anchor Point Tool Alat za dodavanje sidrene tačke
Anchor Point Tool Alat sidrena tačka
@@ -5014,7 +5014,7 @@ imageTrace Outline Obris
@msg its objects are nested too deeply njeni objekti su previše duboko ugniježđeni
@msg its section ending with `%{end}` doesn't end njen odjeljak koji završava s `%{end}` ne završava
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) četkice koje VectorCraft još ne može pročitati izostavljene su iz panela Četkice ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objekti s više ispuna ili poteza, efekata ili četkica stigli su kao njihov iscrtani izgled ({_1}): njihov izgled se još ne može uređivati
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objekti s više ispuna ili poteza, poteza promjenljive širine, efekata ili četkica stigli su kao njihov iscrtani izgled ({_1}): njihov izgled se još ne može uređivati
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding naziv `{name}` nije u UTF-8 i pročitan je kao {label}: ako izgleda pogrešno, otvorite datoteku s drugim legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding naziv `{name}` nije u UTF-8, a dijelovi nisu ispravni ni u {label}: otvorite datoteku s drugim legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) još naziva koji nisu u UTF-8 pročitano je kao {label} ({count})
@@ -6475,3 +6475,4 @@ shape Open Otvoren
# Align to Pixel Grid
Align to Pixel Grid Poravnaj s mrežom piksela
@msg side is left or right, not `{s}` side je left ili right, a ne `{s}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -1253,7 +1253,7 @@ antiAlias Sharp Nítid
Drag to float as a toolbar Arrossega per surar com a barra d’eines
Drag to float the toolbar Arrossega per fer surar la barra d’eines
Drag to move Arrossega per moure
Drag to the window's left edge to dock Arrossega fins a la vora esquerra de la finestra per acoblar
Drag to the window's left or right edge to dock Arrossega fins a la vora esquerra o dreta de la finestra per acoblar
Drag values or labels. Double-click a value to type. Arrossega els valors o les etiquetes. Fes doble clic en un valor per escriure’l.
Draw Dibuixa
Draw Behind Dibuixa al darrere
@@ -5507,7 +5507,7 @@ digits Malayalam Malaiàlam
@msg objects can't be nested more than 32 deep els objectes no es poden niar més de 32 nivells
@msg objects inside a blend, envelope, repeat or Intertwine group can't be attached to a path els objectes dins d’una fusió, un embolcall, una repetició o un grup Intertwine no es poden adjuntar a un traçat
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) els pinzells que VectorCraft encara no pot llegir s'han deixat fora del tauler Pinzells ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet els objectes amb diversos farciments o traços, efectes o pinzells han arribat amb el seu aspecte dibuixat ({_1}): la seva aparença encara no és editable
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet els objectes amb diversos farciments o traços, traços d’amplada variable, efectes o pinzells han arribat amb el seu aspecte dibuixat ({_1}): la seva aparença encara no és editable
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding el nom `{name}` no és UTF-8 i s’ha llegit com a {label}: si sembla incorrecte, obriu el fitxer amb un altre legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding el nom `{name}` no és UTF-8, i algunes parts tampoc no són {label} vàlid: obriu el fitxer amb un altre legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) s’han llegit com a {label} més noms que no són UTF-8 ({count})
@@ -6637,3 +6637,4 @@ breakText Glyphs Glifs
# Align to Pixel Grid
Align to Pixel Grid Alinea a la quadrícula de píxels
@msg only paths and compound paths can be aligned to the pixel grid només els traçats i els traçats compostos es poden alinear a la quadrícula de píxels
@msg side is left or right, not `{s}` side ha de ser left o right, no `{s}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -1289,7 +1289,7 @@ lighting Light Světlo
X: X:
Y: Y:
Drag to float the toolbar Přetažením vytvoříte plovoucí panel nástrojů
Drag to the window's left edge to dock Přetažením k levému okraji okna panel ukotvíte
Drag to the window's left or right edge to dock Přetažením k levému nebo pravému okraji okna panel ukotvíte
# Tools
Crystallize Krystalizace
@@ -5089,7 +5089,7 @@ imageTrace Outline Obrys
@msg its objects are nested too deeply jeho objekty jsou vnořené příliš hluboko
@msg its section ending with `%{end}` doesn't end jeho oddíl končící `%{end}` nemá konec
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) štětce, které VectorCraft zatím neumí načíst, byly vynechány z panelu Štětce ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objekty s více výplněmi nebo tahy, efekty či štětci byly načteny jako vykreslený vzhled ({_1}): jejich vzhled zatím nelze upravovat
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objekty s více výplněmi nebo tahy, tahy s proměnnou šířkou, efekty či štětci byly načteny jako vykreslený vzhled ({_1}): jejich vzhled zatím nelze upravovat
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding název `{name}` není v UTF-8 a byl načten jako {label}: pokud vypadá špatně, otevřete soubor s jiným legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding název `{name}` není v UTF-8 a jeho části nejsou platné ani v {label}: otevřete soubor s jiným legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) další názvy, které nejsou v UTF-8, byly načteny jako {label} ({count})
@@ -6656,3 +6656,4 @@ breakText Glyphs Glyfy
# Align to Pixel Grid
Align to Pixel Grid Zarovnat na mřížku obrazových bodů
@msg only paths and compound paths can be aligned to the pixel grid na mřížku obrazových bodů lze zarovnat jen cesty a složené cesty
@msg side is left or right, not `{s}` side je left nebo right, ne `{s}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -1204,7 +1204,7 @@ lighting Light Lys
X: X:
Y: Y:
Drag to float the toolbar Træk for at frigøre værktøjslinjen
Drag to the window's left edge to dock Træk til vinduets venstre kant for at fastgøre
Drag to the window's left or right edge to dock Træk til vinduets venstre eller højre kant for at fastgøre
Crystallize Krystallisér
Add Anchor Point Tool Tilføj ankerpunkt-værktøj
Anchor Point Tool Ankerpunktværktøj
@@ -4706,7 +4706,7 @@ imageTrace Outline Kontur
@msg its objects are nested too deeply dens objekter er indlejret for dybt
@msg its section ending with `%{end}` doesn't end dens sektion, der ender med `%{end}`, ender ikke
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) pensler, som VectorCraft endnu ikke kan læse, blev udeladt af panelet Pensler ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objekter med flere fyld eller streger, effekter eller pensler kom ind, som de er tegnet ({_1}): deres udseende er endnu ikke redigerbart
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objekter med flere fyld eller streger, streger med variabel bredde, effekter eller pensler kom ind, som de er tegnet ({_1}): deres udseende er endnu ikke redigerbart
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding navnet `{name}` er ikke UTF-8 og blev læst som {label}: hvis det ser forkert ud, så åbn filen med en anden legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding navnet `{name}` er ikke UTF-8, og dele af det er heller ikke gyldig {label}: åbn filen med en anden legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) flere navne, der ikke er UTF-8, blev læst som {label} ({count})
@@ -6440,3 +6440,4 @@ breakText Glyphs Glyffer
# Align to Pixel Grid
Align to Pixel Grid Juster efter pixelgitter
@msg side is left or right, not `{s}` side er left eller right, ikke `{s}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -3937,7 +3937,7 @@ theme Appearance Erscheinungsbild
Dock to Bottom Unten andocken
Dock Control Bar Steuerungsleiste andocken
Drag to float the toolbar Ziehen, um die Werkzeugleiste abzulösen
Drag to the window's left edge to dock Zum Andocken an den linken Fensterrand ziehen
Drag to the window's left or right edge to dock Zum Andocken an den linken oder rechten Fensterrand ziehen
Float Tool Group Werkzeuggruppe ablösen
# Status and error messages (`@msg`), translated where the status bar shows them; the message itself
@@ -4697,7 +4697,7 @@ theme Appearance Erscheinungsbild
@msg its objects are nested too deeply seine Objekte sind zu tief verschachtelt
@msg its section ending with `%{end}` doesn't end sein Abschnitt, der mit `%{end}` enden soll, endet nicht
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) Pinsel, die VectorCraft noch nicht lesen kann, wurden im Bedienfeld „Pinsel“ ausgelassen ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet Objekte mit mehreren Flächen oder Konturen, Effekten oder Pinseln wurden so übernommen, wie sie gezeichnet aussehen ({_1}): Ihr Aussehen ist noch nicht bearbeitbar
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet Objekte mit mehreren Flächen oder Konturen, Konturen mit variabler Breite, Effekten oder Pinseln wurden so übernommen, wie sie gezeichnet aussehen ({_1}): Ihr Aussehen ist noch nicht bearbeitbar
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding Der Name `{name}` ist kein UTF-8 und wurde als {label} gelesen: Wenn er falsch aussieht, öffnen Sie die Datei mit einer anderen legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding Der Name `{name}` ist kein UTF-8, und Teile davon sind auch kein gültiges {label}: Öffnen Sie die Datei mit einer anderen legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) Weitere Namen, die kein UTF-8 sind, wurden als {label} gelesen ({count})
@@ -6243,3 +6243,4 @@ breakText Glyphs Glyphen
# Align to Pixel Grid
Align to Pixel Grid Am Pixelraster ausrichten
@msg only paths and compound paths can be aligned to the pixel grid nur Pfade und zusammengesetzte Pfade können am Pixelraster ausgerichtet werden
@msg side is left or right, not `{s}` side ist left oder right, nicht `{s}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -4145,7 +4145,7 @@ digits Malayalam Μαλαγιάλαμ
Drag to float as a toolbar Σύρετε για αποδέσμευση ως γραμμή εργαλείων
Put back in the toolbar Επαναφορά στη γραμμή εργαλείων
Drag to float the toolbar Σύρετε για αποδέσμευση της γραμμής εργαλείων
Drag to the window's left edge to dock Σύρετε στο αριστερό άκρο του παραθύρου για σύνδεση
Drag to the window's left or right edge to dock Σύρετε στο αριστερό ή στο δεξί άκρο του παραθύρου για σύνδεση
Float Tool Group Αποδέσμευση ομάδας εργαλείων
@msg a PostScript error ({name} in `{op}`) σφάλμα PostScript ({name} στην εντολή `{op}`)
@msg a PostScript error ({name} in `{op}`), in {procs} σφάλμα PostScript ({name} στην εντολή `{op}`), στις διαδικασίες {procs}
@@ -4729,7 +4729,7 @@ digits Malayalam Μαλαγιάλαμ
@msg its objects are nested too deeply τα αντικείμενά του έχουν υπερβολικά βαθιά ένθεση
@msg its section ending with `%{end}` doesn't end η ενότητά του που τελειώνει σε `%{end}` δεν τερματίζεται
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) τα πινέλα που το VectorCraft δεν μπορεί ακόμη να διαβάσει παραλείφθηκαν από τον πίνακα Πινέλα ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet τα αντικείμενα με πολλαπλά γεμίσματα, περιγράμματα, εφέ ή πινέλα εισήχθησαν ως απεικονίσεις ({_1}): η εμφάνισή τους δεν είναι ακόμη επεξεργάσιμη
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet τα αντικείμενα με πολλαπλά γεμίσματα, περιγράμματα, περιγράμματα μεταβλητού πλάτους, εφέ ή πινέλα εισήχθησαν ως απεικονίσεις ({_1}): η εμφάνισή τους δεν είναι ακόμη επεξεργάσιμη
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding το όνομα `{name}` δεν είναι UTF-8 και διαβάστηκε ως {label}: αν φαίνεται λάθος, ανοίξτε το αρχείο με άλλο legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding το όνομα `{name}` δεν είναι UTF-8 και τμήματά του δεν είναι έγκυρα ούτε σε {label}: ανοίξτε το αρχείο με άλλο legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) περισσότερα ονόματα που δεν είναι UTF-8 διαβάστηκαν ως {label} ({count})
@@ -6608,3 +6608,4 @@ breakText Glyphs Γλύφοι
# Align to Pixel Grid
Align to Pixel Grid Στοίχιση στο πλέγμα εικονοστοιχείων
@msg only paths and compound paths can be aligned to the pixel grid μόνο διαδρομές και σύνθετες διαδρομές μπορούν να στοιχιστούν στο πλέγμα εικονοστοιχείων
@msg side is left or right, not `{s}` το side είναι left ή right, όχι `{s}`
Can't render this file because it is too large.
+3 -2
View File
@@ -4022,7 +4022,7 @@ axis Both Ambos
Dock to Bottom Anclar abajo
Dock Control Bar Anclar barra de control
Drag to float the toolbar Arrastrar para hacer flotar la barra de herramientas
Drag to the window's left edge to dock Arrastrar al borde izquierdo de la ventana para anclar
Drag to the window's left or right edge to dock Arrastrar al borde izquierdo o derecho de la ventana para anclar
Float Tool Group Hacer flotar el grupo de herramientas
# Status and error messages (`@msg`), translated where the status bar shows them; the message itself
@@ -4781,7 +4781,7 @@ axis Both Ambos
@msg its objects are nested too deeply sus objetos están anidados a demasiada profundidad
@msg its section ending with `%{end}` doesn't end su sección que termina con `%{end}` no termina
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) los pinceles que VectorCraft aún no puede leer se omitieron del panel Pinceles ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet los objetos con varios rellenos o trazos, efectos o pinceles se importaron con su aspecto dibujado ({_1}): su aspecto aún no se puede editar
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet los objetos con varios rellenos o trazos, trazos de ancho variable, efectos o pinceles se importaron con su aspecto dibujado ({_1}): su aspecto aún no se puede editar
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding el nombre `{name}` no está en UTF-8 y se leyó como {label}: si no se ve bien, abre el archivo con otro legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding el nombre `{name}` no está en UTF-8 y partes de él tampoco son {label} válido: abre el archivo con otro legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) se leyeron como {label} más nombres que no están en UTF-8 ({count})
@@ -6599,3 +6599,4 @@ breakText Glyphs Glifos
# Align to Pixel Grid
Align to Pixel Grid Alinear con cuadrícula de píxeles
@msg only paths and compound paths can be aligned to the pixel grid solo los trazados y los trazados compuestos se pueden alinear con la cuadrícula de píxeles
@msg side is left or right, not `{s}` side es left o right, no `{s}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -1267,7 +1267,7 @@ lighting Light Valo
X: X:
Y: Y:
Drag to float the toolbar Kelluta työkaluriviä vetämällä
Drag to the window's left edge to dock Telakoi vetämällä ikkunan vasempaan reunaan
Drag to the window's left or right edge to dock Telakoi vetämällä ikkunan vasempaan tai oikeaan reunaan
Crystallize Kiteytä
Add Anchor Point Tool Lisää ankkuripiste -työkalu
Anchor Point Tool Ankkuripistetyökalu
@@ -4991,7 +4991,7 @@ imageTrace Outline Ääriviiva
@msg its objects are nested too deeply sen objektit ovat liian syvällä sisäkkäin
@msg its section ending with `%{end}` doesn't end sen `%{end}`-merkintään päättyvä osio ei pääty
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) siveltimet, joita VectorCraft ei vielä osaa lukea, jätettiin pois Siveltimet-paneelista ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objektit, joilla on useita täyttöjä tai viivoja, tehosteita tai siveltimiä, tuotiin piirretyn näköisinä ({_1}): niiden ulkoasua ei voi vielä muokata
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objektit, joilla on useita täyttöjä tai viivoja, vaihtelevan levyisiä viivoja, tehosteita tai siveltimiä, tuotiin piirretyn näköisinä ({_1}): niiden ulkoasua ei voi vielä muokata
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding nimi `{name}` ei ole UTF-8:aa, ja se luettiin koodauksella {label}: jos se näyttää väärältä, avaa tiedosto toisella legacyNameEncoding-arvolla
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding nimi `{name}` ei ole UTF-8:aa, eivätkä kaikki sen osat ole kelvollista {label}-koodausta: avaa tiedosto toisella legacyNameEncoding-arvolla
@msg more names that aren't UTF-8 were read as {label} ({count}) muita nimiä, jotka eivät ole UTF-8:aa, luettiin koodauksella {label} ({count})
@@ -6691,3 +6691,4 @@ breakText Glyphs Merkit
# Align to Pixel Grid
Align to Pixel Grid Tasaa pikseliruudukkoon
@msg only paths and compound paths can be aligned to the pixel grid vain polut ja yhdistelmäpolut voi tasata pikseliruudukkoon
@msg side is left or right, not `{s}` side on left tai right, ei `{s}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -4011,7 +4011,7 @@ axis Both Les deux
Dock to Bottom Ancrer en bas
Dock Control Bar Ancrer la barre de contrôle
Drag to float the toolbar Faire glisser pour rendre la barre d’outils flottante
Drag to the window's left edge to dock Faire glisser vers le bord gauche de la fenêtre pour ancrer
Drag to the window's left or right edge to dock Faire glisser vers le bord gauche ou droit de la fenêtre pour ancrer
Float Tool Group Rendre le groupe d’outils flottant
# Status and error messages (`@msg`), translated where the status bar shows them; the message itself
@@ -4768,7 +4768,7 @@ axis Both Les deux
@msg its objects are nested too deeply leurs objets sont imbriqués trop profondément
@msg its section ending with `%{end}` doesn't end leur section qui se termine par `%{end}` ne se termine pas
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) les formes que VectorCraft ne sait pas encore lire ont été omises du panneau Formes ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet les objets à plusieurs fonds ou contours, effets ou formes ont été importés tels qu’ils sont dessinés ({_1}) : leur aspect n’est pas encore modifiable
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet les objets à plusieurs fonds ou contours, contours de largeur variable, effets ou formes ont été importés tels qu’ils sont dessinés ({_1}) : leur aspect n’est pas encore modifiable
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding le nom `{name}` n’est pas en UTF-8 et a été lu en {label} : s’il semble incorrect, ouvrez le fichier avec un autre legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding le nom `{name}` n’est pas en UTF-8, et certaines parties ne sont pas non plus du {label} valide : ouvrez le fichier avec un autre legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) d’autres noms qui ne sont pas en UTF-8 ont été lus en {label} ({count})
@@ -6651,3 +6651,4 @@ breakText Glyphs Glyphes
# Align to Pixel Grid
Align to Pixel Grid Aligner sur la grille de pixels
@msg only paths and compound paths can be aligned to the pixel grid seuls les tracés et les tracés transparents peuvent être alignés sur la grille de pixels
@msg side is left or right, not `{s}` side vaut left ou right, pas `{s}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -1384,7 +1384,7 @@ antiAlias Sharp शार्प
Drag to float as a toolbar एक टूलबार के रूप में तैरने के लिए खींचें
Drag to float the toolbar टूलबार को तैरने के लिए खींचें
Drag to move आगे बढ़ने के लिए
Drag to the window's left edge to dock खिड़की के बाएं किनारे पर डॉक करने के लिए खींचें
Drag to the window's left or right edge to dock खिड़की के बाएं या दाएं किनारे पर डॉक करने के लिए खींचें
Drag values or labels. Double-click a value to type. मूल्यों या लेबल खींचें। टाइप करने के लिए एक मान को डबल क्लिक करें।
Draw ड्रा
Draw Behind पीछे खींचना
@@ -6998,7 +6998,7 @@ antiAlias Sharp शार्प
@msg warped text objects that came in as drawn art, not as editable type: {_1} मोड़े गए टेक्स्ट ऑब्जेक्ट जो संपादन योग्य टेक्स्ट के बजाय कला के रूप में आए: {_1}
@msg objects that drew differently from the file's own page came in as its art: {_1} उन ऑब्जेक्ट्स को जो फ़ाइल के स्वयं के पृष्ठ से अलग-अलग तरीके से आकर्षित होते हैं, उनकी कला के रूप में आया: {_1}
@msg objects were left out ऑब्जेक्ट्स को छोड़ दिया गया था
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet कई भराव या स्ट्रोक, इफ़ेक्ट या ब्रश के साथ ऑब्जेक्टएं उनके ड्रॉइंग लुक ({_1}) के रूप में आईं: उनकी उपस्थिति अभी तक संपादन योग्य नहीं है
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet कई भराव या स्ट्रोक, परिवर्तनीय चौड़ाई वाले स्ट्रोक, इफ़ेक्ट या ब्रश के साथ ऑब्जेक्टएं उनके ड्रॉइंग लुक ({_1}) के रूप में आईं: उनकी उपस्थिति अभी तक संपादन योग्य नहीं है
@msg offset out of range सीमा से बाहर ऑफसेट
@msg Offset Path: {e} ऑफसेट पाथ: {e}
@msg Offset Path: select paths ऑफसेट पाथ: सिलेक्शन पाथ
@@ -8098,3 +8098,4 @@ theme Appearance अपीयरेंस
@msg the file is larger than the {MAX_INPUT_BYTES} bytes an importer reads फ़ाइल उन {MAX_INPUT_BYTES} बाइट्स से बड़ी है जो इंपोर्टर पढ़ता है
@msg an importer declares 1 to {MAX_EXTENSIONS} `extensions` इंपोर्टर 1 से {MAX_EXTENSIONS} `extensions` घोषित करता है
@msg an importer always gets an import's budget: leave `budget` out इंपोर्टर को हमेशा इंपोर्ट का बजट मिलता है: `budget` छोड़ दें
@msg side is left or right, not `{s}` side left या right होना चाहिए, `{s}` नहीं
Can't render this file because it is too large.
+3 -2
View File
@@ -1161,7 +1161,7 @@ font-technology Variable Változó
Drag to float as a toolbar Húzd el, hogy önálló eszköztárként lebegjen
Drag to float the toolbar Húzd el az eszköztár lebegtetéséhez
Drag to move Húzással áthelyezhető
Drag to the window's left edge to dock Dokkoláshoz húzd az ablak bal szélére
Drag to the window's left or right edge to dock Dokkoláshoz húzd az ablak bal vagy jobb szélére
Step up or down Léptetés fel vagy le
Drag values or labels. Double-click a value to type. Húzd az értékeket vagy a címkéket. Gépeléshez kattints duplán egy értékre.
Draw Rajzolás
@@ -5169,7 +5169,7 @@ imageTrace Outline Körvonal
@msg object {id} is not a path az objektum nem útvonal: {id}
@msg object {id} is not a selected top-level object az objektum nem kijelölt legfelső szintű objektum: {id}
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) a VectorCraft által még nem olvasható ecsetek kimaradtak az Ecsetek panelről ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet a több kitöltésű vagy körvonalú, effektusos vagy ecsetes objektumok a megrajzolt kinézetükkel kerültek be ({_1}): a megjelenésük még nem szerkeszthető
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet a több kitöltésű vagy körvonalú, változó szélességű körvonalú, effektusos vagy ecsetes objektumok a megrajzolt kinézetükkel kerültek be ({_1}): a megjelenésük még nem szerkeszthető
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding a(z) `{name}` név nem UTF-8, ezért {label} kódolással lett beolvasva: ha hibásnak tűnik, nyissa meg a fájlt másik legacyNameEncoding értékkel
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding a(z) `{name}` név nem UTF-8, és egyes részei {label} kódolásban sem érvényesek: nyissa meg a fájlt másik legacyNameEncoding értékkel
@msg more names that aren't UTF-8 were read as {label} ({count}) további nem UTF-8 nevek lettek {label} kódolással beolvasva ({count})
@@ -6640,3 +6640,4 @@ breakText Glyphs Glifák
# Align to Pixel Grid
Align to Pixel Grid Igazítás a pixelrácshoz
@msg only paths and compound paths can be aligned to the pixel grid csak útvonalak és összetett útvonalak igazíthatók a pixelrácshoz
@msg side is left or right, not `{s}` a side értéke left vagy right lehet, nem `{s}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -1204,7 +1204,7 @@ lighting Light Cahaya
X: X:
Y: Y:
Drag to float the toolbar Seret untuk mengambangkan bilah alat
Drag to the window's left edge to dock Seret ke tepi kiri jendela untuk memasang
Drag to the window's left or right edge to dock Seret ke tepi kiri atau kanan jendela untuk memasang
Crystallize Kristalisasi
Add Anchor Point Tool Alat tambah titik jangkar
Anchor Point Tool Alat titik jangkar
@@ -4706,7 +4706,7 @@ imageTrace Outline Kontur
@msg its objects are nested too deeply objeknya bersarang terlalu dalam
@msg its section ending with `%{end}` doesn't end bagian yang berakhir dengan `%{end}` tidak berakhir
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) kuas yang belum dapat dibaca VectorCraft tidak dimasukkan ke panel Kuas ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objek dengan beberapa isian atau goresan, efek, atau kuas masuk sebagai tampilan gambarnya ({_1}): penampilannya belum dapat diedit
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objek dengan beberapa isian atau goresan, goresan dengan lebar bervariasi, efek, atau kuas masuk sebagai tampilan gambarnya ({_1}): penampilannya belum dapat diedit
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding nama `{name}` bukan UTF-8 dan dibaca sebagai {label}: jika terlihat salah, buka file dengan legacyNameEncoding lain
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding nama `{name}` bukan UTF-8, dan sebagiannya juga bukan {label} yang valid: buka file dengan legacyNameEncoding lain
@msg more names that aren't UTF-8 were read as {label} ({count}) nama lain yang bukan UTF-8 dibaca sebagai {label} ({count})
@@ -6440,3 +6440,4 @@ breakText Glyphs Glif
# Align to Pixel Grid
Align to Pixel Grid Sejajarkan ke grid piksel
@msg side is left or right, not `{s}` side adalah left atau right, bukan `{s}`
Can't render this file because it contains an unexpected character in line 2306 and column 5.
+3 -2
View File
@@ -4064,7 +4064,7 @@ axis Both Entrambi
Dock to Bottom Ancora in basso
Dock Control Bar Ancora barra di controllo
Drag to float the toolbar Trascina per rendere mobile la barra degli strumenti
Drag to the window's left edge to dock Trascina sul bordo sinistro della finestra per ancorare
Drag to the window's left or right edge to dock Trascina sul bordo sinistro o destro della finestra per ancorare
Float Tool Group Rendi mobile gruppo strumenti
# Status and error messages (`@msg`), translated where the status bar shows them; the message itself
@@ -4823,7 +4823,7 @@ axis Both Entrambi
@msg its objects are nested too deeply i loro oggetti sono annidati troppo in profondità
@msg its section ending with `%{end}` doesn't end la loro sezione che termina con `%{end}` non termina
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) i pennelli che VectorCraft non sa ancora leggere sono stati esclusi dal pannello Pennelli ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet gli oggetti con più riempimenti o tratti, effetti o pennelli sono stati importati come appaiono disegnati ({_1}): il loro aspetto non è ancora modificabile
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet gli oggetti con più riempimenti o tratti, tratti a larghezza variabile, effetti o pennelli sono stati importati come appaiono disegnati ({_1}): il loro aspetto non è ancora modificabile
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding il nome `{name}` non è in UTF-8 ed è stato letto come {label}: se sembra errato, apri il file con un altro legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding il nome `{name}` non è in UTF-8 e alcune sue parti non sono valide nemmeno in {label}: apri il file con un altro legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) altri nomi non in UTF-8 sono stati letti come {label} ({count})
@@ -6277,3 +6277,4 @@ breakText Glyphs Glifi
# Align to Pixel Grid
Align to Pixel Grid Allinea alla griglia pixel
@msg only paths and compound paths can be aligned to the pixel grid solo i tracciati e i tracciati composti possono essere allineati alla griglia pixel
@msg side is left or right, not `{s}` side è left o right, non `{s}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -4006,7 +4006,7 @@ axis Both 両方
Dock to Bottom 下部にドッキング
Dock Control Bar コントロールバーをドッキング
Drag to float the toolbar ドラッグしてツールバーをフローティング
Drag to the window's left edge to dock ウィンドウの左端までドラッグしてドッキング
Drag to the window's left or right edge to dock ウィンドウの左端または右端までドラッグしてドッキング
Float Tool Group ツールグループをフローティング
# The macOS menu bar (native_menu): the app and Window menus' system items, Settings pages.
@@ -4514,7 +4514,7 @@ swatch Bright Violet ビビッドバイオレット
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise 表示に使われるフォントの 1 行に対して低すぎるフレーム内のエリア内文字は、フレームを高くしました ({_1}): そうしないと文字が表示されません
@msg gradient “{name}” isn't defined in the file: a plain colour stands in for it グラデーション「{name}」はファイル内で定義されていません: 代わりに単色を使用しています
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) VectorCraft がまだ読み込めないブラシ {_1} 個はブラシパネルに追加されませんでした
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet 塗りや線が複数ある、または効果やブラシを適用したオブジェクト {_1} 個は見た目のまま読み込まれました: アピアランスはまだ編集できません
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet 塗りや線が複数ある、可変幅の線がある、または効果やブラシを適用したオブジェクト {_1} 個は見た目のまま読み込まれました: アピアランスはまだ編集できません
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding 名前 `{name}` は UTF-8 ではないため {label} として読み込みました: 正しくない場合は別の legacyNameEncoding でファイルを開いてください
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding 名前 `{name}` は UTF-8 ではなく、一部は {label} としても無効です: 別の legacyNameEncoding でファイルを開いてください
@msg more names that aren't UTF-8 were read as {label} ({count}) UTF-8 ではないほかの名前を {label} として読み込みました ({count})
@@ -6342,3 +6342,4 @@ breakText Glyphs 字形
# Align to Pixel Grid
Align to Pixel Grid ピクセルグリッドに整合
@msg only paths and compound paths can be aligned to the pixel grid ピクセルグリッドに整合できるのはパスと複合パスのみです
@msg side is left or right, not `{s}` side は left または right です(`{s}` ではありません)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -1144,7 +1144,7 @@ lighting Light სინათლე
Toggle single/double column ერთი/ორი სვეტის გადართვა
W: სიგ.:
Drag to float the toolbar გადაათრიეთ ხელსაწყოთა ზოლის მცურავად გადასაქცევად
Drag to the window's left edge to dock მისამაგრებლად გადაათრიეთ ფანჯრის მარცხენა კიდესთან
Drag to the window's left or right edge to dock მისამაგრებლად გადაათრიეთ ფანჯრის მარცხენა ან მარჯვენა კიდესთან
Crystallize კრისტალიზაცია
Add Anchor Point Tool საყრდენი წერტილის დამატების ხელსაწყო
Anchor Point Tool საყრდენი წერტილის ხელსაწყო
@@ -4515,7 +4515,7 @@ font-technology Variable ცვლადი
@msg its objects are nested too deeply მისი ობიექტები ზედმეტად ღრმადაა ჩადგმული
@msg its section ending with `%{end}` doesn't end მისი სექცია, რომელიც `%{end}`-ით მთავრდება, არ სრულდება
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) ფუნჯები, რომლებსაც VectorCraft ჯერ ვერ კითხულობს, ფუნჯების პანელში არ დაემატა ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet რამდენიმე შევსების ან კონტურის, ეფექტის ან ფუნჯის მქონე ობიექტები შემოვიდა დახატული სახით ({_1}): მათი იერსახე ჯერ არ რედაქტირდება
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet რამდენიმე შევსების ან კონტურის, ცვლადი სისქის კონტურის, ეფექტის ან ფუნჯის მქონე ობიექტები შემოვიდა დახატული სახით ({_1}): მათი იერსახე ჯერ არ რედაქტირდება
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding სახელი `{name}` არ არის UTF-8 და წაკითხულია როგორც {label}: თუ არასწორად გამოიყურება, გახსენით ფაილი სხვა legacyNameEncoding-ით
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding სახელი `{name}` არ არის UTF-8 და მისი ნაწილები არც {label}-ში არის სწორი: გახსენით ფაილი სხვა legacyNameEncoding-ით
@msg more names that aren't UTF-8 were read as {label} ({count}) სხვა სახელები, რომლებიც არ არის UTF-8, წაკითხულია როგორც {label} ({count})
@@ -6717,3 +6717,4 @@ font-script Japanese იაპონური
# Align to Pixel Grid
Align to Pixel Grid პიქსელურ ბადეზე გასწორება
@msg only paths and compound paths can be aligned to the pixel grid პიქსელურ ბადეზე მხოლოდ ტრაექტორიების და შედგენილი ტრაექტორიების გასწორებაა შესაძლებელი
@msg side is left or right, not `{s}` side არის left ან right და არა `{s}`
Can't render this file because it is too large.
+3 -2
View File
@@ -3990,7 +3990,7 @@ axis Both 양쪽
Dock to Bottom 아래쪽에 고정
Dock Control Bar 컨트롤 막대 고정
Drag to float the toolbar 드래그하여 도구 모음 분리
Drag to the window's left edge to dock 창 왼쪽 가장자리로 드래그하여 고정
Drag to the window's left or right edge to dock 창 왼쪽 또는 오른쪽 가장자리로 드래그하여 고정
Float Tool Group 도구 그룹 분리
Services 서비스
Hide VectorCraft VectorCraft 숨기기
@@ -4441,7 +4441,7 @@ theme Appearance 외관
@msg area type in frames too short for a line of the font it shows in got taller frames ({_1}): their type wouldn't show otherwise 표시되는 글꼴의 한 줄보다 낮은 프레임의 영역 문자는 프레임을 더 높였습니다({_1}). 그렇지 않으면 문자가 표시되지 않습니다
@msg gradient “{name}” isn't defined in the file: a plain colour stands in for it 그레이디언트 “{name}”이(가) 파일에 정의되어 있지 않아 단색으로 대체했습니다
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) VectorCraft에서 아직 읽을 수 없는 브러시는 브러시 패널에서 제외되었습니다({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet 칠이나 선이 여러 개인 개체, 효과, 브러시가 있는 개체는 그려진 모양({_1})으로 가져왔습니다: 모양새는 아직 편집할 수 없습니다
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet 칠이나 선이 여러 개인 개체, 폭이 변하는 선, 효과, 브러시가 있는 개체는 그려진 모양({_1})으로 가져왔습니다: 모양새는 아직 편집할 수 없습니다
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding 이름 `{name}`은(는) UTF-8이 아니어서 {label}(으)로 읽었습니다: 잘못 보이면 다른 legacyNameEncoding으로 파일을 여세요
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding 이름 `{name}`은(는) UTF-8이 아니고 일부는 {label}에서도 올바르지 않습니다: 다른 legacyNameEncoding으로 파일을 여세요
@msg more names that aren't UTF-8 were read as {label} ({count}) UTF-8이 아닌 다른 이름도 {label}(으)로 읽었습니다({count})
@@ -6238,3 +6238,4 @@ breakText Glyphs 글리프
# Align to Pixel Grid
Align to Pixel Grid 픽셀 격자에 정렬
@msg only paths and compound paths can be aligned to the pixel grid 패스와 컴파운드 패스만 픽셀 격자에 정렬할 수 있습니다
@msg side is left or right, not `{s}` side는 left 또는 right입니다(`{s}` 아님)
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -1218,7 +1218,7 @@ lighting Light Licht
X: X:
Y: Y:
Drag to float the toolbar Sleep om de werkbalk zwevend te maken
Drag to the window's left edge to dock Sleep naar de linkerrand van het venster om te dokken
Drag to the window's left or right edge to dock Sleep naar de linker- of rechterrand van het venster om te dokken
Crystallize Kristalliseren
Add Anchor Point Tool Ankerpunt toevoegen
Anchor Point Tool Ankerpunt-gereedschap
@@ -4718,7 +4718,7 @@ imageTrace Outline Contour
@msg its objects are nested too deeply de objecten ervan zijn te diep genest
@msg its section ending with `%{end}` doesn't end zijn sectie die op `%{end}` eindigt, eindigt niet
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) penselen die VectorCraft nog niet kan lezen, zijn weggelaten uit het deelvenster Penselen ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objecten met meerdere vullingen of lijnen, effecten of penselen zijn als hun getekende uiterlijk binnengekomen ({_1}): hun vormgeving is nog niet bewerkbaar
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objecten met meerdere vullingen of lijnen, lijnen met variabele breedte, effecten of penselen zijn als hun getekende uiterlijk binnengekomen ({_1}): hun vormgeving is nog niet bewerkbaar
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding de naam `{name}` is geen UTF-8 en is gelezen als {label}: als die er verkeerd uitziet, open het bestand dan met een andere legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding de naam `{name}` is geen UTF-8, en delen ervan zijn ook geen geldige {label}: open het bestand met een andere legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) meer namen die geen UTF-8 zijn, zijn gelezen als {label} ({count})
@@ -6491,3 +6491,4 @@ breakText Glyphs Glyphs
# Align to Pixel Grid
Align to Pixel Grid Uitlijnen op pixelraster
@msg side is left or right, not `{s}` side is left of right, niet `{s}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -1217,7 +1217,7 @@ lighting Light Światło
Toggle single/double column Przełącz jedną/dwie kolumny
W: S:
Drag to float the toolbar Przeciągnij, aby odpiąć pasek narzędzi
Drag to the window's left edge to dock Przeciągnij do lewej krawędzi okna, aby zadokować
Drag to the window's left or right edge to dock Przeciągnij do lewej lub prawej krawędzi okna, aby zadokować
Crystallize Krystalizacja
Add Anchor Point Tool Narzędzie Dodaj punkt kontrolny
Anchor Point Tool Narzędzie Punkt kontrolny
@@ -4915,7 +4915,7 @@ imageTrace Outline Kontur
@msg its objects are nested too deeply jego obiekty są zagnieżdżone zbyt głęboko
@msg its section ending with `%{end}` doesn't end jego sekcja kończąca się na `%{end}` nie ma końca
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) pędzle, których VectorCraft jeszcze nie odczytuje, pominięto w panelu Pędzle ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet obiekty z kilkoma wypełnieniami lub obrysami, efektami albo pędzlami wczytano jako ich narysowany wygląd ({_1}): ich wyglądu nie można jeszcze edytować
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet obiekty z kilkoma wypełnieniami lub obrysami, obrysami o zmiennej szerokości, efektami albo pędzlami wczytano jako ich narysowany wygląd ({_1}): ich wyglądu nie można jeszcze edytować
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding nazwa `{name}` nie jest w UTF-8 i została odczytana jako {label}: jeśli wygląda źle, otwórz plik z innym legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding nazwa `{name}` nie jest w UTF-8, a jej części nie są poprawne również w {label}: otwórz plik z innym legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) kolejne nazwy spoza UTF-8 odczytano jako {label} ({count})
@@ -6512,3 +6512,4 @@ breakText Glyphs Glify
# Align to Pixel Grid
Align to Pixel Grid Wyrównaj do siatki pikseli
@msg only paths and compound paths can be aligned to the pixel grid do siatki pikseli można wyrównać tylko ścieżki i ścieżki złożone
@msg side is left or right, not `{s}` side to left lub right, a nie `{s}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -3610,7 +3610,7 @@ axis Horizontal Horizontal
axis Vertical Vertical
axis Both Ambos
Drag to float the toolbar Arraste para flutuar a barra de ferramentas
Drag to the window's left edge to dock Arraste até a borda esquerda da janela para encaixar
Drag to the window's left or right edge to dock Arraste até a borda esquerda ou direita da janela para encaixar
@msg no library `{id}` nenhuma biblioteca `{id}`
@msg select some type first selecione algum texto primeiro
@msg kerning must be a number, "auto", or "optical" O kerning deve ser um número, “auto” ou “optical”
@@ -4350,7 +4350,7 @@ axis Both Ambos
@msg its objects are nested too deeply seus objetos estão aninhados com profundidade excessiva
@msg its section ending with `%{end}` doesn't end sua seção que termina com `%{end}` não termina
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) os pincéis que o VectorCraft ainda não consegue ler ficaram fora do painel Pincéis ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objetos com vários preenchimentos ou traços, efeitos ou pincéis entraram com a aparência desenhada ({_1}): sua aparência ainda não é editável
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objetos com vários preenchimentos ou traços, traços de largura variável, efeitos ou pincéis entraram com a aparência desenhada ({_1}): sua aparência ainda não é editável
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding o nome `{name}` não está em UTF-8 e foi lido como {label}: se parecer errado, abra o arquivo com outro legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding o nome `{name}` não está em UTF-8, e partes dele também não são {label} válido: abra o arquivo com outro legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) mais nomes que não estão em UTF-8 foram lidos como {label} ({count})
@@ -6729,3 +6729,4 @@ swatch Yellow Green Verde-amarelado
# Align to Pixel Grid
Align to Pixel Grid Alinhar à Grade de Pixels
@msg only paths and compound paths can be aligned to the pixel grid somente caminhos e caminhos compostos podem ser alinhados à grade de pixels
@msg side is left or right, not `{s}` side é left ou right, não `{s}`
Can't render this file because it contains an unexpected character in line 6 and column 47.
+3 -2
View File
@@ -4058,7 +4058,7 @@ axis Both Оба
Dock to Bottom Прикрепить снизу
Dock Control Bar Прикрепить панель управления
Drag to float the toolbar Перетащите, чтобы сделать панель инструментов плавающей
Drag to the window's left edge to dock Перетащите к левому краю окна для прикрепления
Drag to the window's left or right edge to dock Перетащите к левому или правому краю окна для прикрепления
Float Tool Group Сделать группу инструментов плавающей
# Status and error messages (`@msg`), translated where the status bar shows them; the message itself
@@ -4817,7 +4817,7 @@ axis Both Оба
@msg its objects are nested too deeply объекты в них вложены слишком глубоко
@msg its section ending with `%{end}` doesn't end раздел, заканчивающийся на `%{end}`, не заканчивается
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) кисти, которые VectorCraft пока не умеет читать, не добавлены в панель «Кисти» ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet объекты с несколькими заливками или обводками, эффектами или кистями импортированы в нарисованном виде ({_1}): их оформление пока нельзя редактировать
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet объекты с несколькими заливками или обводками, обводками переменной ширины, эффектами или кистями импортированы в нарисованном виде ({_1}): их оформление пока нельзя редактировать
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding имя `{name}` не в UTF-8 и прочитано как {label}: если оно выглядит неверно, откройте файл с другим legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding имя `{name}` не в UTF-8, и его части недопустимы и в {label}: откройте файл с другим legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) другие имена не в UTF-8 прочитаны как {label} ({count})
@@ -6745,3 +6745,4 @@ shape Open Открытый
# Align to Pixel Grid
Align to Pixel Grid Выровнять по пиксельной сетке
@msg only paths and compound paths can be aligned to the pixel grid по пиксельной сетке можно выровнять только контуры и составные контуры
@msg side is left or right, not `{s}` side — это left или right, а не `{s}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -3938,7 +3938,7 @@ axis Both Båda
Dock to Bottom Docka nederst
Dock Control Bar Docka kontrollfältet
Drag to float the toolbar Dra för att göra verktygsfältet flytande
Drag to the window's left edge to dock Dra till fönstrets vänsterkant för att docka
Drag to the window's left or right edge to dock Dra till fönstrets vänster- eller högerkant för att docka
Float Tool Group Gör verktygsgruppen flytande
@msg no library `{id}` inget bibliotek `{id}`
@msg select some type first markera någon text först
@@ -4679,7 +4679,7 @@ axis Both Båda
@msg its objects are nested too deeply dess objekt är för djupt kapslade
@msg its section ending with `%{end}` doesn't end dess avsnitt som slutar med `%{end}` avslutas aldrig
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) penslar som VectorCraft ännu inte kan läsa utelämnades från panelen Penslar ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objekt med flera fyllningar eller konturer, effekter eller penslar importerades som sitt ritade utseende ({_1}): deras utseende går inte att redigera än
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet objekt med flera fyllningar eller konturer, konturer med variabel bredd, effekter eller penslar importerades som sitt ritade utseende ({_1}): deras utseende går inte att redigera än
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding namnet `{name}` är inte UTF-8 och lästes som {label}: om det ser fel ut, öppna filen med en annan legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding namnet `{name}` är inte UTF-8, och delar av det är inte heller giltig {label}: öppna filen med en annan legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) fler namn som inte är UTF-8 lästes som {label} ({count})
@@ -8141,3 +8141,4 @@ digits Hindi Hindi
# Align to Pixel Grid
Align to Pixel Grid Justera mot pixelrutnät
@msg only paths and compound paths can be aligned to the pixel grid bara banor och sammansatta banor kan justeras mot pixelrutnätet
@msg side is left or right, not `{s}` side är left eller right, inte `{s}`
Can't render this file because it contains an unexpected character in line 5 and column 25.
+3 -2
View File
@@ -3824,7 +3824,7 @@ axis Both Her İkisi
Dock to Bottom Alta yerleştir
Dock Control Bar Kontrol çubuğunu yerleştir
Drag to float the toolbar Araç çubuğunu ayırmak için sürükleyin
Drag to the window's left edge to dock Sabitlemek için pencerenin sol kenarına sürükleyin
Drag to the window's left or right edge to dock Sabitlemek için pencerenin sol veya sağ kenarına sürükleyin
Float Tool Group Araç Grubunu Yüzer Yap
@msg no library `{id}` `{id}` kütüphanesi yok
@msg select some type first önce bir tür seçin
@@ -4516,7 +4516,7 @@ axis Both Her İkisi
@msg its objects are nested too deeply nesneleri çok derin bir şekilde iç içe geçmiş
@msg its section ending with `%{end}` doesn't end `%{end}` ile biten bölümü bitmiyor
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) VectorCraft'ın henüz okuyamadığı fırçalar Fırçalar panelinin dışında bırakıldı ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet çeşitli dolgulara veya konturlara, efektlere veya fırçalara sahip nesneler çizilmiş görünümleri olarak geldi ({_1}): görünümleri henüz düzenlenemez
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet çeşitli dolgulara veya konturlara, değişken genişlikli konturlara, efektlere veya fırçalara sahip nesneler çizilmiş görünümleri olarak geldi ({_1}): görünümleri henüz düzenlenemez
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding `{name}` adı UTF-8 değil ve {label} olarak okundu: yanlış görünüyorsa dosyayı başka bir legacyNameEncoding ile açın
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding `{name}` adı UTF-8 değil ve bazı bölümleri {label} olarak da geçerli değil: dosyayı başka bir legacyNameEncoding ile açın
@msg more names that aren't UTF-8 were read as {label} ({count}) UTF-8 olmayan diğer adlar {label} olarak okundu ({count})
@@ -6612,3 +6612,4 @@ breakText Glyphs Glifler
# Align to Pixel Grid
Align to Pixel Grid Piksel Izgarasına Hizala
@msg only paths and compound paths can be aligned to the pixel grid piksel ızgarasına yalnızca yollar ve bileşik yollar hizalanabilir
@msg side is left or right, not `{s}` side left veya right olmalıdır, `{s}` değil
Can't render this file because it contains an unexpected character in line 5 and column 25.
+3 -2
View File
@@ -4053,7 +4053,7 @@ axis Both Обидві
Dock to Bottom Прикріпити внизу
Dock Control Bar Прикріпити панель керування
Drag to float the toolbar Перетягніть, щоб від’єднати панель інструментів
Drag to the window's left edge to dock Перетягніть до лівого краю вікна для прикріплення
Drag to the window's left or right edge to dock Перетягніть до лівого або правого краю вікна для прикріплення
Float Tool Group Від’єднати групу інструментів
@msg no library `{id}` немає бібліотеки `{id}`
@msg select some type first спочатку виділіть текст
@@ -4807,7 +4807,7 @@ axis Both Обидві
@msg its objects are nested too deeply об’єкти в них вкладені надто глибоко
@msg its section ending with `%{end}` doesn't end розділ, що закінчується на `%{end}`, не закінчується
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) пензлі, які VectorCraft поки не вміє читати, не додано до панелі «Пензлі» ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet об’єкти з кількома заливками або обведеннями, ефектами або пензлями імпортовані в намальованому вигляді ({_1}): їх оформлення поки що не можна редагувати
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet об’єкти з кількома заливками або обведеннями, обведеннями змінної ширини, ефектами або пензлями імпортовані в намальованому вигляді ({_1}): їх оформлення поки що не можна редагувати
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding назва `{name}` не в UTF-8 і прочитана як {label}: якщо вона виглядає неправильно, відкрийте файл з іншим legacyNameEncoding
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding назва `{name}` не в UTF-8, і її частини недійсні також у {label}: відкрийте файл з іншим legacyNameEncoding
@msg more names that aren't UTF-8 were read as {label} ({count}) інші назви не в UTF-8 прочитано як {label} ({count})
@@ -6257,3 +6257,4 @@ breakText Glyphs Гліфи
# Align to Pixel Grid
Align to Pixel Grid Вирівняти за піксельною сіткою
@msg only paths and compound paths can be aligned to the pixel grid за піксельною сіткою можна вирівняти лише контури і складені контури
@msg side is left or right, not `{s}` side — це left або right, а не `{s}`
Can't render this file because it contains an unexpected character in line 182 and column 5.
+3 -2
View File
@@ -1045,7 +1045,7 @@ font-technology Variable Biến thiên
Drag to float as a toolbar Kéo để thả nổi thanh công cụ
Drag to float the toolbar Kéo để thả nổi thanh công cụ
Drag to move Kéo để di chuyển
Drag to the window's left edge to dock Kéo đến cạnh trái cửa sổ để neo
Drag to the window's left or right edge to dock Kéo đến cạnh trái hoặc phải cửa sổ để neo
Step up or down Tăng hoặc giảm một bước
Drag values or labels. Double-click a value to type. Kéo giá trị hoặc nhãn. Bấm đúp vào giá trị để nhập.
Draw Vẽ
@@ -4824,7 +4824,7 @@ theme Appearance Giao diện
@msg object {id} is not a path đối tượng {id} không phải là đường dẫn
@msg object {id} is not a selected top-level object đối tượng {id} không phải là đối tượng cấp cao nhất đã chọn
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) các cọ mà VectorCraft chưa đọc được đã bị bỏ khỏi bảng Cọ ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet đối tượng có nhiều tô hoặc nét, hiệu ứng hoặc cọ đã vào dưới dạng bề ngoài ({_1}): diện mạo chưa thể chỉnh sửa
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet đối tượng có nhiều tô hoặc nét, nét có độ rộng thay đổi, hiệu ứng hoặc cọ đã vào dưới dạng bề ngoài ({_1}): diện mạo chưa thể chỉnh sửa
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding tên `{name}` không phải UTF-8 và đã được đọc theo {label}: nếu trông sai, hãy mở tệp với legacyNameEncoding khác
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding tên `{name}` không phải UTF-8, và một số phần cũng không hợp lệ trong {label}: hãy mở tệp với legacyNameEncoding khác
@msg more names that aren't UTF-8 were read as {label} ({count}) thêm các tên không phải UTF-8 đã được đọc theo {label} ({count})
@@ -6258,3 +6258,4 @@ breakText Glyphs Glyph
# Align to Pixel Grid
Align to Pixel Grid Căn theo lưới pixel
@msg only paths and compound paths can be aligned to the pixel grid chỉ đường dẫn và đường dẫn phức mới căn theo lưới pixel được
@msg side is left or right, not `{s}` side là left hoặc right, không phải `{s}`
Can't render this file because it contains an unexpected character in line 204 and column 5.
+3 -2
View File
@@ -4227,7 +4227,7 @@ imageTrace Outline 轮廓
@msg an embedded image of {_1} × {_2} pixels was reduced to {_3} × {_4} pixels to keep the layers 为保留图层,{_1} × {_2} 像素的嵌入图像已缩小为 {_3} × {_4} 像素
@msg gradient “{name}” isn't defined in the file: a plain colour stands in for it 文件中未定义渐变“{name}”:已改用纯色代替
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) VectorCraft 尚无法读取的画笔未加入“画笔”面板 ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet 带有多个填色或描边、效果或画笔的对象已按其绘制外观导入 ({_1}):其外观尚不可编辑
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet 带有多个填色或描边、可变宽度描边、效果或画笔的对象已按其绘制外观导入 ({_1}):其外观尚不可编辑
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding 名称 `{name}` 不是 UTF-8,已按 {label} 读取:如果显示不正确,请使用其他 legacyNameEncoding 打开文件
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding 名称 `{name}` 不是 UTF-8,且部分内容在 {label} 中也无效:请使用其他 legacyNameEncoding 打开文件
@msg more names that aren't UTF-8 were read as {label} ({count}) 其他非 UTF-8 名称已按 {label} 读取 ({count})
@@ -5146,7 +5146,7 @@ axis Both 两者
Dock to Bottom 停放到底部
Dock Control Bar 停放控制栏
Drag to float the toolbar 拖动以浮动工具栏
Drag to the window's left edge to dock 拖到窗口左边缘以停放
Drag to the window's left or right edge to dock 拖到窗口左边缘或右边缘以停放
Float Tool Group 浮动工具组
# The macOS menu bar (native_menu): the app and Window menus' system items, Settings pages.
@@ -6497,3 +6497,4 @@ breakText Glyphs 字形
# Align to Pixel Grid
Align to Pixel Grid 对齐像素网格
@msg only paths and compound paths can be aligned to the pixel grid 只有路径和复合路径可以对齐像素网格
@msg side is left or right, not `{s}` side 应为 left 或 right,而不是 `{s}`
Can't render this file because it contains an unexpected character in line 2 and column 73.
+3 -2
View File
@@ -2068,7 +2068,7 @@ file-filter Templates 範本
@msg an embedded image of {_1} × {_2} pixels was reduced to {_3} × {_4} pixels to keep the layers 為保留圖層,{_1} × {_2} 像素的內嵌影像已縮小為 {_3} × {_4} 像素
@msg gradient “{name}” isn't defined in the file: a plain colour stands in for it 檔案中未定義漸層「{name}」:已改用單色替代
@msg brushes VectorCraft can't read yet were left out of the Brushes panel ({_1}) VectorCraft 尚無法讀取的筆刷未加入「筆刷」面板 ({_1})
@msg objects with several fills or strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet 具有多個填色或筆畫、效果或筆刷的物件已依其繪製外觀匯入 ({_1}):其外觀目前尚無法編輯
@msg objects with several fills or strokes, variable-width strokes, effects or brushes came in as their drawn look ({_1}): their appearance isn't editable yet 具有多個填色或筆畫、可變寬度筆畫、效果或筆刷的物件已依其繪製外觀匯入 ({_1}):其外觀目前尚無法編輯
@msg the name `{name}` isn't UTF-8 and was read as {label}: if it looks wrong, open the file with another legacyNameEncoding 名稱 `{name}` 不是 UTF-8,已依 {label} 讀取:若顯示不正確,請以其他 legacyNameEncoding 開啟檔案
@msg the name `{name}` isn't UTF-8, and parts of it aren't valid {label} either: open the file with another legacyNameEncoding 名稱 `{name}` 不是 UTF-8,且部分內容在 {label} 中也無效:請以其他 legacyNameEncoding 開啟檔案
@msg more names that aren't UTF-8 were read as {label} ({count}) 其他非 UTF-8 名稱已依 {label} 讀取 ({count})
@@ -4969,7 +4969,7 @@ axis Both 兩者
Dock to Bottom 停駐到底部
Dock Control Bar 停駐控制列
Drag to float the toolbar 拖曳以浮動工具列
Drag to the window's left edge to dock 拖曳到視窗左側邊緣以停駐
Drag to the window's left or right edge to dock 拖曳到視窗左側或右側邊緣以停駐
Float Tool Group 浮動工具組
# The macOS menu bar (native_menu): the app and Window menus' system items, Settings pages.
@@ -6295,3 +6295,4 @@ breakText Glyphs 字符
# Align to Pixel Grid
Align to Pixel Grid 對齊像素格點
@msg only paths and compound paths can be aligned to the pixel grid 只有路徑和複合路徑可以對齊像素格點
@msg side is left or right, not `{s}` side 應為 left 或 right,而不是 `{s}`
Can't render this file because it contains an unexpected character in line 6 and column 47.
+1 -1
View File
@@ -615,7 +615,7 @@ pub const UI_COMMANDS: &[(&str, &str, &str, &str)] = &[
"window.panel.dock",
"Dock Panel",
"",
"{panel: id or \"tools\", anchor?: id, zone?: tab|center|left|right|top|bottom|above|below, before?: id, group?: bool, column?: bool or n, side?: Start|End, onto?: id, above?: id, below?: id, icon?: bool, collapsed?: bool} dock a panel (with `group`, its whole group): with no destination a floating panel goes back where it floated from (else joins the first dock group) and a hidden one shows where it was; with `anchor` (or `onto`) in the group holding that panel, `zone` as for window.panel.move; with `side`, as a new innermost column of that dock (`column: true` then moves its whole dock column or floating window); `column: n` with no other destination docks its group, or the whole floating window it floats in, as a new dock column at that place (1: next to the icon column, counting inwards; `true`: innermost); `icon: true` puts it in a row of its own at the end of the icon column; `above` / `below` as a group of its own above or below the group holding that panel. `collapsed` then minimizes or restores its group. \"tools\" docks the Tools panel at the left edge of the window, or beside the canvas inside any left-side dock while window.toolsBesideCanvas is on. Returns {layout, closed, floating}",
"{panel: id or \"tools\", anchor?: id, zone?: tab|center|left|right|top|bottom|above|below, before?: id, group?: bool, column?: bool or n, side?: Start|End, onto?: id, above?: id, below?: id, icon?: bool, collapsed?: bool} dock a panel (with `group`, its whole group): with no destination a floating panel goes back where it floated from (else joins the first dock group) and a hidden one shows where it was; with `anchor` (or `onto`) in the group holding that panel, `zone` as for window.panel.move; with `side`, as a new innermost column of that dock (`column: true` then moves its whole dock column or floating window); `column: n` with no other destination docks its group, or the whole floating window it floats in, as a new dock column at that place (1: next to the icon column, counting inwards; `true`: innermost); `icon: true` puts it in a row of its own at the end of the icon column; `above` / `below` as a group of its own above or below the group holding that panel. `collapsed` then minimizes or restores its group. \"tools\" docks the Tools panel at the left edge of the window, or beside the canvas inside any left-side dock while window.toolsBesideCanvas is on; with side: right (or End) on the right, between the canvas and the panels, as dropping it there or on the window's right edge does (default: the side it docked at last; left or Start docks it on the left), returning {panel, floating, pos, side}. Returns {layout, closed, floating}",
),
(
"window.collapseDock",
+45 -5
View File
@@ -1418,13 +1418,53 @@ impl craft_ui::docking::DockContent<String> for PanelContent<'_> {
crate::panels::panel_menu_items(self.app, ui, id);
}
fn group_header_width(&self, _panels: &[String], _active: &String, _floating: bool, _strip_width: f32) -> f32 {
28.0
fn group_header_width(&self, panels: &[String], _active: &String, floating: bool, _strip_width: f32) -> f32 {
let flyout = self.app.ui.open_panel.as_ref().is_some_and(|panel| panels.contains(panel));
if floating || flyout { 52.0 } else { 28.0 }
}
fn group_header(&mut self, ui: &mut egui::Ui, _panels: &[String], active: &String, _floating: bool) {
let rect = egui::Rect::from_center_size(ui.max_rect().center(), egui::vec2(16.0, 16.0));
crate::panels::panel_menu(self.app, ui, active, rect);
fn group_header(&mut self, ui: &mut egui::Ui, panels: &[String], active: &String, floating: bool) {
let header = ui.max_rect();
let flyout = self.app.ui.open_panel.clone().filter(|panel| panels.contains(panel));
let menu_cell = if floating || flyout.is_some() {
let split = (header.right() - 28.0).max(header.left());
let cell = egui::Rect::from_min_max(header.min, egui::pos2(split, header.bottom()));
if cell.is_positive() {
let rect = egui::Rect::from_center_size(cell.center(), egui::vec2(24.0_f32.min(cell.width()), 24.0_f32.min(cell.height())));
let tokens = crate::theme::Tokens::get(ui.ctx());
let response = ui.interact(rect, ui.scope_id().with(("collapse-to-icons", active)), egui::Sense::click());
response.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Button, ui.is_enabled(), tl!("Collapse to Icons")));
let color = if response.hovered() || response.has_focus() { tokens.text_strong } else { tokens.text_dim };
ui.painter().with_clip_rect(rect.intersect(ui.clip_rect())).text(
rect.center(),
egui::Align2::CENTER_CENTER,
"»",
egui::TextStyle::Button.resolve(ui.style()),
color,
);
if response.on_hover_text(tl!("Collapse to Icons")).clicked() {
let result = if let Some(panel) = flyout {
// The opening icon can differ from the currently active tab.
self.app.run("window.panel", json!({"panel": panel}))
} else {
self.app.run("window.panel.dock", json!({"panel": active, "icon": true, "group": true}))
};
match result {
// Dock commands reveal their destination. This control leaves the group collapsed.
Ok(_) => self.app.ui.open_panel = None,
Err(error) => self.app.ui.status = error,
}
}
}
egui::Rect::from_min_max(egui::pos2(split, header.top()), header.max)
} else {
header
};
if menu_cell.is_positive() {
let rect =
egui::Rect::from_center_size(menu_cell.center(), egui::vec2(16.0_f32.min(menu_cell.width()), 16.0_f32.min(menu_cell.height())));
crate::panels::panel_menu(self.app, ui, active, rect);
}
}
}
@@ -25,6 +25,7 @@ struct Window {
frames: usize,
/// The texts its last frame drew, with their rects in the window.
texts: Vec<(String, Rect)>,
accessibility: Vec<egui::accesskit::Node>,
}
struct Host {
@@ -82,7 +83,14 @@ impl Host {
let mut host = host.borrow_mut();
let builder = &viewport.builder;
let rect = Rect::from_min_size(builder.position.unwrap_or_default(), builder.inner_size.unwrap_or(vec2(300.0, 200.0)));
host.windows.entry(id).or_insert_with(|| Window { rect, events: Vec::new(), dropped: Vec::new(), frames: 0, texts: Vec::new() });
host.windows.entry(id).or_insert_with(|| Window {
rect,
events: Vec::new(),
dropped: Vec::new(),
frames: 0,
texts: Vec::new(),
accessibility: Vec::new(),
});
let viewports = host.infos();
let (time, focused) = (host.time, host.focus == id);
let Some(window) = host.windows.get_mut(&id) else { return };
@@ -171,10 +179,13 @@ impl Host {
}
let mut shown = Vec::new();
full.shapes.iter().for_each(|c| texts(&c.shape, &mut shown));
let accessibility =
full.platform_output.accesskit_update.take().map(|update| update.nodes.into_iter().map(|(_, node)| node).collect()).unwrap_or_default();
let mut host = host.borrow_mut();
host.child_layer_order.insert(id, layer_order);
if let Some(window) = host.windows.get_mut(&id) {
window.texts = shown;
window.accessibility = accessibility;
}
host.apply(&full);
}
@@ -349,6 +360,22 @@ fn open_button_menu(h: &mut Harness, viewport: ViewportId, side: f32, rank: impl
menu
}
/// Click a named button, independent of other controls sharing its size or position.
fn open_named_button_menu(h: &mut Harness, viewport: ViewportId, name: &str) -> Pos2 {
let buttons: Vec<Rect> = h.host.borrow().windows[&viewport]
.accessibility
.iter()
.filter(|node| node.role() == egui::accesskit::Role::Button && node.label() == Some(name))
.filter_map(|node| node.bounds())
.map(|bounds| Rect::from_min_max(pos2(bounds.x0 as f32, bounds.y0 as f32), pos2(bounds.x1 as f32, bounds.y1 as f32)))
.collect();
assert_eq!(buttons.len(), 1, "one visible button named {name}");
let at = buttons[0].center();
h.host.borrow_mut().windows.get_mut(&viewport).unwrap().events.extend([Event::PointerMoved(at), button(at, true), button(at, false)]);
h.frames(8);
at
}
/// A click on the ≡ button in the OS window `viewport`, the 16 pt square at the right end of the
/// panel's tab strip. Returns where the button is.
fn open_panel_menu(h: &mut Harness, viewport: ViewportId) -> Pos2 {
@@ -421,14 +448,18 @@ fn the_library_submenu_scrolls_inside_a_short_native_window() {
assert_submenu_reaches_its_end(&mut h, "swatches", 240.0, "Open Swatch Library", "Default Swatches", "Other Library…");
}
/// The Layers panel's Filter popup, under the topmost 24 pt button: the wheel brings its last row
/// The Layers panel's named Filter button opens its popup: the wheel brings its last row
/// into a short window.
#[test]
fn the_layers_filter_popup_scrolls_inside_a_short_native_window() {
let mut h = Harness::new();
h.ctx.enable_accesskit();
let viewport = h.float("layers");
resize_window(&mut h, viewport, 160.0);
open_button_menu(&mut h, viewport, 24.0, |at| -at.y);
// The header's collapse control is also 24 pt square and sits above Filter.
assert!(text_rect(&h, viewport, "»").is_some(), "keep the competing header control in this regression");
open_named_button_menu(&mut h, viewport, "Filter");
assert!(floating_window(&layout_of(&h.app.ui), "layers").is_some(), "Filter must not collapse the panel");
let over = text_rect(&h, viewport, "By Object").expect("the popup's By Object heading");
assert!(text_rect(&h, viewport, "Hidden").is_none_or(|end| end.bottom() > 160.0), "the popup opens at the top of its list");
wheel_down(&mut h, viewport, over.center(), 5000.0);
@@ -1542,3 +1573,31 @@ fn wheel_without_a_move_uses_the_floating_viewports_pointer() {
h.frame(vec![]);
assert_ne!(h.app.run("text.fonts", json!({})).unwrap(), before);
}
#[test]
fn floating_collapse_arrow_in_a_native_window_moves_the_group_to_the_rail() {
let mut h = Harness::new();
h.app.run("window.panel.float", json!({"panel": "layers", "group": true})).unwrap();
h.app.run("window.panel.activate", json!({"panel": "layers"})).unwrap();
h.frames(3);
let viewport = h.viewport("layers");
h.host.borrow_mut().focus = viewport;
let arrow = h.host.borrow().windows[&viewport]
.texts
.iter()
.find(|(text, _)| text == "»")
.map(|(_, rect)| rect.center())
.expect("the native floating header paints a collapse arrow");
for pressed in [true, false] {
h.host.borrow_mut().windows.get_mut(&viewport).unwrap().events.extend([Event::PointerMoved(arrow), button(arrow, pressed)]);
h.frames(1);
}
h.frames(3);
let layout = layout_of(&h.app.ui);
assert!(floating_window(&layout, "layers").is_none());
assert_eq!(group_of(&layout, &"layers".into()), ["properties", "layers", "libraries"]);
assert!(["properties", "layers", "libraries"].iter().all(|p| in_iconic_column(&layout, p)));
let frames = h.host.borrow().windows[&viewport].frames;
h.frames(3);
assert_eq!(h.host.borrow().windows[&viewport].frames, frames, "the emptied palette is no longer rendered");
}
+154
View File
@@ -284,6 +284,45 @@ fn the_tools_panel_floats_by_its_title_bar_and_docks_on_the_left_edge() {
assert_eq!(h.app.ui.toolbar_pos, None, "dropped on the left edge, it docks");
}
/// The Tools panel also docks on the right, left of the panels: dropped on the window's right edge
/// it stands between the canvas and the panels, its flyouts open toward the canvas, the side is
/// kept with the preferences, and it drags back to the left edge.
#[test]
fn the_tools_panel_docks_on_the_right_left_of_the_panels() {
let mut h = Harness::new();
let zone = docked_tools(&h);
h.drag(zone.min + vec2(20.0, 12.0), pos2(600.0, 400.0));
let [x, y] = h.app.ui.toolbar_pos.expect("the Tools panel floats");
h.drag(pos2(x + 20.0, y + 7.0), pos2(SCREEN.right() - 10.0, 500.0));
assert_eq!((h.app.ui.toolbar_pos, h.app.ui.toolbar_right), (None, true), "dropped on the right edge, it docks on the right");
settle(&mut h);
let bar = docked_tools(&h);
let canvas = h.app.canvas_rect.expect("the canvas");
let layers = h.tab("layers").expect("the Layers panel, docked right");
assert!(canvas.right() <= bar.left() + 1.0 && bar.right() <= layers.left() + 1.0, "canvas {canvas:?}, Tools {bar:?}, Layers {layers:?}");
assert!(SCREEN.right() - bar.right() > 100.0, "the panels keep the window's right edge: {bar:?}");
// Its flyouts and drawer open toward the canvas, not over the panels.
let room = crate::toolbar::room(&h.app, &h.ctx);
assert!(room.right() <= bar.right() + 0.5, "{room:?}");
let left = crate::toolbar::beside(bar, 160.0, 8.0, room);
assert!(left + 160.0 <= bar.left() - 7.0, "left of the toolbar: {left}");
let prefs: crate::state::UiState = serde_json::from_slice(&serde_json::to_vec(&h.app.ui).unwrap()).unwrap();
assert!(prefs.toolbar_right, "kept with the preferences");
// Dragged back to the window's left edge, it docks there again.
h.drag(bar.min + vec2(20.0, 12.0), pos2(600.0, 400.0));
let [x, y] = h.app.ui.toolbar_pos.expect("floats again");
h.drag(pos2(x + 20.0, y + 7.0), pos2(12.0, 500.0));
assert_eq!((h.app.ui.toolbar_pos, h.app.ui.toolbar_right), (None, false));
settle(&mut h);
assert!(docked_tools(&h).left() < 10.0, "at the window's left edge again");
// Agents: window.panel.dock takes the side (left/right, or Start/End as panels do).
let r = h.app.run("window.panel.dock", json!({"panel": "tools", "side": "End"})).unwrap();
assert_eq!((r["side"].as_str(), h.app.ui.toolbar_right), (Some("right"), true));
h.app.run("window.panel.float", json!({"panel": "tools"})).unwrap();
assert_eq!(h.app.run("window.panel.dock", json!({"panel": "tools"})).unwrap()["side"], "right", "back on the side it docked at last");
assert!(h.app.run("window.panel.dock", json!({"panel": "tools", "side": "top"})).is_err());
}
/// The docked Tools panel as last drawn.
fn docked_tools(h: &Harness) -> Rect {
h.ctx.data(|d| d.get_temp::<Rect>(crate::toolbar::docked_rect_id())).expect("the docked Tools panel")
@@ -997,3 +1036,118 @@ fn other_function_keys_and_keyboard_guards_keep_their_routes() {
assert_eq!(saved.session.doc().unwrap().doc.node_count(), 2, "F12 reloads the saved document");
std::fs::remove_dir_all(dir).unwrap();
}
fn collapse_to_icons_button(h: &Harness, panel: &str) -> Option<Rect> {
let panel = panel.to_string();
h.ctx.read_response(area_id().with(("header", &panel)).with(("collapse-to-icons", &panel))).map(|r| r.rect)
}
#[test]
fn floating_collapse_arrow_moves_the_whole_group_into_the_icon_rail() {
let mut h = Harness::new();
assert!(collapse_to_icons_button(&h, "properties").is_none(), "docked headers have no extra button");
h.app.run("window.panel.float", json!({"panel": "layers", "group": true})).unwrap();
h.app.run("window.panel.activate", json!({"panel": "layers"})).unwrap();
for _ in 0..3 {
h.frame(vec![]);
}
let before = h.layout();
let members = group_of(&before, &"layers".into());
let columns = end_columns(&before).len();
let arrow = collapse_to_icons_button(&h, "layers").expect("floating header offers collapse to icons");
assert!(arrow.width() <= 24.0 && arrow.height() <= 24.0, "a compact header control: {arrow:?}");
h.click(arrow.center());
let layout = h.layout();
assert!(floating_window(&layout, "layers").is_none(), "the old floating window is removed");
assert_eq!(group_of(&layout, &"layers".into()), members, "all tabs move together");
assert!(members.iter().all(|p| in_iconic_column(&layout, p)));
assert_eq!(end_columns(&layout).len(), columns, "reuse the existing icon rail");
assert!(h.app.ui.open_panel.is_none(), "the group stays collapsed until its icon is clicked");
let icon = h.hit(&Hit::Icon("layers".into())).expect("Layers now has a rail icon");
h.click(icon.center());
assert_eq!(h.app.ui.open_panel.as_deref(), Some("layers"));
assert!(h.tab("layers").is_some(), "the collapsed group can reopen as a flyout");
assert!(collapse_to_icons_button(&h, "layers").is_some(), "the reopened flyout also offers collapse to icons");
}
#[test]
fn floating_collapse_arrow_leaves_other_floating_groups_alone() {
let mut h = Harness::new();
h.app.run("window.panel.float", json!({"panel": "layers"})).unwrap();
h.app.run("window.panel.float", json!({"panel": "color", "below": "layers"})).unwrap();
for _ in 0..3 {
h.frame(vec![]);
}
let arrow = collapse_to_icons_button(&h, "layers").expect("floating group has its own arrow");
h.click(arrow.center());
let layout = h.layout();
assert!(in_iconic_column(&layout, "layers"));
assert!(floating_window(&layout, "color").is_some(), "another group in the same window stays floating");
assert!(!in_iconic_column(&layout, "color"));
assert_eq!(main_panels(&layout), ["properties", "libraries"]);
}
#[test]
fn floating_collapse_arrow_creates_a_rail_when_none_exists_and_keeps_the_menu() {
let mut h = Harness::new();
h.app.run("window.panel.float", json!({"panel": "layers"})).unwrap();
let mut layout = h.layout();
if let Some(Node::Split { second, .. }) = layout.root.as_mut()
&& let Node::Dock { columns, .. } = second.as_mut()
{
columns.retain(|column| !column.iconic);
}
assert!(icon_column(&end_columns(&layout)).is_none());
h.app.ui.docking = Some(layout);
for _ in 0..3 {
h.frame(vec![]);
}
let panel = "layers".to_string();
let menu =
h.ctx.read_response(area_id().with(("header", &panel)).with(("panel-menu", &panel))).expect("the existing panel menu remains available").rect;
let arrow = collapse_to_icons_button(&h, "layers").expect("floating group offers collapse");
assert!(!menu.intersects(arrow), "menu and arrow have separate hit targets");
h.click(menu.center());
assert!(egui::Popup::is_any_open(&h.ctx), "the existing menu still opens");
h.press(egui::Key::Escape, egui::Modifiers::NONE);
h.click(arrow.center());
let layout = h.layout();
assert!(in_iconic_column(&layout, "layers"));
assert!(floating_window(&layout, "layers").is_none());
assert!(h.app.ui.open_panel.is_none(), "collapse does not immediately open a flyout");
assert_eq!(main_panels(&layout), ["properties", "libraries"]);
}
#[test]
fn rail_flyout_collapse_arrow_folds_the_panel_back_without_moving_it() {
let mut h = Harness::new();
let icon = h.hit(&Hit::Icon("swatches".into())).unwrap();
h.click(icon.center());
let before = h.layout();
let arrow = collapse_to_icons_button(&h, "swatches").expect("opened rail panels offer collapse to icons");
h.click(arrow.center());
assert!(h.app.ui.open_panel.is_none());
assert!(craft_ui::docking::flyout::<String>(&h.ctx, area_id()).is_none());
assert_eq!(h.layout(), before, "folding a flyout leaves the dock layout intact");
let icon = h.hit(&Hit::Icon("swatches".into())).expect("the rail icon stays available");
h.click(icon.center());
assert_eq!(h.app.ui.open_panel.as_deref(), Some("swatches"));
assert!(h.tab("swatches").is_some());
}
#[test]
fn rail_flyout_collapse_arrow_closes_the_opening_icon_after_switching_tabs() {
let mut h = Harness::new();
h.app.ui.docking = Some(h.layout());
let icon = h.hit(&Hit::Icon("swatches".into())).unwrap();
h.click(icon.center());
let brushes = h.tab("brushes").unwrap();
h.click(brushes.center());
assert_eq!(h.app.ui.open_panel.as_deref(), Some("swatches"));
let before = h.layout();
let arrow = collapse_to_icons_button(&h, "brushes").expect("the active tab has a collapse arrow");
h.click(arrow.center());
assert!(h.app.ui.open_panel.is_none());
assert!(craft_ui::docking::flyout::<String>(&h.ctx, area_id()).is_none());
assert_eq!(h.layout(), before, "all tabs and their active state stay in the rail");
}
+6
View File
@@ -473,6 +473,11 @@ pub struct UiState {
/// `window.toolsBesideCanvas`). Off by default, and off in state saved without it.
#[serde(default)]
pub tools_beside_canvas: bool,
/// The docked Tools panel stands on the right, between the canvas and the panels, instead of on
/// the left (dropped there, or `window.panel.dock {panel: "tools", side: "right"}`). Off by
/// default, and off in state saved without it.
#[serde(default)]
pub toolbar_right: bool,
pub status_bar: bool,
pub help_bar: bool,
pub dock: bool,
@@ -1010,6 +1015,7 @@ impl Default for UiState {
dock_width: None,
toolbar_pos: None,
tools_beside_canvas: false,
toolbar_right: false,
status_bar: true,
help_bar: true,
dock: true,
+61 -16
View File
@@ -16,7 +16,7 @@ use crate::theme::{self, Tokens};
use crate::{VectorcraftApp, icons, widgets};
pub(crate) const PITCH: f32 = 30.0;
const WIDTH: f32 = 48.0;
pub(crate) const WIDTH: f32 = 48.0;
/// Seconds a press on a tool group's button is held before its flyout opens.
const LONG_PRESS: f64 = 0.35;
/// Width of the grab bar down a flyout's right side: dragging it tears the flyout off.
@@ -105,21 +105,39 @@ fn panel_width(ui: &crate::state::UiState) -> f32 {
}
/// Space reserved inside the protected canvas panel for a visible Tools panel docked beside the
/// canvas (`UiState::tools_beside_canvas`); none while Tools docks at the window's edge.
/// canvas (`UiState::tools_beside_canvas`) or on the canvas's right (`UiState::toolbar_right`);
/// none while Tools docks at the window's left edge.
pub(crate) fn docked_width(ui: &crate::state::UiState) -> f32 {
if ui.tools_beside_canvas && ui.toolbar && ui.screen_mode < 3 && ui.toolbar_pos.is_none() { panel_width(ui) + 6.0 } else { 0.0 }
if (ui.tools_beside_canvas || ui.toolbar_right) && ui.toolbar && ui.screen_mode < 3 && ui.toolbar_pos.is_none() {
panel_width(ui) + 6.0
} else {
0.0
}
}
/// With `UiState::tools_beside_canvas` on, the canvas-edge redocking target, registered even
/// while Tools floats, and the docked panel, which follows the canvas's left edge as dock columns
/// resize, collapse or hide. With it off, `app.rs` draws Tools before the docking layout.
///
/// The canvas's right edge, left of the panels, is a redocking target too, and Tools docked there
/// (`UiState::toolbar_right`) is drawn at it whichever way the setting is.
pub(crate) fn show_beside_canvas(app: &mut VectorcraftApp, ui: &mut Ui) {
if app.ui.tools_beside_canvas && app.ui.toolbar && app.ui.screen_mode < 3 {
crate::tools_float::set_dock_zones(ui.ctx(), ui.available_rect_before_wrap(), WIDTH);
if app.ui.toolbar_pos.is_none() {
show(app, ui);
}
if !app.ui.toolbar || app.ui.screen_mode >= 3 {
return;
}
let canvas = ui.available_rect_before_wrap();
crate::tools_float::set_right_dock_zone(ui.ctx(), canvas, WIDTH);
if app.ui.tools_beside_canvas {
crate::tools_float::set_dock_zones(ui.ctx(), canvas, WIDTH);
}
if app.ui.toolbar_pos.is_none() && (app.ui.tools_beside_canvas || app.ui.toolbar_right) {
show(app, ui);
}
}
/// Is the Tools panel docked on the right, between the canvas and the panels?
pub(crate) fn docked_right(ui: &crate::state::UiState) -> bool {
ui.toolbar_right && ui.toolbar_pos.is_none()
}
pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
@@ -131,8 +149,13 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
let w = panel_width(&app.ui);
match app.ui.toolbar_pos {
None => {
let margin = egui::Margin { left: 6, right: 0, top: 6, bottom: 6 };
let docked = egui::Panel::left("toolbar").resizable(false).exact_size(w + 6.0).frame(egui::Frame::NONE).show(ui, |ui| {
// The card's outer gap faces the edge it docks at.
let (panel, margin) = if app.ui.toolbar_right {
(egui::Panel::right("toolbar"), egui::Margin { left: 0, right: 6, top: 6, bottom: 6 })
} else {
(egui::Panel::left("toolbar"), egui::Margin { left: 6, right: 0, top: 6, bottom: 6 })
};
let docked = panel.resizable(false).exact_size(w + 6.0).frame(egui::Frame::NONE).show(ui, |ui| {
crate::theme::card(ui, &t, margin, |ui| body(app, ui, cols, None));
});
ui.ctx().data_mut(|d| d.insert_temp(docked_rect_id(), docked.response.rect));
@@ -211,7 +234,7 @@ fn body(app: &mut VectorcraftApp, ui: &mut Ui, cols: usize, floating: Option<egu
{
app.ui.status = e;
}
let grip_tip = if floating.is_some() { tl!("Drag to the window's left edge to dock") } else { tl!("Drag to float the toolbar") };
let grip_tip = if floating.is_some() { tl!("Drag to the window's left or right edge to dock") } else { tl!("Drag to float the toolbar") };
gresp.widget_info(|| egui::WidgetInfo::labeled(egui::Role::Button, ui.is_enabled(), grip_tip));
gresp.on_hover_cursor(egui::CursorIcon::Grab).on_hover_text(grip_tip);
// The tools and the controls under them scroll in a window too short for them.
@@ -850,6 +873,27 @@ fn press_travel(ctx: &egui::Context) -> Option<egui::Vec2> {
ctx.input(|i| i.pointer.press_origin().zip(i.pointer.interact_pos()).map(|(a, b)| b - a))
}
/// Where the toolbar's flyouts and drawer may open: the window, or with the toolbar docked right
/// the part left of it, so they open toward the canvas instead of over the panels.
pub(crate) fn room(app: &VectorcraftApp, ctx: &egui::Context) -> egui::Rect {
let mut r = ctx.content_rect();
if docked_right(&app.ui)
&& let Some(x) = ctx.data(|d| d.get_temp::<egui::Rect>(docked_rect_id())).map(|bar| bar.right())
{
r.max.x = r.max.x.min(x);
}
r
}
/// The left edge of something `width` wide opened beside `anchor` (a tool, the toolbar), `gap`
/// from it: on its right when it fits in `screen`, else on its left (a toolbar docked right),
/// kept inside the screen either way.
pub(crate) fn beside(anchor: egui::Rect, width: f32, gap: f32, screen: egui::Rect) -> f32 {
let right = anchor.right() + gap;
let x = if right + width <= screen.right() { right } else { anchor.left() - gap - width };
x.min(screen.right() - width).max(screen.left())
}
fn flyout(app: &mut VectorcraftApp, ctx: &egui::Context) {
if app.ui.flyout.is_none() {
return;
@@ -865,12 +909,13 @@ fn flyout(app: &mut VectorcraftApp, ctx: &egui::Context) {
let active = app.session.tool_id();
let toolbar = ctx.data(|d| d.get_temp::<Option<String>>(flyout_toolbar_id())).flatten();
let mut input = FlyoutInput::default();
let resp = egui::Area::new(egui::Id::unique("tool-flyout")).order(egui::Order::Foreground).fixed_pos(anchor.right_top() + vec2(8.0, -1.0)).show(
ctx,
|ui| {
// Beside the tool, on the side with room for it: toward the canvas from a toolbar docked right.
let size = ctx.memory(|m| m.area_rect(egui::Id::unique("tool-flyout"))).map_or(vec2(160.0, 0.0), |r| r.size());
let x = beside(anchor, size.x, 8.0, room(app, ctx));
let resp =
egui::Area::new(egui::Id::unique("tool-flyout")).order(egui::Order::Foreground).fixed_pos(pos2(x, anchor.top() - 1.0)).show(ctx, |ui| {
flyout_frame(&t, false).show(ui, |ui| input = flyout_body(ui, &t, active, &tools, false, toolbar.as_deref()));
},
);
});
// Dragging the bar tears the flyout off, and so does releasing a press over it (a click, or the
// end of the long press that opened the flyout): it floats where the pointer is.
let at = ctx.input(|i| i.pointer.interact_pos());
+54 -14
View File
@@ -3,11 +3,12 @@
//! docked panel floats it under the pointer, a floating panel's title bar moves it, and a release
//! over the strip along the window's left edge (lit up) docks it there. With Keep Tools Beside the
//! Canvas on (`UiState::tools_beside_canvas`), a release over the canvas's left edge docks it too,
//! and either release docks it beside the canvas. Escape or losing the window's focus puts it back
//! where the drag began.
//! and either release docks it beside the canvas. A release over the canvas's right edge (left of
//! the panels) or the window's right edge docks it on the right, between the canvas and the panels.
//! Escape or losing the window's focus puts it back where the drag began.
//!
//! `window.panel.float {panel: "tools"}` / `window.panel.dock {panel: "tools"}` do the same for
//! agents. Its position is kept with the preferences and in user workspaces, and a position saved
//! agents (`side: right` docks it on the right). Its position and side are kept with the preferences and in user workspaces, and a position saved
//! on a bigger window is clamped into this one when drawn.
use egui::{CornerRadius, Id, Key, Pos2, Rect, Stroke, Vec2, pos2, vec2};
@@ -29,6 +30,8 @@ struct Move {
grab: Vec2,
from: Pos2,
original: Option<[f32; 2]>,
/// The side it was docked on before the drag.
right: bool,
}
fn move_id() -> Id {
@@ -49,6 +52,18 @@ pub(crate) fn tools_zone_id() -> Id {
Id::unique("tools-float-zone")
}
/// The strip along the canvas's right edge, left of the panels, where the Tools panel docks on the
/// right, as last laid out.
pub(crate) fn tools_zone_right_id() -> Id {
Id::unique("tools-float-zone-right")
}
/// Accept a drop left of the panels docked right: the canvas's right edge.
pub(crate) fn set_right_dock_zone(ctx: &egui::Context, canvas: Rect, width: f32) {
let zone = Rect::from_min_max(pos2(canvas.right() - width, canvas.top()), canvas.max);
ctx.data_mut(|d| d.insert_temp(tools_zone_right_id(), zone));
}
fn window_zone_id() -> Id {
Id::unique("tools-float-window-zone")
}
@@ -93,7 +108,7 @@ pub(crate) fn cancel_move_on_escape(app: &mut VectorcraftApp, ctx: &egui::Contex
fn start(app: &VectorcraftApp, ctx: &egui::Context, grab: Vec2) {
let Some(from) = ctx.input(|i| i.pointer.press_origin().or(i.pointer.interact_pos())) else { return };
ctx.data_mut(|d| d.insert_temp(move_id(), Move { grab, from, original: app.ui.toolbar_pos }));
ctx.data_mut(|d| d.insert_temp(move_id(), Move { grab, from, original: app.ui.toolbar_pos, right: app.ui.toolbar_right }));
}
/// The Tools panel's title bar was pressed (`title`, the panel's bounds `bounds`): docked, dragging
@@ -116,13 +131,23 @@ pub(crate) fn drag_tools(app: &mut VectorcraftApp, ctx: &egui::Context, title: &
}
}
/// The docking strip under `at`, if any: the window's left edge, and with `beside_canvas` the
/// canvas's left edge as well.
fn drop_zone(ctx: &egui::Context, at: Pos2, beside_canvas: bool) -> Option<Rect> {
ctx.data(|d| {
/// The docking strip under `at`, if any, and whether it docks the panel on the right: the window's
/// left edge, with `beside_canvas` the canvas's left edge as well, and on the right the canvas's
/// right edge or anywhere along the window's right edge (which lights up the strip left of the
/// panels, where it docks).
fn drop_zone(ctx: &egui::Context, at: Pos2, beside_canvas: bool) -> Option<(Rect, bool)> {
let (left, right) = ctx.data(|d| {
let canvas = d.get_temp::<Rect>(tools_zone_id()).filter(|_| beside_canvas);
[canvas, d.get_temp::<Rect>(window_zone_id())].into_iter().flatten().find(|r| r.contains(at))
})
let left = [canvas, d.get_temp::<Rect>(window_zone_id())].into_iter().flatten().find(|r| r.contains(at));
(left, d.get_temp::<Rect>(tools_zone_right_id()))
});
if let Some(r) = left {
return Some((r, false));
}
let r = right?;
// Read outside the data lock above: egui's context locks are not re-entrant.
let at_edge = at.x >= ctx.content_rect().right() - crate::toolbar::WIDTH && r.y_range().contains(at.y);
(r.contains(at) || at_edge).then_some((r, true))
}
fn highlight(ctx: &egui::Context, zone: Rect) {
@@ -138,6 +163,7 @@ pub fn track(app: &mut VectorcraftApp, ctx: &egui::Context) {
let Some(m) = ctx.data(|d| d.get_temp::<Move>(move_id())) else { return };
if !gesture_allowed(ctx) {
app.ui.toolbar_pos = m.original;
app.ui.toolbar_right = m.right;
ctx.data_mut(|d| d.remove::<Move>(move_id()));
if ctx.input(|i| i.key_pressed(Key::Escape)) {
let frame = ctx.cumulative_frame_nr_for(ctx.viewport_id());
@@ -155,14 +181,15 @@ pub fn track(app: &mut VectorcraftApp, ctx: &egui::Context) {
{
app.ui.toolbar_pos = Some([p.x, p.y]);
}
if let Some(zone) = zone {
if let Some((zone, _)) = zone {
highlight(ctx, zone);
}
return;
}
ctx.data_mut(|d| d.remove::<Move>(move_id()));
if zone.is_some() {
if let Some((_, right)) = zone {
app.ui.toolbar_pos = None;
app.ui.toolbar_right = right;
}
}
@@ -191,9 +218,22 @@ pub(crate) fn command(app: &mut VectorcraftApp, id: &str, p: &Value) -> Option<R
if !(raw.eq_ignore_ascii_case("tools") || raw.eq_ignore_ascii_case("toolbar")) {
return None;
}
Some(position(p).map(|at| {
// Docking: on the left, or on the right left of the panels (default: the side it docked at last).
let right = match p.get("side").and_then(Value::as_str) {
None => Ok(app.ui.toolbar_right),
// `Start` and `End`, as panels take them, too.
Some(s) if s.eq_ignore_ascii_case("left") || s.eq_ignore_ascii_case("start") => Ok(false),
Some(s) if s.eq_ignore_ascii_case("right") || s.eq_ignore_ascii_case("end") => Ok(true),
Some(s) => Err(format!("side is left or right, not `{s}`")),
};
Some(position(p).and_then(|at| {
let right = right?;
app.ui.toolbar_pos = float.then(|| at.map_or(TOOLS_AT, |p| [p.x, p.y]));
app.ui.toolbar |= float;
json!({ "panel": "tools", "floating": float, "pos": app.ui.toolbar_pos })
if !float {
app.ui.toolbar_right = right;
}
let side = if app.ui.toolbar_right { "right" } else { "left" };
Ok(json!({ "panel": "tools", "floating": float, "pos": app.ui.toolbar_pos, "side": side }))
}))
}
+5
View File
@@ -61,6 +61,9 @@ pub struct Workspace {
/// The docked Tools panel sits beside the canvas, inside any left-side panels (built-in
/// workspaces and workspaces saved without it: at the window's left edge).
pub tools_beside_canvas: bool,
/// The docked Tools panel stands on the right, left of the panels (built-in workspaces and
/// workspaces saved without it: on the left).
pub toolbar_right: bool,
/// Main dock column width in screen points while `docking` is none (#1753).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub dock_width: Option<f32>,
@@ -203,6 +206,7 @@ pub fn capture(ui: &UiState, name: &str) -> Workspace {
docking_collapsed: Default::default(),
toolbar_pos: ui.toolbar_pos,
tools_beside_canvas: ui.tools_beside_canvas,
toolbar_right: ui.toolbar_right,
dock_width: ui.dock_width,
toolbars: ui.custom_toolbars.clone(),
}
@@ -231,6 +235,7 @@ pub fn apply(ui: &mut UiState, w: &Workspace) {
ui.docking_hidden_groups = w.docking_hidden_groups.clone();
ui.toolbar_pos = w.toolbar_pos;
ui.tools_beside_canvas = w.tools_beside_canvas;
ui.toolbar_right = w.toolbar_right;
ui.dock_width = w.dock_width;
crate::state::sanitize_toolbar_pos(&mut ui.toolbar_pos);
ui.custom_toolbars = w.toolbars.clone();
File diff suppressed because one or more lines are too long
+4 -1
View File
@@ -1,6 +1,6 @@
# Gaps
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof, #4936; previously minor: G9: EPS gradients drawn as abutting strips import as one gradient, without seams; Tools stays beside the canvas with left-side panels; new gradients follow document color mode; Selection QoL: marquee modes and visible feedback, #5313; Indian-language hyphenation review and license allowlist, #4947; opening a slow file shows a progress window, part of #5930; Hindi catalog and contributor loanword preferences; agent wording pass complete; wider native proofreading welcome; an image zoomed in on is separated, colorized or proofed only where it shows (#4155); scores and estimates unchanged; a document's first frame renders on the render worker, so opening a heavy file no longer freezes the window; scores and estimates unchanged; large images in CMYK documents are shrunk before they are separated into ink planes (#4155); scores and estimates unchanged; `.ai` paths with a Calligraphic brush open brushed, with the 6D Art Pen's channels, and Windows reads the pen's tilt and barrel rotation, #372; scores and estimates unchanged; Compound Shapes add-on, beyond Illustrator; scores and estimates unchanged; Contour add-on, beyond Illustrator; scores and estimates unchanged; a slow first frame is waited for instead of restarting without the graphics adapter, and the menu bar no longer waits for the installed fonts' scan; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof, #4936; previously minor: floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; G9: EPS gradients drawn as abutting strips import as one gradient, without seams; Tools stays beside the canvas with left-side panels; new gradients follow document color mode; Selection QoL: marquee modes and visible feedback, #5313; Indian-language hyphenation review and license allowlist, #4947; opening a slow file shows a progress window, part of #5930; Hindi catalog and contributor loanword preferences; agent wording pass complete; wider native proofreading welcome; an image zoomed in on is separated, colorized or proofed only where it shows (#4155); scores and estimates unchanged; a document's first frame renders on the render worker, so opening a heavy file no longer freezes the window; scores and estimates unchanged; large images in CMYK documents are shrunk before they are separated into ink planes (#4155); scores and estimates unchanged; `.ai` paths with a Calligraphic brush open brushed, with the 6D Art Pen's channels, and Windows reads the pen's tilt and barrel rotation, #372; scores and estimates unchanged; Compound Shapes add-on, beyond Illustrator; scores and estimates unchanged; Contour add-on, beyond Illustrator; scores and estimates unchanged; a slow first frame is waited for instead of restarting without the graphics adapter, and the menu bar no longer waits for the installed fonts' scan; scores and estimates unchanged) · **Target:** Adobe Illustrator 2026 (30.x)
## Feature submission evidence
@@ -113,6 +113,8 @@ from G14 and the budget run of G15: **~170–270 h** one agent, ~50–80 h wall
## G1 Interaction fidelity
- **Panel collapse controls (#5925, in part):** The small » in a floating tab-group header moves that group into the side icon rail, reusing the existing rail or creating one. An opened rail panel has the same control to fold its flyout back into the rail, including after a tab switch. Both controls preserve the tab group and its menu; other floating groups and full-width docked headers stay as they were. Pointer regressions in `crates/ui-egui/src/panel_docking/tests.rs` cover grouped tabs, reopening, separate groups, a missing rail, the menu and flyout closure after switching tabs; `native_tests.rs` covers the native-window click and removal of the empty palette. Scores and estimates unchanged.
- **Done:** Distance Guides ([public page](https://helpx.adobe.com/illustrator/desktop/measure-and-align/grids-and-guides/use-distance-guides-for-accurate-placement.html)): with Smart Guides on and something selected, the Selection tool with Alt/Option held over another object shows the horizontal and vertical distances between them, and over an empty part of an artboard the selection's distances from its four edges, labelled in the General unit; Preferences › Smart Guides › Invoke Distance Guides turns them off. Hover only, as documented: not while moving or nudging. No entry closed; estimates unchanged.
- **Done (part of #4028):** Recolor Artwork › Assign: double-clicking a row's new color opens the Color Picker, as the [public page](https://helpx.adobe.com/illustrator/desktop/manage-colors/modify-colors/assign-new-colors-to-selected-artwork.html) describes. OK sets the row's new color in the model it was picked in, so a CMYK mix is no longer a hex value converted back; Cancel leaves the row. A Lab new color is kept, but can't be typed yet: the picker's Lab fields work in RGB, as for fill and stroke. **Missing:** Lab entry in the picker, swatch libraries in the picker and the row's right-click menu (Pick from Swatches, Limit to Library).
@@ -833,6 +835,7 @@ trap, and the defaults (0.25 pt, 100%, 40%) are VectorCraft's own, the pages giv
| Date | Change | Summary |
|---|---|---|
| 2026-11-08 | minor | G4 type: the spell check engine is done (`text.spelling.check`, `text.spelling.suggest` on craft-proof, #4936); the Check Spelling dialog, Auto Spell Check and the custom dictionary remain; scores and estimates unchanged. |
| 2026-11-08 | minor | Floating tab groups collapse into the side icon rail and opened rail panels fold back into it (#5925, in part); tab groups remain intact. Scores and estimates unchanged. |
| 2026-11-06 | minor | Hindi (`hi`): 7,877-row machine-assisted catalog, zero/one singular plurals, desktop font fallback and optional Devanagari craft-fonts fallback; all 55 translation tests pass; native review and a web font remain; scores and estimates unchanged |
| 2026-11-05 | minor | G15: nearby hits on plain long strokes and outlines check their candidate segments directly; a 200,000-anchor middle hit took about 11–13 ms before and 1.0–1.4 ms after in paired runs. An inside-bounds miss fell from 18.7 ms to 1.6 ms. Estimate unchanged; idle budget run still open. |
| 2026-11-05 | minor | G15: an image zoomed in on is separated, colorized or proofed only for the tiles in view (#4155); 100% zoom on that file 8.6 s → 0.05 s. Estimate unchanged. |
-1
View File
@@ -463,7 +463,6 @@ opens the picker through `run_command` with the remote backend.
| `open_panel` | `{panel}` | Remote only. `panel` is a panel id (`layers`, `swatches`, `colorGuide`, …, as `window.panel` takes) or its display label (`"Color Guide"`), in any case. |
| `screenshot` | `{path?, scale?, artboard?, window?, max_side?, traceViews?}` | Returns MCP image content (`image/png`, base64) plus a text block. Renders the artboard with the pixels of a PNG `export` with `background: "white"` at the same scale; `traceViews:true` draws Image Trace objects in the view set with `imageTrace.setView`, as in the app's window; `window:true` captures the app window (remote only), as `ui_screenshot` does, and `max_side` (1–4096, window captures only) scales the returned image down as described for `ui_screenshot`. With `path` the backend (the headless session or the app) writes the PNG there too; a window capture written there keeps its full size with `max_side`. |
| `open_file` | `{path, importer?, params?}` | Opens any readable file as a new active document: `.vectorcraft`/`.drawcraft`, `.vctemplate`, `.svg`/`.svgz`, `.pdf`/`.ai`, `.ait`, `.eps`/`.epsf`, `.ps`, `.dxf`, `.emf`, `.wmf`, PNG (`.png`/`.pns`), JPEG, GIF, WebP, TIFF, BMP (`.bmp`/`.dib`/`.rle`), Targa (`.tga`/`.vda`/`.icb`/`.vst`), PCX, Pixar (`.pxr`), JPEG 2000 (`.jpf`/`.jpx`/`.jp2`/`.j2k`/`.j2c`/`.jpc`), HEIC/HEIF (`.heic`/`.heif`), JPEG XL (`.jxl`), PSD (`.psd`/`.pdd`)/PSB (an image opens as a document of its pixel size; a Photoshop file as its merged image; a JPEG XL file as its first frame). Templates (`.vctemplate`, `.ait`) open as a new untitled document. PDF, `.ai` and SVG files saved with Preserve Editing reopen as the document they carry. The reply is `document.open`'s: its `warnings` say what didn't come in as it was (an EPS whose PostScript can't be read opens as its preview, and the warning names the PostScript error, the operator and the procedure). A file of an extension only an installed importer plug-in lists opens with that importer ([Plug-ins](#plug-ins)); `importer: "plugin.<id>"` opens the file with the importer it names, which must list the file's extension, also one a built-in format reads (in the app, as File › Open With does), and `params` gives that importer's parameters (their defaults when omitted). `run_command document.formats` lists the formats. |
| `save_file` | `{path?, compress?, version?, preview?, precision?, options?}` | Runs `document.save`: the document's own file in its own format (native `.vectorcraft` unless it was opened from or saved as SVG, PDF or a restorable `.ai`; then `warnings` say what that format loses). A path's extension picks the format (`.vectorcraft`, `.vctemplate`, `.pdf`, `.svg`, `.svgz`, `.ai`: a PDF carrying the native document, which reopens editable). For a native file, `compress: true` gzips it (default: the `useCompression` preference, Preferences › File Handling › Use Compression), `version` (1 to 4) writes it for an older VectorCraft (1 and 2 are never compressed, and `version: 2` with `compress: true` is an error), `preview: true` embeds a PNG of the first artboard, and `precision` (0 to 9) rounds path anchors and handles to that many decimal places for that save only (colors, transforms and type stay exact). `options` passes the format's save options as `run_command document.formats` lists them. When `compress`, `version`, `preview` or `precision` is given both at the top level and in `options` with different values, the call is an error (`` `compress: true` and `options.compress: false` disagree: give the option once ``) and nothing is written. The save drops each option the written format doesn't read. The reply's `warnings` then name each such option given (for example `` `compress` was not used: SVG files don't read it `` for an `.svg` path, and `` `options.preview` was not used: … `` for a key in `options`; a `.ai` file reads `compress` but not `version`, `preview` or `precision`). `run_command document.save` and the other save commands refuse any option given beside the path and in `options` with different values, and give the same warning for a top-level `compress`, `version`, `preview` or `precision`. The headless session and the app receive the same `app.save` params, and the app is also sent `wait: true`: the call returns once the file is written, or with the error (such as a path outside the write root), even with Background Save on. When the save asks first (the question before replacing a file that opened with losses, or on macOS and Linux the file dialog for an untitled document without `path`), the reply is `{pending}` or `"cancelled"` as before, and the save that runs after the answer uses the Background Save preference. |
| `save_file` | `{path?, compress?, version?, preview?, precision?, options?}` | Runs `document.save`: the document's own file in its own format (native `.vectorcraft` unless it was opened from or saved as SVG, PDF or a restorable `.ai`; then `warnings` say what that format loses). A path's extension picks the format (`.vectorcraft`, `.vctemplate`, `.pdf`, `.svg`, `.svgz`, `.ai`: a PDF carrying the native document, which reopens editable). For a native file, `compress: true` gzips it (default: the `useCompression` preference, Preferences › File Handling › Use Compression), `version` (1 to 4) writes it for an older VectorCraft (1 and 2 are never compressed, and `version: 2` with `compress: true` is an error), `preview: true` embeds a PNG of the first artboard, and `precision` (0 to 9) rounds path anchors and handles to that many decimal places for that save only (colors, transforms and type stay exact). `options` passes the format's save options as `run_command document.formats` lists them. When `compress`, `version`, `preview` or `precision` is given both at the top level and in `options` with different values, the call is an error (`` `compress: true` and `options.compress: false` disagree: give the option once ``) and nothing is written. A key in `options` that no save format takes is an error, and nothing is written (see Parameter keys). The save drops each option the written format doesn't read. The reply's `warnings` then name each such option given (for example `` `compress` was not used: SVG files don't read it `` for an `.svg` path, and `` `options.preview` was not used: … `` for a key in `options`; a `.ai` file reads `compress` but not `version`, `preview` or `precision`). For a `.ai` file, these warnings name the kind of `.ai` written: a legacy .ai (version 7) for `aiVersion: "7"`, a non-PDF-compatible .ai for `pdfCompatible: false`, and a PDF-compatible .ai otherwise, including a save that reuses the options the document was last saved with. `run_command document.save` and the other save commands refuse any option given beside the path and in `options` with different values, and give the same warnings for a top-level `compress`, `version`, `preview` or `precision` and for each key in `options` that the format doesn't read. The headless session and the app receive the same `app.save` params, and the app is also sent `wait: true`: the call returns once the file is written, or with the error (such as a path outside the write root), even with Background Save on. When the save asks first (the question before replacing a file that opened with losses, or on macOS and Linux the file dialog for an untitled document without `path`), the reply is `{pending}` or `"cancelled"` as before, and the save that runs after the answer uses the Background Save preference. |
| `export` | `{path?, format?, scale?, artboard?, range?, selection?, outlineText?, options?}` | `svg`, `svgz`, `pdf`, `ai` (the PDF-compatible `.ai` that Save writes, which carries the document when every artboard is written in order), `eps`, `dxf`, `emf`, `wmf`, `png`, `jpg`, `webp`, `gif`, `png8` (an indexed `.png`), `tiff`, `bmp`, `tga`, `psd` (layered), `txt` (the document's text), `vectorcraft` or `template` (a native template). The tool's `format` enum and `document.formats` list them, generated from the engine's format table. When `format` is omitted, it comes from the path's extension. PDF and `.ai` write one page per artboard: all of them, or `artboard` (0-based) / `range` (`"1-3, 5"`, 1-based). SVG and SVGZ write one file per chosen artboard. EPS writes one file of the visible art; with `artboard`, `range`, `options.artboards` or `options: {"useArtboards": true}` it writes one file per chosen artboard, after a central file of the visible art when there are two or more, and `useArtboards: false` keeps the one file. Rasters, DXF, EMF and WMF write one artboard, or one file per chosen artboard with `options: {"useArtboards": true}`. Rasters also take `options: {"rect": [x, y, width, height]}` (page points, y down): that area of the page at the given `ppi`/`scale`, without an artboard and without changing the document; it is refused with `artboard`, `artboards`, `range` or `useArtboards`, with a non-positive or non-finite size, and for non-raster formats. `options` carries more format options (e.g. `{"quality": 80}` for JPEG). `selection: true` exports the selected objects cropped to their bounds (the reply adds their `bounds`, and reports `format` and the encoder's `warnings` as a whole-document export does); `outlineText: true` writes SVG text as paths. Template layers are left out, live effects are kept, and exporting `vectorcraft` never changes the document's path. Without `path` the bytes come back as `dataBase64`. Both backends run the same `document.export` call. |
| `add_text` | `{text, x?, y?, width?, height?, fit?, fitMinPercent?, path?, mode?, pathEffect?, size?, font?, color?, justify?, vertical?}` | Point type at (x, y) (`justify`: `left`, `center` or `right` starts, centres or ends each line on x); area type with `width`/`height` (`fit`: `none`, `autoHeight`, `shrinkText`, as `text.areaOptions`); or `path` + `mode` (`area`/`onPath`) to flow text in or along a path, with `pathEffect` (`rainbow`, `skew`, `3dRibbon`, `stairStep`, `gravity`; SVG export writes type on a path with any effect other than `rainbow` as outlines, with a warning); `justify` (also `auto`, `justifyAll`) aligns the paragraphs of every kind, as `text.setStyle` does later. `vertical: true` uses the existing vertical writing mode for every kind (default `false`, horizontal). Returns `{id}`, with `overflow: true` when the text doesn't fit its frame or path. |
+5 -1
View File
@@ -527,7 +527,11 @@ In this example the character and paragraph styles are cut short; a filter gets
`pixelsOmitted: false`. A CMYK image's pixels are its inks through the working CMYK profile. The pixels of all the
images in one input take at most 16 MiB in base64. A larger image, one whose pixels don't fit what the images before
it in paint order left, and a linked image (`linked: true`) have
`pixelsOmitted: true` and no pixels; the host checks an image's size before it decodes the image. The host never
`pixelsOmitted: true` and no pixels; the host checks an image's size before it decodes the image. An image of any
size also has `pixelsOmitted: true` when its data is missing from the document, when its width or height is 0, when
its data cannot be decoded (or needs more than 64 MiB of decoder memory, or is a TIFF the host rejects as too
large), or when the dimensions in its data differ from `pixelSize`. A plug-in checks `pixelsOmitted` before it
reads `pixelsBase64`, which is present only when `pixelsOmitted` is `false`. The host never
reads a linked image's file for a filter and never gives its path.
- A symbol instance (`"symbolInstance"`): `symbolId` is the name of the symbol it shows, with `missing: true` when the
document has no symbol of that name. `overrides` is `{}`.
+3 -2
View File
@@ -1,6 +1,6 @@
# Roadmap: milestones and what's next
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof, #4936; previously minor: M4/M7: `.ai`/EPS type keeps its tab stops, and right-aligned lines with tabs lay out as the file's page, #6147; M4/M7: `.ai`/EPS type from the text document lays its lines out closer to the file's page, within limits, #6137; numeric fields step on wheel hover without taking keyboard focus, #5949) · **Target:** Adobe Illustrator 2026 (30.x)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof, #4936; previously minor: floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; M4/M7: `.ai`/EPS type keeps its tab stops, and right-aligned lines with tabs lay out as the file's page, #6147; M4/M7: `.ai`/EPS type from the text document lays its lines out closer to the file's page, within limits, #6137; numeric fields step on wheel hover without taking keyboard focus, #5949) · **Target:** Adobe Illustrator 2026 (30.x)
## Feature submission evidence
@@ -153,7 +153,7 @@ native wording review, layout checks and a web Devanagari font remain. [Notes](l
| M12 | Advanced color & art (CMYK/ICC, separations, Gradient Mesh, Live Paint, Image Trace, Graphs) | 🟡 Gradient Mesh, Live Paint, Image Trace (junction-returning pieces retain their positioned boundaries; a simpler contour walk with identical output; an edge's sub-pixel shift is the same from either side; notches read only their own piece; collapsed loops keep few segments; large images trace with a warning; Auto Color keeps rare distinct colours; quick presets trace at once and the Image Trace button opens the adjustment controls; Properties reopens them; tiny fitted contours retain islands and holes; Create Strokes: exact crop EDT, Guo–Hall thinning and maximal-disc graph simplification; 15 presets, the 14 documented ones each with an enhanced version; Ignore Color, Shapes, Auto Grouping and Gradients options; measured in `image-trace-quality.md`; closed outlines retain their interior after straightening; the Flat Logo mode places edges to a fraction of a pixel, restores sharp corners, snaps lines and circles, and refuses gradients; live trace result bounds and selection highlights exclude the retained bitmap except in source-image views), Recolor Artwork, colour management (ICC, soft proofing, separations preview), Graphs (all 9 tools, Graph Data/Type, group-selected series colours persist through regenerate/save/open) done; Graph Design and Marker designs, Transpose and Switch x/y, `\|` label line breaks, the pie options, drop shadows, the legend across the top, overlapping widths, First Row/Column in Front, filled lines, kept label formatting and the Graph Data worksheet done; graph Column designs pending | 6–10 |
| M13 | Automation (Actions ✅ record/playback, persisted; variables, scripting, batch) | 🟡 MCP `add_text` exposes existing vertical point, area and path type; broader scripting and batch work remains | 18–27 |
| M13 | Automation (Actions ✅ record/playback, persisted; variables, scripting, batch) | 🟡 | 18–27 |
| M14 | 1.0 polish (preferences, shortcut editor, workspaces, accessibility, packaging for all OSes) (status messages and readouts use stable responsive allocations sized for localized labels and ordinary coordinates; sample widths cached by language/unit/DPI; regression-tested and contributor-confirmed 2026-10-19; estimates unchanged) | 🟡 Dock groups collapse to their tabs by a double-click on a tab (UI.8); Preferences › General › Constrain Angle acts for the arrow keys, Shift drags and new objects (#394, 2026-10-21); numeric-field pointer selection and focus regression-tested (#1326, in part; short movements still select all); RPM runtime capabilities and glibc floor checked (#1275; real-distro install/start-up pending), Eyedropper cursor with precise-setting checks (#1269), Preferences, shortcut editor (chords with Shift and a punctuation key fire as typed; other apps' text shortcut sets imported), workspaces (each keeps its layout changes until Reset <name>, UI.7), Window › Help Bar shown or hidden apart from the status bar and kept in saved workspaces, panels docked by dragging into any dock group (as tabs or a top/bottom split) or into the icon column, and closed from a dock tab's context menu (UI.6), a custom title bar on Windows/Linux with a Home button, content-sized dialogs, menus that scroll when longer than the window and macOS native menu dismissal done (M14.8), and native macOS save sheets with a Format menu (#866); titled PDF exports set `DisplayDocTitle` for viewers and accessibility checkers (#3646); accessibility and broader Windows/Linux packaging/QA pending | 18–28 |
| M14 | 1.0 polish (preferences, shortcut editor, workspaces, accessibility, packaging for all OSes) (status messages and readouts use stable responsive allocations sized for localized labels and ordinary coordinates; sample widths cached by language/unit/DPI; regression-tested and contributor-confirmed 2026-10-19; estimates unchanged) | 🟡 Dock groups collapse to their tabs by a double-click on a tab (UI.8); Preferences › General › Constrain Angle acts for the arrow keys, Shift drags and new objects (#394, 2026-10-21); numeric-field pointer selection and focus regression-tested (#1326, in part; short movements still select all); RPM runtime capabilities and glibc floor checked (#1275; real-distro install/start-up pending), Eyedropper cursor with precise-setting checks (#1269), Preferences, shortcut editor (chords with Shift and a punctuation key fire as typed; other apps' text shortcut sets imported), workspaces (each keeps its layout changes until Reset <name>, UI.7), Window › Help Bar shown or hidden apart from the status bar and kept in saved workspaces, panels docked by dragging into any dock group (as tabs or a top/bottom split) or into the icon column, and closed from a dock tab's context menu (UI.6), a custom title bar on Windows/Linux with a Home button, content-sized dialogs, menus that scroll when longer than the window and macOS native menu dismissal done (M14.8), and native macOS save sheets with a Format menu (#866); titled PDF exports set `DisplayDocTitle` for viewers and accessibility checkers (#3646); accessibility and broader Windows/Linux packaging/QA pending; floating tab groups collapse into the side icon rail and opened rail panels fold back into it (#5925, in part), with pointer and native-window regression coverage; estimates unchanged | 18–28 |
| — | Interaction fidelity pass (every tool's modifiers, Properties panel per context, isolation, nuance; width-point creation and adjoining adjustment regression-tested; dialog layout reviewed) | ⬜ (new ruler-guide Alt orientation implemented; existing-guide Escape cancellation regression-tested; broader pass remains) | 60–90 |
| — | Hardening at scale (big-file corpus, fuzzing, cross-platform + browser QA) | 🟡 PngSuite integrity and bounded fetching verified (#1729); real-world corpus/manual QA remain | 50–80 |
| M14 follow-up | Color Picker layout (#2008) | ✅ Compact controls, centered header, stacked actions, aligned fields and screen sampling; narrow windows scroll vertically | Included above |
@@ -191,6 +191,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
| Date | Change | Summary |
|---|---|---|
| 2026-11-08 | minor | M7: spell check engine, `text.spelling.check` and `text.spelling.suggest` on craft-proof (#4936); the dialog, Auto Spell Check and the custom dictionary remain; scores and estimates unchanged. |
| 2026-11-08 | minor | Floating tab groups collapse into the side icon rail and opened rail panels fold back into it (#5925, in part); tab groups remain intact. Scores and estimates unchanged. |
| 2026-11-09 | minor | Numeric fields step on wheel hover without taking focus; keyboard focus and existing rounding rules remain (#5949). Scores and estimates unchanged. |
| 2026-11-07 | minor | M2: with the Pen or Curvature tool, Edit › Copy and Cut take the anchors and segments a Cmd/Ctrl-lent Direct Selection picked, not the whole path (#6123); estimates unchanged |
| 2026-11-06 | minor | M2: a shape just drawn moves by its centre point (Show Center) and turns from the rotate zone just outside a bounding-box corner with its tool still active, as the Selection tool's gestures do (#5477); estimates unchanged. |
+5 -2
View File
@@ -1,6 +1,6 @@
# Parity with Adobe Illustrator
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof, #4936; previously minor: `.ai`/EPS type keeps its tab stops, and right-aligned lines with tabs lay out as the file's page, #6147; `.ai`/EPS type from the text document lays its lines out closer to the file's page, within limits, #6137; `.ai` and EPS import: older Art brush objects read; numeric fields step on wheel hover without taking keyboard focus, and a panel wheel turn cannot seed a canvas zoom glide, #5949) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (spell check engine: `text.spelling.check` and `text.spelling.suggest` on craft-proof, #4936; previously minor: floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; `.ai`/EPS type keeps its tab stops, and right-aligned lines with tabs lay out as the file's page, #6147; `.ai`/EPS type from the text document lays its lines out closer to the file's page, within limits, #6137; `.ai` and EPS import: older Art brush objects read; numeric fields step on wheel hover without taking keyboard focus, and a panel wheel turn cannot seed a canvas zoom glide, #5949) · **Target:** Adobe Illustrator 2026 (30.x; public release notes through 30.7, August 2026)
## Feature submission evidence
@@ -233,7 +233,7 @@ dominate; 3D, graphs and automation are specialist). They sum to 105. Feature de
| Export for Screens, Asset Export, slices, Save for Web | 3 | 85% | anti-aliasing ghost borders (#983); TIFF choices and settings are implemented | 2–4 | estimated |
| Print, colour management, separations, flattener | 3 | 80% | print preview fidelity | 2–4 | estimated |
| Automation (Actions, Variables, scripting, batch) | 3 | 60% | a scripting surface (Illustrator: JavaScript/ExtendScript), Batch, image and graph variables, dataset import/export | 8–14 | estimated; MCP `add_text` exposes existing vertical type; score and hours unchanged |
| UI chrome (panels, Properties, workspaces, preferences) | 6 | 80% | Properties per context, 3 Preferences options stored but unused (#394, audited 2026-10-30: Open Documents As Tabs, Scratch Disks, legacy vector clipboard), SVG Interactivity, context menus beyond the canvas, accessibility | 13–20 | estimated; floating panels in OS windows of their own on the desktop (#4135), score and hours unchanged; macOS save Format menu implemented (#866), score and hours unchanged; Eyedropper cursor and precise setting checked (#1269), OS visual QA unverified; numeric-field focus and normal text drags regression-tested (#1326, in part); movements below the click threshold still select all; artboard navigation synchronizes the tool selection before destructive actions; status messages and readouts use stable responsive allocations sized for localized labels and ordinary coordinates (sample widths cached by language/unit/DPI; regression and contributor original-file evidence below; scores/hours unchanged); effect dialogs' colour fields show a swatch that opens the Color Picker (#5365), score and hours unchanged |
| UI chrome (panels, Properties, workspaces, preferences) | 6 | 80% | Properties per context, 3 Preferences options stored but unused (#394, audited 2026-10-30: Open Documents As Tabs, Scratch Disks, legacy vector clipboard), SVG Interactivity, context menus beyond the canvas, accessibility | 13–20 | estimated; floating panels in OS windows of their own on the desktop (#4135), score and hours unchanged; macOS save Format menu implemented (#866), score and hours unchanged; Eyedropper cursor and precise setting checked (#1269), OS visual QA unverified; numeric-field focus and normal text drags regression-tested (#1326, in part); movements below the click threshold still select all; artboard navigation synchronizes the tool selection before destructive actions; status messages and readouts use stable responsive allocations sized for localized labels and ordinary coordinates (sample widths cached by language/unit/DPI; regression and contributor original-file evidence below; scores/hours unchanged); effect dialogs' colour fields show a swatch that opens the Color Picker (#5365), score and hours unchanged; floating tab groups collapse into the side icon rail and opened rail panels fold back into it (#5925, in part), scores and hours unchanged |
| Libraries, Links, Package | 2 | 82% | shared libraries, linked graphics, gradients and patterns as library items (no cloud by design) | 2–3 | estimated |
| Generative AI (Text to Vector, Generative Recolor, Shape Fill, Concept to Vector, Prompt to Edit, Rewrite Text) | 2 | 0% | a pluggable provider and the panels; needs an owner decision on models | 30–60 | measured (absent) |
| **Feature depth** | **105** | **~75%** | | **~250–420** | |
@@ -539,6 +539,8 @@ since 2026-10-01.
## Evidence: what ships today, by area
- **Panel collapse controls (#5925, in part):** The small » in a floating tab-group header moves that group into the side icon rail, reusing the existing rail or creating one. An opened rail panel has the same control to fold its flyout back into the rail, including after a tab switch. Both controls preserve the tab group and its menu; other floating groups and full-width docked headers stay as they were. Pointer regressions in `crates/ui-egui/src/panel_docking/tests.rs` cover grouped tabs, reopening, separate groups, a missing rail, the menu and flyout closure after switching tabs; `native_tests.rs` covers the native-window click and removal of the empty palette. Scores and estimates unchanged.
- 3D lighting presets: `crates/effects/src/lighting_presets.rs`, `crates/engine/src/cmd/effectcmd/lighting_presets_tests.rs` and the pointer tests in `crates/ui-egui/src/dialogs/revolve/tests.rs` cover recognition, atomic targeting, native round trips, preview, Cancel, reset and Undo.
- Window › 3D and Materials panel: `crates/ui-egui/src/panels/three_d.rs` applies a 3D effect by object type (Plane, Extrude, Revolve, Inflate), changes an applied one in place (`effect.setParams` `threeDType`) and edits it with the options dialog's Object, Rotation and Lighting controls, one undo step per change; `crates/engine/src/cmd/effectcmd/three_d_type_tests.rs` and `crates/ui-egui/src/panels/tests_three_d.rs` cover it. No Materials tab.
@@ -1891,6 +1893,7 @@ Edit › Copy and Cut put the selected ruler guides on the clipboard, with the s
| Date | Change | Summary |
|---|---|---|
| 2026-11-08 | minor | Type advanced: spell check engine, `text.spelling.check` lists unknown words per run language at `text.editRange` byte offsets and `text.spelling.suggest` gives suggestions, from craft-proof (#4936); evidence added; the dialog, Auto Spell Check and the custom dictionary remain; scores and hours unchanged. |
| 2026-11-08 | minor | Floating tab groups collapse into the side icon rail and opened rail panels fold back into it (#5925, in part); tab groups remain intact. Scores and estimates unchanged. |
| 2026-11-07 | minor | File formats and Type core: `.ai`/EPS type keeps its paragraphs' tab stops, and right-aligned area type sets the text after a line's last tab against the right edge, the text before it at its stops (#6147); evidence added; scores unchanged. |
| 2026-11-07 | minor | File formats and Type core: `.ai`/EPS type from the text document lays its lines out closer to the file's page: auto leading per line at the paragraph's share, the paragraph's hyphenation options, possibly more of the last lines the file sets in an area frame of its own, and a line a soft return ends justified (#6137); evidence added; scores unchanged. |
| 2026-11-06 | minor | `.ai` and EPS import: Art brushes in the older form of their objects come into the Brushes panel; evidence updated; scores unchanged. |
+3 -3
View File
@@ -68,8 +68,8 @@ against Illustrator's public documentation; **estimated** unless noted.
| CJK: Standard Vertical Roman Alignment | done | Character panel menu › Standard Vertical Roman Alignment (`text.setFormat` / `text.setRangeStyle {standardVerticalRoman}`), with Show East Asian Options, off by default: in vertical type the marked half-width characters (Latin letters, digits and other characters that would lie on their side) stand upright one below the other, each in its upright cell; spaces keep their width and tate-chu-yoko blocks stay across the column; horizontal type keeps the mark and does not change. Character Style Options has its own checkbox for it. Evidence: `tests_vertical::standard_vertical_roman_alignment_stands_half_width_characters_upright`, `typecmd::standard_vertical_roman_is_a_character_attribute_saved_only_when_on` |
| CJK: composite fonts | partial | Type › Composite Fonts… (`type.compositeFonts.set`), with Show East Asian Options: a font made of a font per class of character (Kanji, Kana, Punctuation, Symbols, Roman, Numerals), each with its own style, size and baseline in percent of the type's size, Kana, Roman and Numerals with horizontal and vertical scale, and Kana with Scale at Center; New, Delete Font, Import… and Export… (`.vccomposite`, `type.compositeFonts.import` and `.export`) ([Creating composite fonts](https://help.adobe.com/archive/en_US/illustrator/cs3/illustrator_cs3_help.pdf#page=347)). Composite fonts are listed first in the font menus, saved with the document, and their type is laid out per class; SVG and legacy `.ai` write it as outlines. Not yet: custom character sets, the Units menu, Show Sample. Evidence: `text::composite::tests::characters_fall_in_their_classes`, `tests::composite::composite_type_draws_each_class_in_its_font`, `compositefonts::tests`, `dialogs::composite_fonts::tests` |
| CJK: ruby | partial | ruby (ふりがな) for live text (`type.ruby`): a reading over a byte range, above (right of the column in vertical type) or below, start/centre/end alignment, size, overhang or squeeze; saved natively, laid out with its line growing, drawn on the canvas, in SVG and PDF (as outlines where ruby is present), in EPS, EMF/WMF and legacy `.ai` (legacy `.ai` writes type with ruby as outlines), and kept by Create Outlines, Expand Appearance and flattening, each glyph where the layout puts it (`typecmd::ruby_tests::create_outlines_keeps_the_ruby_where_it_is_laid_out`) | #633 |
| Language per character (Character panel › Language, character styles), text shaped in it | done | `text.setFormat` / `text.setRangeStyle {language}` by name or BCP 47 tag, one of Document Setup's languages (Hindi, Marathi, Gujarati, Bengali, Punjabi, Tamil, Telugu, Kannada, Malayalam, Odia and Urdu added, #4943; Assamese and Sanskrit added, #4947; Serbian: Cyrillic, Serbian: Latin and Croatian); the shaper gets the tag, so the font's language forms apply (OpenType `locl`: Turkish keeps `fi` apart and the dot on its i, #4940); new type takes the document's language; saved as the tag only when set, carried by character styles (#4946). English and eleven Indian languages hyphenate by run language; Sanskrit automatic hyphenation awaits allowlisted patterns (#4947). Not yet: spelling by it, `.ai`/PDF import of a text's language, paragraph styles. Evidence: `features::tests::language_picks_the_fonts_forms`, `features::tests::serbian_gets_the_fonts_serbian_forms`, `typecmd::area_tests::language_is_a_character_attribute_saved_only_when_set`, `tests::typo::indic_language_runs_hyphenate_with_both_composers`, `hyphen::tests::sanskrit_without_allowlisted_patterns_uses_only_exceptions`; spelling is checked in each run's Language (`text.spelling.check`, #4936) |
| Language per character (Character panel › Language, character styles), text shaped in it | done | `text.setFormat` / `text.setRangeStyle {language}` by name or BCP 47 tag, one of Document Setup's languages (Hindi, Marathi, Gujarati, Bengali, Punjabi, Tamil, Telugu, Kannada, Malayalam, Odia and Urdu added, #4943; Serbian: Cyrillic, Serbian: Latin and Croatian; Bulgarian, #5945); the shaper gets the tag, so the font's language forms apply (OpenType `locl`: Turkish keeps `fi` apart and the dot on its i, #4940); new type takes the document's language; saved as the tag only when set, carried by character styles (#4946). Not yet: hyphenation and spelling by it, `.ai`/PDF import of a text's language, paragraph styles. Evidence: `features::tests::language_picks_the_fonts_forms`, `features::tests::serbian_gets_the_fonts_serbian_forms`, `typecmd::area_tests::language_is_a_character_attribute_saved_only_when_set`; spelling is checked in each run's Language (`text.spelling.check`, #4936) |
| Language per character (Character panel › Language, character styles), text shaped in it | done | `text.setFormat` / `text.setRangeStyle {language}` by name or BCP 47 tag, one of Document Setup's languages (Hindi, Marathi, Gujarati, Bengali, Punjabi, Tamil, Telugu, Kannada, Malayalam, Odia and Urdu added, #4943; Assamese and Sanskrit added, #4947; Serbian: Cyrillic, Serbian: Latin and Croatian); the shaper gets the tag, so the font's language forms apply (OpenType `locl`: Turkish keeps `fi` apart and the dot on its i, #4940); new type takes the document's language; saved as the tag only when set, carried by character styles (#4946). English and eleven Indian languages hyphenate by run language; Sanskrit automatic hyphenation awaits allowlisted patterns (#4947). Not yet: `.ai`/PDF import of a text's language, paragraph styles. Evidence: `features::tests::language_picks_the_fonts_forms`, `features::tests::serbian_gets_the_fonts_serbian_forms`, `typecmd::area_tests::language_is_a_character_attribute_saved_only_when_set`, `tests::typo::indic_language_runs_hyphenate_with_both_composers`, `hyphen::tests::sanskrit_without_allowlisted_patterns_uses_only_exceptions`; spelling is checked in each run's Language (`text.spelling.check`, #4936) |
| Language per character (Character panel › Language, character styles), text shaped in it | done | `text.setFormat` / `text.setRangeStyle {language}` by name or BCP 47 tag, one of Document Setup's languages (Hindi, Marathi, Gujarati, Bengali, Punjabi, Tamil, Telugu, Kannada, Malayalam, Odia and Urdu added, #4943; Serbian: Cyrillic, Serbian: Latin and Croatian; Bulgarian, #5945); the shaper gets the tag, so the font's language forms apply (OpenType `locl`: Turkish keeps `fi` apart and the dot on its i, #4940); new type takes the document's language; saved as the tag only when set, carried by character styles (#4946). Not yet: hyphenation by it in languages other than English and the eleven Indian languages above, `.ai`/PDF import of a text's language, paragraph styles. Evidence: `features::tests::language_picks_the_fonts_forms`, `features::tests::serbian_gets_the_fonts_serbian_forms`, `typecmd::area_tests::language_is_a_character_attribute_saved_only_when_set`; spelling is checked in each run's Language (`text.spelling.check`, #4936) |
| No Break | done | Character panel menu (`text.setFormat` / `text.setRangeStyle {noBreak}`): the marked characters stay on one line, including across a space; a run that does not fit after earlier text moves together, and a run longer than the line stays together. Evidence: `layout::wrapping_tests::no_break_keeps_marked_characters_on_one_line`, `tests::no_break_keeps_marked_words_on_one_line`, `typecmd::no_break_is_a_character_attribute_saved_only_when_on` |
| Inline symbols in text | beyond Illustrator | not scored |
| Align Stroke (inside, outside) on live type's character stroke | beyond Illustrator | not scored; `stroke.set {align}` and `text.setRangeStyle {strokeOptions: {align}}`; saved natively, drawn around the glyphs on the canvas and in PDF, EPS and SVG (live text, stroke on the glyph outlines); visual bounds grow with it (#2294) |
@@ -81,7 +81,7 @@ Character Styles menu New/Redefine and footer New capture the first selected cha
| Date | Change | Summary |
|---|---|---|
| 2026-11-08 | minor | Spell check engine (#4936): `text.spelling.check` lists unknown words per run language at `text.editRange` byte offsets and `text.spelling.suggest` gives suggestions, from craft-proof; the dialog, Auto Spell Check and the custom dictionary remain. Spelling noted in the Language per character rows. Scores and estimates unchanged. |
| 2026-11-08 | minor | Spell check engine (#4936): `text.spelling.check` lists unknown words per run language at `text.editRange` byte offsets and `text.spelling.suggest` gives suggestions, from craft-proof; the dialog, Auto Spell Check and the custom dictionary remain. Spelling noted in the Language per character rows, which no longer list it as not yet done; their hyphenation note names the languages that still lack it. Scores and estimates unchanged. |
| 2026-11-07 | minor | Tabs: in right-aligned area type, the text before a line's last tab sits at its stops, measured from the left edge, and the text after it (none, when the tab ends the line) against the right edge; the line was aligned as a whole, which moved its start off the left edge. Illustrator's own layout in a user's `.ai` (used locally, not committed) sets its price lists so. Centred and justified lines keep their alignment as a whole. Scores and estimates unchanged. |
| 2026-11-07 | minor | Soft return: in Justify Left, Center or Right, the line a soft return ends is justified as a wrapped line is; only the paragraph's last line keeps its alignment. Illustrator's own layout in a user's `.ai` (used locally, not committed) justifies those lines. Scores and estimates unchanged. |
| 2026-11-08 | minor | Style panels: a double-click on a user character style's row opens its Character Style Options (`ui.charStyleOptions`), and on a paragraph style's row its Paragraph Style Options (rename); the Character Style Options dialog keeps a fixed width and scrolls its sections in a shorter window, so OK and Cancel stay on screen. Tests `panels::text_styles::tests::double_clicking_a_character_style_row_opens_its_options` and `dialogs::char_style_options::tests::the_sections_scroll_in_a_window_shorter_than_the_general_list`. Scores and estimates unchanged. |
+4 -1
View File
@@ -1,6 +1,6 @@
# UI and interaction parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (numeric fields step on wheel hover without taking keyboard focus, #5949) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged)
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-09 · **Change:** minor (floating tab groups collapse into the side icon rail and opened rail panels fold back into it, #5925 in part; numeric fields step on wheel hover without taking keyboard focus, #5949) · **Target:** Adobe Illustrator 2026 (30.x; Units › East Asian Type acts, with Q and Ha units; scores unchanged)
## Feature submission evidence
@@ -341,6 +341,8 @@ next unit in the order a right-click on a ruler lists them, from Feet back to Po
## Panels, Control bar, Properties, context menus
- **Panel collapse controls (#5925, in part):** The small » in a floating tab-group header moves that group into the side icon rail, reusing the existing rail or creating one. An opened rail panel has the same control to fold its flyout back into the rail, including after a tab switch. Both controls preserve the tab group and its menu; other floating groups and full-width docked headers stay as they were. Pointer regressions in `crates/ui-egui/src/panel_docking/tests.rs` cover grouped tabs, reopening, separate groups, a missing rail, the menu and flyout closure after switching tabs; `native_tests.rs` covers the native-window click and removal of the empty palette. Scores and estimates unchanged.
| Interaction | Status | Evidence |
|---|---|---|
| Selection artwork drag hovers a visible document tab to activate it, then drops editable artwork on its canvas | done | `tests_document_drag`, `edit.dropSelection`, lossless resource and one-step Undo regressions; overflow and cross-window drag remain separate |
@@ -488,6 +490,7 @@ separate. Scores and hours unchanged. [Behavior and command details](development
| Date | Change | Summary |
|---|---|---|
| 2026-11-08 | minor | Floating tab groups collapse into the side icon rail and opened rail panels fold back into it (#5925, in part); tab groups remain intact. Scores and estimates unchanged. |
| 2026-11-09 | minor | Numeric fields step on wheel hover without taking focus; keyboard focus and existing rounding rules remain (#5949). Scores and estimates unchanged. |
| 2026-11-07 | minor | Pen and Curvature tool with Cmd/Ctrl held: Edit › Copy and Cut take the anchors and segments the lent Direct Selection picked, as with Direct Selection chosen, not the whole path; with the Pen's own last anchor selected Copy still takes the whole path (#6123); scores and estimates unchanged |
| 2026-11-07 | minor | Live Corners: with Direct Selection, while every corner of a path is selected, a Shift-click on a corner widget picks that corner alone instead of deselecting it (#5188); adding a hidden corner's widget by Shift-click stays open (#1232); scores and estimates unchanged |