impl_vec!(ComponentArgument, ComponentArgumentVec, ComponentArgumentVecDestructor, ComponentArgumentVecDestructorType, ComponentArgumentVecSlice, OptionComponentArgument);
impl_option!(ComponentArgument, OptionComponentArgument, copy = false, [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]);
/// A namespace-qualified XML name (e.g. `svg:rect` has namespace `"svg"` and local name `"rect"`).
impl_vec!(ExternalResource, ExternalResourceVec, ExternalResourceVecDestructor, ExternalResourceVecDestructorType, ExternalResourceVecSlice, OptionExternalResource);
.or_else(|| Self::guess_mime_from_url(&src, if kind == ExternalResourceKind::Audio { "audio" } else { "video" }));
impl_vec!(ComponentCallbackArg, ComponentCallbackArgVec, ComponentCallbackArgVecDestructor, ComponentCallbackArgVecDestructorType, ComponentCallbackArgVecSlice, OptionComponentCallbackArg);
impl_option!(ComponentCallbackArg, OptionComponentCallbackArg, copy = false, [Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash]);
impl_vec_clone!(ComponentCallbackArg, ComponentCallbackArgVec, ComponentCallbackArgVecDestructor);
impl_vec!(ComponentEnumVariant, ComponentEnumVariantVec, ComponentEnumVariantVecDestructor, ComponentEnumVariantVecDestructorType, ComponentEnumVariantVecSlice, OptionComponentEnumVariant);
impl_option!(ComponentEnumVariant, OptionComponentEnumVariant, copy = false, [Debug, Clone, PartialEq]);
impl_vec_clone!(ComponentEnumVariant, ComponentEnumVariantVec, ComponentEnumVariantVecDestructor);
impl_vec!(ComponentEnumModel, ComponentEnumModelVec, ComponentEnumModelVecDestructor, ComponentEnumModelVecDestructorType, ComponentEnumModelVecSlice, OptionComponentEnumModel);
impl_option!(ComponentEnumModel, OptionComponentEnumModel, copy = false, [Debug, Clone, PartialEq]);
impl_option!(ComponentDefaultValue, OptionComponentDefaultValue, copy = false, [Debug, Clone, PartialEq]);
impl_vec!(ComponentFieldOverride, ComponentFieldOverrideVec, ComponentFieldOverrideVecDestructor, ComponentFieldOverrideVecDestructorType, ComponentFieldOverrideVecSlice, OptionComponentFieldOverride);
impl_option!(ComponentFieldOverride, OptionComponentFieldOverride, copy = false, [Debug, Clone, PartialEq]);
impl_vec_clone!(ComponentFieldOverride, ComponentFieldOverrideVec, ComponentFieldOverrideVecDestructor);
impl_vec!(ComponentFieldNamedValue, ComponentFieldNamedValueVec, ComponentFieldNamedValueVecDestructor, ComponentFieldNamedValueVecDestructorType, ComponentFieldNamedValueVecSlice, OptionComponentFieldNamedValue);
impl_option!(ComponentFieldNamedValue, OptionComponentFieldNamedValue, copy = false, [Debug, Clone, PartialEq]);
impl_vec_clone!(ComponentFieldNamedValue, ComponentFieldNamedValueVec, ComponentFieldNamedValueVecDestructor);
impl_vec!(ComponentFieldValue, ComponentFieldValueVec, ComponentFieldValueVecDestructor, ComponentFieldValueVecDestructorType, ComponentFieldValueVecSlice, OptionComponentFieldValue);
impl_option!(ComponentFieldValue, OptionComponentFieldValue, copy = false, [Debug, Clone, PartialEq]);
impl_vec_clone!(ComponentFieldValue, ComponentFieldValueVec, ComponentFieldValueVecDestructor);
impl_vec!(ComponentDataField, ComponentDataFieldVec, ComponentDataFieldVecDestructor, ComponentDataFieldVecDestructorType, ComponentDataFieldVecSlice, OptionComponentDataField);
impl_option!(ComponentDataField, OptionComponentDataField, copy = false, [Debug, Clone, PartialEq]);
impl_vec!(ComponentDataModel, ComponentDataModelVec, ComponentDataModelVecDestructor, ComponentDataModelVecDestructorType, ComponentDataModelVecSlice, OptionComponentDataModel);
/// Compile function type: takes component definition + target language + data model, returns source code.
impl_vec!(ComponentDef, ComponentDefVec, ComponentDefVecDestructor, ComponentDefVecDestructorType, ComponentDefVecSlice, OptionComponentDef);
impl_vec!(ComponentLibrary, ComponentLibraryVec, ComponentLibraryVecDestructor, ComponentLibraryVecDestructorType, ComponentLibraryVecSlice, OptionComponentLibrary);
"Dom::create_node(NodeType::{}).with_children(vec![Dom::create_text(AzString::from_const_str(\"{}\"))].into())",
fn push_scalar_field(children: &mut Vec<Dom>, field_name: &str, value: &dyn core::fmt::Display) {
let text = alloc::format!("[Error rendering {}:{}]", ci.library.as_str(), ci.component.as_str());
let text = alloc::format!("[Unknown component {}:{}]", ci.library.as_str(), ci.component.as_str());
"{}children.push(Dom::create_text(AzString::from(format!(\"{{}}: {{}}\", \"{}\", {}).as_str())));",
/// * `default_text` - Default text content for the preview, or `None` if the element has no text.
fn builtin_component_def(tag: &str, display_name: &str, default_text: Option<&str>, css: &str) -> ComponentDef {
fn data_field(name: &str, ft: ComponentFieldType, default: Option<ComponentDefaultValue>, description: &str) -> ComponentDataField {
data_field("href", String, Some(D::String(AzString::from_const_str(""))), "URL the link points to"),
data_field("target", String, Some(D::String(AzString::from_const_str(""))), "Where to open the linked document (_blank, _self, _parent, _top)"),
data_field("rel", String, Some(D::String(AzString::from_const_str(""))), "Relationship between current and linked document"),
data_field("alt", String, Some(D::String(AzString::from_const_str(""))), "Alternative text for the image"),
data_field("width", String, Some(D::String(AzString::from_const_str(""))), "Width of the image"),
data_field("height", String, Some(D::String(AzString::from_const_str(""))), "Height of the image"),
data_field("action", String, Some(D::String(AzString::from_const_str(""))), "URL where form data is submitted"),
data_field("method", String, Some(D::String(AzString::from_const_str("GET"))), "HTTP method for form submission (GET or POST)"),
data_field("for", String, Some(D::String(AzString::from_const_str(""))), "ID of the form element this label is for"),
data_field("type", String, Some(D::String(AzString::from_const_str("button"))), "Button type (button, submit, reset)"),
data_field("type", String, Some(D::String(AzString::from_const_str("1"))), "Numbering type (1, A, a, I, i)"),
data_field("type", String, Some(D::String(AzString::from_const_str("text"))), "Input type (text, password, email, number, checkbox, radio, etc.)"),
data_field("name", String, Some(D::String(AzString::from_const_str(""))), "Name of the input for form submission"),
data_field("value", String, Some(D::String(AzString::from_const_str(""))), "Current value of the input"),
data_field("placeholder", String, Some(D::String(AzString::from_const_str(""))), "Placeholder text"),
data_field("checked", Bool, Some(D::Bool(false)), "Whether the checkbox/radio is checked"),
data_field("min", String, Some(D::String(AzString::from_const_str(""))), "Minimum value (for number, range, date)"),
data_field("max", String, Some(D::String(AzString::from_const_str(""))), "Maximum value (for number, range, date)"),
data_field("step", String, Some(D::String(AzString::from_const_str(""))), "Step increment (for number, range)"),
data_field("pattern", String, Some(D::String(AzString::from_const_str(""))), "Regex pattern for validation"),
data_field("maxlength", String, Some(D::String(AzString::from_const_str(""))), "Maximum number of characters"),
data_field("name", String, Some(D::String(AzString::from_const_str(""))), "Name for form submission"),
data_field("multiple", Bool, Some(D::Bool(false)), "Whether multiple options can be selected"),
data_field("size", String, Some(D::String(AzString::from_const_str(""))), "Number of visible options"),
data_field("value", String, Some(D::String(AzString::from_const_str(""))), "Value submitted with the form"),
data_field("label", String, Some(D::String(AzString::from_const_str(""))), "Label for the option group"),
data_field("name", String, Some(D::String(AzString::from_const_str(""))), "Name for form submission"),
data_field("placeholder", String, Some(D::String(AzString::from_const_str(""))), "Placeholder text"),
data_field("maxlength", String, Some(D::String(AzString::from_const_str(""))), "Maximum number of characters"),
data_field("disabled", Bool, Some(D::Bool(false)), "Whether all controls in the fieldset are disabled"),
data_field("for", String, Some(D::String(AzString::from_const_str(""))), "IDs of elements that contributed to the output"),
data_field("name", String, Some(D::String(AzString::from_const_str(""))), "Name for form submission"),
data_field("value", String, Some(D::String(AzString::from_const_str(""))), "Current progress value"),
data_field("max", String, Some(D::String(AzString::from_const_str("1"))), "Maximum value"),
data_field("value", String, Some(D::String(AzString::from_const_str(""))), "Current value"),
data_field("min", String, Some(D::String(AzString::from_const_str("0"))), "Minimum value"),
data_field("max", String, Some(D::String(AzString::from_const_str("1"))), "Maximum value"),
data_field("low", String, Some(D::String(AzString::from_const_str(""))), "Low threshold"),
data_field("high", String, Some(D::String(AzString::from_const_str(""))), "High threshold"),
data_field("optimum", String, Some(D::String(AzString::from_const_str(""))), "Optimum value"),
data_field("open", Bool, Some(D::Bool(false)), "Whether the dialog is active and can be interacted with"),
data_field("src", String, Some(D::String(AzString::from_const_str(""))), "URL of the media resource"),
data_field("autoplay", Bool, Some(D::Bool(false)), "Whether to start playing automatically"),
data_field("preload", String, Some(D::String(AzString::from_const_str("auto"))), "Preload hint (none, metadata, auto)"),
data_field("type", String, Some(D::String(AzString::from_const_str(""))), "MIME type of the resource"),
data_field("kind", String, Some(D::String(AzString::from_const_str("subtitles"))), "Kind of text track (subtitles, captions, descriptions, chapters, metadata)"),
data_field("srclang", String, Some(D::String(AzString::from_const_str(""))), "Language of the track text"),
data_field("label", String, Some(D::String(AzString::from_const_str(""))), "User-readable title for the track"),
data_field("width", String, Some(D::String(AzString::from_const_str("300"))), "Width of the canvas in pixels"),
data_field("height", String, Some(D::String(AzString::from_const_str("150"))), "Height of the canvas in pixels"),
data_field("type", String, Some(D::String(AzString::from_const_str(""))), "MIME type of the embedded content"),
data_field("data", String, Some(D::String(AzString::from_const_str(""))), "URL of the resource"),
data_field("type", String, Some(D::String(AzString::from_const_str(""))), "MIME type of the resource"),
data_field("value", String, Some(D::String(AzString::from_const_str(""))), "Value of the parameter"),
data_field("shape", String, Some(D::String(AzString::from_const_str("default"))), "Shape of the area (default, rect, circle, poly)"),
data_field("coords", String, Some(D::String(AzString::from_const_str(""))), "Coordinates of the area"),
data_field("href", String, Some(D::String(AzString::from_const_str(""))), "URL for the area link"),
data_field("alt", String, Some(D::String(AzString::from_const_str(""))), "Alternative text"),
data_field("target", String, Some(D::String(AzString::from_const_str(""))), "Where to open the linked document"),
data_field("datetime", String, Some(D::String(AzString::from_const_str(""))), "Machine-readable date/time value"),
data_field("value", String, Some(D::String(AzString::from_const_str(""))), "Machine-readable value"),
data_field("title", String, Some(D::String(AzString::from_const_str(""))), "Full expansion or definition"),
data_field("cite", String, Some(D::String(AzString::from_const_str(""))), "URL of the source of the quotation"),
data_field("cite", String, Some(D::String(AzString::from_const_str(""))), "URL explaining the change"),
data_field("datetime", String, Some(D::String(AzString::from_const_str(""))), "Date/time of the change"),
data_field("dir", String, Some(D::String(AzString::from_const_str("ltr"))), "Text direction (ltr, rtl)"),
data_field("name", String, Some(D::String(AzString::from_const_str(""))), "Metadata name"),
data_field("content", String, Some(D::String(AzString::from_const_str(""))), "Metadata value"),
data_field("charset", String, Some(D::String(AzString::from_const_str(""))), "Character encoding"),
data_field("http-equiv", String, Some(D::String(AzString::from_const_str(""))), "HTTP header equivalent"),
data_field("href", String, Some(D::String(AzString::from_const_str(""))), "URL of the linked resource"),
data_field("type", String, Some(D::String(AzString::from_const_str(""))), "MIME type of the linked resource"),
data_field("src", String, Some(D::String(AzString::from_const_str(""))), "URL of external script"),
data_field("type", String, Some(D::String(AzString::from_const_str(""))), "MIME type or module"),
data_field("type", String, Some(D::String(AzString::from_const_str("text/css"))), "MIME type of the style sheet"),
data_field("href", String, Some(D::String(AzString::from_const_str(""))), "Base URL for relative URLs"),
data_field("target", String, Some(D::String(AzString::from_const_str(""))), "Default target for hyperlinks"),
model = model.with_default("text", ComponentDefaultValue::String(AzString::from(prepared.as_str())));
description: AzString::from_const_str("Conditional rendering: shows 'then' if condition is true, else shows 'else' (if provided)."),
data_field("condition", ComponentFieldType::Bool, Some(ComponentDefaultValue::Bool(false)), "The boolean condition to evaluate"),
.and_then(|f| match &f.default_value { OptionComponentDefaultValue::Some(ComponentDefaultValue::Bool(b)) => Some(*b), _ => None })
"if (data.condition) {\n // then branch\n AzDom_createDiv();\n} else {\n // else branch\n AzDom_createDiv();\n}"
"if (data.condition) {\n // then branch\n Dom::div();\n} else {\n // else branch\n Dom::div();\n}"
description: AzString::from_const_str("Iterative rendering: repeats children 'count' times."),
data_field("count", ComponentFieldType::U32, Some(ComponentDefaultValue::U32(3)), "Number of iterations"),
.and_then(|f| match &f.default_value { OptionComponentDefaultValue::Some(ComponentDefaultValue::U32(n)) => Some(*n), _ => None })
"let mut children = Vec::new();\nfor i in 0..data.count {\n children.push(Dom::div());\n}\nDom::div().with_children(children)"
"AzDom container = AzDom_createDiv();\nfor (uint32_t i = 0; i < data.count; i++) {\n AzDom_addChild(&container, AzDom_createDiv());\n}"
"auto container = Dom::div();\nfor (uint32_t i = 0; i < data.count; i++) {\n container.add_child(Dom::div());\n}"
description: AzString::from_const_str("Map data to DOM: applies a template to each item in a collection."),
data_field("data_json", ComponentFieldType::String, Some(ComponentDefaultValue::String(AzString::from_const_str("[]"))), "JSON array of items to map over"),
.and_then(|f| match &f.default_value { OptionComponentDefaultValue::Some(ComponentDefaultValue::String(s)) => Some(s.as_str().to_string()), _ => None })
"let items: Vec<serde_json::Value> = serde_json::from_str(&data.data_json).unwrap_or_default();\nlet children: Vec<Dom> = items.iter().map(|item| {\n Dom::div() // map template\n}).collect();\nDom::div().with_children(children)"
"// Parse data.data_json and map each item\nAzDom container = AzDom_createDiv();\n// TODO: iterate parsed JSON array"
"// Parse data.data_json and map each item\nauto container = Dom::div();\n// TODO: iterate parsed JSON array"
"import json\nitems = json.loads(data.data_json)\ncontainer = Dom.div()\nfor item in items:\n container.add_child(Dom.div())"
impl_vec!(XmlNodeChild, XmlNodeChildVec, XmlNodeChildVecDestructor, XmlNodeChildVecDestructorType, XmlNodeChildVecSlice, OptionXmlNodeChild);
/// Attributes of an XML node (note: not yet filtered and / or broken into function arguments!)
impl_vec!(XmlNode, XmlNodeVec, XmlNodeVecDestructor, XmlNodeVecDestructorType, XmlNodeVecSlice, OptionXmlNode);
pub fn get_html_node<'a>(root_nodes: &'a [XmlNodeChild]) -> Result<&'a XmlNode, DomXmlParseError> {
pub fn get_body_node<'a>(root_nodes: &'a [XmlNodeChild]) -> Result<&'a XmlNode, DomXmlParseError> {
pub fn get_item<'a>(hierarchy: &[usize], root_node: &'a mut XmlNode) -> Option<&'a mut XmlNode> {
if let Some(focusable) = xml_node.attributes.get_key("focusable").and_then(|f| parse_bool(f.as_str())) {
if let Some(tab_index) = xml_node.attributes.get_key("tabindex").and_then(|val| val.parse::<isize>().ok()) {
let is_svg_shape = inside_svg && matches!(tag, "path" | "circle" | "rect" | "ellipse" | "line" | "polygon" | "polyline");
if let Some(focusable) = xml_node.attributes.get_key("focusable").and_then(|f| parse_bool(f.as_str())) {
if let Some(tab_index) = xml_node.attributes.get_key("tabindex").and_then(|val| val.parse::<isize>().ok()) {
let is_svg_shape = inside_svg && matches!(tag, "path" | "circle" | "rect" | "ellipse" | "line" | "polygon" | "polyline");
// render_dom_from_body_node() removed — use render_dom_from_body_node_fast() or str_to_dom()
/// Combines the split string back into its original form while replacing the variables with their
/// Given a string and a key => value mapping, replaces parts of the string with the value, i.e.: