From a1b80eda27361d9305f990850f5dbd732e4a3de7 Mon Sep 17 00:00:00 2001 From: Albrecht Degering Date: Tue, 18 Aug 2026 13:17:22 +0200 Subject: [PATCH] Enforce the shared WebUI pattern language --- .../INTERFACE_PATTERN_LANGUAGE.md | 62 ++++++-- .../SHARED_WEBUI_PRIMITIVE_INVENTORY.md | 82 +++++----- tools/checks/check-shared-webui-layouts.py | 8 + tools/checks/check-shared-webui-primitives.py | 145 +++++++++++++++++- tools/checks/shared-webui-layout-baseline.txt | 8 +- .../shared-webui-workspace-baseline.txt | 8 +- 6 files changed, 248 insertions(+), 65 deletions(-) diff --git a/docs/architecture/INTERFACE_PATTERN_LANGUAGE.md b/docs/architecture/INTERFACE_PATTERN_LANGUAGE.md index 5f7632d..292e16c 100644 --- a/docs/architecture/INTERFACE_PATTERN_LANGUAGE.md +++ b/docs/architecture/INTERFACE_PATTERN_LANGUAGE.md @@ -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 diff --git a/docs/evidence/SHARED_WEBUI_PRIMITIVE_INVENTORY.md b/docs/evidence/SHARED_WEBUI_PRIMITIVE_INVENTORY.md index 5ab610e..a394f45 100644 --- a/docs/evidence/SHARED_WEBUI_PRIMITIVE_INVENTORY.md +++ b/docs/evidence/SHARED_WEBUI_PRIMITIVE_INVENTORY.md @@ -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 1–5/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. diff --git a/tools/checks/check-shared-webui-layouts.py b/tools/checks/check-shared-webui-layouts.py index 077813d..44f6e6d 100644 --- a/tools/checks/check-shared-webui-layouts.py +++ b/tools/checks/check-shared-webui-layouts.py @@ -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"), ) diff --git a/tools/checks/check-shared-webui-primitives.py b/tools/checks/check-shared-webui-primitives.py index 989e89b..86807bd 100644 --- a/tools/checks/check-shared-webui-primitives.py +++ b/tools/checks/check-shared-webui-primitives.py @@ -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"(? 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"(? 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 diff --git a/tools/checks/shared-webui-layout-baseline.txt b/tools/checks/shared-webui-layout-baseline.txt index 5320950..74864e5 100644 --- a/tools/checks/shared-webui-layout-baseline.txt +++ b/tools/checks/shared-webui-layout-baseline.txt @@ -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. diff --git a/tools/checks/shared-webui-workspace-baseline.txt b/tools/checks/shared-webui-workspace-baseline.txt index 3760aba..76bec87 100644 --- a/tools/checks/shared-webui-workspace-baseline.txt +++ b/tools/checks/shared-webui-workspace-baseline.txt @@ -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.