scripts/COMPONENT_SYSTEM_STATUS.md
Date: 2025-02-25
Baseline: doc/COMPONENT_SYSTEM_REPORT.md (v2), doc/PLAN_COMPONENT_HIERARCHY.md, doc/COMPONENT_TYPE_SYSTEM_DESIGN.md
AzComponentDef struct — DONE| Item | Status | Notes |
|---|---|---|
ComponentDef repr(C) struct | ✅ Done | core/src/xml.rs:1974 — simplified: 8 fields (id, display_name, description, css, source, data_model, render_fn, compile_fn). Removed parameters, callback_slots, accepts_text, child_policy, example_xml, node_type, template. |
ComponentId (collection:name) | ✅ Done | core/src/xml.rs:1127 — collection + name fields, qualified_name(), builtin(), new() |
ComponentParam | 🗑 Removed | Was dead type — merged into ComponentDataField with structured ComponentFieldType |
ComponentCallbackSlot | 🗑 Removed | Was dead type — callbacks are now ComponentFieldType::Callback(sig) fields in data_model |
ComponentDataField | ✅ Done | core/src/xml.rs:1470 — name, field_type: ComponentFieldType (structured!), default_value: OptionComponentDefaultValue, required: bool, description |
ChildPolicy enum | 🗑 Removed | Was unused — child acceptance is now derived from data model shape (see PLAN_COMPONENT_HIERARCHY.md §3): text: String field → accepts text, StyledDom field → accepts children, neither → no children |
ComponentSource enum | ✅ Done | Builtin, Compiled, UserDefined |
CompileTarget enum | ✅ Done | Rust, C, Cpp, Python |
ComponentRenderFn / ComponentCompileFn type aliases | ✅ Done | Function pointer types in xml.rs |
RegisterComponentFn / RegisterComponentLibraryFn | ✅ Done | repr(C) callback structs with cb + ctx for FFI |
ComponentLibrary | ✅ Done | name, version, description, components, exportable, modifiable, data_models, enum_models |
ComponentMap with qualified lookup | ✅ Done | get(collection, name), get_unqualified(), get_by_qualified_name(), get_exportable_libraries() |
impl_vec! / impl_option! for all types | ✅ Done | Full FFI-compatible vector/option types |
52 builtin components registered via register_builtin_components() | ✅ Done | core/src/xml.rs:1623 — using builtin_component_def() helper |
builtin_render_fn / builtin_compile_fn | ✅ Done | NodeType-based rendering + multi-language codegen |
user_defined_render_fn / user_defined_compile_fn | ⚠️ Stub | Stub only — creates <div> + optional text. Real impl will use dynamic source editing + hot recompile (see ACTION_PLAN.md) |
Extend split_dynamic_string for format specifiers | ⚠️ Partial | core/src/xml.rs:4086 parses {var:spec} syntax, but format_spec is captured as string only — not applied during rendering. Consumers ignore it. |
ChildPolicy::Specific(StringVec) | 🗑 Removed | ChildPolicy itself removed — validation of allowed children can be done at DOM insertion time via component data model introspection |
| Item | Status | Notes |
|---|---|---|
ExportedLibraryResponse with serde Serialize+Deserialize | ✅ Done | debug_server.rs:373 — JSON-serializable library format |
ExportedComponentDef / ExportedComponentParam / ExportedDataField / ExportedCallbackSlot | ✅ Done | Full JSON round-trip types |
ImportComponentLibrary debug API endpoint | ✅ Done | JSON → ComponentDef conversion, inserts into ComponentMap |
ExportComponentLibrary debug API endpoint | ✅ Done | ComponentDef → JSON export for user-defined libraries |
get_component_registry / get_libraries / get_library_components endpoints | ✅ Done | Full REST-style API |
Dynamic render_fn for JSON components | ⚠️ Stub | user_defined_render_fn creates div+text. Vision: source code editing + hot recompile per component, not XML template expansion |
Dynamic compile_fn for JSON components | ⚠️ Stub | user_defined_compile_fn generates basic code. Vision: editable source per component in $lang, with default codegen from ComponentDef |
| Item | Status | Notes |
|---|---|---|
| Component sidebar: library list | ✅ Done | Shows libraries with counts, selectable |
| Component sidebar: component list per library | ✅ Done | Shows display_name + tag |
| Component detail panel: params, data model, callbacks, CSS, example XML | ✅ Done | Full detail rendering in showComponentDetail() |
| Import Component Library menu item | ✅ Done | Import > Component Library... (file picker) |
| Export Component Library menu item | ✅ Done | Export > Component Library (JSON) |
| Export Code (Rust/C/C++/Python) menu items | ✅ Done | Export > Code (Rust/C/C++/Python) |
| Library dropdown instead of list | ✅ Done | <select id="library-selector"> dropdown in debugger.html |
| Component filter/search | ✅ Done | <input id="component-filter"> with client-side fuzzy match |
| "Create Component" from context menu | ✅ Done | + Component button, hidden when !modifiable |
| Component tree editor (second column) | ❌ Not done | |
| Drag-and-drop components into DOM tree | ❌ Not done | |
| Grey rendering of component internals | ❌ Not done | |
| Live preview with CPU render | ✅ Done | get_component_preview API endpoint + PreviewPanel widget in debugger.js + cpurender::render_component_preview() in layout |
| Context menu: nested library → component insertion | ❌ Not done |
| Item | Status | Notes |
|---|---|---|
export_code debug API endpoint | ✅ Done | Returns ExportedCodeResponse with files map |
| Rust scaffold: Cargo.toml + src/main.rs | ✅ Done | DataModel struct, layout callback, callback stubs |
| C scaffold: main.c with struct + layout | ✅ Done | |
| C++ scaffold: main.cpp with struct + layout | ✅ Done | |
| Python scaffold: main.py with class + layout | ✅ Done | |
| Helper functions (to_pascal_case, map_type_to_rust, etc.) | ✅ Done | |
| ZIP packaging + base64 response | ❌ Not done | Currently returns files as JSON map |
| Component module structure (components/mod.rs) | ❌ Not done | All code in single main file |
| Item | Status | Notes |
|---|---|---|
Builtin compile_fn handles all 4 languages | ✅ Done | builtin_compile_fn handles Rust/C/C++/Python |
For / If / Map structural components | ❌ Not done | |
| Per-language iteration/conditional patterns | ❌ Not done |
| Item | Status |
|---|---|
source_file tracking per component | ❌ |
| Change detection on re-export | ❌ |
| User code preservation markers | ❌ |
Current: Two-section sidebar — "COMPONENT LIBRARIES" (list of libraries) + "COMPONENTS" (list of components in selected library). Each component shows <> DisplayName <tag> with an icon.
Required:
Library: <builtin> ▾ — not a clickable list. Picking a library from the dropdown loads its components below.+ Library and + Component
+ Library creates a new empty user-defined library (name prompt)+ Component creates a new empty component in the current libraryreadonly: true (i.e., builtin/compiled libraries cannot be modified)<> icon prefix, no <tag> suffix — just the display name. Clean list.The ComponentLibrary struct needs a field to indicate whether the library accepts user modifications (add/remove/edit components):
pub struct ComponentLibrary {
// ... existing fields ...
/// Whether this library can be modified by the user (add/remove/edit components)
/// False for builtin and compiled libraries.
pub modifiable: bool,
}
This is separate from exportable:
modifiable = false, exportable = false → builtin (can't change, can't export)modifiable = false, exportable = true → compiled plugin (can't change, can export the JSON definition)modifiable = true, exportable = true → user-created (full control)The debug server should return this field in LibrarySummary and ComponentLibraryInfo.
The main editor area, when a component is selected, should show two columns:
Left column — Component Properties:
<details> section labeled "Universal HTML Attributes"href for Link, src for Image) should be shown first, above the collapsed universal sectionRight column — Mini HTML Tree + Preview:
render_fn given the current data model valuesget_component_previewThe component detail view has two distinct editing surfaces:
A) Structure editing (drag & drop in the mini HTML tree):
render_fn given current data model valuesrender_fnB) Functionality editing (popup source code editor):
source == UserDefined components (hidden for Builtin/Compiled)render_fn / compile_fnDefault render_fn / compile_fn:
All user-defined components start with the SAME default render_fn and compile_fn.
The default render_fn interprets the ComponentDef structure generically — iterating
data model fields, creating DOM nodes for StyledDom slots, and recursively instantiating
sub-components via ComponentMap. When a user writes custom source in the popup editor,
it replaces the default for that specific component.
The default compile_fn generates source code that references predefined function names
of other components. It does NOT need ComponentMap — just emits code.
Current: ComponentDataField is a flat list of (name, field_type, default_value, description) where field_type is a string like "String", "f32", "RefAny".
Required: Data models should support nested custom types. A component's data model is not just "a list of attributes" — it's a type definition that can reference other type definitions.
Example: A UserCard component might have:
UserCardDataModel {
user: UserProfile, // nested custom type
show_avatar: bool,
on_click: RefAny, // backreference slot
}
UserProfile {
name: String,
avatar_url: String,
bio: String,
}
This means ComponentDataField.field_type can be:
"String", "bool", "i32", "f32", "u32", "usize""RefAny" (backreference slot), "OptionString", etc."UserProfile" — references another data model definition"ButtonOnClickCallbackType" — from api.jsonFor code generation, nested types produce nested struct definitions. For the debugger,
nested types show as expandable trees in the data model inspector.
Where are custom data model definitions stored?
Each ComponentLibrary should have a data_models field — a registry of named struct
definitions that components in that library can reference:
pub struct ComponentLibrary {
// ... existing fields ...
/// Named data model types defined by this library
/// Components reference these by name in their field_type
pub data_models: ComponentDataModelVec,
}
/// A named data model (struct definition) for code generation
pub struct ComponentDataModel {
/// Type name, e.g. "UserProfile"
pub name: AzString,
/// Description
pub description: AzString,
/// Fields in this struct
pub fields: ComponentDataFieldVec,
}
Default instantiation: Each component should be able to produce a "default" instance
of its data model — all fields filled with their default_value. This is used:
Requirement: The DOM tree in the Inspector view should also have access to the component palette. Users should be able to drag components from the palette and drop them between existing DOM tree nodes to insert them.
Implementation approach:
insert_node with the component's tag and default attributesNew API endpoint: get_component_preview
{
"op": "get_component_preview",
"component": "builtin:div",
"config": {
"width": 300,
"height": 200,
"theme": "dark",
"os": "macos",
"data": { }
}
}
Returns a base64-encoded PNG image of the component rendered via the CPU renderer
(same path as take_screenshot, but rendering an isolated component subtree).
Preview updates when:
Preview configuration:
width / height — viewport size (null = auto-fit)theme — "light" | "dark" (applies UA stylesheet variant)os — "macos" | "windows" | "linux" (for @media queries and platform-specific rendering)data — JSON object to fill the data model fields (overrides defaults)Implementation: The server receives the component definition, constructs a minimal
DOM tree using render_fn, applies scoped_css, renders via the existing CPU screenshot
path, and returns the base64 image. This does not require a window — it uses the
headless/software renderer.
<> icon in the component list items<tag> suffix after the display name in the listbuiltin:a)ComponentLibrary Updatespub struct ComponentLibrary {
pub name: AzString,
pub version: AzString,
pub description: AzString,
pub components: ComponentDefVec,
pub exportable: bool,
pub modifiable: bool, // NEW: can user add/remove/edit components?
pub data_models: ComponentDataModelVec, // NEW: library-level type definitions
}
ComponentDataModel (New Type)/// A named struct definition used as a data model type.
/// Components reference these by name in ComponentDataField.field_type.
#[repr(C)]
pub struct ComponentDataModel {
/// Type name, e.g. "UserProfile", "TodoItem"
pub name: AzString,
/// Human-readable description
pub description: AzString,
/// Fields in this struct
pub fields: ComponentDataFieldVec,
}
This allows nesting: a field with field_type = "UserProfile" references a
ComponentDataModel with name = "UserProfile" in the same library.
ComponentDef — Current Design (no template field)The template field was considered but explicitly removed per PLAN_COMPONENT_HIERARCHY.md §2.
Instead of storing an XML template, the user's workflow is:
source: AzString field pointing to its source code filerender_fn via hot-reloadThis avoids the complexity of an XML template → code round-trip and keeps the source of truth
in actual source code. The compile_fn generates initial scaffolding, after which the user
owns the source file.
ComponentDef currently has 8 fields: id, display_name, description, css, source,
data_model, render_fn, compile_fn. No template field.
ComponentLibraryInfo gains:
pub modifiable: bool,
pub data_models: Vec<DataModelInfo>,
ComponentInfo currently has: tag, qualified_name, display_name, description, source,
data_model, universal_attributes, callback_slots, css. No template field (removed by design).
LibrarySummary gains:
pub modifiable: bool,
New response type:
pub struct ComponentPreviewResponse {
/// Base64-encoded PNG image data
pub image: String,
/// Width of the rendered image
pub width: u32,
/// Height of the rendered image
pub height: u32,
}
| Endpoint | Purpose |
|---|---|
get_component_preview | Render component preview image (CPU) |
create_library | Create a new empty user-defined library |
delete_library | Delete a user-defined library |
create_component | Create a new empty component in a library |
delete_component | Delete a component from a library |
update_component | Update a component's CSS, data model, source, etc. |
Files: core/src/xml.rs
modifiable: bool field to ComponentLibraryComponentDataModel struct + ComponentDataModelVec (impl_vec!, impl_option!, etc.)data_models: ComponentDataModelVec field to ComponentLibrarytemplate: AzString field to ComponentDefregister_builtin_components(): set modifiable: false, data_models: empty for builtinsbuiltin_component_def(): fields correctuser_defined_render_fn / user_defined_compile_fn are stubs — vision: source-edit-recompile (not template expansion)Files: dll/src/desktop/shell2/common/debug_server.rs
ComponentLibraryInfo, LibrarySummary to include modifiable + data_modelsComponentInfo to include templatebuild_component_registry() to populate new fieldsCreateLibrary { name, description } — implementedDeleteLibrary { name } — implementedCreateComponent { library, name, display_name } — implementedDeleteComponent { library, component } — implementedUpdateComponent { library, component, scoped_css?, data_model?, ... } — implementedGetComponentPreview { component, config } — implementedGetComponentPreview: construct DOM from render_fn, apply CSS, use CPU renderer, return base64 PNG (debug_server.rs:8768)ExportedLibraryResponse / ExportedComponentDef to include data_models (no template)Files: dll/src/desktop/shell2/common/debugger/debugger.html
#component-registry-container + #component-list-container with:
Library: <select id="library-selector">...</select>+ Library | + Component (conditionally shown based on modifiable)<input type="text" id="component-filter" placeholder="Filter..."><div id="component-list-container">...</div> (clean names, no icons)#view-components to two-column layout:
Files: dll/src/desktop/shell2/common/debugger/debugger.js
loadLibraries() → populates a <select> dropdown instead of a listonLibraryChange() handler for dropdown selectionfilterComponents() handler for the filter input (client-side fuzzy match)_renderComponentList():
<> icon prefix<tag> suffixcreateLibrary() handler — prompts for name, posts create_librarycreateComponent() handler — prompts for name, posts create_component+ Library / + Component buttons when modifiable = falseFiles: dll/src/desktop/shell2/common/debugger/debugger.js, debugger.css
showComponentDetail() to render two-column layout:
<details>)update_componentget_component_preview)Files: dll/src/desktop/shell2/common/debugger/debugger.js, debugger.css
component.source == UserDefinedFiles: dll/src/desktop/shell2/common/debug_server.rs, debugger.js
GetComponentPreview handler:
render_fn with default data model valuesscoped_cssget_component_preview when component is selected, on template change, on CSS change, on data model changeFiles: dll/src/desktop/shell2/common/debugger/debugger.js, debugger.html, debugger.css
insert_node API with component tag and defaultsThese are the base types available for ComponentDataField.field_type:
| Type | Rust | C | Python | JSON |
|---|---|---|---|---|
String | String | AzString | str | "hello" |
bool | bool | bool | bool | true |
i32 | i32 | int32_t | int | 42 |
f32 | f32 | float | float | 3.14 |
u32 | u32 | uint32_t | int | 42 |
usize | usize | size_t | int | 42 |
RefAny | RefAny | AzRefAny | RefAny | (backreference) |
OptionString | Option<String> | AzOptionString | Optional[str] | null or "..." |
User types are defined as ComponentDataModel entries in the library's data_models vec.
They are referenced by name in ComponentDataField.field_type.
Code export resolves these to struct definitions in the target language.
Callback type names (e.g., "ButtonOnClickCallbackType") reference definitions in
api.json. The debugger shows the full signature. Code export generates the correct
callback typedef + wiring.
From original COMPONENT_SYSTEM_REPORT.md:
split_dynamic_string applied during rendering (§2.4) — parsing works, application not wiredAzCompileDomContext for structural components (§2.3)ComponentInstance struct for DOM tree (§2.2) — ComponentInstanceDefault exists in core, full ComponentInstance for DOM not yet usedget_component_preview with CPU renderer implementedbuiltin:if, builtin:for, builtin:mapsource_file tracking and user code preservation (Phase 6)From new user requirements (this session):
<select id="library-selector"> in debugger.html+ Library / + Component buttons with modifiable check (§2.1) — hidden when !modifiablemodifiable field on ComponentLibrary (§2.2) — core/src/xml.rs, debug_server.rs<input id="component-filter"> with client-side fuzzy match<> icon, no <tag> suffix) (§2.8) — just display_name<details> element, closed by defaultComponentDataModel (§2.5) — core types + debug serverGetComponentPreview handler overrides defaults from data modelget_component_preview API (CPU render) (§2.7) — endpoint + handler + debugger JS integrationtemplate field on ComponentDef (§3.3)data_models field on ComponentLibrary (§3.1) — core + debug server responseFrom debugger data inspection session:
Json data type moved to azul-core (type definitions + serde methods behind serde-json feature flag)azul-layout::json simplified to re-exports + RefAny serializationGetNodeDataset debug API endpoint — serializes node's dataset RefAny to JSONhas_dataset=true, readonly JSON treejson.to_serde_value() / Json::from_serde_value() for direct serde_json interopComponentOrigin tracks data_model_json: Json for component instances in DOMFrom COMPONENT_TYPE_SYSTEM_DESIGN.md (type system implementation):
ComponentFieldType enum with 20 structured variants (§3.1) — replaces string-based field_typeComponentFieldTypeBox for FFI-safe recursive types (§9.1) — ptr-based, manual Clone/DropComponentCallbackSignature with return_type + args (§3.2) — type_name not needed (args + return_type sufficient per user decision)ComponentCallbackArg with name + arg_type (§3.2)ComponentEnumModel + ComponentEnumVariant (§3.3) — variant missing description fieldComponentDefaultValue enum with 13 typed variants (§3.5) — missing Json(AzString) variantComponentInstanceDefault with library + component + field_overrides (§3.5)ComponentFieldOverride + ComponentFieldValueSource (§3.5) — Literal is AzString (not typed)ComponentFieldValue runtime value enum (§14.3) — missing Some/Vec/Callback/RefAny variantsComponentFieldNamedValue + impl_vec! (§14.3)ComponentDataField.field_type is now ComponentFieldType (§3.4) — structured, not AzStringComponentDataField.required: bool (§3.4)ComponentDataField.default_value: OptionComponentDefaultValue (§3.4)enum_models: ComponentEnumModelVec on ComponentLibrary (§3.6)ComponentCallbackSignature.type_name field for api.json matching (§3.2)ComponentEnumVariant.description field (§3.3)ComponentDefaultValue::Json(AzString) for complex defaults (§3.5)ComponentFieldValueSource::Literal should be typed ComponentFieldValue not AzString (§3.5)ComponentFieldValue missing Some/Vec/Callback/RefAny variants (§14.3)ComponentRenderFn signature: should take &ComponentFieldNamedValueVec not &ComponentDataModel (§12 Phase 4)parse_field_type() / format_field_type() public parser functions (§7)enum_models exposed in debug server JSON responses (§3.6)ExportedLibraryResponse includes data_models + enum_models (§8)ExportedComponentDef includes template (§8)UpdateComponentTree debug API endpoint (§3.5)