The dev panel
The dev panel is the in-browser editor. Two things set the V21 panel apart from what older notes describe: its fields are generated from the schemas, and it lives in a shadow root.
Fields are no longer hand-coded
DevPanelRenderer resolves the block's schema and renders one field per declared property:
controlpicks the widget;sectionpicks the accordion;visibleWhendecides whether the field shows;dependentssays what gets re-rendered when it changes.
The practical consequence: to add a setting to a block, you edit its schema, not the panel. A forgotten dependents entry is the usual reason a field keeps showing a stale value after another one changed.
One way in for writing
Every change goes through a single entry point, WebFormController.setBlockPropertyV21:
{ uuidKey, property, value, … }
It resolves the schema, validates the value, routes it to the right place (properties, the CSS cube, an event, a style preset) and answers with the actions to replay. Rich text, Monaco and events included — nothing escapes it any more.
It writes into ds[GG_EDIT_CONTEXT], the table currently being edited.
Knowing which screen you are on
Three process variables carry the edit context:
| Variable | Meaning |
|---|---|
GG_EDIT_CONTEXT | The table being edited — page content or template content |
GG_IS_MODEL_EDITOR | The template editor is open |
GG_IN_MODEL_RENDER | A template is being rendered inside a page |
Trap: never identify the screen from
WEB_vt_Url. Every panel action arrives on/bweb/call-actionwhatever the screen, so the URL tells you nothing — and splitting it into path segments raises an index error.
The shadow root changes the rules
The panel is mounted in a shadow root. Three habits break:
- implicit globals are dead — a function declared in one panel script is invisible from another unless it is exported;
event-onClickattributes in markup never fire — attach listeners inside the shadow root;document.querySelectordoes not reach panel nodes, and jQuery does not work there either.
And the mirror trap: the panel itself carries data-uuid attributes for the blocks it lists. An unscoped [data-uuid=…] selector aimed at the page also matches panel nodes — which has already destroyed the wrong element on a delete. Scope the selector, and quote and escape the value.
Styling inside the shadow root
output.css repaints every label and input of the panel. A rule written as #devPannel .x has lower specificity and loses on background, border and color (geometry still applies). Prefix with the tab's id and use !important for backgrounds.
Editors bundled by webpack (rich text, Monaco) mount but come out unstyled when style-loader injects into document.head instead of the shadow root. A webpack insert option handles this — recheck it after every rebuild, it has regressed more than once.
Access
The panel is served to a user allowed to edit the page. After a 4D restart the web session is lost and the panel disappears — log in again rather than hunting for a bug.
See also
- The properties contract — what the panel is editing
- Reload or restart — what to reload after changing a schema

