name: dynamic-form-page
description: Guides how to adapt lib/modules/dynamic_form so plugin dashboards or configuration forms render correctly. Use when a request involves /plugin/dynamic-form, DynamicFormPage, new block types, converter tweaks, or controller bindings for a plugin.
Dynamic Form Page Skill
Intent
- Target tasks that mention
DynamicFormPage,/plugin/dynamic-formroutes, or the dynamic form module when a plugin dashboard or configuration form needs adjustment. - Follow this skill when an ask involves wiring a new plugin into
/plugin/dynamic-form, creating/editing block types, updating converters for new API payloads, or improving the Vuetify renderer.
Routing and bindings
lib/main.dartregisters/plugin/dynamic-form/page(tagpage) and/plugin/dynamic-form/form(tagform). Each binding lazy-instantiatesDynamicFormController, callsinitwith/api/v1/plugin/page/<id>or/api/v1/plugin/form/<id>, forwards the optionaltitle, and setsapiSavePath = /api/v1/plugin/<id>for the form route.- Always pass
controllerTagintoDynamicFormPage(or rely onGet.currentRouteas the widget currently does) so it finds the correct controller for your route variant.
Controller and data flow
DynamicFormControllerownsapiPath,apiSavePath,formMode,pageNodes,blocks, andformModel.load()GETsapiPath, parsesDynamicFormResponse, keeps the rawpagetree forVuetifyPageRenderer, and handsconf/modelpayloads toFormBlockConverter.formModelstores field values keyed byprops.model/name.hasFormModelunlocks the AppBar Save button and the editable widgets in_buildBlock.save()returns early when nothing is editable, PUTs toapiSavePath(or POSTs via_saveTrashClean), and updatessaveSuccess/errorText.formModePluginslists IDs such asAutoSignIn,TrashClean,MonitorPaths,SiteStatistic,MedalWall, andnexusinviteethat should default to form mode even when routed through/page. Add new IDs there when a plugin needs editing instead of read-only streaming. UseTrashCleanConverteras the example of combining multiple backend endpoints with both form and dashboard blocks.
Rendering decisions
_toDisplayItemsgroups sequentialStatCardBlocks into_DisplayStatCardGridso stat dashboards stay in 2×2 grids, while other blocks pass through_buildBlock._buildBlockdispatches onFormBlockvariants to the widgets underwidgets/. Update_buildBlock(and_toDisplayItemsif needed) whenever you add a variant.- When
pageNodesis populated andformModeis false,DynamicFormPagerenders the recursiveVuetifyPageRenderer, which already understandsVCard,VRow,VTextField,VSelect,VSwitch,VTable, etc. Extend_VuetifyNodewhen the backend introduces a new component. - The FAB transitions to
/plugin/dynamic-form/formfor the current plugin so users can switch into edit mode._CleanProgressSheetaccompanies the TrashClean pipeline by callingcontroller.triggerClean()andfetchCleanProgress().
Extending the dynamic form page
- Add new block variants to
FormBlock(models/form_block_models.dart), create a matching widget underwidgets/, and render it in_buildBlock. Feed the backend values throughformModelsocontroller.updateFieldcan save them. - Teach
FormBlockConverterto emit the block via_collectConfNodes(configuration payloads) or_collectFormFields(page payloads). Use_extractSwitch,_extractTextField,_extractCron, etc., as templates for reading labels, hints, and values. - If the backend only returns Vuetify nodes, update
widgets/vuetify_renderer.dartto handle the newcomponent/props. Reusevuetify_css.dartfor spacing andvuetify_mappings.dartfor colors/icons. - Always verify that
apiSavePathpoints at the endpoint the backend expects and that the names you derive fromprops.model/props.namematch what the server reads.
References
references/architecture.md— module layout, routing, controller lifecycle, and the TrashClean example.references/block-types.md— the current block/widget map, converter notes, and an extension checklist.