Enforce the shared WebUI pattern language
Dependency Audit / dependency-audit (push) Successful in 1m43s
Deployment Installer / deployment-installer (push) Successful in 6s
Security Audit / security-audit (push) Successful in 11m17s

This commit is contained in:
2026-08-18 13:17:22 +02:00
parent 5bb8028147
commit a1b80eda27
6 changed files with 248 additions and 65 deletions
+48 -14
View File
@@ -82,9 +82,18 @@ retaining the standard inset, and `embedded` owns neither scroll nor inset.
owns module/resource subnavigation plus content; its `split` variant owns
collection/detail panes. It centralizes pane sizing, internal scroll,
responsive collapse/stacking, accessible pane labels and workspace help
identity. `PageHeader` remains available when an exceptional canvas needs only
the shared heading. Specialized layouts such as `AdminPageLayout` compose
these lower-level Core contracts; they do not repeat markup or responsive CSS.
identity. `WorkspaceFrame` is the outer full-height module frame and owns
container or application-viewport height, overflow, surface, landmark, help,
and accessible-name behavior. `PageHeader` remains available when an
exceptional canvas needs only the shared heading. Specialized layouts such as
`AdminPageLayout` compose these lower-level Core contracts; they do not repeat
markup or responsive CSS.
Composite workspaces whose selected contribution supplies its own semantic
heading may use `PageLayout` with its visible header delegated. This preserves
the central inset, loading boundary, help identity, and content frame without
adding a duplicate heading. It is not permission to recreate the page header
locally on ordinary headed pages.
Module CSS may arrange domain content inside a shared layout. It must not
override Core layout internals or copy the outer page, dialog, toolbar, form or
@@ -102,16 +111,34 @@ Migration is incremental and enforceable:
5. promote the next repeated structure only after its variants and extension
points are understood.
The current page-frame and workspace migration has no legacy exceptions. New
raw frames fail the focused layout contract instead of entering a new baseline.
The current structural vocabulary is:
- `ActionToolbar`, `ToolbarGroup`, and `ToolbarSpacer` own action alignment,
density, grouping, accessible toolbar naming, help identity, and responsive
wrapping. Modules may add surface padding or domain-specific presentation;
they do not recreate the flex/wrap skeleton.
distribution, density, grouping, panel/section surfaces, accessible toolbar
naming, help identity, and responsive wrapping. Modules may add
domain-specific presentation; they do not recreate the flex/wrap skeleton.
- `WorkspaceFrame` and `WorkspaceLayout` own application-viewport framing,
surfaces, overflow, list/detail and navigation/content pane geometry,
accessible region identity, and responsive pane behavior. Modules own only
the domain regions placed inside those contracts.
- `FilterBar` owns submitted or live filter/search arrangement, wrapping,
width and surface. `SelectionList`, `SelectionListItem`, and
`SelectionListItemContent` own selectable resource navigation and its
title/description/leading-icon geometry. `CountBadge` owns compact numeric
emphasis. Modules retain filter behavior, selection state, and count meaning.
- `StatePanel` owns whole-surface, compact, inline and fill state presentation
for empty, unavailable, blocked, warning and recoverable-error compositions.
Modules provide the cause, consequence, permitted action and authority.
- `ContentGrid`, `FormGrid`, `FormLayout`, and `GridItem` own equal-column
geometry, standard gaps, alignment, spans, native form semantics, and named
responsive collapse points. A module-local grid remains appropriate only
when unequal tracks or domain visualization semantics are material.
- `ContentSection` owns repeated bordered or subtle content-section surfaces,
density, stacked flow and surrounding rhythm without prescribing a domain
heading or body schema.
- `FormSection` owns form-section heading, description, actions, content flow,
separation, and panel presentation. It does not own field values,
validation, permissions, or domain wording.
@@ -129,15 +156,22 @@ The current structural vocabulary is:
`DialogSection` own the footer action flow, native form flow, and body
grouping used inside it. Modules compose fields and consequences rather than
recreating dialog anatomy.
- `DefinitionPalette`, `DefinitionPaletteGroup`, `DefinitionPaletteItem`, and
`DefinitionNodeIcon`, together with the shared definition-canvas classes,
own reusable graph-editor palette, canvas-control, node-icon, port and empty
overlay visuals. Workflow/Dataflow retain node types, shapes, edges,
validation and execution semantics. `FloatingStatus` owns the common
non-shifting activity overlay.
Raw toolbar tags, the former generic grid and property-list classes, raw
dialog-form wrappers, and module-local definitions of these contracts are
rejected by the focused workspace checks. Dialog widths matching the Core size
scale must use `Dialog size`; other local widths require a reviewed exception
and may only decrease. The next promotion candidates are assignment/picker
composition, filter/search composition, and unified empty/error state
composition. Their APIs must remain composable; a central component is not a
single oversized page template.
Raw toolbar tags, the former generic grid and property-list classes, retired
module-local shells/states/metrics/badges, raw dialog-form wrappers, and
module-local definitions of these contracts are rejected by the focused
workspace checks. Dialog widths matching the Core size scale must use `Dialog
size`; other local widths require a reviewed exception and may only decrease.
Remaining local layout is acceptable only for unequal-track domain editors,
visualizations, trees, timelines, data tables, or domain-specific multi-pane
interaction. Generic resemblance alone is not a reason to create one oversized
page template, while exact repeated structural anatomy must be promoted.
## Surface Archetypes
@@ -1,29 +1,35 @@
# Shared WebUI Primitive Inventory
This 2026-08-18 inventory records the implementation state after the
product-wide toolbar, grid, form-section, dialog-anatomy, metric-group, and
description-list migrations. It is
evidence for prioritization, not a substitute for the normative
product-wide structural consolidation and second duplicate-rule audit. It is
evidence for enforcement, not a substitute for the normative
[interface pattern language](../architecture/INTERFACE_PATTERN_LANGUAGE.md).
## Implemented And Enforced
| Contract | Adoption evidence | Ownership now enforced |
| --- | ---: | --- |
| `ActionToolbar` and groups | 39 source files | Raw module-prefixed toolbar elements and local toolbar component definitions are rejected. |
| `ContentGrid` | 15 source files | Former `dashboard-grid` and `settings-grid` wrappers are rejected. |
| `FormGrid` and `FormLayout` | 53 source files | Former `form-grid` and `admin-form-grid` wrappers are rejected; equal-column dialog and editor grids were migrated even when they had module-prefixed names. |
| `FormSection` | 2 representative source files | The reusable section API, variants, responsive action placement, and heading anatomy are Core-owned; broader adoption is incremental. |
| `DialogActions` | Every Core `Dialog` footer | Footer wrapping and alignment are no longer repeated by consumers. |
| `DialogForm` | 6 source files | Raw `*-dialog-form` form wrappers are rejected. |
| `DialogSection` | 6 source files | Repeated dialog field/content grouping is available as a shared primitive. |
| `MetricGrid` | 29 uses in 25 source files | The former `metric-grid` class and module-specific column overrides were removed. Core now owns 15/auto-fit columns, minimum width, density, block/inset/zero spacing, and collapse. |
| `DescriptionList` and `DescriptionItem` | 51 lists in 28 source files; item composition in 11 files | The former `admin-details-grid` and `detail-list` classes were removed. Core now owns stacked and inline property layouts, density, term width, wrapping, and collapse while preserving native `dl`/`dt`/`dd` semantics. |
| Standard dialog sizing | 9 consumers and 6 duplicated width rules removed | Confirmations and compatible Calendar, credential, Campaign, Files, and Voting dialogs use the Core small/large/wide scale. The 61 remaining specialized selectors across 22 CSS files are registered and decrease-only. |
| `ActionToolbar` and groups | 50 source files | Raw module-prefixed toolbar elements and local toolbar definitions are rejected. Distribution, wrapping, density, grouping and panel/section surfaces are Core-owned. |
| `PageLayout` / `WorkspaceLayout` / `WorkspaceFrame` | 25 / 16 / 17 source files | Headed page anatomy, full-height viewport frames and navigation/list-detail panes no longer repeat inset, heading, notices, loading, shell height, surface, overflow or pane geometry. The raw page-frame and raw workspace exception baselines are both empty. |
| `FilterBar` | 14 source files | Catalogue and pane search/filter rows share width, surface, layout and wrapping. |
| `SelectionList` family | 19 source files | Resource navigation shares selection, title/description, leading-icon and truncation anatomy. |
| `StatePanel` | 25 source files | Whole-surface, compact and fill empty/blocked/error states replace module-local state shells. |
| `CountBadge` | 8 source files | Notification, folder, search, postbox and graph counts use one compact badge contract. |
| `ContentSection` | 5 source files | Repeated bordered/subtle editor sections and compact provenance panels share surface, density, flow and rhythm. |
| `ContentGrid` | 22 source files | Equal-column content geometry and former dashboard/settings/assignment copies are Core-owned. |
| `FormGrid` and `FormLayout` | 53 source files | Former generic/admin grids and equal-column dialog/editor copies use named collapse points and native form semantics. |
| `MetricGrid` / `MetricCard` | 31 / 33 source files | Module-local metric helpers, grids and card visual definitions were removed. |
| `DescriptionList` and `DescriptionItem` | 28 source files | Former generic property grids use semantic `dl`/`dt`/`dd` composition with central density and collapse. |
| `DefinitionPalette`, node/canvas visuals and `FloatingStatus` | 2 Dataflow/Workflow consumers each | The copied graph palette, canvas controls, minimap, node icon/port, empty overlay and activity overlay definitions are Core-owned; graph semantics remain local. |
| `DialogActions`, `DialogForm`, `DialogSection` | every Core footer / 6 / 6 source files | Footer action flow, native dialog form flow and dialog content grouping are Core-owned. |
| Standard dialog sizing | 61 reviewed specialized selectors | Any width matching the Core 460/560/680/1040/1440px scale must use `Dialog size`; the remaining decrease-only exceptions are explicit. |
`tools/checks/check-shared-webui-primitives.py` verifies Core exports and
ownership, representative consumers, the absence of the retired raw anatomy,
and composition of every `Dialog` footer through `DialogActions`.
`tools/checks/check-shared-webui-layouts.py` additionally requires the reviewed
Core and module consumers and rejects any raw page or workspace frame; there
are no remaining allow-listed layout exceptions.
## Dialog Width Classification
@@ -36,31 +42,33 @@ Their exact selector set lives in
for a new selector, a stale baseline entry, or any local width that duplicates
the Core scale.
## Remaining Promotion Candidates
## Audit Result And Deliberate Local Ownership
The post-migration scan still finds domain-specific grids, but the repeated
generic metric and property-list geometry is gone. Remaining grids are mostly
unequal-track editors, visualizations, workflow facts, import mappings, and
collection layouts. The next useful candidates depend on interaction semantics:
The second scan compared exact CSS declaration bodies and JSX anatomy across
every WebUI module after migration. All repeated generic structural candidates
found in that pass were promoted: viewport frames, catalogue/list shells,
filters, selectable lists, state panels, count badges, section frames,
equal-column grids, section headers, metrics, and definition-editor chrome.
The final legacy-baseline pass also migrated Access administration, Core
Settings, Docs, Mail bounce processing, and Organizations to the shared page
and workspace layouts and removed their copied responsive geometry.
| Priority | Remaining pattern | Evidence | Proposed contract |
| --- | --- | ---: | --- |
| 1 | Empty and collection state anatomy | More than 70 module-prefixed empty/state class uses remain; only loading, alerts, blockers, and DataGrid empty actions are centralized | A composable `StatePanel`/`CollectionState` covering empty, recoverable error, permission block, partial result, and next action without hiding domain consequences. First distinguish a whole-surface state from a compact empty row or optional-value placeholder. |
| 2 | Filter and search rows | More than 50 filter/search class tokens cover simple text search, facets, popovers, result counts, active filters, bulk selection, and overlay search | Define `FilterBar` only after the input/submit, live filtering, facet, result-summary, and bulk-action accessibility variants have been compared. Plain action placement already uses `ActionToolbar`. |
| 3 | Assignment/picker groups | `admin-assignment-grid`: 5 uses in 4 files | Promote a selection/assignment composition only after its list, search, empty, policy, and permission variants are compared; plain geometry can already use `ContentGrid`. |
| 4 | Repeated domain fact and statistic grids | `campaign-header-grid` (8), `review-flow-fact-grid` (6), `postbox-form-grid` (5), plus smaller families | Compare semantics before promotion. Some can use `ContentGrid` or `DescriptionList`; others intentionally own unequal tracks or workflow visualization. |
The remaining cross-module declaration matches are not independent component
anatomy. They are small token-based rules such as ellipsis, muted captions,
uppercase terms, or flex-column containment applied to different semantic
elements. Moving those rules into a component would erase meaning; their
visual values already come from Core tokens. Remaining larger local layouts
are deliberately domain-owned:
The other custom grids are mostly bounded domain visualizations or unequal-track
editors: conflict mappings, import mapping, campaign review facts, charts,
calendar time views, connector synchronization, records lists, and definition
editors. They should remain module-owned unless a second domain demonstrates
the same semantics and interaction contract. A shared primitive should not be
created merely because two implementations both use CSS Grid.
- unequal-track editors, import mappings and schema/data tables;
- calendar time grids, charts, graph node shapes and graph edge semantics;
- file/mail/postbox/records explorer panes whose interaction contracts differ;
- timelines, evidence histories, recipient compositions and policy-specific
detail sections;
- compact list-row internals that cannot preserve their semantics through
`SelectionListItemContent`.
## Next Audit
The next bounded slice should compare whole-surface empty states across list,
detail, permission, capability, and recoverable-error contexts and promote only
their shared anatomy. Filter/search composition follows after its live versus
submitted filtering and bulk-selection behavior is explicit. Assignment grids
remain deferred until their permission and policy variants are understood.
A future candidate is promoted only when a new audit identifies repeated
structure plus the same responsive, accessibility and interaction contract.
The enforcement script prevents regression for the patterns centralized in
this pass and maintains the reviewed dialog-width baseline.
@@ -45,12 +45,20 @@ REQUIRED_CONSUMERS = (
pathlib.Path("govoplan-campaign/webui/src/features/operator/OperatorQueuePage.tsx"),
pathlib.Path("govoplan-campaign/webui/src/features/reports/AggregateReportsPage.tsx"),
pathlib.Path("govoplan-campaign/webui/src/features/templates/TemplatesPage.tsx"),
pathlib.Path("govoplan-access/webui/src/features/admin/AdminPage.tsx"),
pathlib.Path("govoplan-core/webui/src/features/settings/SettingsPage.tsx"),
pathlib.Path("govoplan-docs/webui/src/features/docs/DocsPage.tsx"),
pathlib.Path("govoplan-mail/webui/src/features/mail/MailBouncePage.tsx"),
)
REQUIRED_WORKSPACE_CONSUMERS = (
pathlib.Path("govoplan-campaign/webui/src/features/campaigns/CampaignWorkspace.tsx"),
pathlib.Path("govoplan-campaign/webui/src/features/campaigns/CampaignModulePage.tsx"),
pathlib.Path("govoplan-campaign/webui/src/features/templates/TemplatesPage.tsx"),
pathlib.Path("govoplan-approvals/webui/src/features/approvals/ApprovalsPage.tsx"),
pathlib.Path("govoplan-access/webui/src/features/admin/AdminPage.tsx"),
pathlib.Path("govoplan-core/webui/src/features/settings/SettingsPage.tsx"),
pathlib.Path("govoplan-docs/webui/src/features/docs/DocsPage.tsx"),
pathlib.Path("govoplan-organizations/webui/src/features/organizations/OrganizationsPage.tsx"),
)
+143 -2
View File
@@ -42,6 +42,9 @@ CENTRAL_COMPONENTS = {
"ContentGrid": pathlib.Path(
"govoplan-core/webui/src/components/ContentGrid.tsx"
),
"ContentSection": pathlib.Path(
"govoplan-core/webui/src/components/ContentSection.tsx"
),
"FormGrid": pathlib.Path("govoplan-core/webui/src/components/ContentGrid.tsx"),
"FormLayout": pathlib.Path(
"govoplan-core/webui/src/components/ContentGrid.tsx"
@@ -68,6 +71,48 @@ CENTRAL_COMPONENTS = {
"MetricGrid": pathlib.Path(
"govoplan-core/webui/src/components/MetricGrid.tsx"
),
"MetricCard": pathlib.Path(
"govoplan-core/webui/src/components/MetricCard.tsx"
),
"FilterBar": pathlib.Path(
"govoplan-core/webui/src/components/FilterBar.tsx"
),
"StatePanel": pathlib.Path(
"govoplan-core/webui/src/components/StatePanel.tsx"
),
"CountBadge": pathlib.Path(
"govoplan-core/webui/src/components/CountBadge.tsx"
),
"SelectionList": pathlib.Path(
"govoplan-core/webui/src/components/SelectionList.tsx"
),
"SelectionListItem": pathlib.Path(
"govoplan-core/webui/src/components/SelectionList.tsx"
),
"SelectionListItemContent": pathlib.Path(
"govoplan-core/webui/src/components/SelectionList.tsx"
),
"WorkspaceLayout": pathlib.Path(
"govoplan-core/webui/src/components/WorkspaceLayout.tsx"
),
"WorkspaceFrame": pathlib.Path(
"govoplan-core/webui/src/components/WorkspaceFrame.tsx"
),
"DefinitionPalette": pathlib.Path(
"govoplan-core/webui/src/components/DefinitionPalette.tsx"
),
"DefinitionPaletteGroup": pathlib.Path(
"govoplan-core/webui/src/components/DefinitionPalette.tsx"
),
"DefinitionPaletteItem": pathlib.Path(
"govoplan-core/webui/src/components/DefinitionPalette.tsx"
),
"DefinitionNodeIcon": pathlib.Path(
"govoplan-core/webui/src/components/DefinitionNodeIcon.tsx"
),
"FloatingStatus": pathlib.Path(
"govoplan-core/webui/src/components/FloatingStatus.tsx"
),
}
REQUIRED_CONSUMERS = {
"ActionToolbar": (
@@ -82,6 +127,11 @@ REQUIRED_CONSUMERS = {
pathlib.Path("govoplan-campaign/webui/src/features/campaigns/GlobalSettingsPage.tsx"),
pathlib.Path("govoplan-notifications/webui/src/features/notifications/NotificationSettingsPanel.tsx"),
),
"ContentSection": (
pathlib.Path("govoplan-datasources/webui/src/features/datasources/DatasourcesPage.tsx"),
pathlib.Path("govoplan-dist-lists/webui/src/features/distributionLists/DistributionListsPage.tsx"),
pathlib.Path("govoplan-templates/webui/src/features/templates/TemplatesPage.tsx"),
),
"FormGrid": (
pathlib.Path("govoplan-core/webui/src/components/mail/MailServerSettingsPanel.tsx"),
pathlib.Path("govoplan-calendar/webui/src/features/calendar/CalendarEventDialog.tsx"),
@@ -114,10 +164,91 @@ REQUIRED_CONSUMERS = {
pathlib.Path("govoplan-campaign/webui/src/features/operator/OperatorQueuePage.tsx"),
pathlib.Path("govoplan-notifications/webui/src/features/notifications/NotificationSummaryWidget.tsx"),
),
"MetricCard": (
pathlib.Path("govoplan-admin/webui/src/features/admin/AdminOverviewPanel.tsx"),
pathlib.Path("govoplan-approvals/webui/src/features/approvals/ApprovalsPage.tsx"),
pathlib.Path("govoplan-campaign/webui/src/features/campaigns/ReviewSendPage.tsx"),
pathlib.Path("govoplan-voting/webui/src/features/voting/VotingPage.tsx"),
),
"FilterBar": (
pathlib.Path("govoplan-cases/webui/src/features/cases/CasesPage.tsx"),
pathlib.Path("govoplan-dataflow/webui/src/features/dataflow/DataflowPage.tsx"),
pathlib.Path("govoplan-records/webui/src/features/records/RecordsPage.tsx"),
pathlib.Path("govoplan-tasks/webui/src/features/tasks/TasksPage.tsx"),
),
"StatePanel": (
pathlib.Path("govoplan-committee/webui/src/features/committee/CommitteePage.tsx"),
pathlib.Path("govoplan-notifications/webui/src/features/notifications/NotificationCenterPage.tsx"),
pathlib.Path("govoplan-postbox/webui/src/features/postbox/PostboxPage.tsx"),
pathlib.Path("govoplan-risk-compliance/webui/src/features/riskCompliance/RiskCompliancePage.tsx"),
),
"CountBadge": (
pathlib.Path("govoplan-core/webui/src/layout/Titlebar.tsx"),
pathlib.Path("govoplan-mail/webui/src/features/mail/MailboxPage.tsx"),
pathlib.Path("govoplan-search/webui/src/features/search/SearchPage.tsx"),
),
"SelectionListItemContent": (
pathlib.Path("govoplan-approvals/webui/src/features/approvals/ApprovalsPage.tsx"),
pathlib.Path("govoplan-datasources/webui/src/features/datasources/DatasourcesPage.tsx"),
pathlib.Path("govoplan-voting/webui/src/features/voting/VotingPage.tsx"),
),
"WorkspaceLayout": (
pathlib.Path("govoplan-dataflow/webui/src/features/dataflow/DataflowPage.tsx"),
pathlib.Path("govoplan-notifications/webui/src/features/notifications/NotificationCenterPage.tsx"),
pathlib.Path("govoplan-workflow/webui/src/features/workflow/WorkflowPage.tsx"),
),
"WorkspaceFrame": (
pathlib.Path("govoplan-cases/webui/src/features/cases/CasesPage.tsx"),
pathlib.Path("govoplan-portal/webui/src/features/portal/PortalPage.tsx"),
pathlib.Path("govoplan-records/webui/src/features/records/RecordsPage.tsx"),
),
"DefinitionPalette": (
pathlib.Path("govoplan-dataflow/webui/src/features/dataflow/DataflowPage.tsx"),
pathlib.Path("govoplan-workflow/webui/src/features/workflow/WorkflowPage.tsx"),
),
"DefinitionNodeIcon": (
pathlib.Path("govoplan-dataflow/webui/src/features/dataflow/DataflowNode.tsx"),
pathlib.Path("govoplan-workflow/webui/src/features/workflow/WorkflowNode.tsx"),
),
"FloatingStatus": (
pathlib.Path("govoplan-dataflow/webui/src/features/dataflow/DataflowPage.tsx"),
pathlib.Path("govoplan-workflow/webui/src/features/workflow/WorkflowPage.tsx"),
),
}
LEGACY_CSS_SELECTOR = re.compile(
r"(?<![-\w])\.(?:admin-details-grid|detail-list|metric-grid)(?![-\w])"
)
RETIRED_LOCAL_CSS_CLASSES = {
"admin-assignment-grid",
"approval-metrics",
"dataflow-node-count",
"dataflow-shell",
"datasources-metrics",
"datasources-shell",
"dist-lists-metrics",
"dist-lists-shell",
"notifications-count",
"notifications-empty-state",
"notifications-shell",
"review-flow-execution-summary",
"review-flow-fact-grid",
"risk-metrics",
"search-filter-count",
"tasks-empty-detail",
"tasks-shell",
"templates-shell",
"voting-metrics",
"voting-shell",
"workflow-shell",
"dataflow-palette-items",
"workflow-palette-items",
"dataflow-working-indicator",
"workflow-working-indicator",
"datasources-detail-section",
"dist-lists-section",
"templates-section",
}
LOCAL_METRIC_HELPER = re.compile(r"\b(?:function\s+Metric\b|const\s+Metric\s*=)")
CSS_BLOCK = re.compile(r"([^{}]+)\{([^{}]*)\}")
CSS_COMMENT = re.compile(r"/\*.*?\*/", re.DOTALL)
DIALOG_CLASS = re.compile(r"\.([A-Za-z0-9_-]*(?:dialog|modal)[A-Za-z0-9_-]*)", re.IGNORECASE)
@@ -234,10 +365,16 @@ def main() -> int:
)
for path, content in style_text.items():
legacy_match = LEGACY_CSS_SELECTOR.search(CSS_COMMENT.sub("", content))
uncommented = CSS_COMMENT.sub("", content)
legacy_match = LEGACY_CSS_SELECTOR.search(uncommented)
if legacy_match:
line = content.count("\n", 0, legacy_match.start()) + 1
errors.append(f"Legacy shared layout selector is not allowed: {path}:{line}")
for class_name in sorted(RETIRED_LOCAL_CSS_CLASSES):
retired = re.search(rf"(?<![-\w])\.{re.escape(class_name)}(?![-\w])\s*(?:,|\{{)", uncommented)
if retired:
line = content.count("\n", 0, retired.start()) + 1
errors.append(f"Retired module-local shared anatomy selector is not allowed: {path}:{line} ({class_name})")
dialog_exceptions = dialog_width_exceptions(style_text)
baseline = exception_baseline()
@@ -259,6 +396,10 @@ def main() -> int:
if path != owner and definition.search(content):
errors.append(f"Module-local {name} definition is not allowed: {path}")
for path, content in source_text.items():
if LOCAL_METRIC_HELPER.search(content):
errors.append(f"Module-local Metric helper is not allowed; compose MetricCard directly: {path}")
usage_counts: dict[str, int] = {}
for name in CENTRAL_COMPONENTS:
usage = re.compile(rf"<{name}\b")
@@ -291,7 +432,7 @@ def main() -> int:
migrated = ", ".join(
f"{name}={usage_counts[name]} files"
for name in ("ActionToolbar", "ContentGrid", "FormGrid", "FormSection", "DialogForm", "DialogSection", "MetricGrid", "DescriptionList")
for name in ("ActionToolbar", "WorkspaceFrame", "WorkspaceLayout", "FilterBar", "StatePanel", "SelectionList", "CountBadge", "DefinitionPalette", "DefinitionNodeIcon", "FloatingStatus", "ContentSection", "ContentGrid", "FormGrid", "FormSection", "DialogForm", "DialogSection", "MetricGrid", "MetricCard", "DescriptionList")
)
print(f"Shared WebUI primitive contract passed: {migrated}; {len(dialog_exceptions)} reviewed dialog width exceptions; no raw legacy anatomy.")
return 0
@@ -1,6 +1,2 @@
# Existing raw page frames. Remove an entry when that surface adopts the Core
# PageLayout contract. New entries are rejected by check-shared-webui-layouts.py.
govoplan-access/webui/src/features/admin/AdminPage.tsx
govoplan-core/webui/src/features/settings/SettingsPage.tsx
govoplan-docs/webui/src/features/docs/DocsPage.tsx
govoplan-mail/webui/src/features/mail/MailBouncePage.tsx
# Raw page-frame exceptions. Keep this file empty: module pages use the Core
# PageLayout contract and new exceptions are rejected by the layout checker.
@@ -1,6 +1,2 @@
# Existing raw full-canvas workspaces. Remove an entry when that surface adopts
# WorkspaceLayout. New entries are rejected by check-shared-webui-layouts.py.
govoplan-access/webui/src/features/admin/AdminPage.tsx
govoplan-core/webui/src/features/settings/SettingsPage.tsx
govoplan-docs/webui/src/features/docs/DocsPage.tsx
govoplan-organizations/webui/src/features/organizations/OrganizationsPage.tsx
# Raw workspace exceptions. Keep this file empty: module workspaces use the Core
# WorkspaceLayout contract and new exceptions are rejected by the layout checker.