Add String[] as a graphic type for typography (#4141)

* feat: Render List<String> as raw paths in SVG and Vell mode

* chore: code review

* chore: change the hardcoded layout bounds to parley's

* chore: code review

* feat: Split text node to text_layer and text_to_vector node

* fix: CI fail because of difference in nature of Mac and github action

* chore: fix

* chore: replace FontStack as it got removed in parley 0.9

* chore: fmt

* chore: migrate the rendering as of new resource architechture

* chore: add text_layer node to text tool for testing

* code review

* Make boolean ops support the Text type

* chore: Move fallback_font_resource authority from editor to text node

* Fix 'Text Layer' node missing font dropdown

* Change node doc comments from Vec<T> to T[]

* Add migrations from the old Text node to Text -> Text to Vector

* Consolidate

* Rename the text attributes and reorder tilt to come before max_width/height

* Detect legacy Text nodes in the split migration by their trailing separate_glyphs input

* Code review

* Frame Text layer thumbnails by laying out their text for bounds

* Give Text layers click targets and selection outlines via collect_metadata

* Route Text tool through Text to Vector with fill, fixing editing-preview placement

---------

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Jatin Bharti
2026-06-19 19:39:52 +00:00
committed by GitHub
co-authored by Keavon Chambers
parent 13abf9fa8c
commit 5f100946f2
39 changed files with 967 additions and 204 deletions
+2 -1
View File
@@ -7,7 +7,7 @@ use graphic_types::{Artboard, Vector};
use raster_types::{CPU, GPU, Raster};
use vector_types::GradientStops;
/// Constructs a single-row `List<Artboard>` with the given content and metadata stored as row attributes.
/// Constructs a single-element `Artboard[]` with the given content and metadata stored as row attributes.
#[node_macro::node(category(""))]
pub async fn create_artboard<T: IntoGraphicList + 'n>(
ctx: impl ExtractAll + CloneVarArgs + Ctx,
@@ -15,6 +15,7 @@ pub async fn create_artboard<T: IntoGraphicList + 'n>(
#[implementations(
Context -> List<Graphic>,
Context -> List<Vector>,
Context -> List<String>,
Context -> List<Raster<CPU>>,
Context -> List<Raster<GPU>>,
Context -> List<Color>,
+31 -26
View File
@@ -116,6 +116,7 @@ async fn map<Item: AnyHash + Send + Sync + CacheHash>(
List<Raster<CPU>>,
List<Color>,
List<GradientStops>,
List<String>,
)]
content: List<Item>,
#[implementations(
@@ -124,6 +125,7 @@ async fn map<Item: AnyHash + Send + Sync + CacheHash>(
Context -> List<Raster<CPU>>,
Context -> List<Color>,
Context -> List<GradientStops>,
Context -> List<String>,
)]
mapped: impl Node<Context<'static>, Output = List<Item>>,
) -> List<Item> {
@@ -146,6 +148,7 @@ async fn mirror<T: 'n + Send + Clone>(
#[implementations(
List<Graphic>,
List<Vector>,
List<String>,
List<Raster<CPU>>,
List<Color>,
List<GradientStops>,
@@ -280,7 +283,7 @@ fn attach_attribute<T: AnyHash + Clone + Send + Sync + CacheHash>(
List<GradientSpreadMethod>,
)]
mut content: List<T>,
/// The source values to attach. Any `List<U>` wired here is type-erased via an auto-inserted convert.
/// The source values to attach.
#[expose]
source: AttributeDyn,
/// The name to assign to the new destination attribute.
@@ -293,7 +296,7 @@ fn attach_attribute<T: AnyHash + Clone + Send + Sync + CacheHash>(
content
}
/// Reads a named `Vector` attribute from the input list, outputting each value as an element of a new `List<Vector>`.
/// Reads a named `Vector` attribute from the input list, outputting each value as an element of a new `Vector[]`.
#[node_macro::node(category("Attributes: Read"))]
fn read_attribute_vector(
_: impl Ctx,
@@ -309,7 +312,7 @@ fn read_attribute_vector(
result
}
/// Reads a named numeric attribute (`f64`, `u64`, or `u32`) from the input list, outputting each value as an element of a new `List<f64>`. Integer values are converted to `f64`.
/// Reads a named numeric attribute (`f64`, `u64`, or `u32`) from the input list, outputting each value as an element of a new `f64[]`. Integer values are converted to `f64`.
#[node_macro::node(category("Attributes: Read"))]
fn read_attribute_number(
_: impl Ctx,
@@ -330,7 +333,7 @@ fn read_attribute_number(
result
}
/// Reads a named `bool` attribute from the input list, outputting each value as an element of a new `List<bool>`.
/// Reads a named `bool` attribute from the input list, outputting each value as an element of a new `bool[]`.
#[node_macro::node(category("Attributes: Read"))]
fn read_attribute_bool(
_: impl Ctx,
@@ -346,7 +349,7 @@ fn read_attribute_bool(
result
}
/// Reads a named `String` attribute from the input list, outputting each value as an element of a new `List<String>`.
/// Reads a named `String` attribute from the input list, outputting each value as an element of a new `String[]`.
#[node_macro::node(category("Attributes: Read"))]
fn read_attribute_string(
_: impl Ctx,
@@ -362,7 +365,7 @@ fn read_attribute_string(
result
}
/// Reads a named `DAffine2` transform attribute from the input list, outputting each value as an element of a new `List<DAffine2>`.
/// Reads a named `DAffine2` transform attribute from the input list, outputting each value as an element of a new `DAffine2[]`.
#[node_macro::node(category("Attributes: Read"))]
fn read_attribute_transform(
_: impl Ctx,
@@ -378,7 +381,7 @@ fn read_attribute_transform(
result
}
/// Reads a named `Color` attribute from the input list, outputting each value as an element of a new `List<Color>`.
/// Reads a named `Color` attribute from the input list, outputting each value as an element of a new `Color[]`.
#[node_macro::node(category("Attributes: Read"))]
fn read_attribute_color(
_: impl Ctx,
@@ -394,7 +397,7 @@ fn read_attribute_color(
result
}
/// Reads a named `BlendMode` attribute from the input list, outputting each value as an element of a new `List<BlendMode>`.
/// Reads a named `BlendMode` attribute from the input list, outputting each value as an element of a new `BlendMode[]`.
#[node_macro::node(category("Attributes: Read"))]
fn read_attribute_blend_mode(
_: impl Ctx,
@@ -410,7 +413,7 @@ fn read_attribute_blend_mode(
result
}
/// Reads a named `GradientType` attribute from the input list, outputting each value as an element of a new `List<GradientType>`.
/// Reads a named `GradientType` attribute from the input list, outputting each value as an element of a new `GradientType[]`.
#[node_macro::node(category("Attributes: Read"))]
fn read_attribute_gradient_type(
_: impl Ctx,
@@ -426,7 +429,7 @@ fn read_attribute_gradient_type(
result
}
/// Reads a named `GradientSpreadMethod` attribute from the input list, outputting each value as an element of a new `List<GradientSpreadMethod>`.
/// Reads a named `GradientSpreadMethod` attribute from the input list, outputting each value as an element of a new `GradientSpreadMethod[]`.
#[node_macro::node(category("Attributes: Read"))]
fn read_attribute_spread_method(
_: impl Ctx,
@@ -442,7 +445,7 @@ fn read_attribute_spread_method(
result
}
/// Reads a named `GradientStops` attribute from the input list, outputting each value as an element of a new `List<GradientStops>`.
/// Reads a named `GradientStops` attribute from the input list, outputting each value as an element of a new `GradientStops[]`.
#[node_macro::node(category("Attributes: Read"))]
fn read_attribute_gradient_stops(
_: impl Ctx,
@@ -458,7 +461,7 @@ fn read_attribute_gradient_stops(
result
}
/// Reads a named `Artboard` attribute from the input list, outputting each value as an element of a new `List<Artboard>`.
/// Reads a named `Artboard` attribute from the input list, outputting each value as an element of a new `Artboard[]`.
#[node_macro::node(category("Attributes: Read"))]
fn read_attribute_artboard(
_: impl Ctx,
@@ -474,7 +477,7 @@ fn read_attribute_artboard(
result
}
/// Reads a named `Raster<CPU>` attribute from the input list, outputting each value as an element of a new `List<Raster<CPU>>`.
/// Reads a named `Raster` attribute from the input list, outputting each value as an element of a new `Raster[]`.
#[node_macro::node(category("Attributes: Read"))]
fn read_attribute_raster(
_: impl Ctx,
@@ -495,11 +498,11 @@ fn read_attribute_raster(
pub async fn extend<T: 'n + Send + Clone>(
_: impl Ctx,
/// The `List` whose items will appear at the start of the extended `List`.
#[implementations(List<Artboard>, List<Graphic>, List<Vector>, List<Raster<CPU>>, List<Raster<GPU>>, List<Color>, List<GradientStops>)]
#[implementations(List<Artboard>, List<Graphic>, List<Vector>, List<String>, List<Raster<CPU>>, List<Raster<GPU>>, List<Color>, List<GradientStops>)]
base: List<T>,
/// The `List` whose items will appear at the end of the extended `List`.
#[expose]
#[implementations(List<Artboard>, List<Graphic>, List<Vector>, List<Raster<CPU>>, List<Raster<GPU>>, List<Color>, List<GradientStops>)]
#[implementations(List<Artboard>, List<Graphic>, List<Vector>, List<String>, List<Raster<CPU>>, List<Raster<GPU>>, List<Color>, List<GradientStops>)]
new: List<T>,
) -> List<T> {
let mut base = base;
@@ -514,9 +517,9 @@ pub async fn extend<T: 'n + Send + Clone>(
#[node_macro::node(category(""))]
pub async fn legacy_layer_extend<T: 'n + Send + Clone>(
_: impl Ctx,
#[implementations(List<Artboard>, List<Graphic>, List<Vector>, List<Raster<CPU>>, List<Raster<GPU>>, List<Color>, List<GradientStops>)] base: List<T>,
#[implementations(List<Artboard>, List<Graphic>, List<Vector>, List<String>, List<Raster<CPU>>, List<Raster<GPU>>, List<Color>, List<GradientStops>)] base: List<T>,
#[expose]
#[implementations(List<Artboard>, List<Graphic>, List<Vector>, List<Raster<CPU>>, List<Raster<GPU>>, List<Color>, List<GradientStops>)]
#[implementations(List<Artboard>, List<Graphic>, List<Vector>, List<String>, List<Raster<CPU>>, List<Raster<GPU>>, List<Color>, List<GradientStops>)]
new: List<T>,
nested_node_path: List<NodeId>,
) -> List<T> {
@@ -548,6 +551,7 @@ pub async fn wrap_graphic<T: Into<Graphic> + 'n>(
List<Raster<GPU>>,
List<Color>,
List<GradientStops>,
List<String>,
DAffine2,
DVec2,
)]
@@ -556,8 +560,8 @@ pub async fn wrap_graphic<T: Into<Graphic> + 'n>(
List::new_from_element(content.into())
}
/// Converts a `List` of graphical content into a `List<Graphic>` by placing it into an element of a new wrapper `List<Graphic>`.
/// If it is already a `List<Graphic>`, it is not wrapped again. Use the 'Wrap Graphic' node if wrapping is always desired.
/// Converts a list of graphical content into a `Graphic[]` by placing it into an element of a new wrapper `Graphic[]`.
/// If it is already a `Graphic[]`, it is not wrapped again. Use the 'Wrap Graphic' node if wrapping is always desired.
#[node_macro::node(category("General"))]
pub async fn to_graphic<T: IntoGraphicList + 'n>(
_: impl Ctx,
@@ -568,13 +572,14 @@ pub async fn to_graphic<T: IntoGraphicList + 'n>(
List<Raster<GPU>>,
List<Color>,
List<GradientStops>,
List<String>,
)]
content: T,
) -> List<Graphic> {
content.into_graphic_list()
}
/// Removes a level of nesting from a `List<Graphic>`, or all nesting if "Fully Flatten" is enabled.
/// Removes a level of nesting from a `Graphic[]`, or all nesting if "Fully Flatten" is enabled.
#[node_macro::node(category("General"))]
pub async fn flatten_graphic(_: impl Ctx, content: List<Graphic>, fully_flatten: bool) -> List<Graphic> {
// TODO: Avoid mutable reference, instead return a new List<Graphic>?
@@ -596,7 +601,7 @@ pub async fn flatten_graphic(_: impl Ctx, content: List<Graphic>, fully_flatten:
flatten_list(output_graphic_list, current_element, fully_flatten, recursion_depth + 1);
}
// Push any leaf elements we encounter: either `Graphic::Graphic(...)` values beyond the recursion depth, or non-`Graphic::Graphic` variants (e.g. `Graphic::Vector`, `Graphic::Raster*`, `Graphic::Color`, `Graphic::Gradient`)
// Push any leaf elements we encounter: either `Graphic::Graphic(...)` values beyond the recursion depth, or non-`Graphic::Graphic` variants (e.g. `Graphic::Vector`, `Graphic::Raster*`, `Graphic::Color`, `Graphic::Gradient`, `Graphic::Text`)
_ => {
let attributes = current_graphic_list.clone_item_attributes(index);
output_graphic_list.push(Item::from_parts(current_element, attributes));
@@ -611,7 +616,7 @@ pub async fn flatten_graphic(_: impl Ctx, content: List<Graphic>, fully_flatten:
output
}
/// Converts a `List<Graphic>` into a `List<Vector>` by deeply flattening any vector content it contains, and discarding any non-vector content.
/// Converts a `Graphic[]` into a `Vector[]` by deeply flattening any vector content it contains, and discarding any non-vector content.
#[node_macro::node(category("Vector"))]
pub async fn flatten_vector<T: IntoGraphicList + 'n + Send + Clone>(_: impl Ctx, #[implementations(List<Graphic>, List<Vector>)] content: T) -> List<Vector> {
let graphic_list = content.into_graphic_list();
@@ -644,25 +649,25 @@ pub async fn flatten_vector<T: IntoGraphicList + 'n + Send + Clone>(_: impl Ctx,
output
}
/// Converts a `List<Graphic>` into a `List<Raster>` by deeply flattening any raster content it contains, and discarding any non-raster content.
/// Converts a `Graphic[]` into a `Raster[]` by deeply flattening any raster content it contains, and discarding any non-raster content.
#[node_macro::node(category("Raster"))]
pub async fn flatten_raster<T: IntoGraphicList + 'n + Send + Clone>(_: impl Ctx, #[implementations(List<Graphic>, List<Raster<CPU>>)] content: T) -> List<Raster<CPU>> {
content.into_flattened_list()
}
/// Converts a `List<Graphic>` into a `List<Color>` by deeply flattening any color content it contains, and discarding any non-color content.
/// Converts a `Graphic[]` into a `Color[]` by deeply flattening any color content it contains, and discarding any non-color content.
#[node_macro::node(category("General"))]
pub async fn flatten_color<T: IntoGraphicList + 'n + Send + Clone>(_: impl Ctx, #[implementations(List<Graphic>, List<Color>)] content: T) -> List<Color> {
content.into_flattened_list()
}
/// Converts a `List<Graphic>` into a `List<GradientStops>` by deeply flattening any gradient content it contains, and discarding any non-gradient content.
/// Converts a `Graphic[]` into a `GradientStops[]` by deeply flattening any gradient content it contains, and discarding any non-gradient content.
#[node_macro::node(category("General"))]
pub async fn flatten_gradient<T: IntoGraphicList + 'n + Send + Clone>(_: impl Ctx, #[implementations(List<Graphic>, List<GradientStops>)] content: T) -> List<GradientStops> {
content.into_flattened_list()
}
/// Constructs a gradient from a `List<Color>`, where the colors are evenly distributed as gradient stops across the range from 0 to 1.
/// Constructs a gradient from a `Color[]`, where the colors are evenly distributed as gradient stops across the range from 0 to 1.
#[node_macro::node(category("Color"))]
fn colors_to_gradient<T: IntoGraphicList + 'n + Send + Clone>(_: impl Ctx, #[implementations(List<Graphic>, List<Color>)] colors: T) -> List<GradientStops> {
let colors = colors.into_flattened_list::<Color>();