Generated Export API Code Map: what generated firmware exposes, how callbacks and runtime APIs fit together, and what developers are allowed to extend.
Trend Chart and Trend Chart Pro are physically proven Native Components. Both
use stable persisted component identity, collision-safe duplicate-instance
names, the shared Live/Standalone generator, and canonical 95_UserEvents
contracts. Display-name changes do not change their generated SDK identity.
Representative semantic APIs (inspect each generated
90_Studio_Export.h for its exact <Component> stem):
void FG_Add_<TrendChart>_Point(float value);
void FG_Clear_<TrendChart>(void);
void FG_Set_<TrendChart>_WarningThreshold(float value);
void FG_Set_<TrendChart>_AlarmThreshold(float value);
void FG_Add_<TrendChartPro>_Point(float value);
void FG_Clear_<TrendChartPro>(void);
void FG_Set_<TrendChartPro>_Units(const char * units);
void FG_Set_<TrendChartPro>_Warning(float value);
void FG_Set_<TrendChartPro>_Alarm(float value);Where enabled, the generated canonical callback family is:
void FG_On_<Chart>_Point_Added(float value);
void FG_On_<Chart>_Warning(void);
void FG_On_<Chart>_Alarm(void);
void FG_On_<Chart>_Recovered(void);Warning, Alarm, and Recovered fire only on state transitions. Duplicate
instances receive isolated callback identities. Callback declarations and
generated calls share one contract, and regeneration preserves matching
developer-owned bodies in 95_UserEvents.c.
The exporter clears unused history to LV_CHART_POINT_NONE, writes valid
samples by explicit point ID, suppresses unwanted LVGL point dots, draws
threshold helpers behind the data trace, and refreshes runtime updates. These
are private generated details, not application integration points.
The older PWM proof-ready entry below is retained as a superseded historical record.
Power Flow Card is a monitoring-only Native Component with a fixed Grid/Solar/Battery/Load topology. ForgeUILvglExport.ts emits ordinary LVGL containers, labels, three-pixel connection objects and static canonical arrow symbols; it emits no canvas, dynamic graph, backend binding, clickable node, or UserEvent.
Seven optional public setters are generated from the persisted component ID: Grid Value/Flow, Solar Value/Flow, Battery Value/Flow, and Load Value. Text setters copy into bounded per-instance buffers; flow setters clamp to 0 None, 1 Into centre/load, or 2 Out from centre/load. Every setter calls its private refresh function immediately and silently. Duplicate cards have independent objects, state buffers and public namespaces, and display-name changes do not rename the API.
Canvas, Browser Preview and Live Studio use one canonical preview. Live and Standalone Export use this same generated LVGL branch. The 240 x 145 card, four-node topology, readable values, and visible static directions rendered correctly on the physical 1024 x 600 ESP32-P4 display. Status: HARDWARE VALIDATED.
Generated 90 owns FG_Set/Get_<PersistedId>_Value(float) and FG_Set/Get_<PersistedId>_Enabled(bool). Silent setters clamp/quantize and update private LVGL state under a programmatic guard. Genuine interaction calls FG_On_<PersistedId>_Value_Changed(float) and FG_On_<PersistedId>_Enabled_Changed(bool) through preservation-merged 95. Live and Standalone use the same generator. Status: READY FOR HARDWARE PROOF.
WHY 01_SPINE & 02_DEVELOPER & 03_FORGEUI_GEN - means Codex spends less time guessing and is much less likely to:
- edit generated files directly
- duplicate an existing subsystem
- put firmware logic into the Studio
- break ownership boundaries
- revive an obsolete architecture
- touch hardware configuration that is already proven
FORGEUI_LVGL9_COMPLETE__44_OF_44_PRACTICAL_WIDGETS_PROVEN__ESP32P4_VALIDATED__DOCUMENTATION_COMPLETE__READY_FOR_NATIVE_FORGEUI_PLATFORM__2026-08-02
The practical LVGL 9.2 program is complete: 44 of 44 practical registered widgets/components are physically proven on ESP32-P4. Window and Menu are PROVEN. Lottie remains intentionally excluded.
Current ForgeUI Platform milestone: FORGEUI_NATIVE_COMPONENT_3__RELAY_PANEL_PROVEN__ESP32P4_VALIDATED__RUNTIME_SDK_USEREVENTS_MASTER_CONTROL_PROVEN__READY_FOR_PWM_CONTROLLER__2026-08-02.
Dashboard Card is ForgeUI Native Component #1 — PROVEN. The shared Live/Standalone generator emits its private LVGL composition, four semantic Runtime setters and optional root click UserEvent with collision-safe multi-instance names. Browser, Live Studio, Standalone Export and ESP32-P4 behavior are verified. Internal labels, status objects and progress bars remain private and expose no application API or event surface.
Sensor Tile is ForgeUI Native Component #2 — PROVEN. It emits semantic Value, Units, Status, Trend, Timestamp and Colour APIs plus an optional root click hook. Its measurement, threshold and private composite objects share the existing Live/Standalone generator. Physical proof confirms Browser Preview, Live/Standalone, Runtime SDK and UserEvents parity on ESP32-P4. Persisted Native Component identity keeps public Runtime symbols stable, while ownership reconciliation preserves the active developer hook across regeneration and quarantines obsolete custom hooks safely.
Relay Panel is ForgeUI Native Component #3 — PROVEN. Stable component identity emits seven zero-based semantic Runtime functions and genuine-user Channel Changed/Master Changed hooks. The generated runtime owns bounded logical state, suppresses events during setters, and keeps all LVGL objects private. Live and Standalone use the same generator.
This document describes the generated SDK surface currently emitted by the
shared live/Standalone generator. 90_Studio_Export.h contains callable
Runtime APIs. 95_UserEvents.h contains genuine-user callback declarations.
Internal fg_* helpers are implementation details and are not public SDK
functions. All per-component public symbols derive from the component name and
use deterministic collision suffixes.
Batch 1 adds native lv_spangroup, lv_animimg and lv_imagebutton output.
Span is presentation-only. Animation Image serializes initial running state but
has no public API until clean pause/resume/frame ownership is established.
Image Button emits FG_Set_<Name>_Enabled(bool) and
FG_On_<Name>_Clicked(void) through the existing collision-safe runtime and
95_UserEvents path.
Previous platform milestone:
FORGEUI_BOARD_PROFILES__EXPORT_TIME_FEATURE_GATING__LAZY_SYSTEM_TOOLS__CONNECTED_WIFI_45KB_FREE__RAM_OVERLAY__READY_FOR_FINAL_OPERATOR_VALIDATION__2026-07-31.
The earlier physical proof remains valid for Interactive Assets; System Runtime; Hosted Connectivity and Storage; the eleven-component Standard group; and the 2026-07-30 Standard input and selection group. Spinbox now joins the physically proven Standard set through live and Standalone ESP32-P4 output.
- Dashboard resolves through the normal-component export boundary. Industrial HMI, Control Panel, Monitoring, SCADA Overview and Mobile / Portrait are roadmap candidates only.
- Smart Region Boxes and AI-filled controls create no template-specific runtime, API, hook or transport field.
- Focused Layout Designer, AI Region Composer and semantic Box exporter coverage passes.
- Standard QR Code generation is implemented as a native LVGL 9.2.2 output component.
- The live generated
90_Studio_Export.c/.hcontains the QR object, serialized initialization andFG_Set_QR_Code_Text(const char * text). - QR contributes no
userEventHooks, asset C source or95_UserEventsdeclaration. - QR-focused preview/export tests, native ESP32-P4 output, successful mobile scan and Live/Standalone parity are proven.
This document defines the permanent boundary between:
- ForgeUI-generated UI and runtime code; and
- developer-owned application code.
It is an architectural code map, not a milestone diary, proposal, or roadmap.
This document remains separate from 02_DEVELOPER_CODE_MAP.md:
02_DEVELOPER_CODE_MAP.md
→ Whole Studio, System Runtime and Interactive Asset subsystem map
03_ForgeUI_Generated_Export_API_Code_Map.md
→ Generated firmware API, file ownership, hook, and public-setter boundary
Use this map to determine:
- where generated firmware code originates;
- which layer owns API naming and metadata;
- which files Studio writes;
- which files become developer-owned after standalone export;
- how UI input reaches developer code;
- how developer code updates UI outputs;
- which responsibilities must not be duplicated.
The generated Standard Runtime provides a predictable exported-project SDK:
Application code → generated UI
public declarations in 90_Studio_Export.h
implementations in 90_Studio_Export.c
Generated UI → developer application
hook declarations in 95_UserEvents.h
preservation-merged implementations in 95_UserEvents.c
Interactive Standard setters are guarded so application-to-UI updates cannot be misreported as genuine UI-to-application events.
Layout Designer is a Studio authoring subsystem. Before export, it resolves into the existing ForgeUI component document.
Layout Designer / AI Fill
↓
Normal ForgeUI components
↓
Final persisted x/y/w/h
↓
generateForgeUILvglCode()
↓
Existing generated LVGL output
Layout Designer does not create a second exporter, separate generated firmware document, new runtime family, new public API family, new 95_UserEvents hooks or new export payload fields. It does not bypass ForgeUIExportValidation. Its output uses the existing Box, Divider, Heading, Text, Chart, Button and other Standard component branches.
The current library contains Dashboard. Its definition produces normal Box
regions and ordinary placeholder/assigned components and retains
dashboard.header, dashboard.status, dashboard.main,
dashboard.controls and dashboard.footer. Other named layout concepts are
roadmap candidates and must not be treated as registered definitions.
Stable region keys and assignment metadata are Studio/project metadata. They generate no C structs, enums, public APIs, hooks, runtime callbacks or additional files. After layout composition, final persisted component x/y/w/h is the generated geometry source of truth.
AI Fill supplies design-time canonical ForgeUI types, supported serialized props, semantic region assignment, content and ordering. It does not directly generate firmware.
The export boundary begins only after AI output is validated, converted to normal components, assigned to regions, Auto Arranged and inserted into the project document. The exporter therefore receives the same model whether components were manually dropped, created by a template, inserted by AI Fill or subsequently moved or resized. Generated firmware cannot know whether a component originated from AI.
The generated export layer supports five Interactive Asset types:
Interactive Input Runtime
├── Interactive Button
└── Interactive Toggle Switch
Three-Position Input Runtime
└── Interactive Three-Position Toggle Switch
Binary Output Runtime
├── Interactive Light
└── Interactive Status Indicator
System Runtime
├── Application Container
├── System Launcher
├── Display / Brightness
├── Wi-Fi Manager
├── Storage Browser
├── Native LVGL Keyboard
└── Future System Pages
Hosted Connectivity Runtime
├── ESP-Hosted
├── Wi-Fi Remote
├── ESP32-C6
└── SDIO Slot 1
Button is momentary input. Toggle Switch is persistent boolean input. Three-Position Toggle is persistent enum input. Light and Status Indicator are developer-controlled outputs.
ForgeUI generates runtime APIs for two independent categories:
- Interactive Asset Runtime retains its existing artwork-backed runtime families.
- Standard LVGL Component Runtime generates retained APIs based on the semantics of ordinary LVGL widgets.
Standard LVGL components are not Interactive Assets. Their objects, state, helpers, callbacks, public APIs, hook metadata and collision-safe names are generated by studio/src/forgeui/ForgeUILvglExport.ts; studio/export-server.js materializes and preservation-merges the generated API files.
The current physically proven Standard group is verified through Canvas, Browser Preview, generated LVGL and ESP32-P4:
- Led
- Bar
- Arc
- Chart
- Table
- Keyboard
- Calendar
- Scale
- Roller
- MsgBox
- ButtonMatrix
Current generated-output parity work outside the physical milestone includes:
- Standard Canvas artwork export
- TabView explicit geometry and selected-state export
- TileView native
2 × 2swipe-page generation - Line serialized endpoint generation
- Text semantic
textPrimary - Heading semantic
textPrimary - Standard Wi-Fi multiline status presentation
TileView within this group is now physically PROVEN; the other items retain their separately documented evidence levels.
The 2026-07-30 generated-runtime group is also physically proven:
- Input
- Textarea
- Checkbox
- Switch
- Radio
- Progress
- CircularProgress
- NumberInput
- Select
- Spinbox
TabView and TileView are PROVEN. Image, Box and IconButton remain runtime-complete but outside these physical validation milestones.
Previously completed runtime components:
- ✓ LED
- ✓ Bar
- ✓ Arc
- ✓ Chart
- ✓ Keyboard
- ✓ Calendar
- ✓ Roller
- ✓ Message Box
- ✓ Button Matrix
- ✓ TabView
- ✓ Tileview
Newly completed runtime components:
- ✓ Input
- ✓ Textarea
- ✓ Switch
- ✓ Checkbox
- ✓ Radio
- ✓ Progress
- ✓ CircularProgress
- ✓ NumberInput
- ✓ Select
- ✓ Image
- ✓ QRCode
- ✓ Box
- ✓ IconButton
Inspected and intentionally API-free:
- ✓ Scale
- ✓ Line
- ✓ Icon
- ✓ Divider
Completed serialized presentation properties:
- ✓ Button Text
- ✓ Text Value
- ✓ Heading Text
- ✓ Clock Presentation
Slider Canvas movement and temporary preview-value interaction remain isolated from project JSON. Its native LVGL export retains the Slider and generates a collision-safe value setter and genuine-user change hook. Spinner is presentation-only and generates neither. List has no setter but generates one collision-safe item-click hook carrying the zero-based row index and text.
Standard LVGL Component Runtime
├── Scalar / visual output
│ ├── Led
│ ├── Bar
│ ├── Arc
│ ├── Progress
│ └── CircularProgress
├── Streaming output
│ └── Chart
├── Text entry
│ ├── Input
│ ├── Textarea
│ └── NumberInput
│ ├── outer container
│ ├── numeric textarea
│ ├── increment button
│ ├── decrement button
│ └── serialized step
├── Boolean / selectable input
│ ├── Switch
│ ├── Checkbox
│ └── Radio
├── Option selection
│ ├── Roller
│ ├── Select
│ ├── Button Matrix
│ ├── TabView
│ └── Tileview
├── Actions
│ └── IconButton
├── Visibility / dialog services
│ ├── Keyboard
│ ├── Message Box
│ └── Box
├── Date selection
│ └── Calendar
├── Runtime image output
│ └── Image
├── Encoded output
│ └── QRCode
├── Serialized presentation
│ ├── Button
│ ├── Text
│ ├── Heading
│ └── Clock
└── API-free presentation
├── Scale
├── Line
├── Icon
└── Divider
The selected theme reaches generated firmware through one semantic pipeline:
Theme Manager
|
v
Semantic Theme Resolver
|
v
Canvas
|
v
Browser Preview
|
v
LVGL Export
|
v
Generated Firmware
|
v
ESP32-P4
Canvas, Browser Preview and generated LVGL consume equivalent semantic roles:
surfacesurfaceSecondarysurfaceBordertextPrimarytextSecondaryaccentaccentTextdisabledTextselectedSurface
studio/src/forgeui/theme/ForgeThemeContext.tsx supplies the active preview palette. studio/src/forgeui/preview/forgeThemeMap.ts resolves semantic roles, deterministic graphite fallback and contrast-dependent values. studio/src/forgeui/preview/forgePreviewRenderer.tsx supplies the resolved palette to Browser Preview renderers. studio/src/forgeui/ForgeUILvglExport.ts owns propagation of those semantic colours into generated LVGL style calls.
Generated theme ownership explicitly covers Switch checked-state selectors, Select closed-control/arrow/popup/selected-row styling, Circular Progress remaining/completed arcs, and Number Input container/field/stepper/divider states. Native LVGL blue/default styling is overridden at the applicable part/state selectors.
Custom palettes propagate through the same path. Decorative colours are no longer hard-coded for the proven Standard group. Semantic colours such as Standard LED status green intentionally remain independent where appropriate.
Hardware theme changes require:
Theme -> Generate -> Build -> Flash
Runtime hot theme switching on the ESP32-P4 was not added.
| Component | Generated public API | Hook in 95_UserEvents.h |
|---|---|---|
| Led | FG_Set_<Name>(bool on) |
FG_On_<Name>_Changed(bool enabled) |
| Bar | FG_Set_<Name>(int32_t value) |
FG_On_<Name>_Changed(int32_t value) |
| Arc | FG_Set_<Name>(int32_t value) |
FG_On_<Name>_Changed(int32_t value) |
| Trend Chart | FG_Add_<Name>_Point(float value), FG_Clear_<Name>(void), warning/alarm threshold setters |
FG_On_<Name>_Point_Added(float value) plus transition-only Warning, Alarm and Recovered hooks when enabled |
| Trend Chart Pro | FG_Add_<PersistedId>_Point(float value), Clear, Units, Warning and Alarm setters |
Persisted-ID Point Added plus transition-only Warning, Alarm and Recovered hooks when enabled |
| Keyboard | FG_Show_<Name>(void), FG_Hide_<Name>(void) |
FG_On_<Name>_Shown(void), FG_On_<Name>_Hidden(void) |
| Calendar | FG_Set_<Name>_Date(uint16_t year, uint8_t month, uint8_t day) |
FG_On_<Name>_Date_Changed(uint16_t year, uint8_t month, uint8_t day) |
| Roller | FG_Set_<Name>_Selected(uint32_t index) |
FG_On_<Name>_Changed(uint32_t index, const char * text) |
| Message Box | FG_Show_<Name>(void), FG_Close_<Name>(void) |
Shown, Closed and Button Pressed hooks |
| Button Matrix | FG_Set_<Name>_Selected(uint32_t button_index) |
FG_On_<Name>_Button_Selected(uint32_t index, const char * text) |
| TabView | FG_Set_<Name>_Selected(uint32_t tab_index) |
FG_On_<Name>_Changed(uint32_t tab_index) |
| Tileview | FG_Set_<Name>_Selected(uint32_t column, uint32_t row) |
FG_On_<Name>_Changed(uint32_t column, uint32_t row) |
| Input | FG_Set_<Name>_Text(const char * text) |
FG_On_<Name>_Changed(const char * text) |
| Textarea | FG_Set_<Name>_Text(const char * text) |
FG_On_<Name>_Changed(const char * text) |
| Switch | FG_Set_<Name>_Checked(bool checked) |
FG_On_<Name>_Changed(bool checked) |
| Checkbox | FG_Set_<Name>_Checked(bool checked) |
FG_On_<Name>_Changed(bool checked) |
| Radio | FG_Set_<Name>_Selected(bool selected) |
FG_On_<Name>_Changed(bool selected) |
| Progress | FG_Set_<Name>_Value(int32_t value) |
None |
| CircularProgress | FG_Set_<Name>_Value(int32_t value) |
None |
| NumberInput | FG_Set_<Name>_Value(int32_t value) |
FG_On_<Name>_Changed(int32_t value) |
| Select | FG_Set_<Name>_Selected_Index(uint32_t index) |
FG_On_<Name>_Changed(uint32_t index, const char * text) |
| Image | FG_Set_<Name>_Source(const void * src) |
None |
| QRCode | FG_Set_<Name>_Text(const char * text) |
None |
| Box | FG_Set_<Name>_Visible(bool visible) |
None |
| IconButton | FG_Set_<Name>_Enabled(bool enabled) |
FG_On_<Name>_Clicked(void) |
| Canvas | None; serialized container/artwork presentation only | None |
| Button | None; owns serialized Button Text only | None |
| Text | None; owns serialized Text Value only | None |
| Heading | None; owns serialized Heading Text only | None |
| Standard Wi-Fi presentation | None; per-instance projection of existing backend snapshot/status | None |
| Clock | None; owns presentation configuration only | None |
| Scale | None | None |
| Line | None | None |
| Icon | Optional FG_Set_<Name>_Visible, Opacity, Color in 96_FiRuntime.h |
Optional FG_On_<Name>_Clicked(void) |
| Divider | None; presentation-only visual separator | None |
| Slider | FG_Set_<Name>_Value(int32_t value) |
FG_On_<Name>_Changed(int32_t value) |
| Spinbox | FG_Set_<Name>_Value(int32_t value) |
FG_On_<Name>_Changed(int32_t value) |
| Spinner | None; presentation-only native animation | None |
| List | None | FG_On_<Name>_Item_Clicked(uint32_t index, const char * text) |
In this table, CircularProgress is a retained output-only arc. NumberInput is the composed generated numeric textarea plus hardware increment/decrement buttons using serialized step; its existing API and hook names are unchanged.
Interactive semantic state retains the native or composed LVGL object and runtime state, exposes a public control API, guards programmatic updates where LVGL may emit events, adapts genuine LVGL user events, and contributes generated hook metadata. Creation and setter calls are silent. Input, Textarea, Switch, Checkbox, Radio, NumberInput, Select and IconButton follow this boundary.
Output semantic state retains its LVGL object and runtime state and exposes a setter without a developer hook. Progress, CircularProgress, Image, QRCode and Box follow this boundary.
A Layout Designer Region Box remains a Standard Box. Its structural purpose adds no runtime semantics. When it qualifies as a retained non-root Box, its only public contract is FG_Set_<Region_Box_Name>_Visible(bool visible). Region assignment, padding, gaps, arrangement mode, Auto Arrange, layout lock, region role and region key generate no APIs.
Serialized presentation normally generates LVGL presentation only and no runtime
API or hook. Components without semantic runtime state are intentionally
API-free. Divider, Scale and Line are examples. Standard Icon is the explicit
exception: its optional generated presentation state lives in 96_FiRuntime,
and its optional click hook lives in 95.
Chart retains native lv_chart, its primary-Y series, runtime streaming and clear behavior. Shared responsive geometry reserves the Y-label and bottom gutters while keeping point positions aligned with native LVGL content geometry.
Generated Chart output adds non-clickable sibling lv_label objects for:
- Y-axis numeric labels derived from the normalized range and horizontal divisions;
- X-axis point-index labels derived from
0..pointCount - 1.
Labels use selected-theme textSecondary. Responsive density preserves deterministic endpoints when every X index cannot fit. No category names, dates, timestamps or other X-axis semantics are invented. Existing Chart APIs, hooks and silent startup remain unchanged.
Application code calls public functions declared in 90_Studio_Export.h:
FG_Set_Search_Input_Text("abc");
FG_Set_Enable_Switch_Checked(true);
FG_Set_Download_Progress_Value(75);
FG_Set_Mode_Select_Selected_Index(2);
FG_Set_Logo_Image_Source(&fg_icon_about_48px);
FG_Set_Status_Box_Visible(false);
FG_Set_Settings_Icon_Button_Enabled(false);Generated LVGL calls developer hooks declared in 95_UserEvents.h and implemented in 95_UserEvents.c:
FG_On_Search_Input_Changed(const char * text);
FG_On_Enable_Switch_Changed(bool checked);
FG_On_Enable_Logging_Checkbox_Changed(bool checked);
FG_On_Automatic_Mode_Radio_Changed(bool selected);
FG_On_Target_Temperature_Number_Input_Changed(int32_t value);
FG_On_Mode_Select_Changed(uint32_t index, const char * text);
FG_On_Settings_Icon_Button_Clicked(void);An interactive setter must not call its matching genuine-user hook. Per-instance guards prevent application-to-UI updates from being reported as UI-to-application interaction.
The standard Icon exists to provide Studio's built-in icon picker. During export, its selected artwork resolves through the existing image-symbol pipeline and is emitted as a normal LVGL lv_image. It has no retained runtime ownership, public setter or developer callback and does not define a separate runtime component family.
Runtime image changes are handled exclusively by the Standard Image runtime:
void FG_Set_<Image_Name>_Source(const void * src);Developers must use an Image component when application code needs to swap image sources. A duplicate FG_Set_<Icon_Name>_Source(...) API is intentionally not generated.
Divider owns no runtime state and generates neither a declaration in 90_Studio_Export.h nor a hook in 95_UserEvents.h. It remains serialized presentation used only for visual separation.
If a Divider must appear or disappear dynamically, application code should control the containing layout through:
void FG_Set_<Parent_Box_Name>_Visible(bool visible);A Divider-specific visibility API is intentionally not generated.
Standard Canvas is serialized presentation and generates no public runtime API or developer hook.
studio/src/forgeui/ForgeUILvglExport.ts owns:
- bounded parent
lv_obj; - serialized
x/y/w/h; - clipping to parent bounds;
- configured uploaded-asset resolution;
- generated
LV_IMAGE_DECLARE(...); - required asset-source collection;
- child
lv_image; - saved image scale;
- centred alignment;
- transparency preservation.
The generated structure is conceptually:
lv_obj_t * canvas = lv_obj_create(parent);
lv_obj_set_pos(canvas, x, y);
lv_obj_set_size(canvas, width, height);
lv_obj_add_flag(canvas, LV_OBJ_FLAG_CLIP_CHILDREN);
lv_obj_t * image = lv_image_create(canvas);
lv_image_set_src(image, &generated_symbol);
lv_image_set_scale(image, image_scale);
lv_obj_center(image);The exporter and uploaded-asset registry remain authoritative for generated symbols and required asset sources. 90_Studio_Export.c is generated, replaceable output and must not be manually treated as the source of the fix.
The standard Canvas Keyboard is eager and retains its keyboard without creating an owned textarea. It remains unattached by default and is not the lazy reusable System keyboard.
Button Matrix retains its object, selected index and row-break-aware button count. Programmatic selection and LV_EVENT_VALUE_CHANGED share one transition helper. Disabled buttons are protected, one-check and checked state are preserved as distinct from selection, duplicate transitions are suppressed, and names are collision-safe.
The first Button Matrix inspection used a stale running Studio exporter bundle. Restarting Studio regenerated the correct retained runtime. This was stale running generator code, not a split between live and standalone generator architecture.
TabView retains its native LVGL object, selected index and tab count. Export owns explicit internal geometry matching the shared preview: a 34 px tab bar, equal/remainder tab widths, square outer bounds and explicit content sizing. Serialized initial selection is applied silently. Touch and programmatic changes use one transition helper. Programmatic updates call lv_tabview_set_active(..., LV_ANIM_OFF) and LV_EVENT_VALUE_CHANGED handling reads lv_tabview_get_tab_active(). Generated identifiers are collision-safe, semantic styles are emitted explicitly, and the existing API/hook boundary is unchanged.
Tileview is generated as native LVGL 9.2.2 lv_tileview with four full-size
pages in the fixed 2 × 2 ForgeUI coordinate contract. Runtime retains the
selected row and column. FG_Set_<Name>_Selected() changes the native active
tile silently; genuine swipe completion invokes the existing changed hook.
Startup remains silent and generated identifiers are collision-safe.
Native creation uses lv_tileview_create() and lv_tileview_add_tile().
Programmatic selection uses lv_tileview_set_tile(), while genuine-user
changes resolve the active page through lv_tileview_get_tile_active().
Physical ESP32-P4 proof confirms silent startup, horizontal and vertical
navigation, correct coordinates including (1,0), (0,0), (1,1) and
(0,1), one callback per effective tile change, and stable repeated
navigation. Wi-Fi remained connected, SD remained ready, and TabView, Spinbox
and List continued working. TileView is PROVEN.
Generated LVGL retains native single-line lv_textarea and current text. Placeholder remains serialized. Silent FG_Set_<Name>_Text(const char * text) treats NULL as empty, suppresses unchanged values and uses a per-instance guard. Initial text is assigned before callback registration; only genuine LV_EVENT_VALUE_CHANGED invokes the edit hook. Semantic theme and border parity are physically proven across Canvas, Browser Preview, generated LVGL and ESP32-P4.
Touch focuses the textarea only. Generated runtime does not automatically attach a keyboard. Standard Canvas Keyboard and the private reusable System Runtime keyboard remain separate generated architectures.
Textarea retains native multiline lv_textarea and current runtime text. Placeholder and multiline configuration remain separate serialized properties. It uses the same silent guarded setter and genuine-user hook model as Input. Theme/border parity and Canvas/Browser/LVGL/P4 proof are complete. Touch focuses the textarea without automatic keyboard attachment.
Switch retains native lv_switch and checked state. Generated styling explicitly owns LV_PART_MAIN, LV_PART_INDICATOR, LV_PART_INDICATOR | LV_STATE_CHECKED and LV_PART_KNOB. The checked selector intentionally overrides LVGL default blue with the semantic accent. The checked setter applies LV_STATE_CHECKED under a programmatic guard; creation/setter calls remain silent and only user changes invoke the hook. P4 proof is complete.
Checkbox retains native lv_checkbox. Default fallback wording is removed and legacy fallback wording normalizes away; explicit custom labels remain serialized and supported. Checked-state runtime, setter and hook ownership are otherwise unchanged. Semantic theme parity and P4 proof are complete.
LVGL has no dedicated Radio widget in this generated path. Radio retains an lv_checkbox styled with a circular indicator and maps selected state to LV_STATE_CHECKED. The default "Radio" label is removed: generated runtime emits an empty checkbox label unless an explicit custom label exists. The default is indicator-only, custom labels remain supported, and physical parity is proven. Selected setter/hook and independent-selection ownership remain unchanged.
Progress retains output-only native lv_bar, current value and configured range. Its setter-only Value API clamps and suppresses repeated effective values. It has no interaction and contributes no hook. P4 proof is complete.
CircularProgress retains an output-only native lv_arc object and value. FG_Set_<Name>_Value(int32_t value) clamps and suppresses repeated values; no hook is generated. Generated geometry uses a 360-degree background arc rotated 270 degrees, surfaceSecondary remaining arc, accent indicator, explicit widths/opacities, no visible knob and no LV_OBJ_FLAG_CLICKABLE. It cannot be dragged and is physically proven.
Generated NumberInput is composed from an outer border-owning container, retained numeric textarea child, increment button and decrement button. The generated outer frame owns the full bounds; a vertical divider separates the field/stepper area and a horizontal divider separates the two buttons.
Runtime retains value, range and serialized/shared step constant. Native increment/decrement callbacks use int64_t intermediate arithmetic, clamp to minimum/maximum and invoke the existing hook after genuine button changes. The guarded Value setter remains silent. Hardware steppers and full P4 proof are complete.
Select retains native lv_dropdown, selected index and serialized option count. Generated ownership includes the themed closed control and popup list returned by lv_dropdown_get_list(). Semantic border and arrow styling explicitly cover LV_PART_INDICATOR; popup/selected rows explicitly cover LV_PART_SELECTED and checked/pressed combinations, preventing native blue/default leakage. The setter remains clamped/guarded and genuine callbacks retain their generated text buffer. Physical proof is complete.
LVGL-ready Image instances retain native lv_image and current source pointer. The output-only API accepts raw const void *, safely ignores NULL and unchanged pointers, and generates no hook. Pending/unconverted assets keep their placeholder and safely no-op. No asset-ID, path or URL runtime lookup exists.
QRCode retains a native LVGL 9.2.2 lv_qrcode object. Serialized generation calls the public API provided by the installed managed component: lv_qrcode_create, lv_qrcode_set_size, lv_qrcode_set_dark_color, lv_qrcode_set_light_color and lv_qrcode_update with an explicit byte length. That header does not expose lv_qrcode_set_data or lv_qrcode_set_quiet_zone.
The output-only API is:
void FG_Set_<Name>_Text(const char * text);The setter normalizes NULL to an empty string and updates the retained object through lv_qrcode_update(obj, data, strlen(data)). QR Code produces no genuine-user transition, userEventHooks entry or 95_UserEvents declaration/stub. Its modules are native LVGL output, not an uploaded image, so QR contributes no assetSources entry. firmware/ForgeUI-One/sdkconfig.defaults owns the required CONFIG_LV_USE_QRCODE=y.
Non-root Box instances retain lv_obj_t * and visibility only. The setter adds or clears LV_OBJ_FLAG_HIDDEN, suppresses repeated state and generates no hook. Children remain attached to the retained object. Root screen Boxes are excluded.
A Layout Designer smart-region Box remains a Standard Box and preserves the existing void FG_Set_<Name>_Visible(bool visible); contract. It may carry additive Studio metadata such as layoutRegionKey, role, label, padding, gap, arrangement, semantic surface role, semantic border role, radius, border width and opacity.
Arrangement metadata is consumed during authoring, and Auto Arrange resolves assigned child geometry before export. Generated LVGL receives final Box and child geometry; firmware does not perform Auto Arrange or search for layoutRegionId. Region metadata creates no additional API, root exclusion remains unchanged, and children continue through normal traversal. Semantic Region Box styling uses existing theme roles rather than hard-coded Dashboard colours.
The structural role does not create APIs for region assignment, padding, gap, arrangement mode, Auto Arrange, layout lock, region role or region key.
IconButton retains native button and enabled state. The setter applies LV_STATE_DISABLED silently. LV_EVENT_CLICKED invokes the void hook only while enabled. Icon selection remains serialized; no runtime icon-source setter exists.
Batch D software validation covers serialized disabled initialization, Inspector editing, canonical icon presentation, semantic normal/pressed/disabled styling, guarded setters, genuine click routing and deterministic collision-safe multiple instances. QR Code likewise allocates collision-safe retained objects and text setters. Neither boundary introduces a duplicate runtime family.
Canvas track/thumb interaction changes temporary preview value, surrounding component interaction supports movement, Browser Preview remains interactive, and project JSON remains unchanged. Native export retains the Slider. FG_Set_<Name>_Value(int32_t value) normalizes the serialized range, clamps signed inputs, ignores repeats and guards the LVGL update so it cannot notify application code. The LVGL callback updates retained state and invokes FG_On_<Name>_Changed(int32_t value) only for a changed user value. Initial value assignment precedes callback registration, so startup is silent.
Spinner exports native lv_spinner_create(parent) plus lv_spinner_set_anim_params(widget, duration_ms, arc_length_degrees). Geometry, main/indicator arc widths, semantic or explicit colours and opacity are applied directly to the native object. It creates no public declaration, hook, runtime helper source or CMake entry. The branch is naturally export-time gated because it is emitted only while traversing a serialized Spinner.
Spinbox exports native lv_spinbox_create with normalized range, integer
backing value, power-of-ten step, digit/decimal format, rollover, cursor and
semantic styling. Native helper buttons call lv_spinbox_increment and
lv_spinbox_decrement for touch operation and use validated coordinates that
keep both arrows on-screen. Its collision-safe
FG_Set_<Name>_Value(int32_t) setter clamps and remains silent.
FG_On_<Name>_Changed(int32_t) receives the integer backing value only after a
genuine effective native key or helper-button change. It generates no getter,
increment/decrement command, widget-specific source or CMake entry.
The branch is usage-gated by serialized Spinbox presence and participates in
project export-time feature gating. Live Studio firmware and
/export-idf-project consume the same generator; export preflight rejects stale
or malformed output before it can be treated as the build artifact.
90_Studio_Export.c/.h owns replaceable construction, state, setter and event
adapter code. 95_UserEvents.c/.h owns the preservation-merged developer hook.
Duplicate component names receive deterministic suffixes before both APIs are
emitted.
Physical validation on ESP32-P4 with ESP-IDF 5.5.4 and LVGL 9.2.2 confirms
increment, decrement, signed backing values, decimal formatting, rollover,
clamp, multiple instances and live/Standalone parity. Exactly one
FG_On_<Name>_Changed() is emitted per effective user action; initial creation,
repeated/no-op transitions and programmatic setters remain silent.
Native LVGL Spinbox is a selected-digit editor. It is not a free-form numeric text-entry control; use NumberInput for that interaction model.
The current registry records 49 entries: 44 practical Standard LVGL
widgets/components and five Interactive Assets. All 44 practical Standard
LVGL entries are physically proven on ESP32-P4. Window is proven through native
lv_win_create; Menu is proven through native lv_menu_create, independent
instances and repeated forward/Back navigation.
List exports native lv_list_create, optional lv_list_add_text and one
lv_list_add_button per normalized item. It has no public setter. Each button
registers one shared LV_EVENT_CLICKED callback with immutable per-row
{index, text, hook} data. The callback invokes
FG_On_<Name>_Item_Clicked(uint32_t index, const char * text) only after a
genuine click. Construction is silent. Duplicate component names receive
_2, _3 and later suffixes before _Item_Clicked.
Physical ESP32-P4 proof on LVGL 9.2.2 and ESP-IDF 5.5.4 observed one callback for each single touch of Overview, Settings and Diagnostics, carrying indices 0, 1 and 2 with the matching generated text. Repeated taps remained stable; TabView and Spinbox continued operating; Wi-Fi remained connected; SD remained ready; runtime remained stable; and connected-stage internal RAM was approximately 39 KB free. Live and standalone exports both compile from identical generated List C/H. LIST — PROVEN ON ESP32-P4.
Scale is visual only. It owns no runtime value, retained runtime state, setter or hook, so no generator change is appropriate.
Line generates native LVGL lv_line from serialized startX, startY, endX and endY values:
static lv_point_precise_t <name>_pts[] = {
{startX, startY},
{endX, endY}
};
lv_obj_t * <name> = lv_line_create(parent);
lv_line_set_points(<name>, <name>_pts, 2);
lv_obj_set_pos(<name>, x, y);Legacy components without endpoint properties resolve to start (0,0) and end (width,height). Line colour uses semantic surfaceBorder. Line owns no runtime semantics, public API or hook; omission of an API remains intentional.
- Button Text: generated firmware calls
lv_label_set_text(button_label, ...). There is no runtime transition, API or hook. - Text Value: generated firmware calls
lv_label_set_text(label, ...)and explicitly applies semantictextPrimarythroughlv_obj_set_style_text_color(...). Legacy literal white does not override the semantic default. There is no runtime transition, API or hook. - Heading Text: generated firmware calls
lv_label_set_text(heading, ...)and explicitly applies semantictextPrimarythroughlv_obj_set_style_text_color(...). Legacy literal white does not override the semantic default. There is no runtime transition, API or hook. - Clock Presentation: generated firmware owns
hourFormat,showSeconds,blinkSeparator, per-instance presentation state, RTC lookup and formatter generation. It does not own or serialize displayed time.
Standard Wi-Fi presentation is a per-instance projection of existing
fg_wifi_get_snapshot() and fg_wifi_status_text() data. It generates no
public setter or developer hook. Canvas and Browser share the serialized
display-mode, preview-state and optional-signal model; generated output ignores
preview state and maps backend Disabled, Starting, Connecting, Connected,
future Internet Available and Failed states. Collision-safe labels allow
multiple instances. Runtime state mapping and Wi-Fi management remain in the
existing backend.
Each Clock owns presentation configuration, a generated formatter, RTC lookup, separator blinking and per-instance label/timer/separator state. Displayed time is never serialized and Clock Time is never editable. RTC/system time remains authoritative. Generated firmware exposes neither FG_Set_Clock_Time nor FG_On_Clock_Changed.
Interactive Status Indicator remains a setter-only Binary Output Runtime asset with no event hook. It is distinct from the standard Led widget.
System Runtime is generated platform code. Its current implementation uses persistent Application, System Launcher, Display / Brightness and Wi-Fi Manager containers, one lazily created persistent Storage Browser container and one lazily created reusable native LVGL keyboard. Hosted Connectivity Runtime is platform infrastructure, not an Interactive Asset runtime.
Application
→ System
→ Brightness
→ System
→ Application
Navigation currently switches container visibility. It does not recreate the application or its Interactive Assets. Interactive Assets remain alive while System pages are active.
For all five placed Interactive Asset component branches, persisted Canvas x/y/w/h is the generated geometry source of truth. State-image controls use that final geometry for the parent object or transparent container and centre a non-clickable child image inside it. Every required state shares one safe contain-fit scale; state switching changes only the source, not geometry. Dimension resolution uses uploaded-asset registry metadata, PNG IHDR dimensions and generated LVGL descriptors, with scale 256 only as the final safe fallback and valid scales above or below 256. Linked fitted assets are consumed automatically when their IDs replace the original state references. Browser Preview wrappers preserve persisted component geometry, and type-specific preview renderers perform contain-fit rendering inside those bounds. Browser Preview therefore follows the same component-authoritative geometry model as Canvas and generated LVGL.
Interactive Asset API direction remains unchanged:
Button -> FG_On_*_Clicked(void)
Toggle -> FG_On_*_Toggled(bool enabled)
Three-Position -> FG_On_*_Changed(fg_three_way_state_t state)
Light/Status -> FG_Set_*(bool enabled)
ForgeUI Canvas
│
▼
generateForgeUILvglCode()
│
▼
Generated LVGL Source
Generated Button, Toggle and Three-Position hook metadata
Generated Binary Output public API metadata
Generated Asset Source List
│
▼
Client Export Preflight
│
▼
POST /export
│
▼
Server Export Validation
│
▼
Validated Asset Sources
│
▼
Export Server
│
▼
Generated Firmware Files
│
▼
ESP-IDF Build
│
▼
ESP32-P4
The generated output-control path is:
Developer application logic
↓
Generated FG_Set_* public APIs
↓
Shared Binary Output Runtime
↓
Generated LVGL Runtime
↓
ESP32-P4 Display
Interactive Light and Interactive Status Indicator both use this path. The output runtime is no longer a Light-only implementation.
The generated input-control path is:
UI interaction
↓
Generated LVGL runtime
↓
Generated FG_On_* hook
↓
95_UserEvents.c
↓
Developer application logic
The generated System Runtime path is:
Application
↓
Gear Launcher
↓
System Runtime
↓
Display / Brightness
↓
FG_Set_Display_Brightness()
↓
bsp_display_brightness_set()
↓
ESP32-P4 Backlight
The generated Wi-Fi path within the same System Runtime is:
Wi-Fi Manager presentation and UI intent
↓
structured snapshot/network projection
↓
30_WIFI backend
↓
ESP-IDF
↓
ESP-Hosted
↓
ESP32-C6
System Runtime is generated. It does not generate developer callbacks and is not an Interactive Asset. FG_Set_Display_Brightness() is an internal generated hardware bridge, not a public User Events API.
Current generated System pages are:
- Application
- System Launcher
- Display / Brightness
- Wi-Fi Manager
- Storage Browser
- Native LVGL Keyboard
The generated Wi-Fi Manager owns LVGL presentation, a structured selectable network list, Connected and Saved badges, password and forget dialogs, a password textarea, Show / Hide, native keyboard attachment, Connect, Disconnect, Reconnect, Forget, connected details, status, RSSI, gateway, security, station MAC, AP BSSID and periodic backend snapshot projection. Scan and Refresh both invoke the same fg_wifi_scan_start() backend path and initiate scan intent only. Generated LVGL never performs Hosted scanning itself; it projects completed backend models, rebuilds SSID row presentation from completed network data, and owns row visibility and layout only. It does not own esp_wifi_init(), scanning implementation, credentials, reconnect policy, DHCP, Hosted transport or ESP-IDF APIs; those remain 30_WIFI backend responsibilities.
The generated Storage Browser owns demand-created UI, timer, worker, queue, mutex, reusable row pool, paging and internal callbacks. Back requests an asynchronous worker shutdown; generated code deletes resources only after acknowledgement. 40_SD.c separately owns filesystem/backend state.
The generated-to-platform boundary is:
Generated LVGL Wi-Fi Manager
↓
internal generated callbacks
↓
30_WIFI backend
↓
ESP-IDF Wi-Fi
↓
ESP-Hosted
↓
ESP32-C6
Generated code renders state. The non-generated firmware backend owns transport and physical Wi-Fi truth. Hosted scanning executes inside that backend through a blocking Hosted scan. AP count and AP records are retrieved immediately afterward in the same backend task, copied into owned runtime storage, deduplicated by SSID with strongest-record retention, sorted by Connected state and RSSI, and atomically installed as one completed model before LVGL projection. Hosted Connectivity Runtime is generated platform infrastructure and must not enter any Interactive Asset runtime family.
The generated-to-platform boundary is:
Generated Storage Runtime
↓
40_SD backend
↓
SDMMC Slot 0
↓
FAT filesystem
Generated code owns:
- page navigation
- projection
- row rendering
- selection
- paging
- callbacks
The backend owns:
- filesystem
- directory enumeration
- empty-folder detection
- deletion
- Read / Write Test
- SD state
Generated LVGL owns presentation and user intent only. Filesystem truth and operations remain in 40_SD.c/.h.
The reusable asset-generation path feeding those runtime families is:
ForgeUI Canvas
|
v
Open Creator
|
v
Designer
|
v
State Sheet generation
|
v
Linked crop workspace
|
v
Atomic uploaded-asset registration
|
v
Export
|
v
Generated runtime
|
v
ESP32-P4
Direct Creator entry points now cover all five Interactive Assets: Button, Toggle Switch, Three-Position Toggle Switch, Light and Status Indicator. Their private navigation targets are:
interactive-button-designer
interactive-toggle-switch-designer
interactive-three-position-toggle-designer
interactive-light-designer
interactive-status-indicator-designer
These entry points select the current Canvas component and its linked Interactive Asset. The Creator and State Sheet pipeline prepares or edits the Interactive Asset and uploaded state images consumed by export; it does not replace or redefine the generated runtime-family contracts. In particular, Status Indicator Creator navigation does not change the generated Binary Output API direction or FG_Set_* contract.
Fit Bounds to Visible Artwork is an explicit design-time asset operation for Button Normal/Pressed, Toggle OFF/ON, Light OFF/ON, Status Indicator OFF/ON and Three-Position LEFT/CENTER/RIGHT. It measures state artwork before export, calculates one stable union across the required states, creates same-size linked cropped uploaded assets, preserves original uploads, updates the existing Interactive Asset state IDs and remains idempotent. Export consumes whichever uploaded asset IDs the Interactive Asset currently references. Export does not perform alpha cropping, and fitting adds no firmware API or transport metadata.
Export validation is a permanent two-boundary responsibility. It validates the exporter result before materialization without moving runtime generation out of the exporter.
Owns:
- Canvas validation
- Interactive Asset validation
- dimension validation
- duplicate component-ID detection
- uploaded-asset and LVGL-readiness validation
- generated Button, Toggle and Three-Position hook presence and naming validation
- generated Binary Output setter API validation
- Normal/Pressed, Toggle OFF/ON, Three-Position LEFT/CENTER/RIGHT and Binary Output OFF/ON uploaded-state validation
- generated asset-source validation
- structured, ownership-grouped diagnostics
It validates. It does not generate LVGL, firmware, hooks, setters, CMake, or runtime behavior.
The client currently validates expected hook names against userEventHooks, setter declarations against publicApiDeclarations, kind/state references, image readiness and duplicate generated identifiers. Generated enum and runtime-structure text is covered by exporter regression tests; it is not a separate client diagnostic category, so this map does not attribute unimplemented declaration parsing to the preflight.
Client validation runs after candidate source and metadata generation and before the request is sent to the export server. A failure cancels export before server submission.
In addition to materialization, the server owns:
- payload validation
- generated C-source validation
- physical generated-source existence checks
- relative path validation
- expected-symbol and generated-code reference checks
- construction of the validated asset-source list
- validation before filesystem mutation
The server does not trust client validation. No generated firmware files are written before server validation succeeds.
ForgeUI controls cross the generated/developer boundary in one of two directions, with three distinct input signatures.
Input controls originate in the UI runtime and notify application code through generated developer hooks.
Input contracts:
/* Momentary input: Interactive Button */
void FG_On_<Name>_Clicked(void);
/* Persistent binary input: Interactive Toggle Switch */
void FG_On_<Name>_Toggled(bool enabled);
/* Persistent three-state input: Interactive Three-Position Toggle */
void FG_On_<Name>_Changed(fg_three_way_state_t state);Generated Three-Position state type:
typedef enum
{
FG_THREE_WAY_LEFT = -1,
FG_THREE_WAY_CENTER = 0,
FG_THREE_WAY_RIGHT = 1
} fg_three_way_state_t;Input flow:
Interactive Button
↓
LVGL CLICKED event
↓
Generated runtime callback
↓
FG_On_Button_Clicked()
↓
95_UserEvents.c
↓
Developer application logic
Output controls expose public functions implemented by generated UI code. Application code calls those functions to change LVGL objects.
Proven example:
Developer application logic
↓
FG_Set_Status_Light(true)
↓
Shared Binary Output Runtime
↓
LVGL image source changes
↓
Physical Light displays ON state
FG_Set_Status_Light(true);
FG_Set_WiFi_Status(true);Both public APIs are implemented in generated UI code and delegate to the same generated Binary Output Runtime.
Inputs produce generated developer hooks. Outputs expose generated public UI functions.
Input hooks may be void click hooks, bool state hooks or strongly typed enum state hooks. Outputs continue to expose void FG_Set_<Name>(bool enabled);.
Do not implement output controls as fake click hooks. Do not put input-event application logic inside generated UI code.
Standard selection and presentation direction:
TabView touch
↓
generated retained runtime
↓
FG_On_Tab_View_Changed()
Tileview touch
↓
generated retained runtime
↓
FG_On_Tileview_Changed()
Button / Text / Heading Inspector
↓
serialized presentation property
↓
generated LVGL text
↓
no runtime transition
RTC
↓
generated Clock formatter
↓
generated LVGL label
↓
no developer callback or hook
The generated System Runtime owns:
FG_Set_Display_Brightness()- System page switching
- container visibility state
- gear launcher behavior
- Brightness slider behavior
- current-session brightness value
- System Runtime page state
- Wi-Fi Manager LVGL presentation, structured network rows and badges
- password and forget dialogs, native keyboard and connected details
- Scan and Refresh callbacks that invoke the same backend scan-intent path
- selection, Connect, Disconnect, Reconnect and Forget callbacks
- periodic structured backend snapshot/network projection
- completed-model row visibility and list-layout projection
- projection pause while password entry is active
- internal Wi-Fi System navigation
- lazy Storage page/worker creation and acknowledged teardown
- reusable Storage row pool, paging and row rendering
- Storage status projection
- Storage Refresh and Read / Write Test callbacks
- Select Item mode
- Delete Empty Folder workflow and callbacks
These responsibilities remain internal to generated 90_Studio_Export.c. They are not added to userEventHooks, do not create declarations or stubs in 95_UserEvents.*, and are not part of the public developer callback API.
The generated System Runtime does not own Hosted scan execution, AP retrieval, credentials, reconnect policy, transport, DHCP or ESP-IDF calls. Those responsibilities belong to 30_WIFI.c. Generated LVGL projects completed backend snapshots and network models only.
No Wi-Fi User Event hooks are generated. No Wi-Fi public setter APIs are generated. The Wi-Fi System Runtime remains internally generated.
The generated page owns presentation, password dialog, reusable keyboard, network rows, row visibility and layout, connected details, completed-model backend projection and UI intent. Scan and Refresh both call fg_wifi_scan_start(). It consumes completed fg_wifi_snapshot_t and fg_wifi_network_t models from 30_WIFI; it never becomes the source of physical Wi-Fi truth or performs Hosted scanning. Scanning implementation, AP retrieval, persisted credentials, reconnect policy, transport, DHCP and ESP-IDF remain backend-owned. Physical firmware currently supports one persisted ESP-IDF STA configuration, not a multi-network credential database. 95_UserEvents.c remains unrelated to the Wi-Fi System UI.
Generated firmware assumes the proven Hosted configuration supplied by firmware/ForgeUI-One/sdkconfig.defaults:
- ESP-Hosted enabled
- SDIO Host Interface
- SDMMC Slot 1
- 4-bit
- 40000 kHz
- GPIO18
- GPIO19
- GPIO14
- GPIO15
- GPIO16
- GPIO17
- Reset GPIO54
- active-high reset
- reset delay 1500 ms
- restart on failure
- Wi-Fi Remote Hosted backend
- ESP32-C6 target
Generated firmware never hardcodes these values. They come from ESP-IDF configuration.
studio/src/forgeui/ForgeUILvglExport.ts
generateForgeUILvglCode()This is the only LVGL UI exporter.
Runtime generation assumes that its candidate result will pass the dedicated client and server validation boundaries before materialization. Validation policy is not mixed into runtime generation.
- LVGL object generation
- widget creation
- image declarations
- uploaded asset-source collection
- shared runtime structures
- shared runtime callbacks
- shared Binary Output Runtime
- generated Binary Output runtime records
- Interactive Button export
- Interactive Light export
- Interactive Status Indicator export
- Interactive Toggle Switch export
- Interactive Three-Position Toggle export
- shared Toggle Input Runtime and per-instance records
- shared Three-Position Input Runtime and per-instance records
- reusable native System keyboard generation, lazy creation and
lv_keyboard_set_textarea()attachment - Keyboard top-layer ownership, top-left alignment, explicit styles, callbacks and final physical geometry
- export of uploaded assets produced by Toggle and Three-Position State Sheet workflows
- current direct Creator integration through the component and uploaded-asset model
- final persisted component geometry for all five Interactive Asset branches
- component-sized transparent parent or container generation where applicable
- centred non-clickable child image generation
- shared two-state contain-fit scaling for Button Normal/Pressed, Toggle OFF/ON, Light OFF/ON and Status Indicator OFF/ON through the common Binary Output path
- shared three-state contain-fit scaling for Three-Position LEFT/CENTER/RIGHT
- uploaded-asset registry dimension metadata consumption
- PNG IHDR dimension recovery
- generated LVGL descriptor-based dimension fallback
- safe scale-256 fallback and scale values above or below 256
- linked cropped state-asset consumption
- Toggle and Three-Position hook naming and metadata
- input callback-name uniqueness across all hook families
- shared Binary Output Runtime generation
- shared Binary Output setter generation
- per-instance Binary Output runtime record generation
- Button hook generation and metadata
- generated public output APIs and declaration metadata
- calls from generated runtime code into developer hooks
- uniqueness of generated hook and setter names
- standard-component retained LVGL objects
- standard-component retained runtime state
- standard-component transition helpers
- standard-component generated event callbacks
- standard-component public APIs and hook metadata
- native QR Code object construction, serialized colors/quiet zone/data and setter-only text updates
- deterministic sanitized collision-safe standard-component names
- TabView retained runtime generation
- Tileview retained coordinates and native four-page generation
- Button Text generation
- Text Value generation
- Heading Text generation
- Clock Presentation and formatter generation
- Clock RTC field integration and per-instance presentation state
- persistent application container generation
- System launcher container generation
- Brightness container generation
- complete Wi-Fi Manager container, status, connected details and structured selectable rows
- lazy Storage page/worker creation and acknowledged teardown
- reusable Storage rows, paging and projection
- Storage Refresh and Read / Write Test callbacks
- Storage Select Item mode
- Delete Empty Folder callback and workflow
- Connected and Saved presentation
- password and forget dialogs, password textarea, Show / Hide and validation
- internal Scan, Refresh, selection, Connect, Disconnect, Reconnect and Forget callbacks
- native keyboard callbacks and password attachment
- periodic structured backend projection
- projection pause while password entry is active
- generated gear launcher
- generated Back navigation
- container visibility switching
- generated Brightness slider and percentage label
- current-session System brightness state
- internal
FG_Set_Display_Brightness()bridge bsp_display_brightness_set()hardware integration
- writing files to disk
- ESP-IDF project copying
- CMake file writing
- customer hardware behavior
- developer application logic
- GPIO, sensors, motors, relays, networking policy, or business logic
Interactive Light introduced the Binary Output Runtime. Interactive Status Indicator reuses it through the same export descriptor, exported geometry, runtime-record, and setter-generation path. Both use a transparent component-sized container, centred child image and common contain-fit scaling. The previous Status Indicator legacy direct-image export path no longer exists.
The exporter emits Three-Position runtime code that uses fg_three_way_state_t; the enum itself is materialized by the user-event header generator in 95_UserEvents.h.
Do not create a separate Button, Toggle, Three-Position, Light or Status Indicator exporter, or a parallel runtime generator.
The same exporter owns the built-in System Runtime. It creates the Application, System Launcher, Display / Brightness and Wi-Fi Manager containers once, lazily creates and reuses the persistent Storage Browser container, then generates internal callbacks that switch LV_OBJ_FLAG_HIDDEN. It lazily creates one reusable top-layer native keyboard when password entry requires it. Interactive Assets remain instantiated inside the application container. System controls do not contribute developer hook metadata. Generated Wi-Fi code projects structured backend state and sends UI intent; it does not implement scanning, credentials, DHCP, reconnect policy, ESP-IDF calls or Hosted transport. Generated Storage code owns page presentation, rows, paging, selection, callbacks and projection; it does not own filesystem operations, which remain in 40_SD.c/.h.
Before export, Layout Designer has already resolved template structure and Auto Arrange geometry. The exporter walks the resulting ForgeUI component tree and selects the correct component branch. Its traversal owns:
- child traversal
- unique internal LVGL object names
- component position and size
- component-type dispatch
- asset-source collection
- recursive export of nested children
- semantic style generation
- object-name allocation
- runtime API and hook collection
It does not choose the Dashboard template, assign components to regions, compute Auto Arrange layouts, interpret AI region documents or modify region geometry at firmware runtime. Stable region keys are not developer-facing generated identifiers.
After placement, component geometry is authoritative. All five Interactive Asset branches use traversed component x/y/w/h; reusable Interactive Asset dimensions do not replace persisted component geometry during generation. Linked fitted state assets resolve through the normal kind-specific asset lookup when their IDs are current. The same persisted geometry feeds Browser Preview, live firmware and standalone export.
Current internal object names such as obj1, obj2, and obj3 are implementation details. They are suitable for generated LVGL variables but are not the stable developer API.
Developer-facing hook and setter names are derived separately and made unique within an export.
For Keyboard, traversal geometry remains the source of truth. The branch emits map and mode configuration before explicit styles and final top-left position/size so native lv_keyboard defaults cannot replace the Canvas geometry.
generateForgeUILvglCode() returns:
- generated LVGL
code; assetSources: string[]containing generated C source paths required by the UI;userEventHooks: string[]containing sanitized unique Interactive Asset and standard-component hook metadata;publicApiDeclarationsfor Binary Output setters and standard-component public APIs.
These four properties are the complete current contract. Hook families share generalized metadata rather than introducing per-widget transport fields. Binary Output and standard-component declarations use publicApiDeclarations: string[].
Layout Designer adds no layoutRegions, layoutMetadata, template, autoArrange or aiDocument field. Any layout metadata needed by Studio remains in project serialization and is resolved before generated firmware materialization.
The complete Standard Runtime reuses this contract. userEventHooks carries hook metadata for Input, Textarea, Switch, Checkbox, Radio, NumberInput, Select and IconButton. publicApiDeclarations carries declarations for Input, Textarea, Switch, Checkbox, Radio, Progress, CircularProgress, NumberInput, Select, Image, QRCode, Box, IconButton and the previously completed Standard runtime APIs. No transport field or generated-header system was added.
Scaling helpers, retained objects/state, transition helpers and resolved numeric scales live inside generated code. Linked fitted C sources appear in assetSources when fitting has replaced the Interactive Asset's state references. Intrinsic dimensions, alpha metadata, union bounds and crop coordinates do not become export payload fields. Hook metadata remains in userEventHooks, and generated public declarations remain in publicApiDeclarations.
The UI export actions in studio/src/components/Header.tsx send these same four values to both export endpoints:
POST /exportPOST /export-idf-project
Header forwards the validated Standard Runtime hook and declaration metadata unchanged. It does not invent APIs. The frontend owns code generation, metadata collection, client preflight, and transport. It does not write firmware files directly. Validation occurs before materialization.
Every generated asset source included in an export must have:
- a valid relative path under the firmware source boundary;
- an existing generated C file;
- the expected LVGL symbol in that source;
- one unique source and symbol registration;
- successful validation before CMake generation.
Built-in Theme assets participate in this same contract. They are permanent generated firmware assets, not validation-exempt resources.
State Sheets are reusable generation workflows that produce the independent uploaded assets required by existing runtime families.
The Toggle State Sheet Builder uses one master image containing OFF and ON artwork. Two linked crop regions retain a shared crop size while allowing independent positions. Confirming the crops extracts the two state images and registers OFF and ON together.
The Three-Position Creator uses this current path:
Create Three-Position Toggle Set
|
v
One master State Sheet request
|
v
Linked crop workspace
|
v
LEFT / CENTER / RIGHT crops
|
v
Atomic uploaded-asset registration
|
v
Generated Three-Position runtime
The three crop regions share dimensions. Their positions remain independently adjustable, and row remapping can swap which cropped row becomes LEFT, CENTER, or RIGHT without generating another master. This replaces any earlier implication that Three-Position artwork is created by three independent image-generation requests.
studio/src/forgeui/ai/InteractiveAssetAIGenerator.tsx owns the Creator/Designer state, the single Three-Position set request, crop confirmation, and state-row assignment. studio/src/forgeui/ai/StateSheetOverlay.tsx owns the linked crop-region geometry and resize behavior. Direct Canvas entry is routed by ForgeUINavigation.ts and PreviewContainer.tsx; those files select context but do not generate firmware.
studio/src/forgeui/ai/ForgeUIAIImagePipeline.ts owns crop extraction and registration:
- receive the single generated master;
- represent crop results as PNG data URLs;
- decode the Base64 PNG payloads into files for conversion;
- convert all requested state crops;
- register the completed uploaded assets in one atomic batch;
- update the Interactive Asset draft only after the batch succeeds.
Confirm Crops is the commit boundary. The active crop-registration path does not depend on temporary blob URLs; PNG data URLs survive the crop-to-conversion handoff. A partial conversion must not partially register a state set.
State Sheet generation belongs to asset preparation. Export still resolves the resulting uploaded assets through the normal generated asset-source contract.
The InteractiveButton exporter branch owns:
- reading
interactiveAssetId; - resolving the Interactive Button asset by kind;
- resolving Normal and Pressed uploaded assets;
- using the preflight-validated Normal and Pressed image metadata;
- adding required asset C files;
- declaring LVGL images;
- using final persisted component geometry for the parent LVGL button;
- creating the child LVGL image;
- resolving both state dimensions through registry metadata or PNG IHDR;
- calculating one safe Normal/Pressed contain scale;
- emitting the descriptor-based contain-scale helper call when Studio dimensions are unavailable;
- centring the scaled child image;
- creating per-button runtime data;
- attaching the shared Button event callback;
- collecting the unique generated click hook.
Button runtime generation assumes validated image assets. It does not perform late export validation while emitting runtime code.
The Button contain-fit helper evaluates both Normal and Pressed descriptor widths and heights against final component w/h. Both states use the same scale, so press transitions do not change placement. Reliable Studio dimensions produce a numeric scale; generated lv_image_dsc_t.header.w/h supplies the legacy runtime fallback, and invalid descriptors return scale 256.
typedef struct
{
const void * normal_src;
const void * pressed_src;
void (*clicked_cb)(void);
const char * event_name;
} fg_interactive_button_data_t;Each exported Button instance carries:
- its Normal image source;
- its Pressed image source;
- its generated developer callback pointer;
- its generated event name for runtime diagnostics.
This is implemented behavior, not a proposed extension.
static void fg_interactive_button_event_cb(lv_event_t *event)One shared callback serves every exported Interactive Button. Per-instance runtime data supplies the correct images and developer hook.
Behavior:
LV_EVENT_PRESSED
→ select Pressed image
LV_EVENT_RELEASED or LV_EVENT_PRESS_LOST
→ restore Normal image
LV_EVENT_CLICKED
→ print the generated event name
→ call clicked_cb when non-null
Do not generate one LVGL callback implementation per Button.
Each ready Button instance initializes data equivalent to:
static fg_interactive_button_data_t obj1_data = {
.normal_src = &normal_symbol,
.pressed_src = &pressed_symbol,
.clicked_cb = FG_On_Button_Clicked,
.event_name = "FG_On_Button_Clicked",
};It registers the shared callback for:
LV_EVENT_PRESSEDLV_EVENT_RELEASEDLV_EVENT_PRESS_LOSTLV_EVENT_CLICKED
The visual-state runtime and click-hook runtime are parts of the same generated Button implementation.
When visible-artwork fitting has been applied, normalAssetId and pressedAssetId resolve to the linked stable-union crops. Export collects those cropped C sources; it does not modify or export through the superseded original references unless another active asset still uses them.
Each exported Interactive Button receives one unique hook using the FG_On_*_Clicked contract.
Examples:
void FG_On_StartPump_Clicked(void);
void FG_On_StopPump_Clicked(void);
void FG_On_ResetAlarm_Clicked(void);The exporter prevents duplicate hook names by adding a suffix when necessary. Generated C identifiers are sanitized; developer code must use the exact emitted hook name.
InteractiveButton component
↓
unique FG_On_*_Clicked name
↓
userEventHooks returned by generateForgeUILvglCode()
↓
Header export payload
↓
export-server.js
↓
generateUserEventFiles()
↓
95_UserEvents.h declaration
95_UserEvents.c implementation stub
The InteractiveToggleSwitch export path:
- resolves the Toggle Switch by kind;
- resolves OFF and ON uploaded assets;
- collects the currently linked or original required generated C sources;
- uses final persisted component position and size;
- creates a full-size transparent parent LVGL button;
- creates a centred non-clickable child image;
- calculates one common OFF/ON contain scale;
- recovers dimensions through registry metadata, PNG IHDR,
lv_image_dsc_t.header.w/h, then safe scale 256; - creates an independent
fg_toggle_input_trecord; - initializes from the saved state through
fg_toggle_input_set(..., notify=false); - attaches
fg_toggle_input_event_cb(); - generates a unique
FG_On_*_Toggledname and adds it touserEventHooks.
State changes replace the child image source without changing position, scale or parent geometry. The fg_toggle_input_t, fg_toggle_input_set(), fg_toggle_input_event_cb() and void FG_On_<Name>_Toggled(bool enabled); contracts are unchanged.
Actual generated structure:
typedef struct {
lv_obj_t * button;
lv_obj_t * image;
const void * off_src;
const void * on_src;
bool enabled;
void (*toggled_cb)(bool);
} fg_toggle_input_t;Shared helpers:
fg_toggle_input_set()
fg_toggle_input_event_cb()Behavior:
LV_EVENT_CLICKED
→ invert enabled
→ store independent instance state
→ update OFF/ON artwork
→ invoke toggled_cb(new bool state)
The structure, setter and event callback are emitted once. Each Toggle instance owns its button, image, sources, state and callback pointer.
void FG_On_Main_Power_Toggled(bool enabled);generateUserEventFiles() produces a live stub that prints ON or OFF. Initialization uses notify=false, so loading the configured initial state does not call developer code.
Three-Position artwork now comes from one master State Sheet request and three confirmed crops. Export still receives three independent uploaded assets, one each for LEFT, CENTER, and RIGHT. The public enum, generated callback, per-instance runtime record, and touch behavior are unchanged; only the generation workflow changed.
fg_three_way_state_t is generated in 95_UserEvents.h, not 90_Studio_Export.h. Generated 90_Studio_Export.c includes 95_UserEvents.h before using the type in runtime records and callback pointers, and developer implementations include the same header.
typedef enum
{
FG_THREE_WAY_LEFT = -1,
FG_THREE_WAY_CENTER = 0,
FG_THREE_WAY_RIGHT = 1
} fg_three_way_state_t;The InteractiveThreePositionToggleSwitch export path:
- resolves the asset by kind;
- resolves LEFT, CENTER and RIGHT uploaded assets;
- collects all currently linked or original required C sources;
- generates a unique
FG_On_*_Changedname and adds it touserEventHooks; - uses final persisted component position and size for the full transparent rectangular parent button;
- creates a centred non-clickable child image;
- resolves dimensions through registry metadata, PNG IHDR, LVGL descriptors and the safe scale-256 fallback;
- applies one common LEFT/CENTER/RIGHT contain scale, including values above or below 256;
- creates an independent
fg_three_way_input_trecord; - initializes the saved enum state with
notify=false; - attaches the one shared
fg_three_way_input_event_cb().
Actual generated structure:
typedef struct {
lv_obj_t * button;
lv_obj_t * image;
const void * left_src;
const void * center_src;
const void * right_src;
fg_three_way_state_t state;
void (*changed_cb)(fg_three_way_state_t state);
} fg_three_way_input_t;fg_three_way_input_set() validates state, stores it, selects the correct source, updates the image and optionally calls changed_cb. Initialization passes notify=false.
fg_three_way_input_event_cb() converts the absolute pointer coordinate into button-local space:
local_x = point.x - button_coords.x1;first third → FG_THREE_WAY_LEFT
middle third → FG_THREE_WAY_CENTER
last third → FG_THREE_WAY_RIGHT
The parent button owns the full rectangular hit area. The child image is non-clickable, and parent and child are non-scrollable. Transparent artwork does not shrink the interaction bounds.
void FG_On_ModeSelector_Changed(fg_three_way_state_t state);The generated live stub maps enum values to readable LEFT, CENTER and RIGHT output. One shared runtime is emitted; per-instance state, artwork and callback pointers remain independent.
Interactive Light export owns:
- reading
interactiveAssetId; - resolving the Interactive Light asset by kind;
- resolving OFF and ON uploaded assets;
- using the preflight-validated OFF and ON image metadata;
- adding required asset C files;
- creating a transparent container from final persisted component geometry;
- creating a centred non-clickable child LVGL image;
- resolving both state dimensions through registry metadata or PNG IHDR;
- calculating one safe OFF/ON contain scale;
- emitting the descriptor-based contain-scale helper call when Studio dimensions are unavailable;
- selecting its initial source from saved
initialState; - creating a unique public setter name;
- emitting the setter implementation;
- returning its declaration as public API metadata.
Light runtime generation likewise assumes validated image assets and API metadata. It does not perform late export validation while emitting runtime code.
Interactive Light is an output indicator, not an input control.
- It is emitted as an LVGL image.
- The image is centred inside a transparent component-sized container.
- Final container position and size come from persisted component
x/y/w/h. - OFF and ON use one common contain scale.
- State switching changes only the image source, not scale, centre or container geometry.
- The object remains non-clickable.
- It has no Button-style event callback.
- It produces no hook in
95_UserEvents. - Its initial OFF/ON image follows the saved
initialState.
The Light contain-fit helper evaluates both OFF and ON descriptor dimensions against final component geometry. Dimension ownership follows the same chain as Button:
Uploaded Asset Registry metadata
→ PNG IHDR dimensions
→ generated lv_image_dsc_t.header.w/h
→ safe scale 256
When visible-artwork fitting has been applied, Light resolution and assetSources use the linked stable-union OFF/ON crops. Original uploads remain unchanged and are exported only when still actively referenced elsewhere.
Example declaration:
void FG_Set_Status_Light(bool enabled);The generated implementation delegates to the Light instance's Binary Output runtime record:
void FG_Set_Status_Light(bool enabled)
{
fg_binary_output_set(&fg_status_light_output, enabled);
}Contract:
false → OFF artwork
true → ON artwork
Setter names use FG_Set_*. Duplicate API names receive a numeric suffix.
InteractiveLight or InteractiveStatusIndicator component
↓
unique FG_Set_* name
↓
setter implementation in generated LVGL code
↓
publicApiDeclarations returned by generateForgeUILvglCode()
↓
Header export payload
↓
export-server.js
↓
generateStudioExportHeader()
↓
declaration in 90_Studio_Export.h
The export server validates declarations against the supported setter signature before writing them:
void FG_Set_<Name>(bool enabled);The generated Binary Output Runtime is the permanent implementation family for two-state output assets.
It is unrelated to Toggle Input even though both cross an API boundary with a boolean:
Toggle Switch: UI changes state → developer hook receives bool
Binary Output: developer calls setter → UI changes state
Generated runtime structure:
typedef struct
{
lv_obj_t * image;
const void * off_src;
const void * on_src;
bool enabled;
} fg_binary_output_t;Generated state-change function:
static void fg_binary_output_set(
fg_binary_output_t * output,
bool enabled
)
{
if (!output || !output->image)
{
return;
}
output->enabled = enabled;
lv_image_set_src(
output->image,
enabled ? output->on_src : output->off_src
);
}This structure and function are generated once per export. Interactive Light introduced them; Interactive Status Indicator reuses them unchanged.
The Binary Output API contract is unchanged by contain scaling. fg_binary_output_set() continues to switch only image source. Light and Status Indicator object creation use final component geometry, a transparent component-sized container, a centred child image and one shared OFF/ON scale before the runtime record is used.
Every exported Binary Output instance creates its own fg_binary_output_t record containing its LVGL image, OFF source, ON source, and saved state. Multiple records remain independent even when instances reuse the same artwork.
Every generated Binary Output setter calls fg_binary_output_set() with its own record. Future Binary Output assets must reuse this implementation rather than emit another binary state structure or switching function.
The generated System Runtime owns one physically proven reusable native lv_keyboard, whose internal key surface is LVGL's native lv_buttonmatrix. It is created lazily when a System password field requires text entry, retained as one reusable instance and reattached through lv_keyboard_set_textarea().
The exporter owns this runtime contract:
- create the password textarea with password masking enabled;
- open from textarea focus or click through an idempotent helper;
- lazily create one
lv_keyboardparented tolv_layer_top(); - apply
LV_OBJ_FLAG_IGNORE_LAYOUTandLV_OBJ_FLAG_FLOATING; - associate the active textarea through
lv_keyboard_set_textarea(); - select
LV_KEYBOARD_MODE_TEXT_LOWER, retaining native lowercase, uppercase/shift, number, symbol, space, Backspace and Done behavior; - apply explicit ForgeUI main/item styles;
- replace the native default alignment with
LV_ALIGN_TOP_LEFT; - apply physical top-layer position
(0, 350)and size1024 × 250; - move the keyboard to the foreground and reposition the password dialog while entry is active;
- detach and hide the keyboard on Done or Cancel.
No diagnostic colors, geometry-debug styling or verbose runtime logging belong to this final architecture.
The control-width map uses LVGL's numeric buttonmatrix width units combined with control flags. Normal alpha widths and special-key widths are normalized row by row; Space remains deliberately wider, while mode, Backspace, Enter, arrows and confirm retain only the relative width required by the Studio layout. These are proportional controls, not screen-resolution compensation.
Current row width units are:
row 1: 1#/alpha/Backspace = 4
row 2: ABC/alpha/Enter = 3
row 3: symbol/alpha keys = 1
row 4: keyboard/left = 2, Space = 12, right/confirm = 2
Keyboard export therefore owns:
- lazy creation and one reusable instance;
lv_layer_top()ownership;lv_keyboard_set_textarea()attachment;- password input, Done and Cancel interaction;
- password Show / Hide through the surrounding generated dialog;
LV_ALIGN_TOP_LEFTcorrection for the nativelv_keyboarddefault alignment;- four-row map order;
- relative buttonmatrix width map;
- explicit main and item padding;
- explicit row and column gaps;
- explicit item font and line spacing;
- theme-preserving border, color and radius styles;
- map/mode/style/alignment/geometry call ordering;
- physically proven top-layer geometry and touch interaction;
- reusable System-dialog architecture without claiming future pages already consume it.
Interactive Status Indicator export owns:
- reading
interactiveAssetId; - resolving the Interactive Status Indicator asset by kind;
- resolving OFF and ON uploaded artwork;
- using the preflight-validated OFF and ON image metadata;
- adding required asset C files without duplicating reused artwork sources;
- creating a Binary Output export descriptor from final persisted component geometry;
- creating a transparent component-sized container and centred non-clickable child image;
- resolving dimensions through registry metadata, PNG IHDR, LVGL descriptors and safe scale 256;
- applying the common Binary Output OFF/ON contain scale;
- selecting its initial source from saved
initialState; - creating a unique
fg_binary_output_truntime record; - creating a deterministic public setter name;
- emitting a setter that delegates to
fg_binary_output_set(); - returning its declaration as public API metadata.
The exporter consumes the final saved OFF/ON asset references and component geometry only. It does not export Creator navigation, Inspector onboarding, local Canvas preview click behavior or temporary design-time state. It emits one centred non-clickable child image, one independent fg_binary_output_t record and one generated FG_Set_* API per Status Indicator.
- Canvas right-click can open the exact linked Status Indicator Creator.
- An unconfigured component opens a fresh unsaved draft.
- A new Status Indicator defaults to
120 × 72on Canvas. - Canvas and Browser Preview share centered contain-fit rendering that preserves intrinsic artwork aspect ratio.
- Canvas click may toggle a temporary local OFF / ON preview state for visual verification.
- Local preview toggling does not mutate saved
initialState, persistence or generated firmware. - The exported Status Indicator remains non-clickable, emits no user hook and changes runtime state only through
FG_Set_<Name>(bool enabled).
- OFF artwork is stored in
off_src. - ON artwork is stored in
on_src. - the saved initial state initializes the instance independently.
falseselects OFF andtrueselects ON.- multiple Status Indicator and Light records can coexist.
- no click callback is registered.
- no event hook or metadata is generated for
95_UserEvents.c/.h.
Example generated record and setter:
static fg_binary_output_t fg_wifi_status_output = {
.image = NULL,
.off_src = &wifi_status_off,
.on_src = &wifi_status_on,
.enabled = false,
};
void FG_Set_WiFi_Status(bool enabled)
{
fg_binary_output_set(&fg_wifi_status_output, enabled);
}Interactive Status Indicator contributes its setter declaration through publicApiDeclarations, exactly like Interactive Light. Only its per-instance record, artwork references, initial state, and setter name are unique; the Binary Output Runtime implementation remains shared.
A screen created from any implemented Layout Designer definition appears as ordinary generated LVGL objects: region lv_obj Boxes, Divider/Line presentation, Heading/Text labels, Charts, status indicators, Progress or Circular Progress, Buttons and other assigned controls.
90_Studio_Export.c contains no Layout Designer engine. It must not contain template selection, AI prompting, region-assignment logic, Auto Arrange calculations, Studio Inspector metadata or region drag/resize behavior. These are resolved before generation.
90_Studio_Export.h receives no Layout Designer declarations beyond APIs already generated by the contained normal components. 95_UserEvents.* receives no Layout Designer hook family; it receives only hooks generated by actual contained interactive controls.
Example:
Dashboard Controls region
├── Start Button
├── Stop Button
└── Reset Button
Generated boundary:
Region Box
→ normal generated lv_obj
→ optional existing FG_Set_<Region>_Visible()
Buttons
→ normal generated LVGL buttons
→ existing behavior or hooks according to component type
The region creates no Start, Stop or Reset semantics. The contained controls own their existing runtime contracts.
Owns generated implementation:
- LVGL object creation
- generated component instances
- generated runtime structures
- shared runtime callbacks
- Button callback wiring
- Button parent geometry, shared Normal/Pressed contain scale and centred child image
- calls into Button user hooks
- shared Toggle Input Runtime, per-instance records and bool-hook calls
- shared Three-Position Input Runtime, per-instance records and enum-hook calls
- shared Binary Output Runtime and per-instance records
- reusable native keyboard instance, top-layer ownership, callbacks, textarea attachment and explicit styles
- final keyboard top-left alignment and physically proven
(0, 350),1024 × 250geometry - Binary Output OFF/ON runtime image references
- Light component-sized container, shared OFF/ON contain scale and centred child image
- generated Button, Toggle, Three-Position and Binary Output LVGL descriptor fallback helpers
- Light public setter implementations
- Interactive Status Indicator public setter implementations
- Standard LVGL Component Runtime retained objects and state
- standard-component transition helpers and generated event callbacks
- standard-component public API implementations and calls into generated hooks
- TabView retained runtime, setter and touch callback
- Tileview native parent/pages, retained coordinates, silent setter and swipe callback
- retained Input and Textarea objects, current text and programmatic guards
- retained Switch, Checkbox and Radio objects and checked/selected state
- retained Progress value and range
- retained NumberInput textarea, value, range and step metadata
- retained Select dropdown, selected index and option count
- retained Image object and source pointer
- retained native QR Code object, serialized data/colors/quiet zone and text setter
- retained Box object and visibility
- retained IconButton object and enabled state
- generated Standard setters, LVGL event adapters and calls into genuine-user hooks
- serialized Button text generation
- serialized Text label generation
- serialized Heading label generation
- Clock Presentation defaults, RTC lookup, formatter, separator blinking and per-instance state
- generated System Runtime
- persistent Application, System Launcher, Display / Brightness and Wi-Fi Manager containers
- lazily created persistent Storage page
- Storage callbacks and row projection
- Storage paging and Select Item mode
- Delete Empty Folder UI workflow
- generated status, structured network rows, badges and connected details
- password and forget dialogs, password validation and Show / Hide
- internal Scan, Refresh, selection, Connect, Disconnect, Reconnect and Forget callbacks
- native keyboard callbacks and periodic structured backend projection
- generated gear launcher
- generated Back navigation and container visibility switching
- generated Brightness slider, percentage label and current-session value
- internal
FG_Set_Display_Brightness()brightness control - Waveshare backlight call through
bsp_display_brightness_set() fg_studio_export_create(...)
These System additions are generated automatically with the application. They are replaceable runtime integration, not developer-owned product logic.
It must not contain customer product behavior or permanent application logic. The generated backlight bridge is built-in ForgeUI platform integration and remains internal. Wi-Fi credentials, scanning implementation, DHCP, reconnect policy, ESP-IDF calls and transport startup remain owned by 30_WIFI.c and must not be generated. Storage filesystem operations remain owned by 40_SD.c/.h.
Owns generated public declarations:
fg_studio_export_create(lv_obj_t *parent)- generated Binary Output
FG_Set_*APIs for Light and Status Indicator - generated Standard LVGL Component Runtime public APIs
- generated QR Code
FG_Set_<Name>_Text(const char * text)declarations - generated TabView and Tileview public setter declarations
- required public includes and C/C++ linkage guards
It does not contain user implementations.
The completed declarations include the new Text, Checked, Selected, Selected_Index, Value, Source, Visible and Enabled setter families. Required public types include bool, int32_t, uint32_t, const char * and const void *.
Divider, Scale, Line, Clock, Button Text, Text and Heading generate no runtime
declarations in 90_Studio_Export.h. Runtime-enabled Icon instances generate
their declarations/implementations in 96_FiRuntime.h/.c; 90 includes the 96
public header when present. The header includes the required LVGL, boolean and
fixed-width integer types, but does not own fg_three_way_state_t; that enum is
generated in 95_UserEvents.h. Other public APIs remain implemented in generated
90_Studio_Export.c.
90_Studio_Export.c and 90_Studio_Export.h are generated and replaceable whenever Studio exports.
Never place developer application logic in them.
Failed client or server validation preserves the previous generated state:
90_Studio_Export.c90_Studio_Export.h95_UserEvents.c95_UserEvents.h- generated asset sources
- generated
CMakeLists.txt
These files remain unchanged until a successful export replaces them. Preservation on failed validation is part of the generated export API contract, not merely an implementation convenience.
Built-in generated Theme assets participate in validation exactly like uploaded assets. A missing built-in generated C file correctly stops export before firmware mutation.
The physical Build & Flash regression exposed missing Neural Core and Carbon Fiber sources. It was resolved by restoring the legitimate generated firmware assets and verifying their expected LVGL symbols. Validation was strengthened and retained; it was not bypassed or weakened.
Studio generates declarations for all collected Interactive Asset and standard-component hooks and the Three-Position enum type:
#pragma once
#include <stdbool.h>
#include <stdint.h>
typedef enum
{
FG_THREE_WAY_LEFT = -1,
FG_THREE_WAY_CENTER = 0,
FG_THREE_WAY_RIGHT = 1
} fg_three_way_state_t;
#ifdef __cplusplus
extern "C" {
#endif
void FG_On_Start_Clicked(void);
void FG_On_Main_Power_Toggled(bool enabled);
void FG_On_ModeSelector_Changed(fg_three_way_state_t state);
void FG_On_Tab_View_Changed(uint32_t tab_index);
void FG_On_Tileview_Changed(uint32_t column, uint32_t row);
void FG_On_Search_Input_Changed(const char * text);
void FG_On_Notes_Textarea_Changed(const char * text);
void FG_On_Enable_Wi_Fi_Switch_Changed(bool checked);
void FG_On_Enable_Logging_Checkbox_Changed(bool checked);
void FG_On_Automatic_Mode_Radio_Changed(bool selected);
void FG_On_Target_Temperature_Number_Input_Changed(int32_t value);
void FG_On_Mode_Select_Changed(uint32_t index, const char * text);
void FG_On_Settings_Icon_Button_Clicked(void);
#ifdef __cplusplus
}
#endifThe generated UI includes this header when an Interactive Asset or standard component requires hooks.
Studio preservation-merges Button, Toggle, Three-Position and standard-component hook implementations for the current live export. Existing matching developer implementations survive regeneration; missing declarations and missing stubs are appended. Stable server tests cover signatures, readable state output and body preservation. Representative stubs are:
#include "95_UserEvents.h"
#include <stdio.h>
void FG_On_Button_Clicked(void)
{
printf("[ForgeUI User Event] FG_On_Button_Clicked\n");
}
void FG_On_Main_Power_Toggled(bool enabled)
{
printf("[ForgeUI User Event] FG_On_Main_Power_Toggled: %s\n", enabled ? "ON" : "OFF");
}
void FG_On_ModeSelector_Changed(fg_three_way_state_t state)
{
const char * text = state == FG_THREE_WAY_LEFT ? "LEFT" : state == FG_THREE_WAY_RIGHT ? "RIGHT" : "CENTER";
printf("[ForgeUI User Event] FG_On_ModeSelector_Changed: %s\n", text);
}Interactive Light and Interactive Status Indicator do not add anything to these files. No Binary Output asset generates a hook in 95_UserEvents.c/.h.
Default Input and Textarea stubs print text safely; Switch prints ON/OFF; Checkbox prints CHECKED/UNCHECKED; Radio prints SELECTED/UNSELECTED; NumberInput prints its integer value; Select prints index and text; and IconButton prints a click event. Matching developer bodies survive, missing declarations and stubs are appended, and unrelated hooks are not deleted.
Progress, Image, Box, Icon, Divider, Scale, Line, Clock, Button Text, Text and Heading add no hooks. There is no FG_On_Clock_Changed.
Coordinates:
- Build & Flash
- Clean Build & Flash
- standalone ESP-IDF project export
- client preflight before export submission
- transport of validated exporter metadata
- transport of
userEventHooksfor Button, Toggle Switch, and Three-Position Toggle hooks - transport of standard-component hook metadata
- transport of Binary Output and standard-component public API metadata
The exact exporter fields transported are code, assetSources, userEventHooks, and publicApiDeclarations. Hook and public-API metadata carry the exact signatures established by the exporter.
Header coordinates export and starts flashing only after export succeeds. It does not validate generated firmware files itself, invent hooks or setters, materialize files, or define runtime behavior.
Header does not transport Layout Designer templates or AI region documents to the firmware export server. It transports only the unchanged exporter result.
studio/export-server.js
- receiving and validating generated code and API metadata
- validating payloads, paths, generated C sources, physical source existence, symbols, and source references
- producing the validated asset-source list
- normalizing supported public declarations
- merging supported public declarations
- merging all supported user-hook declarations and missing implementations
- preserving existing matching developer hook bodies and unrelated hooks
- generating
90_Studio_Export.h - generating
95_UserEvents.c/.h - writing generated files
- copying required image assets
- generating CMake source lists
- live firmware export
- standalone ESP-IDF project export
- project packaging and maintenance operations
- LVGL component generation
- Button, Toggle Switch, Three-Position Toggle, Light, or Status Indicator runtime behavior
- hook-name or runtime-API invention
- hook signature invention beyond materializing exporter metadata
- Light setter-name selection
- application logic
- customer hardware behavior
The server recognizes and materializes validated const char * text hooks, boolean checked/selected hooks, int32_t value hooks, uint32_t index-plus-text hooks and void click hooks. It normalizes Source, Visible, Enabled, Checked, Selected, Selected_Index, Text and Value setter declarations. It adds required includes and the Three-Position enum when needed.
The exporter decides API and hook names and signatures. The server validates, normalizes, preservation-merges and writes that metadata. It never invents runtime or widget behavior.
export-server.js does not interpret Dashboard templates, smart regions or Auto Arrange metadata. It validates and materializes already-generated LVGL through the unchanged four-field contract.
POST /export
The endpoint receives the exact payload fields code, assetSources, userEventHooks, and publicApiDeclarations. They carry generated C source, required assets, Interactive Asset and standard-component hooks, and Binary Output and standard-component public declarations.
It generates and writes:
firmware/ForgeUI-One/main/90_Studio_Export.cfirmware/ForgeUI-One/main/90_Studio_Export.hfirmware/ForgeUI-One/main/95_UserEvents.cfirmware/ForgeUI-One/main/95_UserEvents.hfirmware/ForgeUI-One/main/CMakeLists.txt- required generated image assets
The live 95_UserEvents.c/.h files are safely regeneration-merged. Matching developer-written hook bodies and unrelated hooks are preserved; missing declarations and stubs are appended.
POST /export-idf-project
The endpoint creates an independent project under:
C:\ForgeUI-Exports
The generated project contains:
main/
├── 90_Studio_Export.c
├── 90_Studio_Export.h
├── 96_FiRuntime.c # only when a Standard Icon runtime API is enabled
├── 96_FiRuntime.h # only when a Standard Icon runtime API is enabled
├── 95_UserEvents.c
├── 95_UserEvents.h
├── required generated assets
└── CMakeLists.txt
Studio creates all four API-layer files at export time. Once the standalone project exists, its 95_UserEvents.c/.h copies become the developer-owned integration layer for Interactive Asset and standard-component callbacks. Developer code calls Binary Output and standard-component APIs declared by 90_Studio_Export.h. ForgeUI Studio does not continuously regenerate, build, flash, or synchronize that exported project.
Standalone developers call generated UI APIs through 90_Studio_Export.h and implement genuine-event reactions in 95_UserEvents.c:
#include "90_Studio_Export.h"
#include "95_UserEvents.h"
void FG_On_Mode_Select_Changed(uint32_t index, const char * text)
{
machine_set_mode(index);
}
void app_update_ui(void)
{
FG_Set_Download_Progress_Value(75);
FG_Set_Status_Box_Visible(true);
}This application logic must not be placed in live generated 90_Studio_Export.c.
| File or concern | Live ForgeUI-One firmware | Standalone exported project |
|---|---|---|
90_Studio_Export.c |
Studio-generated and replaceable | Generated UI implementation |
90_Studio_Export.h |
Studio-generated and replaceable | Generated public API declarations and feature-gated Fi runtime include |
96_FiRuntime.c/.h |
Studio-generated and replaceable | Standard Fi Icon retained presentation state and public setters |
95_UserEvents.c/.h |
Studio-generated and preservation-merged; existing bodies survive and missing declarations/stubs are appended | Developer-owned application/hook layer after export |
Generated image .c files |
Studio-managed | Exported project assets |
CMakeLists.txt |
Generated by export server | Generated project build registration |
| GPIO, I/O, hardware actions | Do not keep permanently here | Add in developer-owned application layer |
| Product/business logic | Do not keep permanently here | Developer-owned |
- Studio owns the live firmware workspace and may regenerate its generated UI and hook files.
- Studio creates the standalone project's initial hook files.
- After export, the developer owns the standalone project and its
95_UserEvents.c/.happlication layer. - Generated public UI functions remain declared/implemented in 90, except the dedicated Standard Fi Icon presentation contract in 96; 90 includes its public header when emitted.
- Developer code includes
90_Studio_Export.hto call generated UI control APIs. - Generated UI code includes
95_UserEvents.hto call genuine-user hooks implemented in the standalone95_UserEvents.c. - Permanent product logic must not be stored in the live Studio firmware copy.
Do not claim that live 95_UserEvents.c/.h files are manually created or disposable. Live regeneration preserves matching developer hook bodies and appends missing generated declarations and stubs. Standalone copies become fully developer-owned after export.
The export server generates the component source list and includes:
main.c
01_FG_Runtime.c
20_RTC.c
30_Audio.c
30_WIFI.c
40_SD.c
90_Studio_Export.c
95_UserEvents.c
required generated asset sources
Only server-validated asset sources are admitted to the generated CMake list. Both the generated UI runtime and generated/developer hook layer are compiled into the ESP-IDF application.
Do not create a separate CMake pipeline for Interactive Assets or public APIs.
ForgeUI owns:
- UI structure
- LVGL widget creation
- visual runtime behavior
- generated input callback entry points
- generated public output functions
- API naming and uniqueness
- export metadata
- generated firmware files
Developer code owns:
- GPIO
- sensors
- Wi-Fi application behavior
- MQTT and BLE application behavior
- motors and relays
- business logic
- device-specific actions
- decisions about when to call generated output APIs
Example standalone application integration:
#include "90_Studio_Export.h"
#include "95_UserEvents.h"
void FG_On_StartPump_Clicked(void)
{
pump_start();
FG_Set_Running_Light(true);
}
void FG_On_Main_Power_Toggled(bool enabled)
{
power_set_enabled(enabled);
FG_Set_Power_Status(enabled);
}
void FG_On_ModeSelector_Changed(fg_three_way_state_t state)
{
machine_set_mode(state);
}ForgeUI owns the callback signatures and FG_Set_* APIs. The developer owns the example hardware functions and the decisions that connect input events to output state.
The validated pipeline successfully exports Hero artwork, the Industrial Carbon Theme, Interactive Button, Interactive Light, and Interactive Status Indicator together. Their layout and artwork align across the Canvas, Browser Preview, and physical ESP32-P4 output.
The generated firmware retains the complete exported UI and no longer falls back to an empty fg_studio_export_create(). Button runtime, the shared Binary Output Runtime, generated hooks, public setters, per-instance runtime records, asset sources, generated headers, and generated CMake participate in the same successful export.
Physically verified across Canvas, Browser Preview, generated LVGL and ESP32-P4:
- Led
- Bar
- Arc
- Chart
- Table
- Keyboard
- Calendar
- Scale
- Roller
- MsgBox
- ButtonMatrix
The generated output uses the selected semantic palette for component surfaces, secondary surfaces, borders, primary and secondary text, accents, contrast-selected accent text, disabled text and selected surfaces. Standard LED status green remains semantically independent. Chart retains native lv_chart and adds responsive non-clickable sibling labels for Y values and X point indexes.
The later 2026-07-30 input and selection runtime group is separately physically proven below; no proof is implied for other runtime-complete controls.
Physically confirmed:
- Normal artwork displayed
- Pressed artwork displayed on touch
- release restored Normal artwork
- physical click detected
- generated shared callback executed
- generated user hook executed
- resized and visible-bounds-fitted artwork matched Canvas and Browser Preview
- generated contain-fit scaling used final persisted component geometry
Monitor output:
[ForgeUI] FG_On_Button_Clicked clicked
[ForgeUI User Event] FG_On_Button_Clicked
Physically confirmed:
- OFF and ON image assets exported
- saved initial ON state displayed
FG_Set_*public setter generated- Light remained non-clickable
- resized and visible-bounds-fitted artwork matched Canvas and Browser Preview
- common OFF/ON contain scaling used final persisted component geometry
- firmware remained stable
Physically confirmed for the exercised control:
- OFF and ON artwork displayed through touch state changes
- persistent boolean runtime selected the correct artwork
- generated Toggle interaction remained stable
This proof does not claim unperformed multi-instance or stress coverage.
Physically confirmed:
- Binary Output runtime
- generated
FG_Set_*control path - shared Binary Output runtime
- shared Binary Output exported geometry
- component-sized transparent container
- centred child image
- contain-fit scaling
- Browser Preview parity
- resized component geometry
- stable runtime behaviour
Design-time local preview clicking is intentionally not exported and is not physical input behavior.
Physically confirmed:
- one generated LEFT/CENTER/RIGHT artwork set exported as three independent assets
- the full rectangular control was divided into three working touch zones
- each zone selected the correct LEFT, CENTER, or RIGHT runtime state
- the generated
FG_On_*_Changed(fg_three_way_state_t state)callback reported the matching readable state - initialization did not spuriously notify application code
- repeated interaction remained stable without a crash
The proof validates the current component instance and generated runtime. It does not imply unperformed multi-instance stress coverage.
Physically confirmed:
- generated gear launcher
- generated System launcher
- generated Brightness page
- generated Back navigation
- generated Brightness slider and live percentage
- real ESP32-P4 brightness control through
bsp_display_brightness_set() - current-session brightness retention
- generated Wi-Fi Manager
- Scan and Refresh through the same Hosted backend path
- live Hosted scan results and a populated Available Networks list
- live RSSI and security
- Connected and Saved markers
- repeated Scan and Refresh replacing previous results
- no duplicate or stale SSID rows observed
- password dialog, password masking and Show / Hide
- reusable native keyboard visibility, password entry, Done and Cancel
- correct top-layer keyboard geometry and touch interaction
- Connect, Disconnect, Reconnect and Forget workflows
- complete connection workflow physically proven
- connected details including gateway, station MAC and AP BSSID
- deterministic Browser Preview parity
- approximately 63 FPS after keyboard optimisation
- Interactive Assets remain alive and operational while System Runtime pages are active and after return to the application
- only minor UI polish remains
Physically confirmed:
- lazy Storage page creation
- acknowledged worker shutdown and resource deletion
- Refresh
- directory browsing
- folder navigation
- Previous / Next paging
- Read / Write Test
- Select Item mode
- Delete Empty Folder
- Hosted Wi-Fi coexistence
- repeated Storage use without crash
Delete File, Rename, Format, Mount / Unmount and recursive deletion are not claimed as implemented or physically proven.
The recovered physical proof includes:
transport: Identified slave [esp32c6]
H_API: Transport active
FG_WIFI: STA started
FG_WIFI: WiFi hosted init READY
Station mode: Connected
Got IP
SD mounted OK
SD TEST PASS
The physical ESP32-P4 run proved Hosted transport, the complete generated Wi-Fi Manager workflow, SD coexistence, System Runtime and Interactive Asset Runtime operating simultaneously.
- Wi-Fi READY
- Wi-Fi connected
- IP assigned
- SD READY
- SD TEST PASS
- Hosted Wi-Fi and SD operating simultaneously
- no crash after interaction
The Button, exercised Toggle Switch, Three-Position input-hook path, shared Light/Status Indicator Binary Output setter path and generated System Runtime are implemented and physically proven within the scopes stated above. Keyboard geometry, alignment, row fill and functional special keys are also proven on the 1024x600 ESP32-P4 display.
Generated runtime is physically proven for Input, Textarea, Checkbox, Switch, Radio, Progress, Circular Progress, Number Input and Select.
The final whole-screen Canvas ↔ P4 comparison proved graphite/orange semantic theme and border parity within this group. Number Input generated hardware steppers operated using serialized step and clamping. Switch used the amber checked state instead of LVGL blue. Circular Progress remained output-only with no knob or dragging, and Progress remained output-only and noninteractive. Input/Textarea focus did not automatically attach a keyboard.
-
the current combined exporter, Layout Designer/AI composer and export-server selection passes 295/296 tests across 35/36 suites;
-
all 33
ForgeUILvglExportsuites pass, including QR Code generation and the unchanged four-field result contract; -
the one failing export-server preflight fixture is the already-recorded pair of missing default-theme C sources, not a QR, Layout Designer, API or hook failure;
-
live generated-file inspection confirms QR construction/data update in
90_Studio_Export.c, its Text setter declaration in90_Studio_Export.h, no QR declaration in95_UserEvents.*,CONFIG_LV_USE_QRCODE=y, and CMake registration of90_Studio_Export.cplus95_UserEvents.c; -
the eleven-component Standard theme/parity regression passes 158/158;
-
Graphite/orange, Cyber teal and Nordic light are verified;
-
custom palette export is verified;
-
theme parity is verified through Canvas, Browser Preview, generated LVGL and physical ESP32-P4 for the eleven named Standard components;
-
Chart regression coverage includes native chart geometry, responsive gutters, Y-axis labels, X-axis point indexes and non-clickable sibling labels;
-
TypeScript, ESP-IDF 5.5.4 / LVGL 9.2.2 build, firmware flash and
git diff --checkpass for this milestone; -
System Runtime exporter regressions cover persistent containers, gear generation, internal page callbacks, visibility switching, the
10–100Brightness slider, hardware bridge and complete Wi-Fi Manager generation; -
Storage Runtime exporter and preview regressions cover lazy page creation, persistent reuse, reusable rows, paging, Select Item mode and the Delete Empty Folder workflow;
-
Wi-Fi generator regressions cover structured network rows, connected-detail fields, password dialog, open/protected connection branching and internal intent callbacks;
-
Studio and Browser Preview System regressions cover navigation, live brightness, deterministic Wi-Fi parity, password and forget workflows, session retention, disabled placeholders and preservation of application interaction;
-
focused LVGL exporter regressions pass;
-
Keyboard exporter lazy-creation, reusable-instance, textarea attachment, geometry, call-order and control-width regressions pass;
-
State Sheet and crop-pipeline tests pass;
-
Three-Position generated runtime regressions pass;
-
Button final-geometry, registry-dimension, PNG IHDR, LVGL descriptor fallback and linked-crop export regressions pass;
-
Light final-geometry, common OFF/ON scale, centring, initial-state and linked-crop export regressions pass;
-
Toggle, Status Indicator and Three-Position final-geometry, common state-scale, centring, fallback and linked fitted-asset export regressions pass;
-
Canvas, Browser Preview, live/standalone generator and persistence regressions preserve geometry ownership;
-
focused Led, Bar, Arc, Chart, standard Keyboard, Calendar, Roller, Message Box, Button Matrix, TabView and Tileview runtime tests pass;
-
focused Input, Textarea, Switch, Checkbox, Radio, Progress, NumberInput, Select, Image, Box and IconButton tests pass;
-
Switch focused exporter suite passed 7 tests; Radio suites passed 13; Progress suites passed 9; Circular Progress suites passed 5; Number Input reached 19 focused tests during stepper implementation, with 12 exporter outer-container and 8 shared-preview tests; Select focused suites passed 19;
-
an initially unused generated NumberInput step constant caused an ESP-IDF build failure before native steppers existed; the implemented stepper callbacks now consume that constant and generated runtime builds cleanly;
-
stale running Studio bundles required restart/refresh before regeneration produced the repaired exporter output;
-
focused Button Text, Text Value, Heading Text and Clock Presentation serialization, preview and exporter tests pass;
-
standard-component transition, clamping, collision-safe naming and hook-generation coverage passes;
-
developer hook preservation and missing declaration/stub merge coverage passes;
-
the complete LVGL exporter suite reached 212/212 after IconButton;
-
the Canvas component regression suite reached 51/51;
-
generated API and preservation tests reached 33/33;
-
live and standalone generation/preservation coverage passes for the new hooks;
-
TypeScript validation passes;
-
live
/exportand standalone/export-idf-projectvalidation pass for the standard runtime milestone; -
generated-output verification passes;
-
ESP-IDF firmware build passes for the recorded Wi-Fi Manager implementation and standard runtime milestone;
-
export-server.jssyntax validation passes; -
scoped diff validation for this subsystem passes.
The complete export-server suite is not claimed as passing while fg_upload_1024x600_neural_core_67dd4ba0.c and fg_upload_carbon_fiber_be774fd2.c are absent. That unrelated default-theme fixture issue is reported without weakening the validation boundary.
This physical record includes the named eleven-component Standard group and the 2026-07-30 Standard input and selection runtime group. No broader physical claim is inferred.
| Problem | Start here | Then inspect |
|---|---|---|
| Layout looks correct in Canvas but generated Boxes are missing | ForgeUILvglExport.ts Box traversal |
final project component list and Box parent/root classification |
| Layout child is missing from generated output | component export traversal | project insertion result, component type and parent/child representation |
| Region Box style differs from Browser Preview | Box branch in ForgeUILvglExport.ts |
semantic surface/border roles, radius, opacity and preview palette |
| Region Box receives an unexpected hook | Box export metadata | ensure Box remains setter-only visibility with no userEventHooks entry |
| Layout Designer adds an unexpected header declaration | publicApiDeclarations |
confirm only contained normal components contribute APIs |
| Auto Arrange runs on the P4 or appears in generated C | incorrect architecture | Auto Arrange must finish before generateForgeUILvglCode() |
Generated firmware references layoutRegionId |
incorrect exporter coupling | remove runtime interpretation of Studio-only region metadata |
| AI response shape reaches export server | Header.tsx transport |
AI must resolve into normal project components before export |
| Layout geometry is wrong in generated C | final serialized component x/y/w/h |
Layout Designer geometry and project insertion, not generated-file patching |
Generated layout is manually fixed in 90_Studio_Export.c |
wrong ownership boundary | repair Layout Designer/project/exporter source and regenerate |
| Generated LVGL object is wrong | ForgeUILvglExport.ts component branch |
resolved component props and Interactive Asset |
| Button hook is absent from export result | ForgeUILvglExport.ts Button branch |
userEventHooks set and hook naming |
| Button hook name is wrong or duplicated | hook-name helpers in ForgeUILvglExport.ts |
component name, asset label/name, uniqueness set |
| Button visual state is wrong | fg_interactive_button_event_cb generation |
per-instance Normal/Pressed sources |
| Button export ignores resized geometry | ForgeUILvglExport.ts Button branch |
persisted component w/h, parent size and child image centring |
| Button hardware artwork remains at native size | generated lv_image_set_scale(...) |
Normal/Pressed contain scale and dimension fallback path |
| Button CLICKED does not reach hook | Button event registration | .clicked_cb, 95_UserEvents.h/.c |
| Led setter or changed hook is missing | ForgeUILvglExport.ts Led runtime |
export-server.js, 95_UserEvents.* |
| Bar setter, range or changed hook is wrong | ForgeUILvglExport.ts Bar runtime |
export-server.js, 95_UserEvents.* |
| Arc setter, range or changed hook is wrong | ForgeUILvglExport.ts Arc runtime |
export-server.js, 95_UserEvents.* |
| Chart add/clear APIs or hooks are wrong | ForgeUILvglExport.ts Chart runtime |
export-server.js, retained chart/series, 95_UserEvents.* |
| Chart axis labels or gutters are wrong | ForgeUIStandardChart.ts shared layout |
StandardChartPreview.tsx, generated sibling label positions and Chart padding |
| Standard Keyboard Show/Hide hooks are wrong | ForgeUILvglExport.ts Keyboard runtime |
export-server.js, retained unattached keyboard, 95_UserEvents.* |
| Calendar date setter or hook is wrong | ForgeUILvglExport.ts Calendar runtime |
export-server.js, retained date, 95_UserEvents.* |
| Roller selection or text hook is wrong | ForgeUILvglExport.ts Roller runtime |
export-server.js, option metadata, 95_UserEvents.* |
| Message Box visibility or button hook is wrong | ForgeUILvglExport.ts Msgbox runtime |
export-server.js, panel metadata, 95_UserEvents.* |
| Button Matrix selection or hook is wrong | ForgeUILvglExport.ts ButtonMatrix runtime |
export-server.js, button map/count, 95_UserEvents.* |
| TabView selection is wrong | ForgeUILvglExport.ts Tabview runtime |
retained index/count, shared transition helper, lv_tabview_set_active() and lv_tabview_get_tab_active() |
| Tileview coordinate is wrong | ForgeUILvglExport.ts Tileview branch and shared preview |
retained row/column, native tile map, neighbour directions and active-tile lookup |
| Input setter does not update text | ForgeUILvglExport.ts Input runtime |
retained lv_textarea, NULL handling, unchanged comparison and focused Input exporter test |
| Input hook fires from setter | Input programmatic guard in ForgeUILvglExport.ts |
callback registration order, LV_EVENT_VALUE_CHANGED, generated 90_Studio_Export.c |
| Textarea placeholder and value are confused | Textarea exporter branch | serialized placeholder, retained current text and focused Textarea test |
| Switch setter or hook is wrong | Switch export map/branch | LV_STATE_CHECKED, guard, LV_EVENT_VALUE_CHANGED, 95_UserEvents.* |
| Checkbox label or checked state is wrong | Checkbox exporter branch | serialized label, native lv_checkbox, checked guard |
| Radio is expected to be mutually exclusive | Radio exporter branch | independent retained lv_checkbox instances; no group registry exists |
| Progress hook is incorrectly generated | Progress export metadata | setter-only publicApiDeclarations, absence from userEventHooks |
QR declaration is missing from 90_Studio_Export.h |
QR entry in publicApiDeclarations |
Header payload and normalizePublicApiDeclarations() Text-signature acceptance |
| QR renders incorrectly in generated C | ForgeUILvglExport.ts QR branch |
serialized size/colors/quiet zone/data and native lv_qrcode_set_* calls |
| QR setter exists but firmware QR support is absent | firmware/ForgeUI-One/sdkconfig.defaults |
confirm CONFIG_LV_USE_QRCODE=y and rebuild |
| QR unexpectedly creates a hook or asset source | QR output classification | it must contribute only generated code plus a public declaration |
| NumberInput clamp or parser is wrong | NumberInput exporter branch | textarea text parsing, int32_t range/step and programmatic guard |
| Select index or callback text is wrong | Select exporter branch | option count, clamp, lv_dropdown_get_selected_str() and callback buffer |
| Image source setter safely no-ops | Image exporter branch | LVGL-ready resolution, NULL/unchanged pointer checks and pending placeholder |
| Box visibility setter is wrong | Box exporter branch | retained non-root object, LV_OBJ_FLAG_HIDDEN, child attachment |
| IconButton disabled click still fires | IconButton exporter branch | enabled state, LV_STATE_DISABLED, LV_EVENT_CLICKED adapter |
| Slider Canvas interaction is mistaken for runtime behavior | StandardSliderPreview.tsx |
Canvas/Browser mode and unchanged generated LVGL |
New hook is missing from 95_UserEvents |
userEventHooks from ForgeUILvglExport.ts |
Header payload, export-server.js, preservation merge |
New public declaration is missing from 90_Studio_Export.h |
publicApiDeclarations from ForgeUILvglExport.ts |
Header payload and normalizePublicApiDeclarations() |
| Export server rejects a supported new signature | export-server.js normalizers/materializers |
component exporter test and export-server.test.js |
| Duplicate or collision suffix is wrong | deterministic allocation in ForgeUILvglExport.ts |
API, hook, retained-object and guard names |
| Button Text is missing | ForgeUIStandardButton.ts |
Button Inspector/Preview and Button exporter branch |
| Text Value is missing | ForgeUIStandardText.ts |
Text Inspector/Preview and Text exporter branch |
| Heading text is wrong | ForgeUIStandardHeading.ts |
Heading Inspector/Preview and Heading exporter branch |
| Clock formatting is wrong | ForgeUIStandardClock.ts and generated Clock formatter |
presentation properties, generated format and fg_rtc_get() |
| Clock Preview mismatches export | ClockPreview.tsx |
shared Clock presentation helper and Browser Preview renderer |
| Clock separator blinking is wrong | generated per-instance Clock callback | blinkSeparator, separator-visible state and one-second timer |
| Clock 12-hour formatting is wrong | generated Clock formatter | modulo conversion, midnight/noon and AM/PM |
| Clock seconds formatting is wrong | generated Clock formatter | showSeconds and RTC seconds field |
| Multiple Clocks overwrite one another | Clock allocation in ForgeUILvglExport.ts |
per-instance label, timer, separator state and collision-safe identifiers |
| Toggle hook is absent | Toggle branch in ForgeUILvglExport.ts |
userEventHooks, sanitized _Toggled name, and generateUserEventFiles() |
| Toggle callback receives the wrong bool | generated fg_toggle_input_event_cb() |
inversion order, fg_toggle_input_set(), and .toggled_cb |
| Toggle visual state does not persist | per-instance fg_toggle_input_t record |
saved initialState and OFF/ON sources |
| Toggle runtime is duplicated | runtime emission in ForgeUILvglExport.ts |
single fg_toggle_input_t / fg_toggle_input_set() generation guard |
| Three-Position hook is absent | Three-Position branch in ForgeUILvglExport.ts |
userEventHooks, sanitized _Changed name, and generateUserEventFiles() |
fg_three_way_state_t is missing |
generateUserEventFiles() |
_Changed hook detection and generated 95_UserEvents.h |
| Wrong Three-Position zone is selected | generated fg_three_way_input_event_cb() |
thirds calculation and component width |
| Three-Position coordinates are wrong away from x=0 | fg_three_way_input_event_cb() |
point.x - button_coords.x1 local conversion and bounds check |
| Child image intercepts touch | Three-Position object creation | child clickable flag and parent full-bounds clickability |
| Three-Position State Sheet is missing or stale | InteractiveAssetAIGenerator.tsx three-position-set flow |
master request, crop workspace and linked asset ID |
| LEFT/CENTER/RIGHT crops map to the wrong states | State Sheet row mapping | unique row assignments and crop-to-state draft update |
| Three-Position runtime is duplicated | runtime emission in ForgeUILvglExport.ts |
single fg_three_way_input_t / fg_three_way_input_set() generation guard |
| Generated LEFT/CENTER/RIGHT stub is wrong | generateUserEventFiles() |
_Changed signature branch and readable-state expression |
| Light setter is missing from C source | Light export preparation in ForgeUILvglExport.ts |
LVGL readiness and unique API name |
| Light declaration is missing from header | publicApiDeclarations export result |
Header payload and generateStudioExportHeader() |
| Light starts in wrong state | Light export branch | saved initialState and initial image symbol |
| Light is clickable or generates a hook | Light export branch | remove Button-style callback/hook behavior |
| Light export ignores resized geometry | ForgeUILvglExport.ts Light branch |
persisted component x/y/w/h, transparent container and centred child image |
| OFF/ON state change alters visible size or position | generated Light contain-scale call | common state-pair scale, identical centring and linked crop dimensions |
| Numeric contain scale is wrong | contain-scale calculation in ForgeUILvglExport.ts |
final component dimensions and both state-image dimensions |
| Legacy image emits the wrong scale | forgeUIResolveUploadedAssetDimensions() |
registry metadata, PNG IHDR bytes, generated LVGL descriptor helper, safe 256 fallback |
| PNG IHDR fallback is not used | ForgeUIUploadedAssetRegistry.ts |
data-URL decoding and forgeUIParsePngDimensions() |
| LVGL descriptor fallback is missing | generated Button or Light contain-scale helper | both lv_image_dsc_t.header.w/h values and scale-256 guard |
| Fitted artwork exports original state symbols | kind-specific Interactive Asset state IDs | linked cropped uploaded assets, resolver and assetSources |
| Visible-artwork fitting changes original uploads | ForgeUITwoStateVisibleBounds.ts |
linked crop registration and state-reference replacement |
| Configured preview remains stale after asset replacement | kind-specific Canvas preview | Interactive Asset registry event and uploaded-asset registry refresh; this occurs before generated export |
| Status Indicator setter is missing | Binary Output export preparation in ForgeUILvglExport.ts |
kind-aware lookup, LVGL readiness, and unique API name |
| Status Indicator starts in wrong state | Status Indicator export branch | saved initialState and per-instance runtime record |
| Status Indicator looks stretched in Canvas or Browser Preview | InteractiveStatusIndicatorPreview.tsx |
intrinsic image dimensions and centered contain-fit styles; this is outside generated firmware ownership |
| Browser Preview ignores resized component bounds | forgePreviewRenderer.tsx |
commonStyle, Browser Preview wrapper, fillContainer, type-specific preview renderer and centred contain-fit rendering |
| Status Indicator Canvas click does nothing | InteractiveStatusIndicatorCanvasPreview.tsx |
temporary local preview state; do not debug generated FG_Set_* for this design-time issue |
| Status Indicator is clickable on hardware | ForgeUILvglExport.ts Status Indicator branch |
verify no event callback or clickable flag was emitted |
| Status Indicator setter does not change state | generated fg_binary_output_t record and FG_Set_* |
saved OFF/ON symbols and setter-to-runtime-record mapping |
| Binary Output instances affect each other | generated runtime records | setter-to-record mapping and unique runtime names |
| Binary Output Runtime is duplicated | runtime emission in ForgeUILvglExport.ts |
shared fg_binary_output_t and fg_binary_output_set() generation |
| Export rejected before files are written | ForgeUIExportValidation.ts |
export-server.js |
| Missing generated C source | export-server.js |
Uploaded Asset Registry |
| Invalid generated asset source | ForgeUIExportValidation.ts |
exporter asset collection |
| Stale uploaded state asset blocks export | ForgeUIExportValidation.ts asset-kind/state checks |
Uploaded Asset Registry and generated source existence |
| Duplicate generated API | ForgeUIExportValidation.ts |
ForgeUILvglExport.ts |
Duplicate LV_IMAGE_DECLARE |
ForgeUILvglExport.ts |
export validation |
| Theme asset validation failure | export-server.js |
built-in Theme assets |
| Selected theme does not reach generated LVGL | ForgeUILvglExport.ts semantic palette resolution |
ForgeThemeContext.tsx, forgeThemeMap.ts, export payload palette |
| Canvas and Browser theme output differ | forgePreviewRenderer.tsx |
ComponentPreview.tsx, shared Standard preview palette props |
| Custom palette falls back unexpectedly | forgeThemeMap.ts |
generateForgeUILvglCode() palette option and live/standalone export call sites |
| P4 still shows the previous theme | generated LVGL style calls | confirm Theme -> Generate -> Build -> Flash completed |
| Direct Open Creator action targets the wrong asset | ForgeUINavigation.ts |
PreviewContainer.tsx, component ID and linked Interactive Asset ID |
| Confirm Crops does not complete | InteractiveAssetAIGenerator.tsx |
crop validity, PNG data URLs and ForgeUIAIImagePipeline.ts |
| Only part of a state set is registered | ForgeUIAIImagePipeline.ts |
conversion completion before forgeUIAddUploadedAssets() atomic batch |
| Toggle linked crops drift in size | Toggle State Sheet crop workspace | shared crop dimensions and linked resize rules |
| Keyboard is centered or offset at runtime | ForgeUILvglExport.ts Keyboard branch |
LV_ALIGN_TOP_LEFT after map/mode and final lv_obj_set_pos() ordering |
| Password dialog does not open | generated network-row callback | selected network security, dialog pointer and hidden flags |
| Password textarea does not attach keyboard | generated focus/click callback | lv_keyboard_set_textarea(), reusable keyboard pointer and top layer |
| Keyboard appears off-screen | generated keyboard geometry | LV_ALIGN_TOP_LEFT, (0, 350), lv_layer_top() coordinates |
| Wi-Fi projection makes password entry laggy | password-dialog projection pause | repeated label/row updates and obsolete diagnostic logging |
| Keyboard outer size falls back to native geometry | Keyboard final setup ordering | final lv_obj_set_size() and absence of later alignment/size calls |
| Keyboard special keys have wrong proportions | Keyboard buttonmatrix control array | numeric width units, control flags and row totals |
| Keyboard theme padding changes key fill | Keyboard LV_PART_MAIN / LV_PART_ITEMS styles |
explicit pad, gaps, border, font and style ordering |
| API metadata is absent from request | Header.tsx |
/export and /export-idf-project payloads |
| Input hook metadata is missing from payload | Header.tsx |
exact userEventHooks field from generateForgeUILvglCode() |
| Server rejects a valid hook signature | generateUserEventFiles() suffix handling |
_Clicked, _Toggled, _Changed validation and endpoint payload |
| Generated header is wrong | generateStudioExportHeader() |
normalizePublicApiDeclarations() |
| Hook files are missing | generateUserEventFiles() |
received userEventHooks and export endpoint |
| Live files are unexpectedly replaced | live ownership policy | /export writes generated live output |
| Standalone project lacks APIs | /export-idf-project |
copied/written files and public metadata |
| CMake cannot find hooks or assets | generated CMakeLists.txt |
export server source collection |
| Linker reports missing Button callback | 95_UserEvents.h/.c |
exact generated hook name |
| Linker reports missing Binary Output setter | 90_Studio_Export.h/.c |
exact Light or Status Indicator setter declaration/definition |
| Physical behavior differs from export | generated C and symbols | LVGL object wiring and copied asset sources |
| Canvas, Browser Preview, live and standalone sizes differ | persisted component geometry and generateForgeUILvglCode() |
shared generator result, contain-fit scale, centring and selected linked state assets |
| Gear missing | ForgeUILvglExport.ts System Runtime generation |
generated system_gear, application container and foreground ordering |
| System Runtime is not generated | generateForgeUILvglCode() output |
System container construction in generated 90_Studio_Export.c |
| System page does not open | generated fg_system_open_cb() |
gear event registration, System container pointer and hidden flags |
| Brightness slider is missing | generated Brightness container construction | lv_slider_create, range and object visibility |
| Brightness updates preview only | generated FG_Set_Display_Brightness() |
bsp_display_brightness_set(), BSP include and initialized display brightness |
| Brightness updates hardware only | ForgeUISystemContext.tsx and ForgeUISystemSurface.tsx |
preview slider state, label and brightness filter |
| System navigation fails | generated fg_system_show_page() and Back callbacks |
target container and LV_OBJ_FLAG_HIDDEN changes |
| Application disappears after closing System | generated application container | close callback, application hidden flag and foreground ordering |
| Container visibility switching is incorrect | generated System callbacks | all persistent container pointers and hide/show order |
| Storage page is missing | ForgeUILvglExport.ts Storage generation |
generated lazy Storage container |
| Storage Refresh fails | generated Storage Refresh callback | fg_sd_get_snapshot() and bounded request path |
| Storage browsing fails | generated Storage projection | fg_sd_list_directory() |
| Delete Folder remains disabled | generated Storage row callback | Storage projection, persistent row metadata and empty-folder flag |
| Delete Folder fails | generated Delete Empty Folder callback | fg_sd_delete_empty_folder() |
| Hosted transport fails | sdkconfig.defaults |
Hosted SDIO configuration |
| Scan works but Refresh does not | generated Scan callback and generated Refresh callback | both invoke fg_wifi_scan_start() |
| Rows remain blank after scan | backend completed network model and generated LVGL projection | row visibility assignment and list rebuild |
| Structured network rows do not populate | generated backend projection | 30_WIFI structured cache and fg_wifi_get_networks() |
| Connected details are missing or unreadable | generated detail labels and explicit styles | fg_wifi_snapshot_t, text color and opacity |
| Reconnect does nothing | generated reconnect callback | fg_wifi_reconnect() and persisted STA configuration |
| Forget leaves Saved state visible | generated forget callback | fg_wifi_forget(), snapshot saved flag and row projection |
| Wi-Fi rows ignore active theme | generated row styles | Browser Preview palette-derived row values |
| Browser Preview and physical Wi-Fi differ | deterministic preview state | ForgeUIWifiPage.tsx and generated snapshot projection |
studio/src/forgeui/layout/ForgeUILayoutDesigner.tsresolves template and child geometry before export.studio/src/forgeui/ai/ForgeAIRegionComposer.tsconverts AI semantic regions into normal components before export.studio/src/forgeui/ForgeUILvglExport.tsconsumes the final normal component document.studio/src/components/Header.tsxtransports only the unchanged exporter result.studio/export-server.jsvalidates and materializes only the unchanged generated-output contract.90_Studio_Export.*contains normal generated components and their existing APIs.95_UserEvents.*contains hooks only for actual interactive components, never for Layout Designer itself.
Detailed Studio ownership remains in 02_DEVELOPER_CODE_MAP.md.
Owns generated LVGL source, all five Interactive Asset branches, shared Button/Toggle/Three-Position/Binary Output runtimes, and the complete Standard LVGL Runtime generation. Standard ownership includes semantic theme propagation; Circular Progress retained runtime; Number Input container, textarea, stepper buttons and callbacks; Select closed/popup styling; Switch checked-state styling; Checkbox/Radio fallback-label normalization; native Chart plus responsive sibling axes; native QR Code creation and setter-only data updates; other retained objects/state; setters; LVGL event adapters; hook metadata; and collision-safe names. It also owns Button/Text/Heading serialized presentation, per-instance Clock formatting, the built-in System Runtime and all existing exporter metadata. It never writes files directly or owns Hosted transport, physical Wi-Fi truth or Storage filesystem operations.
studio/src/forgeui/theme/ForgeThemeContext.tsxsupplies the active preview palette.studio/src/forgeui/preview/forgeThemeMap.tsowns semantic role resolution, contrast selection and deterministic graphite fallback.studio/src/forgeui/preview/forgePreviewRenderer.tsxpasses the palette to Browser Preview Standard renderers.studio/src/components/editor/ComponentPreview.tsxpasses the active palette to Canvas Standard renderers.studio/src/forgeui/ForgeUIStandardChart.tsowns shared Chart geometry and deterministic axis models.studio/src/forgeui/preview/StandardChartPreview.tsxrenders the shared Canvas/Browser Chart surface and axes.
Owns uploaded image records, intrinsic dimension metadata, alpha-content metadata, PNG IHDR parsing and the registry-first dimension resolution used before generated LVGL descriptor fallback.
Owns non-destructive stable state-set alpha unions, compatible source-dimension validation, component-geometry mapping and linked crop generation used by Button, Toggle, Light, Status Indicator and Three-Position before export. Its filename and Button/Light compatibility APIs remain unchanged.
Owns the Button-facing Normal/Pressed wrapper over shared visible-bounds fitting.
Owns the Light-facing OFF/ON wrapper and linked fitted-asset naming over shared visible-bounds fitting.
Coordinates Build & Flash, Clean Build & Flash, standalone export, client preflight, and unchanged transport of code, assetSources, userEventHooks, and publicApiDeclarations to both export endpoints. Never invents runtime APIs or validates materialized firmware files.
Owns validation of accepted metadata; recognition and materialization of the supported new text, boolean, integer, index/text and click hooks and Text/Value/Checked/Selected/Selected_Index/Source/Visible/Enabled declarations; preservation merge; generated headers; disk writes; assets; CMake; and project packaging. The exporter decides names and signatures. The server never invents widget behavior.
Owns generated UI plus Interactive Asset, Standard LVGL Component and System Runtime implementations. Standard ownership includes retained Input/Textarea objects and guards; explicit Switch checked styling; Checkbox/Radio state and normalized labels; Progress range/value; Circular Progress retained object/value and output arc; Number Input outer container, textarea, increment/decrement buttons, shared step constant and callbacks; Select dropdown/index/count and popup styling; remaining retained objects/state; generated setters; LVGL event adapters; and calls into genuine-user hooks. Never contains developer product logic.
Owns fg_studio_export_create(...), every Binary Output setter and every completed Standard Runtime public declaration, including QR Code Text setters, with required public types including bool, int32_t, uint32_t, const char * and const void *. It contains no API for Icon, Divider, Scale, Line, Clock, Button Text, Text or Heading. The Three-Position enum is owned by 95_UserEvents.h. Never contains user implementations.
In live firmware, owns preservation-merged Interactive Asset and Standard hook implementations, including Input, Textarea, Checkbox, Switch, Radio, NumberInput, Select and IconButton hook families. Existing matching bodies survive, missing stubs are appended and unrelated hooks remain. Output-only Progress and CircularProgress intentionally add no hook. Other setter-only and presentation/API-free components add none. In standalone export, this becomes developer-owned callback/application code.
In live firmware, owns the generated Three-Position enum and preservation-merged Interactive Asset and Standard hook declarations, including text, checked/selected, integer, index/text and click signatures. Missing declarations are appended. It declares no Progress, CircularProgress, Image, Box, Icon, Divider, Scale, Line, Clock, Button Text, Text or Heading hook. In standalone export, it forms the developer-owned hook interface.
Owns compilation source registration and is generated by the export server.
Preserve these rules:
generateForgeUILvglCode()remains the only LVGL UI exporter.- Interactive Button uses a void
FG_On_*_Clicked(void)hook. - Interactive Toggle Switch uses a persistent
FG_On_*_Toggled(bool enabled)hook. - Interactive Three-Position Toggle uses
FG_On_*_Changed(fg_three_way_state_t state)and local component coordinates for touch selection. - Binary Output Interactive Assets generate public
FG_Set_*UI functions and no event hooks; interactive Standard setters never directly invoke their genuine-user hooks. - Button, Toggle, and Three-Position calls live in
90_Studio_Export.c; their enum/declarations/stubs live in95_UserEvents.c/.h. - All Binary Output setters are declared and implemented entirely in
90_Studio_Export.h/.c. Header.tsxtransports exporter metadata but does not create APIs.export-server.jswrites files but does not define widget behavior.- Live
95_UserEvents.c/.hfiles are Studio-generated and preservation-merged; matching developer bodies survive and missing declarations/stubs are appended. - Standalone
95_UserEvents.c/.hfiles become developer-owned after export. - Customer hardware and business logic never belongs in generated UI files.
- Client validation occurs before export submission.
- Server validation occurs before filesystem mutation.
- Only validated asset sources are written to CMake.
- Built-in Theme assets participate in validation.
- Generated runtime assumes validated inputs.
- Failed validation preserves the previous generated firmware.
- The Binary Output Runtime is generated once per export.
- Every Binary Output Interactive Asset reuses the shared runtime implementation.
- Future Binary Output assets extend the existing runtime rather than generating new runtime implementations.
- Toggle Input Runtime is generated once per export.
- Three-Position Input Runtime is generated once per export.
- Per-instance runtime state remains independent in every runtime family.
- Generated enum types are visible wherever hook declarations and developer implementations require them.
- Header and export server transport or materialize metadata but never invent runtime APIs.
- Hook generation preserves exact sanitized unique names across all hook families.
- Missing or stale state assets fail validation before filesystem mutation.
- Three-Position generation uses one master State Sheet request, but export and runtime retain three independent LEFT/CENTER/RIGHT uploaded assets.
- Confirm Crops registers a complete converted state set atomically; partial state-set registration is not a valid current path.
- State Sheet row remapping changes crop-to-state assignment without changing the generated runtime API.
- Keyboard map/mode configuration precedes explicit style,
LV_ALIGN_TOP_LEFT, final position and final size. - Keyboard dimensions come from the Canvas component; no global or resolution-specific scaling is introduced.
- Creator navigation selects component/asset context, while the exporter remains the sole owner of generated LVGL and runtime behavior.
- Direct Creator access across all five Interactive Assets does not change generated API direction.
- Status Indicator Canvas Preview may toggle local state for visual verification, but temporary preview state is never exported.
- Generated Status Indicator LVGL remains non-clickable and Binary Output state remains developer-controlled through
FG_Set_*. - Component geometry becomes authoritative after placement; reusable Interactive Asset dimensions do not overwrite persisted component geometry during export.
- Generated LVGL position and size for all five Interactive Asset branches always use persisted component
x/y/w/h. - Interactive Asset families that export two-state contained artwork reuse the common contain-fit scaling model rather than inventing independent geometry rules.
- Legacy image dimensions resolve in this order: uploaded-asset registry metadata, PNG IHDR, generated LVGL descriptor dimensions, then safe scale 256.
- Button Normal/Pressed, Toggle OFF/ON, Light OFF/ON and Status Indicator OFF/ON each use one common state-pair scale and centred child-image placement; Three-Position LEFT/CENTER/RIGHT uses one common three-state scale.
- Canvas, Browser Preview, live firmware and standalone export must remain visually equivalent for the same persisted component geometry and state assets.
- Visible-artwork fitting exports linked cropped state assets and never modifies the original uploaded images.
- Browser Preview wrappers preserve persisted component geometry before type-specific contain-fit rendering.
- System Runtime is generated separately from Interactive Assets.
- System Runtime generates no User Event callbacks.
- Interactive Assets remain alive while System Runtime pages are active.
- System Runtime currently uses generated persistent-container visibility switching.
- Closing System restores the existing application container without recreating it.
- Future animation must preserve generated runtime ownership, application state and Interactive Asset lifetime.
- Hosted Wi-Fi transport uses SDIO Slot 1.
- SD storage uses SDMMC Slot 0.
- The complete Wi-Fi Manager is generated System Runtime presentation and intent code.
- Generated LVGL projects structured backend snapshots and network records; it never owns physical Wi-Fi truth.
30_WIFIowns scanning, credentials, reconnect policy, DHCP and ESP-IDF interaction.- Physical firmware supports one persisted ESP-IDF STA configuration, not a multi-network database.
- Browser Preview Wi-Fi remains deterministic and hardware-independent.
- The native LVGL keyboard is created lazily as one reusable top-layer instance.
- System password fields attach through
lv_keyboard_set_textarea(), and keyboard geometry usesLV_ALIGN_TOP_LEFTbefore absolute positioning. - Periodic Wi-Fi projection pauses while password entry is active.
- No Wi-Fi User Event callbacks or public setter APIs are generated.
95_UserEvents.cremains unrelated to Wi-Fi System UI.sdkconfig.defaultsowns the permanent Hosted configuration.- Generated firmware assumes, but does not define, Hosted transport configuration.
- Scan and Refresh must always use the same backend scan path.
- Generated LVGL owns Wi-Fi presentation only.
- Hosted Connectivity Runtime owns physical scan execution and AP retrieval.
- Completed backend models are projected into LVGL.
- Generated code must never duplicate Hosted scan logic.
- Storage Runtime is generated System Runtime and never becomes an Interactive Asset or public developer API.
- Storage filesystem logic remains in
40_SD.c/.h. - Generated Storage code owns presentation and user intent only.
- Storage page and worker construction remain lazy; teardown waits for worker acknowledgement before deleting resources.
- The Storage worker and reusable row pool remain bounded.
- The Storage request model remains compact: operation kind, one path and one name.
- The shared Storage projection model remains compact.
- Delete File remains disconnected from generated LVGL until physically proven.
- Standard LVGL Component Runtime remains independent from Interactive Asset Runtime and System Runtime.
- Standard runtime objects, state, helpers, callbacks and API implementations belong in
90_Studio_Export.c. - Standard public declarations belong in
90_Studio_Export.h; semantic hooks belong in preservation-merged95_UserEvents.*. - Interactive Status Indicator remains a setter-only Binary Output Runtime asset with no event hook.
- Scale remains API-free while it owns no runtime value.
- TabView owns semantic selected-index runtime state.
- Tileview owns semantic coordinate runtime state.
- Button, Text and Heading own serialized visible content only.
- Clock owns presentation configuration only; RTC/system time owns displayed time.
- Line remains intentionally API-free.
- Generated Clock exposes no runtime setter.
- Generated Clock exposes no user hook.
- Per-instance guards suppress setter-generated LVGL events for interactive Standard controls.
- Creation and initial serialized assignment remain silent.
- Standard runtime ownership is limited to meaningful semantic state.
- Output-only Standard components generate setters without hooks.
- Presentation-only Standard components remain API-free.
- Radio has no mutual-exclusion grouping model.
- NumberInput is a composed generated control whose outer container owns the frame and whose textarea plus increment/decrement buttons consume serialized step.
- Select options remain serialized and have no runtime editing API.
- Image accepts LVGL source pointers, not asset IDs, paths or URLs.
- Box owns runtime visibility only.
- IconButton icon selection remains serialized.
- Slider runtime is complete and uses the standard silent-setter/genuine-user-hook contract.
- Deterministic collision suffixes cover new APIs, hooks, retained objects and guards.
- The exporter result remains exactly
code,assetSources,userEventHooksandpublicApiDeclarations. - No second generated-header or hook-generator architecture exists.
- Proven Standard components consume semantic theme roles instead of hard-coded decorative colours.
- Canvas, Browser Preview and generated LVGL resolve equivalent Standard semantic roles.
- Missing semantic values use the deterministic graphite fallback.
- Standard status colours may remain semantically independent where appropriate.
- Hardware theme changes require Theme -> Generate -> Build -> Flash; runtime hot switching is not implemented.
- Chart axis labels remain non-clickable siblings and do not alter native
lv_chartruntime behavior. - Chart X labels remain deterministic point indexes unless a future serialized X-axis model is explicitly added.
- The flashed ESP32-P4 remains the final generated-runtime proof; Canvas and Browser Preview are expected to match generated LVGL.
- Exporter changes are verified through Generate, inspection of regenerated
90_Studio_Export.c, Build, Flash and physical comparison. - The Standard Canvas Keyboard and private reusable System Runtime keyboard remain independent generated architectures.
- Restart or refresh the running Studio bundle after exporter changes and before regeneration.
- Layout Designer resolves into normal ForgeUI components before export.
- Layout Designer is not a generated firmware runtime family.
- Layout template mode creates no new public API family.
- Layout template mode creates no new User Event hook family.
- Smart Region Boxes remain Standard Box components.
- Region structure does not change the existing Box visibility API contract.
- Auto Arrange runs before export and never at firmware runtime.
- Generated firmware does not interpret
layoutRegionId. - Stable region keys are Studio/project metadata, not generated developer identifiers.
- AI Fill output is converted to the normal component document before export.
- Generated firmware cannot distinguish manual components from AI-filled components.
- The exporter result remains exactly
code,assetSources,userEventHooksandpublicApiDeclarations. - Header and export server do not transport or interpret AI region documents.
- Layout Designer must not introduce a parallel exporter.
- Layout Designer must not introduce another generated-header or hook architecture.
- Existing contained components retain their normal APIs and hooks.
- Final persisted
x/y/w/hremains the generated geometry source of truth. - Layout Region Box styling uses semantic theme roles.
- Layout Designer hardware proof must not be claimed until Generate -> inspect C -> Build -> Flash -> physical comparison is complete.
- QRCode owns native encoded output and a Text setter only; it generates no hook or asset source.
- QR generated firmware requires
CONFIG_LV_USE_QRCODE=y.
FORGEUI_NATIVE_COMPONENT_1__DASHBOARD_CARD_PROVEN__ESP32P4_VALIDATED__READY_FOR_SENSOR_TILE__2026-08-02
- Dashboard Card's Value, Units, Status and Progress setters and optional root click hook are verified on ESP32-P4.
- Collision-safe multi-instance output passes the shared Live/Standalone path.
- Internal composite objects remain private generated implementation details.
- Sensor Tile is next and is not implemented in this documentation pass.
FORGEUI_LVGL9_COMPLETE__44_OF_44_PRACTICAL_WIDGETS_PROVEN__ESP32P4_VALIDATED__DOCUMENTATION_COMPLETE__READY_FOR_NATIVE_FORGEUI_PLATFORM__2026-08-02
- All 44 practical registered LVGL widgets/components are physically proven on ESP32-P4 through their documented boundaries.
- Menu's native navigation, Back history and multi-instance independence are physically proven; it deliberately adds no public SDK surface in this pass.
- The generated Runtime SDK foundation now carries the completed practical LVGL program into ForgeUI Platform development.
- The shared Live/Standalone generator emits
lv_menu_create, native pages, sections, item containers, separators, load-page events, initial root page and LVGL-owned back history. - Multiple Menu instances use independent
objN_page_Nand item symbols. - No public Menu declaration or
95_UserEventshook is generated in this pre-proof pass. Menu is IMPLEMENTED — READY FOR PHYSICAL PROOF.
FORGEUI_LVGL9_CLOSURE_BATCH2__WINDOW_ESP32P4_PROVEN__DOCUMENTATION_ALIGNED__READY_FOR_MENU__2026-08-02
- Native
lv_win_create, header/title/button and content-object generation is Registry-backed and shared by Live Studio and Standalone Export. - Two generated Window instances and their independent close controls passed physical Waveshare ESP32-P4 validation. Window is PROVEN.
- Window adds no public Runtime SDK declaration or
95_UserEventshook at this save point. Menu is the final practical LVGL 9.2 implementation target.
FORGEUI_LVGL9_CLOSURE_BATCH1__SPAN_ANIMIMAGE_IMAGEBUTTON_ESP32P4_PROVEN__DOCUMENTATION_ALIGNED__READY_FOR_WINDOW_MENU__2026-08-02
- Native
lv_spangroup,lv_animimgandlv_imagebuttonoutput passed ESP32-P4 Live/Standalone parity. - Span remains presentation-only; Animation Image remains API-free; Image Button retains only enabled control and a genuine click hook.
- Window is implemented and ready for physical proof; Menu is next after proof.
- Span and Animation Image add no public SDK surface.
- Image Button adds only
FG_Set_<Name>_Enabled(bool)andFG_On_<Name>_Clicked(void)through existing generated ownership. - All three native branches are shared by Live and Standalone Export and remain recorded here at their historical pre-proof boundary; physical validation completed on 2026-08-02.
FORGEUI_FINAL_LVGL9_AUDIT__39_REGISTERED_STANDARD_WIDGETS_PROVEN__5_PRACTICAL_CLOSURE_WIDGETS__2026-08-01
- No runtime API changed during the audit.
- Span and Animation Image remain API-free unless meaningful application state is established; Image Button, Window and Menu receive APIs or UserEvents only for genuine native semantics.
FORGEUI_STANDARD_WIDGET_PIPELINE__39_OF_39_STANDARD_WIDGETS_ESP32P4_PROVEN__BATCH_D_COMPLETE__READY_FOR_FORGEUI_WIDGETS__2026-08-01
- All Standard generated API and UserEvent families retain their existing ownership; Batch D added no duplicate runtime concept or unnecessary public API.
- QR Code, Icon Button, Icon and Canvas completed physical ESP32-P4 proof and Live/Standalone parity, closing the Standard Widget Library at 39/39.
FORGEUI_BOARD_PROFILES__EXPORT_TIME_FEATURE_GATING__LAZY_SYSTEM_TOOLS__CONNECTED_WIFI_45KB_FREE__RAM_OVERLAY__READY_FOR_FINAL_OPERATOR_VALIDATION__2026-07-31
ForgeUILvglExport.tsconsumes the persisted Board Profile feature set and removes disabled includes, models, callbacks, pages, timers and assets.export-server.jswrites00_ForgeUI_Features.hand applies the same profile to live and standalone CMake/source/component-manifest output.FG_FEATURE_WIFI,FG_FEATURE_SD_CARDandFG_FEATURE_RTCgate backend-facing behavior;FG_FEATURE_WIFI_MANAGER,FG_FEATURE_STORAGE_BROWSER,FG_FEATURE_DIAGNOSTICSandFG_FEATURE_SETTINGSgate their generated UI/runtime owners.FG_FEATURE_RTC 0skips every DS3231 access and enters software/NVS time initialization directly; it does not remove time from Clock or Runtime APIs.- The export source of truth is persisted
firmwareFeatures.rtc, shared by Live Studio and Standalone Export. Missing legacy values normalize totrue. This project/board option is configured at ForgeUI Studio → Board: ESP32-P4 7B → Configure Hardware → Hardware Configuration → Optional Hardware → External RTC, not in the generated runtime System menu. Enabled mode retains the DS3231 at I2C0x68and fallback behavior. - Wi-Fi Manager/dialogs and Storage UI/worker resources are lazy. Diagnostics is implemented. The compact RAM overlay in the built ELF is not reproducible from the current managed-component source and remains a durability issue.
FORGEUI_WIDGET_REGISTRY__LAYOUT_TEMPLATE_LIBRARY__QRCODE_RUNTIME__READY_FOR_QR_HARDWARE_PROOF__2026-07-30
- Generated architecture: The implemented Dashboard definition resolves to
ordinary components and the unchanged four-field exporter result. QR Code
generates native LVGL QR construction plus a setter-only
declaration/implementation in
90_Studio_Export.*; it adds no hook, UserEvent stub, asset source or transport field. - Validation: Focused Layout Designer/AI composition and QR exporter tests pass. Live generated
90_Studio_Export.c/.hinspection confirmslv_qrcode_set_*initialization andFG_Set_QR_Code_Text. QR ESP-IDF build, flash and physical scan proof remain pending.
FORGEUI_LAYOUT_DESIGNER__DASHBOARD_SMART_REGIONS_AUTO_ARRANGE_AI_FILL__CANVAS_AND_BROWSER_PREVIEW_MANUALLY_VERIFIED__READY_FOR_EXPORT_AND_HARDWARE_PROOF__2026-07-30
- What changed: Added the first Dashboard Layout Designer vertical slice using normal Box regions, region assignment, Auto Arrange and AI Fill. Layout Designer output remains ordinary ForgeUI components before entering the generated export pipeline.
- Why: Direct AI-owned pixel coordinates produced crowded layouts. ForgeUI now resolves template geometry and component placement before export while preserving the generated firmware API boundary.
- Generated architecture:
generateForgeUILvglCode()still receives the normal component model and returns onlycode,assetSources,userEventHooksandpublicApiDeclarations. Smart regions create no runtime family, public APIs, hooks or new transport fields. Region Boxes use the existing Box branch and semantic theme styling. - Validation: Canvas and Browser Preview were manually verified, including AI Fill Dashboard. Focused Layout Designer and Box exporter tests passed. Full generated-C inspection, production build, ESP-IDF build, flash and physical ESP32-P4 parity remain pending.
FORGEUI_STANDARD_LVGL_PARITY__INPUT_TEXTAREA_CHECKBOX_SWITCH_RADIO_PROGRESS_CIRCULAR_PROGRESS_NUMBER_INPUT_SELECT__SEMANTIC_THEME_AND_BORDER_PARITY__ESP32P4_PROVEN__2026-07-30
- What changed: Completed generated semantic-theme, border, state and geometry parity for Input, Textarea, Checkbox, Switch, Radio, Progress, CircularProgress, NumberInput and Select. Added the completed Number Input hardware runtime, Circular Progress output runtime, Select popup runtime, Switch checked-state runtime and fallback-label normalization.
- Why: Generated LVGL had diverged from Canvas/Browser Preview, native theme defaults leaked into checked/selected states, Number Input lacked device-side steppers, Circular Progress was incomplete, and stale Studio bundles could regenerate old C after exporter source changes.
- Generated architecture:
ForgeUILvglExport.tsgenerates explicit semantic LVGL selectors, retained Circular Progress state, composed Number Input container/textarea/buttons/callbacks, Select popup styling and checked/fallback normalization.90_Studio_Export.*owns the generated implementations/APIs; interactive hooks remain preservation-merged in95_UserEvents.*; Progress and CircularProgress remain hook-free. - Validation: Focused component/exporter suites passed, the NumberInput step constant is actively consumed, generated runtime builds cleanly, and final whole-screen ESP32-P4 comparison proved theme, border and interaction parity within the named scope.
FORGEUI_STANDARD_LVGL_THEME_PARITY__ELEVEN_COMPONENTS__CANVAS_BROWSER_GENERATED_LVGL_ESP32P4_PROVEN__2026-07-29
- What changed: Added semantic theme propagation through Canvas, Browser Preview and generated LVGL for Led, Bar, Arc, Chart, Table, Keyboard, Calendar, Scale, Roller, MsgBox and ButtonMatrix; completed generated Chart Y-axis values and X-axis point-index labels; and verified custom palette export.
- Why: Generated firmware needed to preserve the selected Studio theme and preview appearance without hard-coded decorative colours, duplicated rendering architecture or changes to native runtime APIs and hooks.
- Final generated architecture:
ForgeThemeContext.tsxsupplies the selected palette,forgeThemeMap.tsresolves semantic roles and deterministic fallback, preview renderers consume those roles, andForgeUILvglExport.tsemits equivalent LVGL styles. Chart retains nativelv_chartplus responsive, non-clickable siblinglv_labelaxes. P4 theme changes remain Generate -> Build -> Flash. - Validation: The eleven-component regression passes 158/158. Graphite/orange, Cyber teal, Nordic light, custom palette export, Canvas parity, Browser parity, generated LVGL parity, TypeScript, ESP-IDF 5.5.4 / LVGL 9.2.2 build, firmware flash, physical ESP32-P4 review and
git diff --checkare proven.
FORGEUI_STANDARD_LVGL_RUNTIME_V1__DEVELOPER_API_SURFACE__INTERACTIVE_OUTPUT_PRESENTATION_CLASSIFICATION__ARCHITECTURE_COMPLETE__2026-07-29
- What changed: Added generated APIs and hooks for Input, Textarea, Switch, Checkbox, Radio, NumberInput, Select, IconButton, Slider and List; setter-only APIs for Progress, Image and Box; classified Icon, Divider and Spinner according to their API-free contracts; preserved Button/Text/Heading/Clock presentation ownership; and extended collision-safe naming and hook preservation.
- Final architecture: Interactive state uses a guarded setter plus genuine-user hook; output state uses a setter only; serialized presentation has no runtime API; and components with no semantic state remain API-free.
90_Studio_Export.*remains generated, while live95_UserEvents.*remains preservation-merged and standalone copies become developer-owned. - Validation: Exporter reached 212/212, Canvas reached 51/51, generated API/preservation reached 33/33, focused component suites and TypeScript/syntax/diff checks passed. One unrelated full export-server fixture failure may remain due to the two missing default-theme assets. No blanket physical hardware proof is claimed.
FORGEUI_STANDARD_LVGL_RUNTIME_APIS__TABVIEW_TILEVIEW__TEXT_COMPONENT_PROPERTIES__CLOCK_PRESENTATION__ARCHITECTURE_COMPLETE__2026-07-29
- What changed: Added TabView active-index runtime, Tileview active-coordinate runtime, Button Text, Text Value, Heading Text and Clock Presentation; recorded Line and Scale as intentionally API-free; and repaired per-instance Clock presentation ownership.
- Final architecture: Semantic standard-widget state receives retained APIs and hooks, serialized visible content generates LVGL presentation without runtime transitions, and Clock formats RTC-owned time without a Clock setter or changed hook.
- Validation: This save point originally recorded automated evidence only. TabView and TileView subsequently completed their separately documented ESP32-P4 physical proof.
Dashboard is implemented. Industrial HMI, Control Panel, Monitoring, SCADA Overview and Mobile / Portrait are roadmap candidates. Any additional definition must resolve into the same normal component model before export. It must not create another exporter, generated runtime, transport field, template-specific generated header or template-specific UserEvent file.
Current implemented pages:
- Application
- Launcher
- Display / Brightness
- Wi-Fi Manager
- Storage Browser
- Native LVGL Keyboard
Future pages:
- Bluetooth
- Sound
- Device
Storage Browser is part of the reusable generated System Runtime and is the reference architecture for demand creation, bounded backend projection and acknowledged worker teardown. Future pages may reuse typed navigation, structured cards, backend projection and the shared native keyboard where text entry is required. They do not become Interactive Assets, generate User Event callbacks or public APIs.
Each future System page follows:
Generated LVGL Page
+
Internal callbacks
+
Firmware backend
These future pages are not implemented today. No future hardware or public API is defined by this extension rule.
Current proven runtime families:
Interactive Input Runtime
├── Interactive Button
└── Interactive Toggle Switch
Three-Position Input Runtime
└── Interactive Three-Position Toggle Switch
Binary Output Runtime
├── Interactive Light
└── Interactive Status Indicator
Standard LVGL Component Runtime
├── Scalar / visual output: Led, Bar, Arc, Progress, CircularProgress
├── Streaming output: Chart
├── Text entry: Input, Textarea, NumberInput
├── Boolean / selectable input: Switch, Checkbox, Radio
├── Option selection: Roller, Select, Button Matrix, TabView, Tileview
├── Actions: IconButton
├── Visibility / dialog services: Keyboard, Message Box, Box
├── Date selection: Calendar
├── Runtime image output: Image
├── Encoded output: QRCode
├── Serialized presentation: Button, Text, Heading, Clock
└── API-free presentation: Scale, Line, Icon, Divider
System Runtime
├── Application
├── Launcher
├── Display / Brightness
├── Wi-Fi Manager
├── Storage Browser
├── Native LVGL Keyboard
└── Future System Pages
Hosted Connectivity Runtime
├── ESP-Hosted
├── Wi-Fi Remote
├── ESP32-C6
└── SDIO Slot 1
Future Standard APIs follow:
Interactive semantic state → public control API → retained state → genuine-user hook
Output semantic state → public setter only
Serialized presentation → no runtime API
No semantic state → intentionally API-free
Genuine future concepts include Radio Group, dynamic Select options, Gauge, Meter, Seven Segment and Numeric Display. Implemented Slider, Radio, Checkbox, NumberInput, Select, Progress, CircularProgress, Image and QRCode must not be described as future runtimes.
Future controls must extend the existing exporter, export-result metadata, Header transport, export-server materialization, generated files, and ownership model. They must not introduce a parallel exporter, a second hook generator, a second generated-header system, or a separate firmware API layer.
Any compatible two-state artwork family must reuse the established geometry path: component-authoritative bounds, registry metadata, PNG IHDR recovery, LVGL descriptor fallback, one safe contain scale and centred image placement. If it supports visible-artwork fitting, it must create linked cropped assets through the shared two-state visible-bounds framework and preserve original uploads.
Generated export also supports reusable generation workflows:
Reusable generation workflow
Master image
-> linked crop regions
-> state-to-row remapping
-> atomic uploaded-asset registration
-> existing runtime-family export
Toggle uses the pattern for two states and Three-Position uses it for three. The architectural extension point is an N-state generation workflow: one master, N linked crop regions, an explicit unique state mapping, and one atomic registration boundary. This describes how a future state set should feed an existing or deliberately extended runtime contract; it does not claim that additional widgets or runtime families are implemented.
The built-in System Runtime is part of the interface ForgeUI generates. It extends generated platform behavior alongside the application without changing the developer callback boundary or becoming an Interactive Asset. Its generated Wi-Fi page renders and controls the separate Hosted Connectivity Runtime through internal callbacks and the non-generated 30_WIFI backend; generated code never owns the Hosted transport. Its generated Storage Browser renders and controls the separate Storage Runtime through internal callbacks and the non-generated 40_SD backend; generated code never owns filesystem operations.
ForgeUI generates the interface. ForgeUI exposes the interface. The developer supplies the application.
Programmatic UI updates and genuine user interaction remain separate API directions.
Maintain this document only when the generated API or ownership boundary changes.
Standard List owns no setter and no retained selection runtime API. Each
exported List contributes one collision-safe semantic hook to
userEventHooks:
void FG_On_<List_Name>_Item_Clicked(
uint32_t index,
const char * text);ForgeUILvglExport.ts creates immutable per-row event data containing the
zero-based index, generated label text and that List's hook. Each native
lv_list_add_button registers the shared fg_list_item_clicked_cb exactly
once for LV_EVENT_CLICKED. The shared callback forwards the event data to the
developer hook. Construction, hydration and startup do not call it.
Duplicate component names use the standard collision suffix before
_Item_Clicked, for example
FG_On_System_Menu_Item_Clicked and
FG_On_System_Menu_2_Item_Clicked.
Both live generation and standalone export carry the hook through the existing
userEventHooks payload. generateUserEventFiles() emits and
preservation-merges the matching declaration and implementation in
95_UserEvents.h/.c; no new payload field or public declaration in
90_Studio_Export.h is introduced.
This dated boundary recorded the original 39/39 milestone. The current
practical LVGL 9.2 proof total is 42. Image export uses
native lv_image_create(), persisted intrinsic dimensions and canonical
source-aware Contain scaling; the physical proof emitted scale 60 from the
serialized model. Line export uses native lv_line_create() with persisted
geometry and style. Both passed Live/Standalone ESP32-P4 proof. Button, Heading,
Box and Divider are also proven. Text export now applies complete multiline content,
geometry, wrap mode and alignment consistently. Icon export now derives
source-aware scale from the shared 92% automatic target and emits centered
pivots/alignment. Text has since completed physical validation and is
PROVEN. Icon final re-proof is also PROVEN.
The complete canonical pipeline and 90/95/96 ownership explanation is maintained
in 09_FORGEUI_FI_RUNTIME_GUIDE.md; this code
map remains authoritative for generated signatures and file locations.
For each used Standard Icon whose generateRuntimeApi is enabled, Studio emits
FG_Set_<ComponentName>_Visible(bool), Opacity(uint8_t) and Color(uint32_t)
in 96_FiRuntime.h, with implementation and retained pre-init state in
96_FiRuntime.c. 90_Studio_Export.c binds the actual lv_image or lv_label.
When enableClick is true, 90 attaches one LV_EVENT_CLICKED callback and 95
declares/preserves FG_On_<ComponentName>_Clicked(void). The setters are silent.
Application examples, ownership rules and LVGL task-safety guidance for Native
Components are maintained in
10_FORGEUI_NATIVE_COMPONENT_RUNTIME_GUIDE.md.
Names are deterministic and collision-safe; the selected Fi asset does not name
the API. Runtime-disabled instances emit no setters, click-disabled instances
emit no hook, and an unused 96 layer is omitted from files, headers and CMake.
Current status: PROVEN ON ESP32-P4. Physical
ESP32-P4 evidence proves the 90 → 95 path: the three separate generated hooks
FG_On_Comp_MS9QE1N7GA5O3_Clicked,
FG_On_Comp_MS9Q2MXPEJP7D_Clicked and
FG_On_Comp_MS9Q42SGCB4EB_Clicked each fired exactly once per deliberate tap,
with no startup callback. SD remained ready; Wi-Fi failure during the run was
unrelated. Batch D also physically proved the 90 → 96 path: Visible,
Opacity and Color setters, retained pre-bind state, repeated-setter
suppression, click-disabled non-interaction, independent presentation instances
and Live/Standalone parity.
Network Status Card is HARDWARE VALIDATED and read-only. The shared exporter emits six silent persisted-ID setters:
void FG_Set_<NetworkStatusCard>_Connected(bool connected);
void FG_Set_<NetworkStatusCard>_Network_Name(const char * name);
void FG_Set_<NetworkStatusCard>_IP_Address(const char * ip);
void FG_Set_<NetworkStatusCard>_Signal_Strength(int32_t percent);
void FG_Set_<NetworkStatusCard>_Status_Text(const char * text);
void FG_Set_<NetworkStatusCard>_Network_Type(int32_t type);API identity is rename-stable and duplicate instances are isolated. No
FG_On_* contract or 95_UserEvents entry is generated. Canvas monitoring
values initialize LVGL; a live Wi-Fi snapshot overrides them before the
optional System Wi-Fi Manager page gate. ESP32-P4 validation confirms live
SSID, DHCP IPv4, RSSI-derived signal, and Online/Offline projection on the
application page.
Tank Level Card is HARDWARE VALIDATED and display-only. The shared exporter emits six silent persisted-ID setters:
void FG_Set_<TankLevelCard>_Level(float percent);
void FG_Set_<TankLevelCard>_Volume(float value);
void FG_Set_<TankLevelCard>_Capacity(float value);
void FG_Set_<TankLevelCard>_Units(const char * units);
void FG_Set_<TankLevelCard>_LowLevel(float value);
void FG_Set_<TankLevelCard>_HighLevel(float value);The contract is rename-stable and duplicate-instance isolated. Private fg_*
LVGL objects remain generator-owned. No FG_On_* contract or 95_UserEvents
entry is generated because Tank Level Card has no genuine user interaction. See
docs/FORGEUI_TANK_LEVEL_CARD.md.
Battery Card is HARDWARE VALIDATED and read-only. The shared exporter emits
seven silent persisted-ID setters for percentage, voltage, current, charging,
health, runtime minutes, and temperature. APIs are rename-stable and duplicate-
instance isolated; private LVGL remains internal and no FG_On_* contract is
generated. Battery Card intentionally contributes no UserEvents in the current
release. Browser Preview, Live Studio, and generated LVGL parity, successful
ESP-IDF build and flash, and physical rendering are confirmed; exhaustive
physical setter interaction is not claimed. See
docs/FORGEUI_BATTERY_CARD.md.
IO Monitor is HARDWARE VALIDATED and read-only. The shared exporter emits
four semantic, bool-returning setter families using the persisted-ID stem:
bool FG_Set_<IOMonitor>_DigitalInput(const char * channel, bool state);
bool FG_Set_<IOMonitor>_DigitalOutput(const char * channel, bool state);
bool FG_Set_<IOMonitor>_AnalogInput(const char * channel, float value);
bool FG_Set_<IOMonitor>_AnalogOutput(const char * channel, float value);The APIs are rename-stable, duplicate-instance isolated, backed by bounded row
storage, and silent. They are the supported application boundary; direct access
to private fg_* LVGL objects is unnecessary. IO Monitor emits no touch-related
FG_On_* contract. In particular, row-selected/state-changed callbacks are not
part of the current documented API, and setter-driven state projection is not a
UserEvent. See docs/FORGEUI_IO_MONITOR.md.