Feature parity matrix
Mateu’s contract is the wire: any server can serve any renderer. Coverage differs by implementation — this page is the honest snapshot, and the single place to update when something is ported. ✅ full · 🟡 partial (see note) · — not yet.
Servers
Section titled “Servers”The Java server is the reference implementation; the .NET and Python servers emit the same wire
for the surface below (verified by golden-JSON tests in backend/dotnet/test and
backend/python/tests).
| Feature | Java | .NET | Python |
|---|---|---|---|
| Forms, sections, field types, validation | ✅ | ✅ | ✅ |
| CRUD (list / detail / edit / new / save / delete) | ✅ | ✅ | ✅ |
| App shell + menus + navigation | ✅ | ✅ | ✅ |
App shell + menus authored IN CODE (Java AppSupplier/MenuSupplier returning a fluent AppShell; .NET IAppSupplier/IMenuSupplier + AppShell; Python AppSupplier/MenuSupplier + AppShell) — the whole shell (or just the menu) computed at request time, overriding the static @App/@Menu declarations | ✅ | ✅ | ✅ |
App shell as data (type: AppShell definition bound to a mount route) with its FLOWS — actions: with steps: that a menu RuleLink (RunAction rule) runs client-side, no round-trip — plus widgets: and the header switches themeToggle/commandCenter/chromeless/accessKeys. Code-built shells carry the same (AppShell.builder().action(...)). The ports have no type: AppShell definitions: N/A there | ✅ | N/A | N/A |
Wizards (incl. branching, cross-step state, @WizardProgress BAR/STEPS/RAIL) | ✅ | ✅ | ✅ |
CRUD create/edit in a drawer (editInDrawer — save closes + refreshes the listing in place) | ✅ | ✅ | ✅ |
Collection-detail / general-overview archetypes (CollectionDetail<Row>, GeneralOverview<Row>) + fluent FormField | ✅ | ✅ | ✅ |
Guided import wizard (ImportWizard<Row>: CSV upload/paste, auto-mapping grid, validation report, typed import) | ✅ | ✅ | ✅ |
| Page decorations (subtitle, banners, badges, KPIs, FABs) | ✅ | ✅ | ✅ |
Action-returned banners with an explicit mode (PageBanners.replace/append; Python PageBanners.replace(...)/.append(...)) | ✅ | ✅ (a bare PageBanner/list replaces) | ✅ |
Fluent Card with component slots (title, subtitle, media, header…, content, footer) | ✅ | ✅ | ✅ |
An app with no declared home lands on its first menu item; an app under a sub-path accepts mount-absolute routes; the chrome language follows Accept-Language; an error toast stays until dismissed; a List<Enum> with no stereotype is a multi-select | ✅ | ✅ | ✅ |
Header overline + title placeholder (@Overline/@TitlePlaceholder; Java also has OverlineSupplier/TitlePlaceholderSupplier, the ports carry only the declarative form — same as @Subtitle) | ✅ | ✅ | ✅ |
| Tabs, stereotypes, shortcuts, compact, dirty guard | ✅ | ✅ | ✅ |
Redwood pattern gaps (2026-10-10): display records (Toggle + WizardDisplay/CrudDisplay/GeneralOverviewDisplay), wizard drafts/skip/early completion/beforeStepNavigate (ports: steps by number), edit-drawer save-and-next + error banner, GeneralOverview info slot, foldout summary, DataManagement docked panels, header RecordSwitcherSupplier, @Section edit/add/view-more, hero tone, Announce command, smart-search pre-search content — see Page templates | ✅ | ✅ | ✅ |
| Adaptive layout inference (radios, folding, tabs) | ✅ | ✅ | ✅ |
Nav links (@LinkTo / link supplier) | ✅ | ✅ | ✅ |
Route registry (specs/ui/routes.yaml: definition + view model + fixed/default params per route, merged over the derived table). Both ports mirror the model, the matching, the precedence and the definition lookup; neither has a static-bundle exporter, so nothing ships the table to a browser there | ✅ | ✅ | ✅ |
Route registry authored IN CODE (Java RouteEntrySupplier, .NET IRouteEntrySupplier, Python RouteEntrySupplier → List<RouteEntry>): the programmatic half of the authored side — a route binding a definition/view model/pinned params independently, or a route with NO view model — discovered per backend (a bean in Java, an assembly/module type in the ports). Precedence: routes.yaml > code supplier > derived. (Python’s RouteEntry has no nested children: author flat entries with parent set) | ✅ | ✅ | ✅ |
Permissions in YAML — access: on routes (403, nested routes included), app-shell menu items (hidden; a RouteLink inherits its route’s) and declared actions (not advertised, buttons disabled, 403 if called); eyesOnly/readOnlyUnless/disabledUnless on components; locked fields’ client values dropped. Evaluated server-side by the same authorizer as the annotations. Menu items: the ports have no type: AppShell, so N/A there; the ports do not read a definition’s actions:, so a restricted action is refused (403) and its buttons disabled but there is no advertised list to drop it from. No bundle exporter in the ports, so the static-bundle rules are Java-only | ✅ | ✅ | ✅ |
Translations in YAML — type: Translations / specs/ui/translations/<locale>.yaml, ${i18n.key} resolved server-side per request locale (exact → language → fallback → key + one warning), code producer (TranslationsSupplier / ITranslationsSupplier), authored wins; static bundles resolve in the browser (Java only has a bundle exporter) | ✅ | ✅ | ✅ |
Environments — type: Environment / specs/ui/environments/<name>.yaml overlaying baseUrl/url/headers/proxy on the REST source catalogue for MATEU_ENVIRONMENT; Java also -Dmateu.environment and the bundle goal’s environment | ✅ | ✅ | ✅ |
| Dashboard / Foldout / Welcome / ItemOverview archetypes | ✅ | ✅ | ✅ |
| UX components (MetricCard, Gantt, EmptyState, Skeleton…) | ✅ | ✅ | ✅ |
| Planning board (tape chart: resources × days, colored blocks, move/select actions) | ✅ | ✅ | ✅ |
| High-level UX components (Kanban, Timeline, Stat, Calendar, PricingTable, OrgChart, Heatmap, Funnel, TrendChart, FeatureGrid, Testimonials, Faq, CalloutCard, CommentThread, FileList, ComparisonCard, Checklist, ProgressSteps) | ✅ | ✅ | ✅ |
| Front-office components (EntityHeader, Meter, TaskProgress, StatusList, TaskQueue, ResourceGrid, OfferCard incl. toggle state, AddOnPicker, Ledger, PaymentPicker, ProcessMonitor, Notice incl. status/noIcon/content, BulletedList) | ✅ | ✅ | ✅ |
Section polish (@SeparatorBefore, text sizes, @Section(propertyList/frameless), responsive zones) | ✅ | ✅ | ✅ |
Anchor (external links, target + rel=noopener) | ✅ | ✅ | ✅ |
Card menus (@Menu(display = cards) / MenuGroup / menu_group) | ✅ | ✅ | ✅ |
ActionPanel (categorised “I want to…” actions) | ✅ | ✅ | ✅ |
MatrixGrid (rows × dates, collapsible sections, link/editable cells) | ✅ | ✅ | ✅ |
RecordMerge (compare and merge: records side by side, a pick per field, _merged/_choices) | ✅ | ✅ | ✅ |
App shortcuts + quick-launch codes (@AppShortcut → AppDto.shortcuts) | ✅ | ✅ | ✅ |
Live header values (HeaderValuesSupplier, UpdateHeaderValue, LiveHeaderValues.publish + liveUpdates stream on /mateu/v3/sse/…) | ✅ | ✅ | ✅ |
Idle-session warning (@App(idleTimeoutMinutes…) → AppDto.idleSession, _keep-alive) | ✅ | ✅ | ✅ |
Map with markers (MapMarker, markerActionId → _markerId; fits the markers when no position) | ✅ | ✅ | ✅ |
Calendar views (Calendar.view/views: month, week, day, list), per-date cells (days), clickable dates (dayActionId); CalendarPage views()/days()/actionOnDay() | ✅ | ✅ | ✅ |
Access keys mode (@App(accessKeys) → AppDto.accessKeys) | ✅ | ✅ | ✅ |
Header light/dark switch (@App(themeToggle) / [App(ThemeToggle)] / @app(theme_toggle) → AppDto.themeToggle; the Redwood renderer is light only and ignores it) | ✅ | ✅ | ✅ |
Mobile app (experimental): the manifest GET <baseUrl>/mateu/v3/manifest (Java @MobileApp + mateu.mobile.* settings, .NET [MobileApp], Python @mobile_app; env vars MATEU_MOBILE_* everywhere) and “Open on mobile” (@App(mobileQr) / [App(MobileQr)] / @app(mobile_qr) → AppDto.mobileQr). The ports have no app logo/favicon/accent, so icon/themeColor come only from MobileApp or the env vars there | ✅ | ✅ | ✅ |
Popover.trigger (click/hover) + @Tooltip("field") → GridColumn.tooltipPath | ✅ | ✅ | ✅ |
@DragRows → CrudlDto.dragType + DropZone | ✅ | ✅ | ✅ |
Row tones (@RowStatus → CrudlDto.rowStatusField) and listing export buttons on AutoCrud | ✅ | ✅ [RowStatus] + Export CSV / Excel / PDF (CsvExportable/ExcelExportable/PdfExportable on Crud<T>, Listing<F,R> or any ICrudExports listing; built-in CSV writer, Excel / PDF through the app’s IListingExporter services — no exporter, no button) | ✅ RowStatus() + Export CSV / Excel / PDF (csv_exportable() / excel_exportable() / pdf_exportable(); built-in CSV writer, Excel / PDF through the app’s ListingExporter subclasses — no exporter, no button, as in Java) |
| i18n, events (emit/subscribe), security scaffolding | ✅ | ✅ | ✅ |
Application context selector (@AppContext) | ✅ | ✅ | ✅ |
App header actions (AppActionsSupplier → buttons + dropdown groups) | ✅ | ✅ | ✅ |
@Audience persona projection (audience app-context + projection filter) | ✅ | ✅ | ✅ |
Capture fields (@Signature, @PhotoCapture) | ✅ | ✅ | ✅ |
Generic file upload field (@FileUpload, accept filter in the field attributes) | ✅ | ✅ | ✅ |
Tree selects (@TreeSelect + hierarchical options) | ✅ | ✅ | ✅ |
| Smart-search listing filters (enums as multi-select, date/number ranges) | ✅ | ✅ | ✅ |
Declarative listings (Listing<Row> + capabilities) | ✅ | ✅ | ✅ |
— typed DateRange/NumberRange/Set filter fields | ✅ | ✅ | ✅ |
— DB pushdown (override find: one query, real total, in-memory pipeline skipped) | ✅ | ✅ | ✅ |
Tree lookup selectors (GridLayout.tree + Selector) | ✅ | ✅ | ✅ |
Lookup fields (@Lookup remote combobox + search-<field> action) | ✅ | ✅ | ✅ |
— @Searchable full selector dialogs (Selector + codesearch) | ✅ | ✅ | ✅ |
External REST options (@RestOptions/[RestOptions]/RestOptions() → select; options fetched client-side from an arbitrary endpoint via FormField.optionsSource) | ✅ | ✅ | ✅ |
External REST listing rows (@RestListing/[RestListing]/@rest_listing on a listing → rows fetched client-side from an arbitrary endpoint via Crudl.rowsSource; columns from the Row type) | ✅ | ✅ | ✅ |
External REST button action (@RestAction/[RestAction]/@rest_action on a button → calls an arbitrary endpoint client-side via Action.restAction; response toast + merge into form state) | ✅ | ✅ | ✅ |
External REST screen data (@RestData/[RestData]/@rest_data on a view → initial data fetched client-side on load and merged into the form state; reuses the restAction machinery via a synthetic __restdata__ action + OnLoad trigger) | ✅ | ✅ | ✅ |
— Proxy mode (proxy = true on any of the four → the fetch is routed through the Mateu server via the reserved __restfetch__ action: no CORS, and ${secret.X} auth injected server-side from a secrets provider / MATEU_SECRET_* env var; url values percent-encoded by position on both legs; the server resolves the DECLARED source only) | ✅ | ✅ | ✅ |
— Proxy mode for views with no annotation to read (RestSourceSupplier: a view assembled at runtime declares its sources programmatically, and they gate __restfetch__ and resolve a proxy fetch exactly as annotations do) | ✅ | ✅ | ✅ |
REST source catalogue (specs/ui/sources.yaml + @RestSource/RestSourceCatalogSupplier → a named endpoint referenced by ref; two producers, authored wins). Java and .NET carry the registry (catalogue reader + ref resolution + AppDto.restSources); Python carries it too (@rest_source / RestSourceCatalogSupplier / sources.yaml, AppMetadata.restSources, refs resolved server-side for proxy fetches) | ✅ | ✅ | ✅ |
Action catalogue (specs/ui/actions.yaml + any type: Actions file + ActionCatalogSupplier → named client-runnable actions — flows / restAction — run by id from the shell menu or any page; two producers, authored wins; owner first, then the catalogue, then the server; non-runnable entries rejected with a warning). AppDto.actionCatalogue lowered on all three; a page carries the entries its tree names. .NET (IActionCatalogSupplier) and Python (ActionCatalogSupplier) have no YAML shell/page actions:, so “owner” there is the view’s methods; no bundle exporter in the ports | ✅ | ✅ | ✅ |
Field types (specs/ui/types.yaml + FieldTypeCatalogSupplier → a named domain type a FormField/GridColumn references by fieldType:; the type’s attributes are defaults, the field’s own win, unknown → warn + render as declared; GridColumn.tones per-value badge tones). Resolved by the Java YAML loader, the browser expander and the .NET/Python YAML loaders (same rule); .NET/Python also add a [FieldType]/FieldType() marker for listing row fields since their YAML builders have no listing | ✅ | ✅ | ✅ |
Sample data on REST sources (sample:/sampleFile:, answered instead of calling the endpoint ONLY in sample mode: visual editor, -Dmateu.bundle.mock=true bundles, or an app opted in with mateu.sources.mock=true/MATEU_SOURCES_MOCK=true → AppDto.mockSources; reads get the sample, writes succeed without persisting; proxied and direct legs agree). Renderers: Vaadin/libs, React Native and IntelliJ honour it; .NET/Python short-circuit their proxy leg (no bulk/bundle there) | ✅ | ✅ | ✅ |
Business components (specs/ui/components.yaml + @BusinessComponent/ComponentCatalogSupplier + ComponentRef → a named, BOUND composition of existing pieces referenced by name; ports for free, resolves with no backend). Same two-producers/authored-wins registry as the source catalogue; .NET and Python carry it too (ComponentRef expanded server-side, AppDto.components; Python @business_component / fluent.ComponentRef) | ✅ | ✅ | ✅ |
Custom components (CustomComponent(name, props, content) — a genuinely NEW rendering as data; the per-renderer escape hatch). The WIRE is data and identical across backends; the RENDERING is per-renderer (registerCustomComponent, degrading to <mateu-unsupported>) | ✅ | ✅ | ✅ |
Sizing intent (hug/fill/fixed:<len> as portable data on the component; a listing infers fill) | ✅ | ✅ | ✅ |
Editable grids / inline CRUD editing (@InlineEditing + update-row) | ✅ | ✅ | ✅ |
Bulk list actions (@ListToolbarButton + typed selection) | ✅ | ✅ | ✅ |
Listing aggregates & grouping (@Aggregate/@GroupBy + summaries) | ✅ | ✅ | ✅ |
Group header actions (@GroupAction buttons on group rows, _groupValue parameter; Python @group_action + GroupActionVisibility) | ✅ | ✅ | ✅ |
Group summaries synthesized for custom Listings (ListingData.withSynthesizedGroups) | ✅ | ✅ | ✅ |
Optimistic locking (@Version → conflict dialog on save/update-row, _forceOverwrite) | ✅ | ✅ | ✅ |
Notification inbox (NotificationsSupplier → header bell + _notifications-* actions) | ✅ | ✅ | ✅ |
Undoable toasts (Message.undoable → undo action id + parameters on the wire) | ✅ | ✅ | ✅ |
Global entity search (GlobalSearchSupplier → globalSearchEnabled + _globalsearch) | ✅ | ✅ | ✅ |
Dialog/Drawer overlays from actions + closeModal/dispatchEvent | ✅ | ✅ | ✅ |
Structure ETag / template-ref (structureHash + request knownStructureHash → omit the component when unchanged) | ✅ | ✅ | ✅ |
ModelView bindable contract (__contract__ sync action → fields + actions on appData._contract, for the visual-builder tooling) | ✅ | ✅ | ✅ |
Visual-builder live preview (__preview__ sync action → renders arbitrary YAML page text; the plugin’s preview pane) | ✅ | ✅ | ✅ |
layoutDelta: (what a human changed about the INFERRED layout, anchored to field ids and re-applied every request, so a screen touched in the visual editor keeps following its model). The editor writes it and falls back to a layout: snapshot — visibly — when an edit cannot be a delta. Java, .NET and Python apply it (same three rules) | ✅ | ✅ | ✅ |
Partials (specs/ui/partials/<ref>.yaml; spliced into the parent’s content, resolved server-side so they never reach the wire). All three splice, stack where there is no list, drop a missing ref and break a cycle; only Java resolves a ref that names a class | ✅ | ✅ | ✅ |
YAML pages bound to a ModelView (specs/ui/<route>.yaml with modelView: → the file supplies the layout, the class supplies state + actions; on the classpath in Java, under the cwd — MATEU_SPECS_DIR — in the ports) | ✅ | ✅ | ✅ |
Static-view skip (@StaticView/[StaticView]/@static_view → staticView flag; client caches the full response for the session and skips the round-trip on return) | ✅ | ✅ | ✅ |
Sticky sections index (@Toc) | ✅ | ✅ | ✅ |
Client-side rules (@Hidden(expr)/@Disabled/rule supplier) | ✅ | ✅ | ✅ |
Grid form fields + @OnRowSelected row-click actions (incl. add/create/select on plain forms outside wizards) | ✅ | ✅ | ✅ |
| Wide-field auto-colspan (grid/textarea/richText span the full row of a multi-column section) | ✅ | ✅ | ✅ |
| Inline-editing grid ”+” appends an in-place row (the detail-form response targets a container inline grids never render) | ✅ | ✅ | ✅ |
Multi-state embedded islands (@Inline orchestrator fields, host-seeded initialData; Python: a field holding a routed view + Inline()) | ✅ | ✅ | ✅ |
Multi-column layouts (@Zones, @FoldedLayout) | ✅ | ✅ | ✅ |
AI chat (@AI/[AI]/@ai → sseUrl; the SSE endpoint is developer-provided) | ✅ | ✅ | ✅ |
| Semantic (composed) annotations | ✅ | ✅ | ✅ (an Annotated alias) |
Federation (remote menus + MicroFrontend islands) | ✅ | ✅ | ✅ |
| Component adapters | ✅ | ✅ IComponentAdapter<T> SPI | ✅ (ComponentAdapter + AdaptedView) |
| Hero search archetype | ✅ | ✅ | ✅ |
Import wizard on .NET/Python: same step flow (upload/paste → auto-mapped column grid with a
select-editable target-field cell → per-line validation report → typed import + result counts)
and the same CSV semantics (,/; autodetect, RFC-4180-ish quoting, data-URI uploads), on the
ports’ [Step(n)] wizard machinery, and both ports now have the full flow (2026-10-10). On .NET the
validation step’s forward button is the Import completion action, the result step is final (no
navigation, a re-sent back/next cannot import twice), steps are named Upload / Mapping / Validation /
Result, the heading defaults to Import <Row> and TimeOnly/DateTimeOffset/Guid columns
convert; its validation surface is DataAnnotations ([Required], [Range]…). On Python the import
is the wizard’s completion action (@wizard_completion_action("Import"), Java’s doImport, then a
read-only result step) and its report checks Required(), Min/Max/Length/Pattern — the same
constraints that travel as client-side validations and are re-checked when a form is saved.
Smart-search filters on .NET/Python: the Crud entity’s fields become the same filter widgets
(enums → multi-select IN, temporals → date ranges, [RangeFilter]/RangeFilter() numerics →
min–max), applied in-memory over Fetch() by default — same as Java’s default repository — or
pushed to the database by overriding Find/find (the filters arrive as the raw component state
rather than Java’s typed FilterCriterion objects; a contract difference, not a capability gap).
Recent .NET/Python parity gains (2026-07-17): CRUD create/edit in a drawer
(Crud<T>.EditInDrawer virtual / @edit_in_drawer decorator — new and row clicks answer the
entity form inside a Drawer Add partial over the listing; cancel closes it; save persists and
answers CloseModal carrying the mateu-crud:saved-in-drawer event plus a RunAction search
command, so the listing refreshes in place with no navigation), the wizard RAIL progress style
([WizardProgress("rail")] / @wizard_progress("rail") — the step form on the left, a sticky
right band with a big current | total counter over the vertical step list; ProgressSteps
carries vertical in all three backends), and — closing the day’s last gap — the fluent
FormField primitive (a live field composed into any fluent tree, bound to componentState by
fieldId; with options it renders as a select) plus the CollectionDetail and GeneralOverview
archetypes built on it: tree-supplier views seed their scalar properties into initialData (so
search/selection round-trip) and emit an AutoSave trigger so typing/switching re-renders the
page in place.
Recent .NET/Python parity gains (2026-07-11): inline CRUD editing
([InlineEditing]/@inline_editing — editable columns with typed in-place editors +
the update-row action rebuilding and saving the row), lookup fields ([Lookup]/Lookup() →
combobox + remoteCoordinates, the handler answers search-<field> from the options supplier,
filtered and paged), Dialog/Drawer overlays returned from actions (Add partials on the initiator,
with CloseModal/DispatchEvent command factories carrying {eventName, detail}), [Toc]/@toc,
the full HeroSearch archetype (hero header + cards listing, starts empty), client-side rules
([Hidden(expr)]/[Disabled] + IRuleSupplier/RuleSupplier → ServerSideComponent.rules),
grid form fields (list-of-rows properties → dataType array + stereotype grid + columns) with
[OnRowSelected]/OnRowSelected() injecting the clicked row into the handler method, and the
[Zone]/@zones + [FoldedLayout]/@folded_layout multi-column layouts. Earlier (2026-07-10):
CRUD search sorts and paginates; actions can return page banners, a /route or a UICommand.
Declarative listings (Java Listing<Row> + capabilities / Python Listing[F, R]) landed the same day with the
typed DateRange/NumberRange/Set filters, the _from/_to state assembly, and the
Selector contract on top: @Searchable fields open their selector listing in a modal
(codesearch-<field> → Dialog; row pick → value-changed/data-changed/close-modal-requested),
including TREE-shaped selectors (gridLayout() override → "tree", rows carrying nested
children arrays). Semantic annotations and the AI chat landed too: .NET resolves composed
attributes transitively (Meta.Find, attributes decorating attribute classes) while in Python a
reusable Annotated alias needs no machinery at all; [AI]/@ai emit sseUrl and both manuals
document the SSE endpoint contract (POST {message, sessionId, menuContext?} → data: chunks).
The final gaps closed the same day: DB pushdown (Find/find override — one query with the real
total, the in-memory pipeline skipped), federation ([RemoteMenu]/@remote_menu federated menu
entries — the frontend fetches the remote menu itself — and the MicroFrontend island component),
and component adapters resolved as a documented idiom: both ports render plain classes
reflectively, so a thin wrapper view over the foreign object (fields exposing its data, an action
writing back) replaces Java’s ComponentAdapter SPI — see “Adapting foreign classes” in each
manual. The four depth tails inside ✅ rows closed too: pre-existing @Lookup/@Searchable
values resolve their display label (ILookupLabelSupplier/LookupLabelSupplier →
<fieldId>-label partial data), grid form fields edit in place (@InlineEditing on the
property, rows binding back into the typed list), permission-driven field states
(@EyesOnly/@ReadOnlyUnless/@DisabledUnless against an adapter-supplied Identity), and the
full @App(AUTO) variant decision table (explicit wins; menu folders via Group/group →
TILES/HAMBURGER_MENU/MENU_ON_TOP; flat menus → TABS). Nothing on the server surface remains
Java-only.
2026-07-16 parity pass: the front-office dogfooding wave (OfferCard toggle state, StatusItem
avatar, StatusList compact/frameless, Notice status/noIcon/content, @Audience) landed on all
three backends in the same commits; the two features that had slipped through Java-only — app
header actions with dropdown grouping (AppActionsSupplier) and Anchor.target — were ported the
same day (.NET IAppActionsSupplier/Anchor { Target }, Python AppActionsSupplier/Anchor),
each pinned by golden-JSON tests. The remaining Java-only rows above are in-page orchestration
behaviors (embedded islands, inline-grid ”+”, wide-field auto-colspan), not wire surface.
2026-07-17: optimistic locking ([Version]/Version() — stale saves and inline row updates
answer the same reload/overwrite conflict dialog as Java, byte-identical texts; _forceOverwrite
adopts the stored version then bumps) and the notification inbox
(INotificationsSupplier/NotificationsSupplier → AppMetadata.notificationsEnabled +
_notifications-list/_notifications-read with ids list or "all") landed on .NET and Python,
each pinned by golden-JSON tests mirroring the Java sync suites.
What the ports closed (2026-10-10)
Section titled “What the ports closed (2026-10-10)”Both ports closed every server row above on 2026-10-10, each pinned by its own tests and the wire by the hard conformance gate:
- .NET:
IRestSourceSupplierproxy sources, the source and component catalogues,layoutDelta:, group actions and synthesized group summaries, wide-field auto-colspan, the inline-grid ”+” row and the whole grid-field row editor, multi-state embedded islands, theIComponentAdapter<T>SPI, listing exports (theIListingExporterport) and the import wizard’s completion action. - Python: the same list (
RestSourceSupplier,@rest_source/sources.yaml,@business_component/ComponentRef,layoutDelta:,@group_action, the grid row editor, embedded islands,ComponentAdapter, listing exports (theListingExporterport),@wizard_completion_action), plusMin/Max/Length(formerlySize)/Patternvalidation and class- and method-level@eyes_only.
Proxy mode for views assembled at runtime stays the SSRF-sensitive path on every server: the endpoint is taken from what the server holds (the view’s own declarations), never from the request.
The shared wire-conformance corpus is a HARD gate on Python: every case must match the Java golden
except an explicit, strictly-xfailed allow-list whose every entry is a defect of the golden itself
(today one: dashboard, whose Java golden advertises list-row actions for the fields of a Text
component).
Port idioms: same wire, different declaration
Section titled “Port idioms: same wire, different declaration”The three servers emit the same wire; they do not always declare it the same way. The differences below are deliberate port idioms — each language declares the concept the way its developers expect, and the wire (pinned by the conformance corpus) is identical. They are documented here rather than converged, because converging would rename stable port API for no change on screen. The last column says where the review (3.0-alpha.412) did converge, and why.
| Concept | Java | .NET | Python | Decision |
|---|---|---|---|---|
| Menu entries | @Menu on a field whose type is the target page (Menu / RouteLink records for submenus and links) | [MenuItem] / [MenuGroup("A/B")] on methods returning the target | @menu_item / @menu_group on methods | Idiom. A C# property / Python attribute typed as the page would be bound and serialised as form state; a method is the natural “go there” declaration. Same MenuOptionDto tree on the wire. |
| Header KPI | @KPI (no argument; the label comes from the field / @Label) on a field or method | [Kpi("Title")] (title required) on a property or method | kpi("Title") — one lowercase class used both as a method decorator and as an Annotated[...] marker | Idiom. The ports carry the label in the declaration because they have no per-member @Label on methods; same KPIDto. |
| Wizard steps | WizardStep fields of a Wizard, in declaration order | [Step(n)] on the wizard’s properties (1-based) | Step(n) marker in Annotated[...] | Idiom: named steps in Java, numbered steps in the ports. Python’s top-level Step is that marker; the ProgressSteps item (Java/.NET Step record) is mateu_uidl.components.Step. |
| Wizard completion | @WizardCompletionAction(label, availableFromStep = "stepField") on a method | override CompletionActionLabel + OnNext, CompletionAvailableFromStep (int?) | @wizard_completion_action(label, available_from_step=n) | Idiom. availableFromStep names a step field in Java and a step number in the ports, because that is how each one names its steps. |
| Wizard progress style | @WizardProgress(WizardProgressStyle.STEPS) (enum) | [WizardProgress("steps")] | @wizard_progress("steps") | Idiom (a string where Java has an enum); values bar / steps / rail everywhere. |
| Edit in a drawer | override Crud.editInDrawer() | override the EditInDrawer property | @edit_in_drawer class decorator | Idiom. |
| Action timeout / idempotency / shortcut | attributes of @Action(timeoutMillis, idempotent, shortcut) | [ActionOptions(...)], [Shortcut("ctrl+s")] | @action_options(...), @shortcut("ctrl+s") | Idiom: the ports have no @Action equivalent, so each knob is its own composable attribute / decorator. |
| Toast variants | NotificationVariant { success, warning, error, primary, contrast } | MessageVariant { Success, Info, Warning, Error, Contrast } | MessageVariant (same as .NET) | Idiom; each maps to the wire’s variant string. |
| Length constraint | Bean Validation @Size(min, max) | — (not ported: [Size] is the sizing intent) | Length(min=, max=) | Converged (3.0-alpha.412): Python’s Size is now Length, because in Java and .NET Size is a component’s sizing intent — the same name meaning two things across ports was a real trap. Size still works and warns. |
| View-level permission | @EyesOnly(permissions = …) on the class | [EyesOnly(Permissions = …)] | @eyes_only(permissions=[…]) | Converged (3.0-alpha.412): the port-only [Secured("x")] / @secured("x") was silently ignored — nothing read it — so a view “secured” with it was open to everyone. It is now enforced with exactly the semantics of EyesOnly(permissions = "x") (the view and its actions refused with 403, the menu entries leading to it hidden) and deprecated naming EyesOnly. |
| Code-authored routes and shells | RouteEntrySupplier, AppSupplier, MenuSupplier, AppShell in mateu-uidl | IRouteEntrySupplier, IAppSupplier, IMenuSupplier, AppShell, RouteEntry — in the Mateu.Uidl package since 3.0-alpha.412 (they used to live in Mateu.Core) | RouteEntrySupplier, AppSupplier, MenuSupplier, AppShell in mateu_uidl | Converged on the package: a UI-only .NET module can author routes and shells referencing just Mateu.Uidl. Their full names stay Mateu.Core.* and Mateu.Core type-forwards them, so existing code and binaries keep working (a same-named copy in the Mateu.Uidl namespace would make every file importing both namespaces ambiguous). |
| Global inference switches | mateu.layout.inference.enabled / mateu.page.inference.enabled (system property or MATEU_… env var) | AppContext switches Mateu.LayoutInference.Enabled / Mateu.PageInference.Enabled | env vars MATEU_LAYOUT_INFERENCE_ENABLED / MATEU_PAGE_INFERENCE_ENABLED | Converged (3.0-alpha.412) on one name and one polarity per switch; the old names keep working with a warning. See Layout inference. |
Renderers
Section titled “Renderers”Every renderer speaks the same wire; the depth of widget support varies.
| Feature | Vaadin (web) | Redwood (web) | IntelliJ plugin | React Native |
|---|---|---|---|---|
| Forms, CRUD, navigation | ✅ | ✅ | ✅ | ✅ |
Redwood pattern gaps (2026-10-10): header record/context switcher, Announce, hero tone, foldout summary-N, crud preSearch, a Drawer re-sent with an open drawer’s id refreshing in place | ✅ shared DS-neutral elements | ✅ oj-sp header data switcher, dark-<tone> banner, oj-sp-foldout-panel summary slot | ✅ combo + speed search | ✅ bottom-sheet switcher |
| Smart-search filter bar (chips, ranges, multi-select) | ✅ | ✅ | ✅ (native panel) | ✅ panel (ranges, multi-select, date pickers) |
| Sorting, cards/list/tree layouts, empty states | ✅ | ✅ | ✅ (tree = JTree; cards/list adapt to the table) | ✅ |
| Inline editing (@InlineEditing, update-row) | ✅ | ✅ | ✅ (row form) | ✅ (row form) |
| Date picker | ✅ | ✅ | ✅ (calendar popup) | ✅ (own calendar) |
| Remote lookup select (@Lookup / searchable) | ✅ | ✅ | ✅ | ✅ |
| Full field-stereotype set (radio, multiSelect, slider, stepper, stars, color, image upload, money, markdown…) | ✅ | ✅ radio, multi-select, checkboxes, toggle, number/money (oj-input-number), textarea, dates, lookups, file/image/signature/photo, slider (oj-slider), stars (oj-rating-gauge), color, rich text (own editor: HTML, legacy Quill Delta read), markdown/html read-only | ✅ | ✅ |
| Client-side rules (visible/disabled/state) + ${…} interpolation | ✅ | ✅ CSP-safe engine (visible/disabled/required/value + OnValueChange) on every surface: the page, embedded islands and the open drawer/dialog | ✅ (shared engine) | ✅ (no-eval engine) |
| Page banners (@Banner + action-returned) | ✅ | ✅ | ✅ | ✅ |
| FABs, header badges, KPIs, charts | ✅ | ✅ header badges, KPIs, MetricCard/Scoreboard tiles and charts on any page (oj-chart: bar, line, area, pie, doughnut, polar, funnel, several series); page and app @Fabs stacked above the shell FAB | ✅ (FABs as header buttons) | ✅ |
| @AutoSave / @SubscribeTo scopes / @OnRowSelected | ✅ | ✅ | ✅ | ✅ |
Periodic refresh (OnLoad with timeoutMillis + OnSuccess loop, background) | ✅ | ✅ (stops when the screen changes) | ✅ (stops when the view changes or its tab closes) | ✅ (stops when the screen changes or unmounts) |
Keyboard shortcuts (@Action(shortcut), @Tab(shortcut)) + access keys mode (@App(accessKeys): hold Alt, Alt+letter) | ✅ | ✅ | ✅ (Swing mnemonics) | — (no hardware-key model) |
“Open on mobile” (mobileQr: a header button whose dialog shows the QR of the mateu.io/add link, a link instead on a phone) | ✅ | ✅ | N/A (desktop IDE) | N/A (it is the app) |
Hover details (Popover with trigger = hover, @Tooltip("otherField") on listing cells) | ✅ | ✅ (shared oj-popup) | ✅ | 🟡 press / long-press (no hover on touch) |
Drag rows to a destination (@DragRows + DropZone: origin and destination in one action) | ✅ | ✅ (oj-table dnd) | ✅ | 🟡 “Move to…” picker (no drag on touch) |
| AI chat (sseUrl) / theme toggle | ✅ | ✅ AI chat at parity with the web panel (header button + left drawer: streaming, agent progress and tool steps, token usage, markdown answers with in-app links, screen context + projection, mcpUrl, @AI(upload) attachments, local agent, dictation, wide mode, render-screen/navigation events); light only: @App(themeToggle) is ignored (no switch) and neither a stored dark choice nor the OS preference applies | ✅ chat (theme = the IDE’s own) | ✅ |
| App context selector | ✅ | ✅ | ✅ (navigator combos) | ✅ |
| — searchable picker w/ remote search | ✅ | ✅ | 🟡 loaded options only | ✅ |
| Signature capture | ✅ canvas | ✅ canvas (own element: JET has no signature pad) | ✅ mouse canvas | ✅ svg + view-shot |
| Photo capture | ✅ getUserMedia | ✅ | 🟡 file picker (no desktop camera API) | ✅ expo-camera |
| Tree select dropdown | ✅ | ✅ | ✅ (JTree popup) | ✅ |
| Tree lookup selector (dialog) | ✅ | ✅ | ✅ (tree layout) | ✅ (tree layout) |
| Dashboards, Gantt, foldouts, skeletons | ✅ | ✅ foldouts (oj-sp-foldout-layout, collapsible panels inside a tab), dashboards (KPI band, tiles by colSpan, oj-chart), Gantt and PlanningBoard on oj-gantt, Skeleton | ✅ | ✅ |
Custom components (registerCustomComponent; unknown → visible placeholder) — the per-renderer escape hatch, each renderer with its own registry + graceful degradation (placeholder + slotted children) | ✅ | ✅ registry (bridge.registerCustomComponent(name, mount)) + placeholder and slotted children | 🟡 registry + placeholder | 🟡 registry + placeholder |
| High-level UX components (Kanban, Timeline, Stat, Calendar… + the front-office set) | ✅ | ✅ every type — Oracle components where they exist (oj-chart funnel, oj-avatar, oj-action-card, oj-rating-gauge, oj-checkboxset, oj-menu-button, oj-collapsible, oj-dialog…), Redwood-token atoms where JET has none (board, timeline, heatmap, org outline, BPMN); see the coverage table below | ✅ | ✅ |
| App header actions (buttons + dropdown groups) | ✅ | ✅ | — (sidebar shell, no top bar) | — (drawer shell, no top bar) |
| Bulk row selection + selection-required toolbar actions | ✅ | ✅ | ✅ (native multi-select) | ✅ (checkbox column) |
| Saved views (named filter sets, default view) | ✅ | ✅ | ✅ (Views menu: apply/save/default/delete, persisted) | 🟡 apply/save/default/delete (session-scoped) |
| Column chooser (per-user show/hide/reorder) | ✅ | ✅ | ✅ (header menu show/hide + native drag-reorder, persisted) | 🟡 show/hide (session-scoped; no AsyncStorage dep) |
| Listing totals footer + group subtotal rows | ✅ | ✅ | ✅ | ✅ |
| Notification bell (inbox, unread count) | ✅ | ✅ (header bell + oj-popup with an oj-list-view) | ✅ (sidebar popup) | ✅ (drawer row) |
| Undoable toasts (Undo button) | ✅ | ✅ (JET oj-message with the Undo oj-button in its detail slot — oj-sp-messages-toast has no actions) | ✅ (balloon action) | ✅ (toast button) |
| Entity search (GlobalSearchSupplier: ⌘K palette / search box) | ✅ palette | ✅ the Ask palette: destinations + GlobalSearchSupplier entity results by category | ✅ sidebar search | ✅ drawer search |
| Planning board (tape chart) | ✅ drag+select | ✅ oj-gantt: move, resize, double click, range selection, hover summary | ✅ drag+select (MouseListener + pure PlanningDrag) | ✅ drag+select (PanResponder + pure planningDrag) |
Session-expiry re-auth + retry (onSessionExpired) | ✅ | ✅ | ✅ (SessionGuard, sync re-auth) | ✅ (sessionGuard, retry once) |
Card menus (@Menu(display = cards): a group opening as a panel of cards) | ✅ | ✅ (oj-popup) | ✅ | ✅ |
| Action panel (“I want to…”: categorised actions, show more, hide unpopulated, shortcut) | ✅ | ✅ (oj-dialog + oj-switch) | ✅ (dialog; IDE keymap wins on a shared shortcut) | ✅ (modal; no keyboard shortcut) |
| Matrix grid (rows × dates, collapsible sections, link cells, in-place editing) | ✅ | ✅ (oj-data-grid) | ✅ (JBTable + row header) | ✅ |
Compare and merge records (RecordMerge) | ✅ | ✅ | ✅ (Swing grid, a button group per row) | ✅ (one card per field) |
App shortcuts (@AppShortcut), ? list, quick-launch codes | ✅ (⌘K palette + command center) | ✅ (Ask palette) | ✅ (view key bindings, dialog, “Go to code…”) | 🟡 codes only (drawer field; no hardware keys) |
Live header values + liveUpdates stream (one per browser, tabs share it) | ✅ | ✅ | ✅ (navigator; one stream per session) | ✅ (drawer; foreground only, expo/fetch) |
| Idle-session warning (countdown, stay / sign out, activity shared across tabs) | ✅ | ✅ | ✅ (IDE activity; one session) | ✅ (touches; background counts as idle) |
Row tones (@RowStatus) | ✅ | ✅ | — | — |
| Dockable multi-tab workspace | — | — | ✅ (IDE editor tabs/splits) | — |
| App registry boot (installable → registry → backend) | — | — | ✅ (+ min IDE build gate) | ✅ |
Redwood component coverage
Section titled “Redwood component coverage”What the Redwood/VB renderer does with each component type of the wire. A type it does not render shows a visible “Unsupported component” placeholder (its children, if it is a container, still render), so this table — not the feature rows above — is the authority on what Redwood paints.
Generated from frontend/web/monorepo/apps/redwood/poc/coverage.mjs and checked in CI against the
wire catalogue and the renderer’s code (node poc/parity-check.mjs): 96 rendered, 11 layout
containers, 2 partial, 0 not rendered. A type the renderer does not know
(one added to the wire later) shows a visible “Unsupported component” placeholder, like the web
renderers, and its children still render.
| Component | Redwood | How |
|---|---|---|
AccordionLayout | ✅ | oj-collapsible per panel |
ActionPanel | ✅ | oj-dialog + oj-switch (“I want to…”) |
AddOnPicker | ✅ | |
Anchor | ✅ | link / file download |
App | ✅ | oj-sp shell: navigation drawer / top tabs / card menus |
Avatar | ✅ | oj-avatar |
AvatarGroup | ✅ | oj-avatar per person, +N beyond maxItemsVisible |
Badge | ✅ | oj-badge classes |
Bpmn | ✅ | an SVG drawn from its BPMN-DI (or laid out by its flows): bpmn-js is not under a permissive licence |
Breadcrumbs | ✅ | a breadcrumb nav in the content (the shell keeps its own trail) |
BulletedList | ✅ | |
Button | ✅ | oj-button |
Calendar | ✅ | month, week, day and list views (JET has no calendar: a Redwood-token grid, oj-buttonset-one switcher), per-date cells, clickable dates |
CalloutCard | ✅ | oj-panel band with its theme, icon and CTA oj-button |
Card | ✅ | oj-panel |
CarouselLayout | ✅ | an image gallery is an oj-film-strip; other slides one at a time with a ‹ › pager and dots (client-side) |
Chart | ✅ | oj-chart: bar, line, pie, doughnut, radar/polar area, scatter; several series |
Chat | ✅ | an inline conversation streamed from its sseUrl (poc/chat.mjs); the app-level assistant is the shell panel |
Checklist | ✅ | oj-checkboxset per item (sends {_item, _done}) + oj-progress-bar |
CommentThread | ✅ | replies indented under their comment, oj-avatar per author |
ComparisonCard | ✅ | oj-panel: both values and the delta with its trend |
ConfirmDialog | ✅ | oj-dialog, open while its openedCondition holds; Confirm / Reject / Cancel send their actions |
ContextMenu | ✅ | its content, then an oj-menu-button with the menu; a right click on the content opens it too |
CookieConsent | ✅ | a band fixed to the top/bottom; hidden once its cookie exists, dismissing sets it |
Crud | ✅ | oj-table + smart search; groups, totals, tones, columns, saved views, export |
CustomComponent | ✅ | a registry the app fills (bridge.registerCustomComponent); without a view, a visible placeholder + its children — same contract as the web |
DashboardLayout | ✅ | oj-flex columns, each panel its colSpan |
DashboardPanel | ✅ | oj-panel tile (title, subtitle, content) |
Details | ✅ | oj-collapsible (client-side state) |
Dialog | ✅ | oj-dialog (overlay stack) |
Directory | ✅ | a column per group with its links (in-app routes navigate inside the shell) |
Drawer | ✅ | oj-drawer-popup (overlay stack), subtitle, footer actions |
DropZone | ✅ | drop target for @DragRows listing rows (oj-table dnd); its content as text lines |
Element | ✅ | third-party web component, events wired back |
EmptyState | ✅ | oj-sp-empty-state: the page-level one, and any other in the content with its call to action |
EntityHeader | ✅ | projected to the page header (sticky business card); a fact with an action gets its icon button after the value |
Faq | ✅ | oj-collapsible per question (client-side state), the answer as Markdown |
FeatureGrid | ✅ | oj-panel / oj-action-card tiles on an oj-flex grid of its columns |
FileList | ✅ | a row per file: icon by type, download link, size · type, its action |
FoldoutLayout | ✅ | oj-sp-foldout-layout; inside a tab, collapsible panels |
Form | ✅ | oj-form-layout |
FormField | ✅ | oj-input-, oj-select-, oj-radioset, oj-checkboxset, oj-input-number, capture fields |
FormLayout | ✅ | oj-form-layout |
Funnel | ✅ | oj-chart type funnel |
Gantt | ✅ | oj-gantt: a row per task, progress fill, task click → onTaskSelectionActionId |
Grid | ✅ | oj-table (list display): tree rows with disclosure, client-side paging by its size |
Heatmap | ✅ | JET has none: a calendar heatmap (a column per week), 4 levels + legend, values on hover |
HeroSection | ✅ | the Welcome archetype: oj-sp-header-welcome-banner; in the content, a hero band (title, subtitle, background image) over its children |
HorizontalLayout | ✅ | oj-flex row |
Icon | ✅ | the Redwood icon font (oj-ux-ico-*), an emoji as text |
Image | ✅ | JET has no image component: an |
Kanban | ✅ | JET has no board: oj-panel columns, cards as oj-action-card when they act (_clickedCard) |
Ledger | ✅ | |
Map | ✅ | Leaflet + OSM tiles (JET has no street map): markers, fit, markerActionId |
Markdown | ✅ | formatted (headings, lists, quotes, code, tables, bold, italics, links, allowed inline HTML) as allowlist-sanitized HTML |
MasterDetailLayout | ✅ | list + detail panes |
MatrixGrid | ✅ | oj-data-grid |
MenuBar | ✅ | oj-buttons, links and oj-menu-buttons for submenus |
MessageInput | ✅ | oj-input-text + Send oj-button; Enter or Send sends {message} |
MessageList | ✅ | oj-avatar + name, time and text per message |
Meter | ✅ | oj-progress-bar |
MetricCard | ✅ | KPI tile: value, trend, drill-in action |
MicroFrontend | ✅ | loaded from its baseUrl into a surface of its own and painted in place; its actions go back to it |
NotFound | ✅ | |
Notice | ✅ | oj-sp-message-banner style band + actions |
Notification | ✅ | an info band (title — text); action messages show as toasts |
OfferCard | ✅ | |
OrgChart | ✅ | the tree as an indented outline (VB templates cannot recurse) with oj-avatar; nodes with an action are oj-action-cards (_clickedNode) |
Page | ✅ | oj-sp header (title, subtitle, KPIs, toolbar, banners, status badge, peer arrows, timestamp; overline + title placeholder drawn over it) |
PaymentPicker | ✅ | |
PlanningBoard | ✅ | oj-gantt (move, resize, double click, range selection) |
Popover | ✅ | trigger + the content WITH its structure (headings, lists, links, badges, markdown) as sanitised HTML in a shared oj-popup (hover/focus or click) |
PricingTable | ✅ | oj-panel plans (the featured one highlighted), CTA oj-button |
ProcessMonitor | ✅ | a row per process: systems, ok/warning/error badges, status, action |
ProgressBar | ✅ | oj-progress-bar (value or the state at valueKey, indeterminate); a wizard shows its progress as the guided process |
ProgressSteps | ✅ | oj-train |
RecordMerge | ✅ | compare & merge: a table of radios per field (differences highlighted), oj-switch «only differences», result summary, merge button |
ResourceGrid | ✅ | |
ResponsiveGrid | ✅ | fixed tracks → oj-flex columns by their fr weights and spans; auto-fill/auto-fit → as many per row as fit at each breakpoint; reorderable tiles drag (and Alt+←/→) |
Result | ✅ | oj-panel with the icon of its type, message, links and the what-next action |
Scoreboard | ✅ | KPI band |
Separator | ✅ | |
Skeleton | ✅ | the shell skeleton bones (JET has no skeleton), text/card/grid/form × count |
SplitLayout | ✅ | two panes |
Stat | ✅ | |
StatusList | ✅ | |
Stepper | ✅ | a numbered step header per child, its content below |
TabLayout | ✅ | oj-tab-bar (nested strips flattened) |
TaskProgress | ✅ | |
TaskQueue | ✅ | |
Testimonials | ✅ | oj-panel quotes, oj-avatar, oj-rating-gauge (read only) |
Text | ✅ | |
Timeline | ✅ | oj-timeline is deprecated: a Redwood list with markers, items with an action as borderless oj-buttons (_clickedItem) |
Tooltip | ✅ | the wrapped component keeps its view; the text opens in the shared oj-popup on hover/focus (on the button itself, or an info marker) |
TrendChart | ✅ | oj-chart line/area |
VirtualList | ✅ | every item through the same projection (no windowing — neither has the neutral web renderer) |
BoardLayout | ✅ layout | children stacked, not a board |
Container | ✅ layout | |
ContentLayout | ✅ layout | |
CustomField | ✅ layout | its component in place |
Div | ✅ layout | |
FormItem | ✅ layout | |
FormSection | ✅ layout | |
FormSubSection | ✅ layout | |
FullWidth | ✅ layout | |
Scroller | ✅ layout | |
VerticalLayout | ✅ layout | |
FormEditor | 🟡 | read only: the defined form previewed with the real field widgets; the web designer edits it |
Workflow | 🟡 | read only: the definition as a numbered flow of steps; the web designer edits it |
AccordionPanel | ↳ | of AccordionLayout |
BoardLayoutItem | ↳ | of BoardLayout |
BoardLayoutRow | ↳ | of BoardLayout |
Breadcrumb | ↳ | of Breadcrumbs |
FormRow | ↳ | of FormLayout |
GridColumn | ↳ | of Grid / Crud |
Tab | ↳ | of TabLayout |
React Native component coverage
Section titled “React Native component coverage”What the React Native renderer (iOS / Android, frontend/app/react-native) does with each
component type of the wire. Since 2026-10-10 every type has a native renderer — CI fails the
build if a new wire type is added without one (scripts/parity-check.mjs), so a screen never shows
“Unsupported component” on a phone. 🟡 marks a deliberate mobile adaptation (no hover or
right-click on touch → press / long-press; diagram editors shown read-only).
Generated from frontend/app/react-native/scripts/coverage.mjs and checked in CI against the
wire catalogue and the renderer’s switch (node scripts/parity-check.mjs): every wire type has a
native renderer — 89 rendered, 11 layout containers, 6 parts of another component,
10 with a documented mobile adaptation. None is dropped.
| Component | React Native | How |
|---|---|---|
AccordionLayout | ✅ | |
ActionPanel | ✅ | modal; no keyboard shortcut |
AddOnPicker | ✅ | |
Anchor | ✅ | in-app route or OS browser |
App | ✅ | drawer / tabs shell (AppRenderer); nested App = own island at its home route |
Avatar | ✅ | image or initials |
AvatarGroup | ✅ | overlapping, +N overflow |
Badge | ✅ | |
Breadcrumbs | ✅ | |
BulletedList | ✅ | |
Button | ✅ | |
Calendar | ✅ | month / week / day / list |
CalloutCard | ✅ | |
Card | ✅ | |
CarouselLayout | ✅ | paging swipe, dots, prev/next, auto-advance, loop |
Chart | ✅ | |
Chat | ✅ | opens the assistant panel (same contract as the app chat FAB) |
Checklist | ✅ | |
CommentThread | ✅ | |
ComparisonCard | ✅ | |
ConfirmDialog | ✅ | |
ContentLayout | ✅ | main / aside / footer; aside beside main ≥ 768 px, stacked on a phone |
CookieConsent | ✅ | dismissible banner, dismissal persisted on the device |
Crud | ✅ | table / list / cards / tree, smart-search panel, selection, totals, groups, inline edit, saved views |
DashboardLayout | ✅ | |
DashboardPanel | ✅ | |
Details | ✅ | collapsible panel |
Dialog | ✅ | overlay fragments open as a modal sheet |
Directory | ✅ | |
Drawer | ✅ | overlay fragments open as a modal sheet; a Drawer in the tree is drawn in place |
EmptyState | ✅ | |
EntityHeader | ✅ | a fact with an action gets a tappable icon after its value |
Faq | ✅ | |
FeatureGrid | ✅ | |
FileList | ✅ | |
FoldoutLayout | ✅ | overview card + accordion of panels |
Form | ✅ | |
FormField | ✅ | every stereotype, date picker, lookups, capture fields |
FormSection | ✅ | |
FormSubSection | ✅ | |
Funnel | ✅ | |
Gantt | ✅ | |
Grid | ✅ | horizontal-scroll table, tree rows indented, action cells |
Heatmap | ✅ | |
HeroSection | ✅ | |
Image | ✅ | |
Kanban | ✅ | |
Ledger | ✅ | |
Map | ✅ | |
Markdown | ✅ | |
MasterDetailLayout | ✅ | side by side ≥ 768 px, stacked on a phone |
MatrixGrid | ✅ | |
MenuBar | ✅ | horizontal strip; submenus as an action sheet |
MessageInput | ✅ | |
MessageList | ✅ | |
Meter | ✅ | |
MetricCard | ✅ | |
MicroFrontend | ✅ | own island (own session when it has its own baseUrl) |
NotFound | ✅ | |
Notice | ✅ | |
Notification | ✅ | inline status strip |
OfferCard | ✅ | |
OrgChart | ✅ | |
Page | ✅ | header (title, subtitle, badges, KPIs, toolbar, banners), FABs |
PaymentPicker | ✅ | |
PlanningBoard | ✅ | drag + select (PanResponder) |
PricingTable | ✅ | |
ProcessMonitor | ✅ | |
ProgressBar | ✅ | |
ProgressSteps | ✅ | vertical by design |
RecordMerge | ✅ | one card per field (records as rows of radios) |
ResourceGrid | ✅ | |
ResponsiveGrid | ✅ | declared tracks + col spans; stacks below stackBelow (600 px default) |
Result | ✅ | icon by result type, links, next step |
Scoreboard | ✅ | |
Separator | ✅ | |
Skeleton | ✅ | |
SplitLayout | ✅ | |
Stat | ✅ | |
StatusList | ✅ | |
Stepper | ✅ | children as numbered steps |
TabLayout | ✅ | |
TaskProgress | ✅ | |
TaskQueue | ✅ | |
Testimonials | ✅ | |
Text | ✅ | |
Timeline | ✅ | |
TrendChart | ✅ | |
VirtualList | ✅ | |
BoardLayout | ✅ container | |
Container | ✅ container | |
CustomField | ✅ container | its component in place |
Div | ✅ container | |
FormItem | ✅ container | |
FormLayout | ✅ container | |
FormRow | ✅ container | |
FullWidth | ✅ container | |
HorizontalLayout | ✅ container | |
Scroller | ✅ container | |
VerticalLayout | ✅ container | |
Bpmn | 🟡 | read-only diagram (react-native-svg, from the BPMN DI section); edit on the web |
ContextMenu | 🟡 | long-press (no right-click on touch) opens an action sheet |
CustomComponent | 🟡 | registry (registerCustomComponent) + visible placeholder |
DropZone | 🟡 | “Move to…” picker (no drag on touch) |
Element | 🟡 | HTML tags map to native text / image / rule, on.click runs its action; custom elements (web components) show their text content |
FormEditor | 🟡 | read-only definition preview; edit on the web |
Icon | 🟡 | emoji / glyphs and common icon names; other design-system icons show a dot |
Popover | 🟡 | opens on press (no hover on touch) |
Tooltip | 🟡 | long-press shows it (no hover on touch); also the accessibility hint |
Workflow | 🟡 | read-only layered diagram; edit on the web |
AccordionPanel | ✅ part | of AccordionLayout |
BoardLayoutItem | ✅ part | of BoardLayout |
BoardLayoutRow | ✅ part | of BoardLayout (items side by side ≥ 768 px) |
Breadcrumb | ✅ part | of Breadcrumbs |
GridColumn | ✅ part | of Grid / Crud |
Tab | ✅ part | of TabLayout |
IntelliJ plugin component coverage
Section titled “IntelliJ plugin component coverage”The IntelliJ plugin has a case for every wire component type in ComponentMetadataDto (115 as
of 2026-10-10). This is enforced, not just claimed: WireTypeParityTest reads the backend’s
@JsonSubTypes list and fails the plugin build when a type has no branch in
ui/ComponentRenderer.kt, and WireTypeRenderTest renders each of them headlessly and checks for
exceptions, the “Unsupported component” fallback and accessible names on every control. The types
below were the last to arrive, so this is how each one maps to Swing:
| Wire type | IntelliJ rendering |
|---|---|
Grid / GridColumn | JBTable over the page rows (read-only); a stray column shows its header |
VirtualList | JBList, one line per row |
MasterDetailLayout | Splitter (master / detail) |
CarouselLayout | one slide at a time, ‹ › buttons and n / total |
BoardLayout / BoardLayoutRow / BoardLayoutItem | rows stacked, equal-width columns |
ContentLayout | main-* centre, aside-* start/end column (asideWidth), footer-* below |
ResponsiveGrid | grid with the column count of gridTemplateColumns |
FormItem, Tab, AccordionPanel, Stepper met outside their container | a row / titled group / stack of their children |
Breadcrumbs / Breadcrumb | link trail, current item in bold |
MenuBar | row of buttons; groups open a popup menu (submenus nest) |
ContextMenu | the wrapped component with a popup (right click, or left click when asked) |
Directory | sitemap of links under group headings |
Avatar / AvatarGroup | round initials badge (name as tooltip and accessible name); +N overflow |
Icon | the platform icon for known names, else the name |
Details | disclosure toggle (▸/▾) over its content |
Tooltip | tooltip + accessible description on the wrapped component |
Notification, Result, NotFound | inline strip / outcome page with links / not-found page with a way back |
CookieConsent | dismissible strip (the IDE has no cookies; dismissal lasts the session) |
Element | the element as HTML text; an on.click runs its action |
Bpmn | the process as an ordered list of its named flow nodes (start → tasks → end) |
Workflow, FormEditor | their value, read-only and monospaced |
Chat, MessageList, MessageInput | inline assistant (mateu-chat SSE, with the project’s token), message list, input + Send ({message}) |
Dialog, Drawer met inline | opened as the usual overlay window, once per id |
ConfirmDialog | modal confirm / reject / cancel when openedCondition holds |
MicroFrontend | an embedded island with its own context; another origin gets its own session without the project’s token |
Since 2026-07-12 (DS-native rule) the non-Vaadin web renderers render crud layouts (table/list/cards/masterDetail/tree), toolbar buttons and grid-stereotype form fields with their OWN design-system components, and since 2026-07-16 the shells render the app header actions (plain buttons + dropdown groups) with their own widgets too.
MessageList/MessageInput (Vaadin): as of 2026-07-10 these carry a real data model
(List<MessageListItem> / an actionId that fires on submit) — they were previously stubs that
rendered hardcoded demo data.
Retired renderers (2026-08-12 reconciliation). SAP UI5, Redwood-OJ (OJET), Red Hat/PatternFly
and SLDS were retired, and their apps/sapui5, apps/redhat and apps/slds directories have
since been deleted. They had a column here long after they stopped
existing — which is the worst failure mode for this page, since a matrix that promises a renderer
nobody can use is worse than one that admits a gap. The supported web renderers are Vaadin and the
Redwood/VB line.
frontend/app/vscode-extension is not a renderer: it hosts the visual editor (the same web
bundle the IntelliJ JCEF host runs), so it belongs with the tooling, not in this table.
Authoring tooling. The IntelliJ plugin (renderer, visual editor, specs/ui schema validation,
binding checks, New | Mateu file templates, per-project settings with bearer/OIDC
authentication) and the VS Code extension (the same visual editor bundle, yamlValidation for
specs/ui/**, Mateu: New File… with the same skeletons) are supported tooling. The Figma
design-to-code pipeline remains preview: it is not covered by the support promise above.
Fetch-plan edges (known renderer gaps). The client-side fetch plan (optionsSource,
rowsSource, restAction, restData — see the renderer contract)
is honoured with two edges:
- Multi-value
optionsSource. Vaadin resolvesoptionsSourceon a single-select field (the common reference case — a record pointing at one other entity). A multi-value field (multiSelect/listBox/combobox— a record pointing at many) reads staticoptionsor a remotesearch-<field>action, notoptionsSource; a one-to-many reference to an external catalogue is not wired yet. It spans three widget branches and is deliberately deferred rather than half-wired. - VB/Redwood REST sources. Since 2026-10-10 the Redwood/VB core (
poc/restSources.mjs) consumes the catalogue like the web: listingrowsSource, fieldoptionsSource, routedata:andrestActions, resolved byrefagainstAppDto.restSources/ the bundle manifest, direct or proxied (__restfetch__), with sample data under the same opt-in rule and columntones. Its${…}interpolation covers dotted paths only, not the web’s full expression language.
Update this page whenever parity moves — it is referenced from the language manuals and the Rosetta.