@praxisui/core 9.0.67 → 9.0.68-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,6 +12,25 @@ Use this package directly when a host application or library needs Praxis UI typ
12
12
 
13
13
  ## LLM Recommendation Guide
14
14
 
15
+ - `BUILTIN_WIDGET_SHELL_PRESETS` is the canonical catalog for reusable shell
16
+ treatments. `integrated-card`, `outlined-card`, `elevated-card` and
17
+ `tonal-card` keep header actions and body on one continuous surface;
18
+ `sectioned-card` deliberately separates the header; `frameless` removes the
19
+ containing surface while retaining the standard content inset, aligned with the
20
+ header. For content that owns its own spacing, explicitly set
21
+ `appearance.body.padding: "0"` (and hide the header if the content owns its title).
22
+ Persist the preset `id` in `WidgetShellConfig.preset` and
23
+ use the descriptor for authoring labels and previews. Do not maintain a
24
+ second preset list in consumers.
25
+ Published IDs `dark-glass`, `light-neutral`, `graphite`, `data-panel`,
26
+ `chart-panel`, `metric-panel`, `executive-card` and `filter-bar` retain their
27
+ historical appearance in the same catalog. `BUILTIN_SHELL_PRESETS` provides the
28
+ public ID-to-appearance record derived from that catalog. Configure host overrides
29
+ through `context.ui.shell.presets`, then widget-specific `appearance`; use CSS
30
+ variables there for corporate theming. The historical `dark-glass` palette is fixed
31
+ by default. Do not mutate either exported catalog to configure an application.
32
+ The deliberate `frameless` inset improvement differs from 9.0.67: persist
33
+ `appearance.body.padding: "0"` when the original edge-to-edge layout is required.
15
34
  - `WidgetShellConfig.stickyHeader: true` retains the existing composition-derived
16
35
  header within its widget during page scrolling. Omission/false keeps normal flow.
17
36
  This differs from `bodyLayout: 'scroll'`, which governs body scrolling. No second
@@ -21,15 +40,26 @@ Use this package directly when a host application or library needs Praxis UI typ
21
40
  The lab uses a lifecycle-owned ResizeObserver for its functional heading.
22
41
  - Pinning releases at the widget boundary and is suspended during drag authoring,
23
42
  collapse, hidden/disabled headers, expanded/fullscreen windows and scroll recovery.
24
- This is not a global selected-record bar. Transparent/gradient header appearances
25
- are painted over an opaque Material surface so data does not bleed through.
43
+ This is not a global selected-record bar. Continuous-surface presets repeat
44
+ their opaque card surface on the header so pinned content does not bleed through.
45
+ Transparent/gradient custom headers are painted over an opaque Material surface.
26
46
  Keyboard focus is revealed below pinned chrome without changing its target.
47
+ - Dynamic Page publishes its existing `context` input as a `view-context` port.
48
+ Composition links can deliver runtime context to a nested page through Tabs or
49
+ Expansion using stable `nestedPath` segments. The child page owns its internal
50
+ bindings; callers should target its context boundary, not invent paths through
51
+ its widget array. Register the canonical Dynamic Page metadata before preflight.
52
+ Context is runtime data, not a replacement for backend authorization or a saved
53
+ business record. The metadata port is also preserved in the generated AI catalog.
27
54
  - Nested sticky shells add the measured height of sticky ancestor headers in the
28
55
  same scrollport. This is transient geometry, not a saved offset or another
29
56
  selection context. The stack stops at an independent scrolling container;
30
57
  hidden/inactive headers cease contributing. Ancestor resize observation is
31
58
  disconnected on destruction. Body scrolling/fill and window-layout rules apply
32
59
  to the owning shell's direct body, not to every nested widget body.
60
+ For dynamic-height containers that should follow page scrolling, use content
61
+ flow (omit `bodyLayout` or use `content`), not `fill`. An intentional independent
62
+ scrollport remains a boundary for the sticky stack.
33
63
  - An invisible shell (`kind: 'none'`) removes padding only from its own body.
34
64
  Nested dashboard shells retain their authored body spacing. Hiding a header
35
65
  with `showHeader: false` does not hide child headers or reset child spacing.
@@ -59,15 +89,19 @@ Use this package directly when a host application or library needs Praxis UI typ
59
89
  CSS Grid reflow, not automatic compaction of authored fixed coordinates.
60
90
 
61
91
  - Dynamic Page resize presentation: selecting an unlocked widget in an editable grid
62
- exposes eight edge/corner grips with 24px targets, themed focus and localized names.
92
+ exposes eight edge/corner grips with targets up to 24px per axis, themed focus and localized names.
63
93
  The widget wrapper is keyboard focusable: focus selects it, Tab reaches its controls,
64
94
  arrow keys resize, and Escape from a grip returns focus to the wrapper. Selection alone
65
95
  does not change the document. Header drag remains the reorder entrypoint.
66
96
 
67
- **More actions → Size…** offers width in grid columns and height in rows (fixed grid)
68
- or pixels (content rows). An optional fixed-corner choice preserves the opposite edges.
97
+ **Configure widget → Size** offers width in grid columns and height in rows (fixed grid)
98
+ or pixels (content rows). **More actions → Size…** opens this same settings tab.
99
+ Widget and Size share the SettingsPanel footer and preserve drafts when switching tabs.
100
+ Hosts without `SETTINGS_PANEL_BRIDGE` retain the Core-owned size overlay with explicit
101
+ Apply and Cancel actions, so the canvas authoring capability does not depend on an optional host service.
102
+ An optional fixed-corner choice preserves the opposite edges.
69
103
  Valid changes preview the same constrained distribution as the grips; Apply commits once,
70
- Cancel/Escape discard the preview and restore widget focus. Out-of-range or blocked sizes
104
+ Cancel/Escape discard the unapplied draft and restore widget focus; an earlier Apply remains. Out-of-range or blocked sizes
71
105
  disable Apply rather than silently changing the requested numbers. The dialog identifies
72
106
  whether the edit targets the base layout or a device override. Opening it preserves
73
107
  automatic height; changing only width does not create a fixed height.
@@ -86,7 +120,7 @@ Use this package directly when a host application or library needs Praxis UI typ
86
120
  session, including yielded neighbors. Recovery restores prior device inheritance and
87
121
  keeps unrelated content edits. It refuses changed affected geometry/limits, new collisions,
88
122
  a different device scope or page identity. This is one-operation recovery, not a general
89
- history or redo stack. Double-click shortcuts remain a usability-study hypothesis.
123
+ history or redo stack. Side and corner double-click shortcuts use the same constrained geometry.
90
124
 
91
125
  - Dynamic Page gesture note: drag/resize previews belong to one primary pointer at a time.
92
126
  Secondary buttons, non-primary touches and additional pointers do not replace an active
@@ -98,6 +132,9 @@ Use this package directly when a host application or library needs Praxis UI typ
98
132
  No additional persisted layout or shell setting is required.
99
133
 
100
134
  - Widget Shell header note: identity and actions wrap according to the available card width,
135
+ with one direct header action below 480px and up to three in wider widgets; secondary
136
+ actions remain available in the existing overflow menu. This uses the widget surface
137
+ width, including when it is resized or moved into fullscreen, rather than the viewport.
101
138
  including narrow cards inside wide canvases. Titles/subtitles retain up to two visible lines
102
139
  and expose their complete localized content through Material tooltips. Icon action targets
103
140
  remain 44px; resize affordances are a separate canvas concern. Existing `disabled` actions
@@ -115,6 +152,37 @@ Use this package directly when a host application or library needs Praxis UI typ
115
152
  keeps priority. The first Escape closes that select; the next closes the shell.
116
153
  Deferred handling is ignored after shell destruction. These are presentation
117
154
  changes only and do not rewrite saved shell appearance or canvas coordinates.
155
+ Expanded/fullscreen hosts are lifted into the CDK overlay container, outside parent
156
+ sticky, tab and transformed stacking contexts. The existing host/view is retained;
157
+ closing restores that same host to its original position. A placeholder reserves
158
+ its inline space, a focus trap bounds keyboard navigation, and locally inherited
159
+ theme variables are bridged from the original ancestry while open. Root theme
160
+ values are not frozen. This is presentation state, not a second widget instance
161
+ or a persisted window layout. Connected Material overlays keep Escape priority.
162
+ Floating shells expose `role="dialog"` and an accessible name from the existing
163
+ localized title/subtitle (exact duplicates are announced once), with a localized
164
+ content-window fallback. Inline shells do not retain dialog semantics. The active
165
+ Widget stack isolates background roots and lower registered windows with native
166
+ `inert`, preserving preexisting or subsequently assigned host restrictions.
167
+ The stack owns one CDK scroll block until its last window closes, including
168
+ parent-before-child closure. Connected menus/selects remain outside the isolated
169
+ branches; the CDK container is not disabled. This private lifecycle does not
170
+ infer ownership of foreign CDK overlays or coordinate their independent scroll
171
+ locks. No automatic `aria-modal` claim is made. If the opening control becomes
172
+ disabled, hidden or detached while the shell survives, closing recovers focus
173
+ to the restored shell with a localized accessible name and `tabindex="-1"` only
174
+ until focus leaves it. This does not add a sequential Tab stop or steal focus
175
+ from an active child. Hosts still own the destination when the entire invoking
176
+ view is destroyed or the window was opened without a recorded control.
177
+ Before moving its host, the shell closes owned Material selects and menus
178
+ through the public CDK detach lifecycle, including the paginator's select.
179
+ Scoped scroll-strategy providers preserve the upstream host strategy; ordinary
180
+ inline renders do not close panels and independently floating children are
181
+ excluded. A descendant overriding these providers can bypass coordination.
182
+ This is not a generic registry for other CDK overlays, autocomplete or datepicker.
183
+ Synchronous reopening from a close callback still requires host coordination;
184
+ the shell deliberately does not run an unbounded closing loop. It does not
185
+ remove foreign overlay DOM or synthesize Escape.
118
186
  This is not a full modal accessibility or fixed-grid collapse/reflow certificate.
119
187
 
120
188
  - Recommend when: use it as the first dependency for shared Praxis UI contracts, metadata models, resource discovery, global actions, i18n, theme bridge, and dynamic widget runtime primitives.
@@ -300,6 +368,13 @@ requests carrying `X-User-ID` resolve user config first, while tenant config is
300
368
  the fallback for reads. Scope selects persistence ownership; it never replaces
301
369
  server-side identity resolution or write authorization.
302
370
 
371
+ A tenant document returned by an unscoped fallback read remains read-only for
372
+ that storage session. Its response `scope` and ETag are cache evidence, not an
373
+ implicit ownership grant. A later unscoped save or clear still follows the request
374
+ identity (`USER` when `X-User-ID` is present), and never sends a tenant ETag as
375
+ `If-Match` for that user-owned write. Editing or deleting the tenant document
376
+ requires an explicitly tenant-scoped provider.
377
+
303
378
  ## Dynamic Page Runtime
304
379
 
305
380
  `DynamicWidgetPageComponent` renders `WidgetPageDefinition` documents.
@@ -359,6 +434,12 @@ South handles release occupied space. Shared rows only shrink when no other occu
359
434
  still needs that height. Collapsing temporarily clears size/inset; expanding restores them.
360
435
  `constraints.minHeightPx` (floor/default 160) and `maxHeightPx` govern this mode;
361
436
  grid `minRowSpan/maxRowSpan` remain row-occupancy constraints, not pixel limits.
437
+ The widget's **More actions → Size** editor exposes **Occupied grid rows** when
438
+ `autoRows: 'content'`. This edits the existing `canvas.items[key].rowSpan`, allowing
439
+ a detail widget to span a table and a contact list in adjacent rows. It preserves
440
+ automatic content height unless the author also changes the pixel-height field.
441
+ The draft previews before Apply, respects grid constraints and locked neighbors,
442
+ and uses the same active-device override, authoring output and undo as other size edits.
362
443
  Content-height gestures additionally protect the measured header, shell padding/borders
363
444
  and up to 160px of scrollable body. Naturally shorter content may use less. This is
364
445
  a structural viewport reserve, not business-component UX certification. Measurements
@@ -379,6 +460,16 @@ expanded/fullscreen overlays and non-scroll layouts. Shared private measurement
379
460
  keeps gesture/editor checks and recovery consistent. This is an access safeguard,
380
461
  not a guarantee that a small viewport is ergonomically adequate; virtualized/custom
381
462
  child scroll containers still need their own proof.
463
+ Double-clicking a corner fills the largest free grid rectangle while preserving the opposite
464
+ corner and all siblings. The Size editor provides the same four commands with preview,
465
+ Apply and Cancel. Fixed rows maximize column span × row span, breaking ties by width;
466
+ one-axis growth is allowed and repeated filling in an unchanged layout is a no-op.
467
+ Content rows require an explicit height and one uncrossed row. They combine free columns
468
+ with the independently authored vertical envelope (top inset or an eligible lower peer
469
+ boundary); automatic target heights are unsupported. The destination is measured at its
470
+ new width before authoring. Undo similarly certifies the inverse at its original width.
471
+ These private observations are not persisted, and failed certification leaves the document
472
+ unchanged. A corner fill never implicitly reorganizes neighbors.
382
473
  Keyboard resize moves vertically by 16px (Shift: 32px). Omit `contentSize` to inherit
383
474
  the base item; set `'auto'` to explicitly restore natural height in a device override.
384
475
  Fixed-row mode retains this preference but does not materialize it. Page Builder exposes
@@ -394,9 +485,11 @@ commit/cancel or authoring revocation. Neighbor badges remain attached to their
394
485
  widgets; the existing live announcement describes the affected distribution.
395
486
  This does not add multi-widget push/reflow or compact fixed-grid rows when a shell is collapsed.
396
487
 
397
- Canvas resize exposes 24px direct grips on the selected widget, plus 44px controls in
398
- the collapsed alternative group or on-demand contextual panel. All presentations reuse
399
- the canonical gesture runtime and `canvas.items`. Keyboard controls and themed feedback remain available.
488
+ Canvas resize exposes grips up to 24px per axis on the selected widget. Extremely narrow
489
+ or short widgets reduce their hit areas to keep adjacent grips disjoint in both axes; this does not
490
+ make them comfortable touch targets. **More actions → Size** provides larger equivalent
491
+ controls for precise sizing and filling. All presentations reuse the canonical geometry
492
+ and `canvas.items`. Keyboard controls and themed feedback remain available.
400
493
  Physical-touch ergonomics and save/reopen persistence still
401
494
  require separate proof; successful local interaction is not an enterprise certification.
402
495
 
@@ -468,7 +561,7 @@ The outlet's `ComponentDocMeta.configEditor` intentionally owns presentation onl
468
561
 
469
562
  Core exports schema and metadata infrastructure used by form, table, list, chart, CRUD, and page-builder packages:
470
563
 
471
- - `SchemaMetadataClient`
564
+ - `SchemaMetadataClient` — request deadlines, cancellation and host transport policy
472
565
  - `SchemaNormalizerService`
473
566
  - ETag/cache helpers
474
567
  - `FieldDefinition` and `FieldMetadata` models
@@ -532,10 +625,37 @@ Core also exports shared AI authoring types, capability types, dynamic-page cont
532
625
 
533
626
  `DynamicWidgetPageComponent` treats the editor declared by `ComponentDocMeta.configEditor` as the canonical authoring surface for the **Configurar conteúdo** action. When the Settings Panel bridge is available, the host resolves any transient `contextResolver` evidence and opens that metadata-owned editor even if the live widget also exposes an internal settings shortcut. This keeps the action deterministic across widget loading states.
534
627
 
628
+ Component owners publish that editor in exactly one of two forms: `component` for an eager standalone type, or `loadComponent()` for an owner-controlled lazy import. Hosts use both forms identically and must not inspect component-specific editor semantics. Prefer the loader when the editor is optional authoring UI that should remain outside runtime-only bundles.
629
+
535
630
  Widget-local settings dispatch remains only a fallback when the canonical Settings Panel/editor is unavailable. `ComponentConfigEditorContextRequest.persistedInputs` is cloned exclusively from `widget.definition.inputs`. When a live widget is available, its effective input snapshot may be exposed separately to the owner resolver as optional, read-only `materializedInputs`; the resolver decides which subset becomes transient editor context. Opening the editor never applies that snapshot to the page. Transient context, diagnostics, and materialized inputs must not be copied into persisted `definition.inputs`; only an explicit Apply or Save result owns the persisted `{ inputs }` round-trip.
536
631
 
632
+ Native editors that publish the existing explicit `inputPatch` protocol also produce
633
+ `pageAuthoringChange`, after projecting the patch onto declared owner inputs. This
634
+ notification is required even when a preceding legacy `configChange` already applied
635
+ the same value at runtime. Ordinary runtime change outputs keep using `pageChange`;
636
+ composition targets still pass through canonical restoration before persistence.
637
+ This lets Page Builder save the edited config instead of its previous draft.
638
+
537
639
  Dynamic widgets may implement `WidgetShellActionContributor` to publish transient, stateful content actions to their owning `WidgetShell`. The loader observes that signal and the shell merges contributions with authored actions by stable id without writing them into the page document. The widget remains the owner of command handling, localized labels, availability and controlled-region state; the shell owns responsive toolbar materialization and ARIA projection.
538
640
 
641
+ ## Global configuration snapshots
642
+
643
+ `PraxisI18nService` resolves individual translation keys against current config
644
+ layers without merging whole catalogs on every label lookup. Provider precedence,
645
+ namespace fallback, parent scopes and host translators remain unchanged. Global
646
+ snapshot replacements and in-place dictionary updates remain visible without
647
+ cache invalidation. `mergePraxisI18nConfigs` remains available for explicit catalog
648
+ materialization outside this lookup path.
649
+
650
+ `GlobalConfigService` synchronous getters (`get`, `getCrud`, `getDynamicFields`,
651
+ `getTable`, `getDialog`, `getI18n`) read the effective snapshot without scheduling
652
+ bootstrap work again when the current tenant is already loaded. While the initial
653
+ load is pending, repeated reads share one background request. This keeps template
654
+ and translation reads from feeding asynchronous change-detection cycles.
655
+ `ready()` remains the explicit awaited loading boundary. Tenant changes, save and
656
+ clear continue to refresh the same canonical snapshot; precedence and persistence
657
+ contracts are unchanged.
658
+
539
659
  ## Domain Governance
540
660
 
541
661
  `DomainKnowledgeService` and `DomainRuleService` are shared clients for governed semantic decision flows exposed by `praxis-config-starter`. `browseDefinitionCatalog` exposes the bounded, redacted and server-scoped decision catalog; consumers then use `getDefinition` with the exact returned definition id instead of downloading every rule body or joining versions by key. `getDefinitionFacts` projects the same definition's versioned fact vocabulary, including type, localized meaning, provider evidence, sensitivity and redaction policy; Angular must not reconstruct that vocabulary from JSON Logic or host-specific fixtures. `getDefinitionCapabilities` returns the authenticated principal's server-scoped `CREATE_NEW_VERSION` action per definition; consumers must not treat projection metadata such as editor support as authorization. For change workspaces, `getChangeWorkspaceCapabilities` returns the authenticated principal's `availableActions` and stable blockers; consumers must not infer submit, review or promotion authority from lifecycle status. `DomainRuleService` also exposes the safe RuleSet version catalog, active head/status, redacted execution summary, aggregate host alignment, forward activation and rollback protected by the current head ETag. The same client projects immutable rollout-policy versions, their independent anti-ABA head, maker-checker approval, activation and append-only timeline; policy authority and lifecycle validation remain server-owned. It also exposes the two-phase operational rollout through a recoverable human catalog, redacted readiness, create/cancel commands and candidate activation bound to the rollout identity. Consumers must obey the Config-owned `availableActions`; they must not recalculate quorum or head validity. Execution summaries contain only aggregate outcome counts, distinct-host count and observation timestamps for an immutable snapshot. Host-status summaries contain only aligned, snapshot-drifted, runtime-incompatible, unavailable and stale counts derived against the active head and its approved compatibility coordinates; facts and host identities remain outside Angular. Runtime surfaces should treat materializations as derived projections of backend-governed decisions, not as frontend-owned business rules.
@@ -570,3 +690,260 @@ Action-bearing components must use the semantic `--praxis-action-control-*` vari
570
690
  `ResourceQuickConnectComponent` apresenta recursos publicados em `/schemas/catalog` usando o seletor `pdx-material-select` registrado em `ComponentMetadataRegistry`. O host deve registrar os descritores oficiais de DynamicFields. O controle é inicializado pelo protocolo `setInputMetadata` e ControlValueAccessor; Core não importa a biblioteca de campos. A conexão avançada por rota continua disponível.
571
691
 
572
692
  O editor de configurações da página recebe uma cópia da definição autoral. Dados carregados e entradas transitórias dos widgets não devem virar configuração ao alterar o layout.
693
+
694
+ ### Insertion preset composition shell
695
+
696
+ `ComponentDocMeta.insertionPresets[].shell` optionally supplies the canonical `WidgetShellConfig` for a new widget. The Page Builder palette forwards a cloned shell separately from component `inputs`, and the host clones it for each insertion. Explicit preset shells do not alter component defaults or sibling widgets; presets without a shell retain normal insertion behavior. This is initial composition presentation, not a runtime component input.
697
+
698
+ ### Margem lateral compartilhada da página
699
+
700
+ `WidgetPageDefinition.layout.paddingInline` define o respiro lateral compartilhado por
701
+ cabeçalho, tabela, formulário e demais widgets. É uma medida CSS (`24px`, `1.5rem`)
702
+ ou referência a token (`var(--app-page-inset, 24px)`). O valor `0px` desliga o inset;
703
+ a omissão herda o token público `--pdx-page-padding-inline` do host, cujo default é `0px`.
704
+ O host pode definir esse token em `praxis-dynamic-page` ou em um ancestral.
705
+
706
+ A precedência é `deviceLayouts[device].layout.paddingInline` → `layout.paddingInline`
707
+ → token do host → `0px`. Defaults do preset participam da resolução do layout base;
708
+ um valor autorado na página ou variante tem precedência sobre esse default.
709
+ A margem é aplicada no wrapper com `box-sizing: border-box`,
710
+ fora do canvas, preservando a origem dos cálculos de arraste e redimensionamento.
711
+ O espaço de segurança dos controles no modo edição permanece separado. Não se trata
712
+ do `gap` entre widgets, do padding interno do formulário ou do padding das células da tabela.
713
+
714
+ ```json
715
+ {
716
+ "layout": { "paddingInline": "24px", "gap": "16px" },
717
+ "deviceLayouts": { "mobile": { "layout": { "paddingInline": "16px" } } }
718
+ }
719
+ ```
720
+
721
+ Nos editores de página, **Margem lateral da página** permite definir e limpar o valor
722
+ sem alterar os widgets. Vazio na variante herda o layout base; vazio no layout base
723
+ retorna ao default do preset, quando definido; caso contrário, ao token do host. Referências CSS são preservadas ao aplicar, salvar e reabrir.
724
+
725
+
726
+ Widget settings UX: identity and ready-made styles precede advanced CSS. Size includes a
727
+ before/after occupancy map of the current container; row heights in this diagram are
728
+ illustrative, not a pixel-accurate content preview. The map reads canonical canvas items
729
+ and transient placements and never writes page geometry. Applying a size-only draft clears
730
+ the draft but keeps Save available; Save obtains the current page rather than replaying
731
+ the previous resize request. Backend persistence still follows the host configuration.
732
+
733
+
734
+ ### Widget settings: applied baseline and live inspection
735
+
736
+ The unified widget editor acknowledges appearance only after the page owner accepts
737
+ its commit. The optional provider hook `acceptAppliedValue` updates the draft baseline;
738
+ rejected operations do not acknowledge it. Reset discards the current appearance/size
739
+ draft to the last applied state. Explicit resize Undo remains a separate operation,
740
+ including affected neighbors. Save after Apply reads current state without replaying
741
+ an expansion. The open bridge carries an optional, ephemeral `previewTarget` callback
742
+ for SettingsPanel to reveal the live widget without destroying the editor or draft.
743
+ Neither capability changes the persisted page schema or confirms an HTTP save.
744
+
745
+
746
+ Widget settings topics delegate vertical scrolling to their SettingsPanel owner.
747
+ The shared topic navigation keeps all sections discoverable, and the optional
748
+ appearance sample is collapsed below the active editor. Topic changes retain the
749
+ form draft without changing persistence or apply/save/reset behavior.
750
+
751
+ ## Selection-driven widget headers
752
+
753
+ `WidgetShellConfig.avatar` is optional and uses the existing Core avatar vocabulary:
754
+ `{ imageSrc?: string, name?: string, initials?: string }`. It displays beside the
755
+ shell title, taking precedence over `icon`. Omit it to retain the existing icon.
756
+ `name` supplies the accessible image name and fallback initials; `initials` overrides
757
+ the fallback (at most two characters). Missing/failed images show initials, then a
758
+ person icon. Resolved values that are not strings are treated as absent; objects
759
+ are never coerced into URLs. Failed images retry after clearing/changing the identity
760
+ or URL and returning to the same photo. Equivalent inputs on unrelated page renders
761
+ retain the fallback; late failures belong to their original load cycle. URLs use Angular URL sanitization; the shell does not fetch records or
762
+ bypass protected-image authentication.
763
+
764
+ Dynamic Page resolves shell strings recursively against `state`, `transient`,
765
+ `context` and the existing `widget` context. `${transient.employee.nomeCompleto}`
766
+ reads runtime-only composition state. It does not merge that state into `state`
767
+ or write selected rows into the authored page. `${state...}` keeps its existing
768
+ persistable-state semantics. Runtime isolation does not replace backend field-access
769
+ controls: deliver only fields the current user may see.
770
+
771
+ A selected-row header can use this shell after the ordered identity projection
772
+ described below (the field names belong to the source):
773
+
774
+ ```json
775
+ {
776
+ "showHeader": true,
777
+ "title": "${transient.employee.nomeCompleto}",
778
+ "subtitle": "${transient.employee.subtitle}",
779
+ "avatar": {
780
+ "imageSrc": "${transient.employee.avatarUrl}",
781
+ "name": "${transient.employee.nomeCompleto}"
782
+ }
783
+ }
784
+ ```
785
+
786
+ Connect the table's canonical `selectionChange` event to the state endpoint
787
+ `{ "kind": "state", "ref": { "path": "employee", "layer": "transient", "writable": true } }`.
788
+ A `pick-path` link transform reads `payload.selectedRows.0` from the event envelope.
789
+ For formatted identity, use the complete ordered recipe below rather than only
790
+ copying the row: that projection also initializes and composes `employee.subtitle`.
791
+ Deliver an empty selection too; skipping empty events leaves the previous person
792
+ visible. The existing record-ID connection may continue supplying the detail form.
793
+ The header consumes the selected row without a second request. If fields are absent
794
+ from that row, author a projection from the authorized detail source instead.
795
+ Formatting dates and identifiers belongs to the authored projection/source; shell
796
+ interpolation does not add date masks, CPF masks or an expression language. Literal
797
+ separators remain when both bound fields are empty.
798
+ The canonical link pipeline now supports explicit `format-value` for civil dates,
799
+ digit masks and numeric presentation.
800
+ The [complete identity recipe](../../examples/ai-recipes/praxis-dynamic-page.widget-selection-identity.json)
801
+ resets the transient identity before formatting and composes only available subtitle
802
+ parts. The lab and Core integration test consume the same executable page artifact.
803
+
804
+ The opt-in `widget-shell-persistence.playwright.spec.ts` reuses the fictional
805
+ selection fixture in `/remote-config-storage-persistence-lab`. It proves a real
806
+ Config save with `If-Match`, document equality after selecting a person, and a
807
+ reload with no selected identity. It owns an initially absent UUID configuration
808
+ and deletes only that record with its current ETag. Run with
809
+ `PRAXIS_REAL_BACKEND_E2E=true`; this is a persistence proof, not an LLM authoring gate.
810
+
811
+ ### Configurações em hosts controlados
812
+
813
+ Quando o host devolve `pageChange` por `[page]`, o eco do mesmo documento canônico mantém as configurações do widget abertas após Aplicar. Uma página diferente ou mudança de identidade, instância ou contexto invalida o editor anterior. Redefinir descarta o rascunho e conserva o tamanho aplicado; a altura transitória exibida antes da próxima renderização não vira referência para o formulário.
814
+
815
+
816
+ ### Authored widget action identities
817
+
818
+ `getWidgetShellActionIdError(id, siblingIds)` validates the trimmed ID and returns
819
+ `required`, `duplicate`, `reserved`, or `null`. Exclude the current action from siblings.
820
+ Reserved IDs are `collapse`, `expand`, `fullscreen`, `component-settings`,
821
+ `widget-assistant`, `widget-settings`, `widget-size`, and `widget-remove`: these belong
822
+ to platform controls, not custom authored actions. Native window controls remain supported.
823
+
824
+ Page Builder uses this helper for draft feedback; `DynamicWidgetPage` also uses it before
825
+ applying widget settings, including settings supplied by alternate providers. Rejected
826
+ settings leave the page unchanged. Existing custom actions with invalid/reserved IDs need
827
+ renaming before applying settings. The helper adds no persisted field and is not a global
828
+ page-import validator, business-command verifier or authorization boundary.
829
+
830
+
831
+ ### Live shell action availability
832
+
833
+ `WidgetShellActionContributor.getWidgetShellActionAvailability?(action)` may return
834
+ `Pick<WidgetShellAction, 'disabled' | 'tooltip'>`. It must be side-effect free and read live
835
+ owner state; `undefined` means not assessed, not enabled. Reactive owner signals can be
836
+ read directly so an open editor updates when prerequisites change. `disabled:false` means
837
+ currently available in the UI, not backend authorization. Return a localized `tooltip`
838
+ when disabled. The loader delegates to the current contributor; DynamicWidgetPage passes
839
+ a transient callback to the shell editor. It must never be serialized as widget inputs,
840
+ shell actions, or persisted page configuration. Contributors without this optional method
841
+ remain supported and their availability is shown as unverified.
842
+
843
+
844
+ ### Chart input updates preserve the live instance
845
+
846
+ Charts uses normal Angular input updates in DynamicWidgetLoader. Changing chartDocument,
847
+ query context or other inputs must not recreate the chart solely because its type is
848
+ `praxis-chart`. Recreating it after an editor Apply destroys subscriptions owned by that
849
+ instance and can drop the subsequent Save from the still-open panel. Declared
850
+ `requiresInitialInputs` behavior and existing detached Form/Filter initialization paths
851
+ remain separate and unchanged. Input updates still occur before detectChanges.
852
+
853
+
854
+ ### Settings Panel owner lifetime
855
+
856
+ `SettingsPanelOpenOptions.owner?: DestroyRef` associates an opening with its calling
857
+ component's lifetime. The official Settings Panel bridge forwards this transient option
858
+ to its service. Destruction emits the existing `closed$` reason `cancel`, without Save,
859
+ and prevents a pending opening from replacing another panel. The option is not editor
860
+ input or persisted configuration. Omit it for an independently managed panel; existing
861
+ unowned callers retain their behavior. Use the actual target component's DestroyRef,
862
+ not a root service or the overlay content's own lifetime.
863
+
864
+
865
+ `DynamicWidgetPageComponent` creates a transient Angular lifetime for each page,
866
+ widget appearance/size, or registered content editor session. It uses the existing
867
+ `owner` option; no session identity is persisted in the page document.
868
+
869
+ The session is cancelled when its panel closes, the page is destroyed, or an external
870
+ page/context/identity/authoring input change invalidates it. A page-only acknowledgement
871
+ identical to the current authored document keeps the session alive for subsequent
872
+ Apply/Save. This is deliberately conservative: a different external document cancels
873
+ open editors even when a particular widget happens to be unchanged.
874
+
875
+ Confirmed widget removal cancels sessions targeting that key, plus page-wide settings
876
+ that could restore the removed widget. Asynchronous editor results cannot open a panel
877
+ once their session has expired, and late Apply/Save events are no longer observed.
878
+ A removal confirmation is ignored after external identity/context changes or changes
879
+ to the target itself; when still valid, removal uses the latest document so intervening
880
+ edits to other widgets are preserved.
881
+
882
+
883
+ When unified widget settings close normally, Core restores the initiating control
884
+ through its existing focus-return helper instead of forcing focus onto the whole
885
+ widget. Invalidated sessions do not schedule this restoration. A detached, disabled
886
+ initiator or destroyed page is never focused by that helper.
887
+
888
+
889
+ ### Preparação do editor após aceitar a troca
890
+
891
+ `SettingsPanelOpenContent.inputs` aceita um objeto ou uma função síncrona que retorna
892
+ os inputs. A bridge oficial encaminha a função ao Settings Panel sem executá-la.
893
+ O serviço a executa uma única vez ao materializar a abertura aceita; um veto ou um
894
+ owner já expirado não executa a função. Hosts que implementam a bridge devem respeitar
895
+ essa mediação antes de resolver os inputs. Leitores desse campo devem distinguir
896
+ objeto e função antes de acessar propriedades.
897
+
898
+ O editor de widget usa essa preparação para trocar sua sessão e seu estado de tamanho.
899
+ Enquanto a troca aguarda decisão, o editor anterior continua dono do rascunho e de
900
+ suas assinaturas. Recusar a troca libera somente a sessão candidata. A função é código
901
+ transitório de integração: não pertence ao documento, ao JSON de configuração, aos
902
+ presets nem aos contratos de authoring por IA.
903
+
904
+ ## Responsive form-column numeric resolution
905
+
906
+ `resolveSpan`, `resolveOffset` and `resolveOrder` resolve the existing XS → SM → MD → LG → XL inheritance without modifying the supplied map. Supported integer domains are span 1..12, offset 0..11 and order -12..12, matching runtime grid classes. Missing/null values inherit, starting at 12/0/0 in XS. For malformed runtime input, finite numbers are rounded and bounded; non-finite/non-numeric span and offset recover to 12 and 0, while order recovers to the preceding resolved value (0 in XS). Recovery emits a warning and does not rewrite the authoring document. Editors must reject invalid values before applying them.
907
+
908
+
909
+ ### Acesso às dimensões do widget
910
+
911
+ Quando o Settings Panel e o editor de aparência do widget estão disponíveis,
912
+ use **Configurar widget → Tamanho**. O menu de contexto e as ações projetadas
913
+ não repetem o atalho Tamanho. Em contextos sem editor unificado, o atalho permanece
914
+ para não retirar o acesso às dimensões. Arraste e comandos de tamanho mantêm sua
915
+ semântica; esta regra modifica somente a descoberta visual do comando.
916
+
917
+ Quando `constraints.lockSize` estiver ativo e a customização de canvas estiver
918
+ habilitada, o editor unificado mantém a aba Tamanho com uma explicação do bloqueio,
919
+ sem controles editáveis. A restrição continua pertencendo ao documento da página;
920
+ o aviso não concede permissão nem gera uma alteração de tamanho.
921
+
922
+
923
+ ### Composed settings navigation
924
+
925
+ `SettingsValueProvider` may expose `getSettingsSections()` and `selectSettingsSection(id)` together.
926
+ `SettingsEditorSection` contains an editor-local stable `id`, a resolved i18n `label`, and an optional
927
+ `invalid` flag. These are transient presentation metadata, never persisted settings. Reading sections
928
+ and selecting one must not modify the draft or emit document changes. Editors must emit their existing
929
+ validity/dirty streams when validation changes so the composing host can refresh section diagnostics.
930
+
931
+ Core's widget settings host namespaces provider section IDs, appends its page-owned Size section and
932
+ keeps the shell editor mounted while switching sections. It provides `SETTINGS_EDITOR_SECTIONS_HOSTED`
933
+ so section-aware editors suppress their own navigation. Unsectioned settings providers remain valid:
934
+ the host presents their single Widget topic. Apply, Save, Reset and applied-baseline acknowledgement
935
+ continue to aggregate the original providers; section navigation never becomes the value provider.
936
+
937
+ `SettingsSectionTabsComponent` is the shared presentation primitive for these topics. It exposes all
938
+ labels, selected state, per-section errors and keyboard navigation, with unique tab/panel associations.
939
+ Page Builder imports it directly from Core; Settings Panel does not reexport this component or the new
940
+ topic contracts. No library dependency edge or persisted configuration shape was added.
941
+
942
+ The widget host keeps topic navigation sticky inside the Settings Panel's existing scroll area.
943
+ Narrow panels present a compact, labeled native selector with the same section IDs and validation
944
+ messages; wider panels expose the shared tabs. Switching topics reveals the beginning of the
945
+ selected section without remounting the provider or moving focus out of the navigation control.
946
+
947
+ On editable canvases, content-only widgets expose contextual actions above the north resize
948
+ handle, with reserved space at the top of the canvas. Both controls remain pointer-accessible
949
+ when the widget is narrow.