@praxisui/crud 9.0.0-beta.80 → 9.0.0-beta.82

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
@@ -159,7 +159,11 @@ const explicitEditAction: CrudAction = {
159
159
  ```
160
160
 
161
161
  Drawer mode works without a host adapter: the package opens its standard form
162
- dialog host with drawer presentation. Provide `CRUD_DRAWER_ADAPTER` only when a
162
+ dialog host with drawer presentation. The native drawer remains anchored to its
163
+ configured viewport edge while applying initial or remembered size and while
164
+ maximizing or restoring. The default edge is right/top; a configured left edge
165
+ is preserved by the same lifecycle. `defaults.modal.edgeGap` controls the
166
+ viewport inset while maximized. Provide `CRUD_DRAWER_ADAPTER` only when a
163
167
  host must replace that standard shell with a real corporate drawer implementation.
164
168
 
165
169
  For native edit drawers, the runtime presents the selected record's canonical
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-07-19T22:17:32.055Z",
3
+ "generatedAt": "2026-07-20T13:31:18.568Z",
4
4
  "packageName": "@praxisui/crud",
5
- "packageVersion": "9.0.0-beta.80",
5
+ "packageVersion": "9.0.0-beta.82",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 2,
@@ -5771,18 +5771,34 @@
5771
5771
  "minWidth": {
5772
5772
  "type": "string"
5773
5773
  },
5774
+ "minHeight": {
5775
+ "type": "string"
5776
+ },
5774
5777
  "maxWidth": {
5775
5778
  "type": "string"
5776
5779
  },
5780
+ "maxHeight": {
5781
+ "type": "string"
5782
+ },
5777
5783
  "density": {
5778
5784
  "type": "string"
5779
5785
  },
5786
+ "backdropStyle": {
5787
+ "enum": [
5788
+ "blur",
5789
+ "dim",
5790
+ "transparent"
5791
+ ]
5792
+ },
5780
5793
  "canMaximize": {
5781
5794
  "type": "boolean"
5782
5795
  },
5783
5796
  "rememberLastState": {
5784
5797
  "type": "boolean"
5785
5798
  },
5799
+ "closeOnSave": {
5800
+ "type": "boolean"
5801
+ },
5786
5802
  "startMaximized": {
5787
5803
  "type": "boolean"
5788
5804
  },
@@ -5793,7 +5809,27 @@
5793
5809
  "type": "boolean"
5794
5810
  },
5795
5811
  "fullscreenBreakpoint": {
5796
- "type": "string"
5812
+ "type": "number"
5813
+ },
5814
+ "position": {
5815
+ "type": "object",
5816
+ "properties": {
5817
+ "top": {
5818
+ "type": "string"
5819
+ },
5820
+ "bottom": {
5821
+ "type": "string"
5822
+ },
5823
+ "left": {
5824
+ "type": "string"
5825
+ },
5826
+ "right": {
5827
+ "type": "string"
5828
+ }
5829
+ }
5830
+ },
5831
+ "edgeGap": {
5832
+ "type": "number"
5797
5833
  }
5798
5834
  }
5799
5835
  },
@@ -5826,6 +5862,7 @@
5826
5862
  "open-mode-unsupported",
5827
5863
  "drawer-runtime-unavailable",
5828
5864
  "modal-size-invalid",
5865
+ "drawer-position-lifecycle-unstable",
5829
5866
  "back-policy-invalid"
5830
5867
  ],
5831
5868
  "description": "Configures CRUD-owned route/modal/drawer defaults consumed by the launcher, default dialog-backed drawer runtime and optional host drawer adapter."
@@ -5836,6 +5873,7 @@
5836
5873
  "open-mode-supported",
5837
5874
  "modal-size-valid",
5838
5875
  "drawer-runtime-available",
5876
+ "drawer-position-lifecycle-stable",
5839
5877
  "back-policy-valid",
5840
5878
  "settings-panel-shell-compatible"
5841
5879
  ],
@@ -6191,6 +6229,12 @@
6191
6229
  "code": "CRUD_DRAWER_RUNTIME_AVAILABLE",
6192
6230
  "description": "Drawer open mode uses the default dialog-backed drawer runtime with CRUD-owned chrome, responsive right-panel sizing, Escape/backdrop handling and semantic close/save results; a host drawer adapter is optional only for shell-specific presentation."
6193
6231
  },
6232
+ {
6233
+ "validatorId": "drawer-position-lifecycle-stable",
6234
+ "level": "error",
6235
+ "code": "CRUD_DRAWER_POSITION_LIFECYCLE_STABLE",
6236
+ "description": "A drawer must preserve its configured viewport edge through open, remembered-size restoration, maximize and restore."
6237
+ },
6194
6238
  {
6195
6239
  "validatorId": "back-policy-valid",
6196
6240
  "level": "error",
@@ -6261,6 +6305,7 @@
6261
6305
  "FormConfig and FieldMetadata edits must delegate to dynamic-form or metadata-editor manifests.",
6262
6306
  "TableConfig edits must delegate to praxis-table; CRUD owns only shell orchestration and list surface binding.",
6263
6307
  "Delete behavior is destructive and requires explicit confirmation plus backend capability support.",
6308
+ "Drawer position and edgeGap must round-trip without changing the configured edge during open, remembered-size restoration, maximize or restore.",
6264
6309
  "crudContext must remain reference-stable across change detection cycles."
6265
6310
  ],
6266
6311
  "examples": [
@@ -6379,6 +6424,23 @@
6379
6424
  },
6380
6425
  "isPositive": true
6381
6426
  },
6427
+ {
6428
+ "id": "crud-drawer-left-edge",
6429
+ "request": "Open CRUD forms in a left drawer with an 8px maximized inset.",
6430
+ "operationId": "dialog.size.set",
6431
+ "target": "dialogHost",
6432
+ "params": {
6433
+ "defaultOpenMode": "drawer",
6434
+ "modal": {
6435
+ "position": {
6436
+ "left": "0",
6437
+ "top": "0"
6438
+ },
6439
+ "edgeGap": 8
6440
+ }
6441
+ },
6442
+ "isPositive": true
6443
+ },
6382
6444
  {
6383
6445
  "id": "crud-permissions-no-delete",
6384
6446
  "request": "Hide delete when the resource capabilities do not allow it.",
@@ -6621,9 +6683,9 @@
6621
6683
  {
6622
6684
  "chunkIndex": 16,
6623
6685
  "chunkKind": "authoring_manifest",
6624
- "content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.0.0\",\"componentId\":\"praxis-crud\",\"ownerPackage\":\"@praxisui/crud\",\"configSchemaId\":\"CrudMetadata\",\"chunkSection\":\"operations\",\"operations\":[{\"operationId\":\"surface.edit.configure\",\"title\":\"Configure edit surface\",\"scope\":\"interaction\",\"targetKind\":\"editSurface\",\"target\":{\"kind\":\"editSurface\",\"resolver\":\"crud-action-by-id:edit\",\"ambiguityPolicy\":\"fail\",\"required\":true},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"crud-edit-surface-configure\",\"handlerContract\":{\"reads\":[\"CrudMetadata.actions\",\"CrudMetadata.resource.idField\",\"CrudLauncherService.resolveOpenMode\",\"api_metadata\"],\"writes\":[\"CrudMetadata.actions[]\",\"CrudMetadata.actions[].form\",\"CrudMetadata.actions[].params\",\"CrudMetadata.actions[].back\"],\"identityKeys\":[\"actionId\"],\"failureModes\":[\"action-not-found\",\"id-param-missing\",\"open-mode-binding-incomplete\",\"resource-edit-not-supported\",\"submit-url-not-canonical\"],\"description\":\"Configures edit action routing/form binding and ensures id parameter mapping is available to the launcher.\"}}],\"validators\":[\"action-exists\",\"id-field-known\",\"id-param-mapping-valid\",\"open-mode-binding-complete\",\"schema-url-canonical\",\"submit-url-canonical\",\"resource-edit-supported\",\"form-child-operation-delegated\"],\"affectedPaths\":[\"actions[].openMode\",\"actions[].route\",\"actions[].formId\",\"actions[].form\",\"actions[].params\",\"actions[].back\"],\"submissionImpact\":\"affects-schema-backed-data\",\"preconditions\":[\"crud-metadata-loaded\",\"resource-capabilities-resolved\"]},{\"operationId\":\"surface.view.configure\",\"title\":\"Configure view surface\",\"scope\":\"interaction\",\"targetKind\":\"viewSurface\",\"target\":{\"kind\":\"viewSurface\",\"resolver\":\"crud-action-by-id:view\",\"ambiguityPolicy\":\"fail\",\"required\":true},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"crud-view-surface-configure\",\"handlerContract\":{\"reads\":[\"CrudMetadata.actions\",\"CrudLauncherService.resolveOpenMode\",\"DynamicFormDialogHostComponent\",\"api_metadata\"],\"writes\":[\"CrudMetadata.actions[]\",\"CrudMetadata.actions[].form\",\"CrudMetadata.actions[].params\",\"CrudMetadata.actions[].back\"],\"identityKeys\":[\"actionId\"],\"failureModes\":[\"action-not-found\",\"open-mode-binding-incomplete\",\"resource-view-not-supported\",\"readonly-form-delegation-missing\"],\"description\":\"Configures view action binding and delegates readonly form behavior to the dynamic-form manifest.\"}}],\"validators\":[\"action-exists\",\"open-mode-binding-complete\",\"schema-url-canonical\",\"resource-view-supported\",\"form-child-operation-delegated\",\"readonly-form-delegation-valid\"],\"affectedPaths\":[\"actions[].openMode\",\"actions[].route\",\"actions[].formId\",\"actions[].form\",\"actions[].params\",\"actions[].back\"],\"submissionImpact\":\"config-only\",\"preconditions\":[\"crud-metadata-loaded\",\"resource-capabilities-resolved\"]},{\"operationId\":\"delete.enabled.set\",\"title\":\"Configure delete behavior\",\"scope\":\"interaction\",\"targetKind\":\"deleteBehavior\",\"target\":{\"kind\":\"deleteBehavior\",\"resolver\":\"crud-action-by-id:delete\",\"ambiguityPolicy\":\"fail\",\"required\":true},\"inputSchema\":{\"type\":\"object\",\"required\":[\"enabled\"],\"properties\":{\"enabled\":{\"type\":\"boolean\"},\"actionId\":{\"type\":\"string\"},\"requiresConfirmation\":{\"type\":\"boolean\"},\"autoDelete\":{\"type\":\"boolean\"},\"form\":{\"type\":\"object\",\"properties\":{\"submitUrl\":{\"type\":\"string\"},\"submitMethod\":{\"enum\":[\"delete\"]},\"apiEndpointKey\":{\"type\":\"string\"}}}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"crud-delete-behavior-set\",\"handlerContract\":{\"reads\":[\"CrudMetadata.actions\",\"CrudMetadata.resource\",\"GET /{resource}/{id}/capabilities\"],\"writes\":[\"CrudMetadata.actions[]\",\"CrudMetadata.actions[].requiresConfirmation\",\"CrudMetadata.actions[].autoDelete\",\"CrudMetadata.actions[].form\"],\"identityKeys\":[\"actionId\"],\"failureModes\":[\"delete-action-not-found\",\"resource-delete-not-supported\",\"destructive-delete-not-confirmed\",\"delete-submit-url-not-canonical\"],\"description\":\"Enables or disables delete behavior with capability checks and explicit confirmation for destructive changes.\"}}],\"destructive\":true,\"requiresConfirmation\":true,\"validators\":[\"delete-action-exists\",\"resource-delete-supported\",\"destructive-delete-confirmed\",\"submit-url-canonical\",\"permissions-delete-valid\"],\"affectedPaths\":[\"actions[].disabled\",\"actions[].requiresConfirmation\",\"actions[].autoDelete\",\"actions[].form\",\"actions[].form.submitUrl\",\"actions[].form.submitMethod\",\"actions[].form.apiEndpointKey\"],\"submissionImpact\":\"affects-schema-backed-data\",\"preconditions\":[\"crud-metadata-loaded\",\"resource-capabilities-resolved\",\"explicit-confirmation-provided\"]},{\"operationId\":\"dialog.size.set\",\"title\":\"Configure CRUD dialog host defaults\",\"scope\":\"interaction\",\"targetKind\":\"dialogHost\",\"target\":{\"kind\":\"dialogHost\",\"resolver\":\"crud-dialog-host-defaults\",\"ambiguityPolicy\":\"fail\",\"required\":true},\"inputSchema\":{\"type\":\"object\",\"properties\":{\"defaultOpenMode\":{\"enum\":[\"route\",\"modal\",\"drawer\"]},\"modal\":{\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"string\"},\"height\":{\"type\":\"string\"},\"minWidth\":{\"type\":\"string\"},\"maxWidth\":{\"type\":\"string\"},\"density\":{\"type\":\"string\"},\"canMaximize\":{\"type\":\"boolean\"},\"rememberLastState\":{\"type\":\"boolean\"},\"startMaximized\":{\"type\":\"boolean\"},\"disableCloseOnEsc\":{\"type\":\"boolean\"},\"disableCloseOnBackdrop\":{\"type\":\"boolean\"},\"fullscreenBreakpoint\":{\"type\":\"string\"}}},\"back\":{\"type\":\"object\"}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"crud-dialog-host-set\",\"handlerContract\":{\"reads\":[\"CrudMetadata.defaults\",\"CrudLauncherService.resolveOpenMode\",\"DialogService\",\"DynamicFormDialogHostComponent\",\"CRUD_DRAWER_ADAPTER\"],\"writes\":[\"CrudMetadata.defaults.openMode\",\"CrudMetadata.defaults.modal\",\"CrudMetadata.defaults.back\"],\"identityKeys\":[\"crudId\"],\"failureModes\":[\"open-mode-unsupported\",\"drawer-runtime-unavailable\",\"modal-size-invalid\",\"back-policy-invalid\"],\"description\":\"Configures CRUD-owned route/modal/drawer defaults consumed by the launcher, default dialog-backed drawer runtime and optional host drawer adapter.\"}}],\"validators\":[\"open-mode-supported\",\"modal-size-valid\",\"drawer-runtime-available\",\"back-policy-valid\",\"settings-panel-shell-compatible\"],\"affectedPaths\":[\"defaults.openMode\",\"defaults.modal\",\"defaults.back\"],\"submissionImpact\":\"config-only\",\"preconditions\":[\"crud-metadata-loaded\"]},{\"operationId\":\"permissions.set\",\"title\":\"Configure CRUD permissions\",\"scope\":\"interaction\",\"targetKind\":\"permissions\",\"target\":{\"kind\":\"permissions\",\"resolver\":\"crud-resource-capabilities\",\"ambiguityPolicy\":\"fail\",\"required\":true},\"inputSchema\":{\"type\":\"object\",\"properties\":{\"requiredCapabilities\":{\"type\":\"array\",\"items\":{\"enum\":[\"create\",\"view\",\"edit\",\"delete\"]}},\"actionPermissions\":{\"type\":\"object\"},\"denyWhenMissingCapability\":{\"type\":\"boolean\"}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"crud-permissions-set\",\"handlerContract\":{\"reads\":[\"GET /{resource}/capabilities\",\"GET /{resource}/{id}/capabilities\",\"CrudMetadata.actions\"],\"writes\":[\"CrudMetadata.actions[].disabled\",\"CrudMetadata.actions[].visibleWhen\",\"CrudMetadata.actions[].requiresConfirmation\"],\"identityKeys\":[\"resourcePath\"],\"failureModes\":[\"capability-not-found\",\"action-permission-conflict\",\"delete-permission-without-confirmation\"],\"description\":\"Aligns CRUD action visibility/disablement with resource capabilities without inventing a second permission source.\"}}],\"validators\":[\"resource-capabilities-resolvable\",\"action-permission-supported\",\"delete-permission-requires-confirmation\",\"permissions-do-not-shadow-backend\"],\"affectedPaths\":[\"actions[].disabled\",\"actions[].visibleWhen\",\"actions[].requiresConfirmation\"],\"submissionImpact\":\"config-only\",\"preconditions\":[\"crud-metadata-loaded\",\"resource-capabilities-resolved\"]}]}",
6686
+ "content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.0.0\",\"componentId\":\"praxis-crud\",\"ownerPackage\":\"@praxisui/crud\",\"configSchemaId\":\"CrudMetadata\",\"chunkSection\":\"operations\",\"operations\":[{\"operationId\":\"surface.edit.configure\",\"title\":\"Configure edit surface\",\"scope\":\"interaction\",\"targetKind\":\"editSurface\",\"target\":{\"kind\":\"editSurface\",\"resolver\":\"crud-action-by-id:edit\",\"ambiguityPolicy\":\"fail\",\"required\":true},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"crud-edit-surface-configure\",\"handlerContract\":{\"reads\":[\"CrudMetadata.actions\",\"CrudMetadata.resource.idField\",\"CrudLauncherService.resolveOpenMode\",\"api_metadata\"],\"writes\":[\"CrudMetadata.actions[]\",\"CrudMetadata.actions[].form\",\"CrudMetadata.actions[].params\",\"CrudMetadata.actions[].back\"],\"identityKeys\":[\"actionId\"],\"failureModes\":[\"action-not-found\",\"id-param-missing\",\"open-mode-binding-incomplete\",\"resource-edit-not-supported\",\"submit-url-not-canonical\"],\"description\":\"Configures edit action routing/form binding and ensures id parameter mapping is available to the launcher.\"}}],\"validators\":[\"action-exists\",\"id-field-known\",\"id-param-mapping-valid\",\"open-mode-binding-complete\",\"schema-url-canonical\",\"submit-url-canonical\",\"resource-edit-supported\",\"form-child-operation-delegated\"],\"affectedPaths\":[\"actions[].openMode\",\"actions[].route\",\"actions[].formId\",\"actions[].form\",\"actions[].params\",\"actions[].back\"],\"submissionImpact\":\"affects-schema-backed-data\",\"preconditions\":[\"crud-metadata-loaded\",\"resource-capabilities-resolved\"]},{\"operationId\":\"surface.view.configure\",\"title\":\"Configure view surface\",\"scope\":\"interaction\",\"targetKind\":\"viewSurface\",\"target\":{\"kind\":\"viewSurface\",\"resolver\":\"crud-action-by-id:view\",\"ambiguityPolicy\":\"fail\",\"required\":true},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"crud-view-surface-configure\",\"handlerContract\":{\"reads\":[\"CrudMetadata.actions\",\"CrudLauncherService.resolveOpenMode\",\"DynamicFormDialogHostComponent\",\"api_metadata\"],\"writes\":[\"CrudMetadata.actions[]\",\"CrudMetadata.actions[].form\",\"CrudMetadata.actions[].params\",\"CrudMetadata.actions[].back\"],\"identityKeys\":[\"actionId\"],\"failureModes\":[\"action-not-found\",\"open-mode-binding-incomplete\",\"resource-view-not-supported\",\"readonly-form-delegation-missing\"],\"description\":\"Configures view action binding and delegates readonly form behavior to the dynamic-form manifest.\"}}],\"validators\":[\"action-exists\",\"open-mode-binding-complete\",\"schema-url-canonical\",\"resource-view-supported\",\"form-child-operation-delegated\",\"readonly-form-delegation-valid\"],\"affectedPaths\":[\"actions[].openMode\",\"actions[].route\",\"actions[].formId\",\"actions[].form\",\"actions[].params\",\"actions[].back\"],\"submissionImpact\":\"config-only\",\"preconditions\":[\"crud-metadata-loaded\",\"resource-capabilities-resolved\"]},{\"operationId\":\"delete.enabled.set\",\"title\":\"Configure delete behavior\",\"scope\":\"interaction\",\"targetKind\":\"deleteBehavior\",\"target\":{\"kind\":\"deleteBehavior\",\"resolver\":\"crud-action-by-id:delete\",\"ambiguityPolicy\":\"fail\",\"required\":true},\"inputSchema\":{\"type\":\"object\",\"required\":[\"enabled\"],\"properties\":{\"enabled\":{\"type\":\"boolean\"},\"actionId\":{\"type\":\"string\"},\"requiresConfirmation\":{\"type\":\"boolean\"},\"autoDelete\":{\"type\":\"boolean\"},\"form\":{\"type\":\"object\",\"properties\":{\"submitUrl\":{\"type\":\"string\"},\"submitMethod\":{\"enum\":[\"delete\"]},\"apiEndpointKey\":{\"type\":\"string\"}}}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"crud-delete-behavior-set\",\"handlerContract\":{\"reads\":[\"CrudMetadata.actions\",\"CrudMetadata.resource\",\"GET /{resource}/{id}/capabilities\"],\"writes\":[\"CrudMetadata.actions[]\",\"CrudMetadata.actions[].requiresConfirmation\",\"CrudMetadata.actions[].autoDelete\",\"CrudMetadata.actions[].form\"],\"identityKeys\":[\"actionId\"],\"failureModes\":[\"delete-action-not-found\",\"resource-delete-not-supported\",\"destructive-delete-not-confirmed\",\"delete-submit-url-not-canonical\"],\"description\":\"Enables or disables delete behavior with capability checks and explicit confirmation for destructive changes.\"}}],\"destructive\":true,\"requiresConfirmation\":true,\"validators\":[\"delete-action-exists\",\"resource-delete-supported\",\"destructive-delete-confirmed\",\"submit-url-canonical\",\"permissions-delete-valid\"],\"affectedPaths\":[\"actions[].disabled\",\"actions[].requiresConfirmation\",\"actions[].autoDelete\",\"actions[].form\",\"actions[].form.submitUrl\",\"actions[].form.submitMethod\",\"actions[].form.apiEndpointKey\"],\"submissionImpact\":\"affects-schema-backed-data\",\"preconditions\":[\"crud-metadata-loaded\",\"resource-capabilities-resolved\",\"explicit-confirmation-provided\"]},{\"operationId\":\"dialog.size.set\",\"title\":\"Configure CRUD dialog host defaults\",\"scope\":\"interaction\",\"targetKind\":\"dialogHost\",\"target\":{\"kind\":\"dialogHost\",\"resolver\":\"crud-dialog-host-defaults\",\"ambiguityPolicy\":\"fail\",\"required\":true},\"inputSchema\":{\"type\":\"object\",\"properties\":{\"defaultOpenMode\":{\"enum\":[\"route\",\"modal\",\"drawer\"]},\"modal\":{\"type\":\"object\",\"properties\":{\"width\":{\"type\":\"string\"},\"height\":{\"type\":\"string\"},\"minWidth\":{\"type\":\"string\"},\"minHeight\":{\"type\":\"string\"},\"maxWidth\":{\"type\":\"string\"},\"maxHeight\":{\"type\":\"string\"},\"density\":{\"type\":\"string\"},\"backdropStyle\":{\"enum\":[\"blur\",\"dim\",\"transparent\"]},\"canMaximize\":{\"type\":\"boolean\"},\"rememberLastState\":{\"type\":\"boolean\"},\"closeOnSave\":{\"type\":\"boolean\"},\"startMaximized\":{\"type\":\"boolean\"},\"disableCloseOnEsc\":{\"type\":\"boolean\"},\"disableCloseOnBackdrop\":{\"type\":\"boolean\"},\"fullscreenBreakpoint\":{\"type\":\"number\"},\"position\":{\"type\":\"object\",\"properties\":{\"top\":{\"type\":\"string\"},\"bottom\":{\"type\":\"string\"},\"left\":{\"type\":\"string\"},\"right\":{\"type\":\"string\"}}},\"edgeGap\":{\"type\":\"number\"}}},\"back\":{\"type\":\"object\"}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"crud-dialog-host-set\",\"handlerContract\":{\"reads\":[\"CrudMetadata.defaults\",\"CrudLauncherService.resolveOpenMode\",\"DialogService\",\"DynamicFormDialogHostComponent\",\"CRUD_DRAWER_ADAPTER\"],\"writes\":[\"CrudMetadata.defaults.openMode\",\"CrudMetadata.defaults.modal\",\"CrudMetadata.defaults.back\"],\"identityKeys\":[\"crudId\"],\"failureModes\":[\"open-mode-unsupported\",\"drawer-runtime-unavailable\",\"modal-size-invalid\",\"drawer-position-lifecycle-unstable\",\"back-policy-invalid\"],\"description\":\"Configures CRUD-owned route/modal/drawer defaults consumed by the launcher, default dialog-backed drawer runtime and optional host drawer adapter.\"}}],\"validators\":[\"open-mode-supported\",\"modal-size-valid\",\"drawer-runtime-available\",\"drawer-position-lifecycle-stable\",\"back-policy-valid\",\"settings-panel-shell-compatible\"],\"affectedPaths\":[\"defaults.openMode\",\"defaults.modal\",\"defaults.back\"],\"submissionImpact\":\"config-only\",\"preconditions\":[\"crud-metadata-loaded\"]}]}",
6625
6687
  "sourcePointer": "projects/praxis-crud/src/lib/ai/praxis-crud-authoring-manifest.ts",
6626
- "contentHash": "92415d7558c36c1e3d4905f035f62038f0386f669470b8a70f834af9584462d3",
6688
+ "contentHash": "fd3bf3bc17cd6b87cb72593a28663e189ef6ce74dd1cfb7f72dbae38b434c3bb",
6627
6689
  "sourceKind": "component_definition",
6628
6690
  "sourceId": "praxis-crud",
6629
6691
  "corpusVersion": "1.0.0"
@@ -6631,9 +6693,9 @@
6631
6693
  {
6632
6694
  "chunkIndex": 17,
6633
6695
  "chunkKind": "authoring_manifest",
6634
- "content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.0.0\",\"componentId\":\"praxis-crud\",\"ownerPackage\":\"@praxisui/crud\",\"configSchemaId\":\"CrudMetadata\",\"chunkSection\":\"operations\",\"operations\":[{\"operationId\":\"form.childOperation.delegate\",\"title\":\"Delegate child form/table/dialog authoring operation\",\"scope\":\"global\",\"targetKind\":\"childOperation\",\"target\":{\"kind\":\"childOperation\",\"resolver\":\"child-authoring-manifest-operation\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"required\":[\"childComponentId\",\"childOperationId\",\"reason\"],\"properties\":{\"childComponentId\":{\"enum\":[\"praxis-dynamic-form\",\"praxis-table\",\"praxis-dialog\",\"praxis-settings-panel\"]},\"childOperationId\":{\"type\":\"string\"},\"reason\":{\"type\":\"string\"},\"childTarget\":{\"type\":\"object\"},\"childParams\":{\"type\":\"object\"}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"crud-child-operation-delegate\",\"handlerContract\":{\"reads\":[\"PRAXIS_DYNAMIC_FORM_AUTHORING_MANIFEST\",\"PRAXIS_TABLE_AUTHORING_MANIFEST\",\"PRAXIS_DIALOG_AUTHORING_MANIFEST\",\"PRAXIS_SETTINGS_PANEL_AUTHORING_MANIFEST\"],\"writes\":[\"delegatedAuthoringOperations[]\"],\"identityKeys\":[\"childComponentId\",\"childOperationId\"],\"failureModes\":[\"child-manifest-missing\",\"child-operation-not-found\",\"attempted-local-child-config-write\"],\"description\":\"Records explicit delegation when requested edits belong to child manifests instead of CRUD orchestration.\"}}],\"validators\":[\"child-manifest-available\",\"child-operation-known\",\"no-local-form-config-write\",\"no-local-table-config-write\",\"delegation-target-valid\"],\"affectedPaths\":[\"delegatedAuthoringOperations\"],\"submissionImpact\":\"none\",\"preconditions\":[\"child-manifest-available\"]}]}",
6696
+ "content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.0.0\",\"componentId\":\"praxis-crud\",\"ownerPackage\":\"@praxisui/crud\",\"configSchemaId\":\"CrudMetadata\",\"chunkSection\":\"operations\",\"operations\":[{\"operationId\":\"permissions.set\",\"title\":\"Configure CRUD permissions\",\"scope\":\"interaction\",\"targetKind\":\"permissions\",\"target\":{\"kind\":\"permissions\",\"resolver\":\"crud-resource-capabilities\",\"ambiguityPolicy\":\"fail\",\"required\":true},\"inputSchema\":{\"type\":\"object\",\"properties\":{\"requiredCapabilities\":{\"type\":\"array\",\"items\":{\"enum\":[\"create\",\"view\",\"edit\",\"delete\"]}},\"actionPermissions\":{\"type\":\"object\"},\"denyWhenMissingCapability\":{\"type\":\"boolean\"}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"crud-permissions-set\",\"handlerContract\":{\"reads\":[\"GET /{resource}/capabilities\",\"GET /{resource}/{id}/capabilities\",\"CrudMetadata.actions\"],\"writes\":[\"CrudMetadata.actions[].disabled\",\"CrudMetadata.actions[].visibleWhen\",\"CrudMetadata.actions[].requiresConfirmation\"],\"identityKeys\":[\"resourcePath\"],\"failureModes\":[\"capability-not-found\",\"action-permission-conflict\",\"delete-permission-without-confirmation\"],\"description\":\"Aligns CRUD action visibility/disablement with resource capabilities without inventing a second permission source.\"}}],\"validators\":[\"resource-capabilities-resolvable\",\"action-permission-supported\",\"delete-permission-requires-confirmation\",\"permissions-do-not-shadow-backend\"],\"affectedPaths\":[\"actions[].disabled\",\"actions[].visibleWhen\",\"actions[].requiresConfirmation\"],\"submissionImpact\":\"config-only\",\"preconditions\":[\"crud-metadata-loaded\",\"resource-capabilities-resolved\"]},{\"operationId\":\"form.childOperation.delegate\",\"title\":\"Delegate child form/table/dialog authoring operation\",\"scope\":\"global\",\"targetKind\":\"childOperation\",\"target\":{\"kind\":\"childOperation\",\"resolver\":\"child-authoring-manifest-operation\",\"ambiguityPolicy\":\"fail\",\"required\":false},\"inputSchema\":{\"type\":\"object\",\"required\":[\"childComponentId\",\"childOperationId\",\"reason\"],\"properties\":{\"childComponentId\":{\"enum\":[\"praxis-dynamic-form\",\"praxis-table\",\"praxis-dialog\",\"praxis-settings-panel\"]},\"childOperationId\":{\"type\":\"string\"},\"reason\":{\"type\":\"string\"},\"childTarget\":{\"type\":\"object\"},\"childParams\":{\"type\":\"object\"}}},\"effects\":[{\"kind\":\"compile-domain-patch\",\"handler\":\"crud-child-operation-delegate\",\"handlerContract\":{\"reads\":[\"PRAXIS_DYNAMIC_FORM_AUTHORING_MANIFEST\",\"PRAXIS_TABLE_AUTHORING_MANIFEST\",\"PRAXIS_DIALOG_AUTHORING_MANIFEST\",\"PRAXIS_SETTINGS_PANEL_AUTHORING_MANIFEST\"],\"writes\":[\"delegatedAuthoringOperations[]\"],\"identityKeys\":[\"childComponentId\",\"childOperationId\"],\"failureModes\":[\"child-manifest-missing\",\"child-operation-not-found\",\"attempted-local-child-config-write\"],\"description\":\"Records explicit delegation when requested edits belong to child manifests instead of CRUD orchestration.\"}}],\"validators\":[\"child-manifest-available\",\"child-operation-known\",\"no-local-form-config-write\",\"no-local-table-config-write\",\"delegation-target-valid\"],\"affectedPaths\":[\"delegatedAuthoringOperations\"],\"submissionImpact\":\"none\",\"preconditions\":[\"child-manifest-available\"]}]}",
6635
6697
  "sourcePointer": "projects/praxis-crud/src/lib/ai/praxis-crud-authoring-manifest.ts",
6636
- "contentHash": "96031b3089d1be63af010cebdbf9b91b9cadd28b1b0350ca940a2e0b7727a008",
6698
+ "contentHash": "626b7e33a2f7248d888938c7871c10835318980d934d916133b2c331f3047906",
6637
6699
  "sourceKind": "component_definition",
6638
6700
  "sourceId": "praxis-crud",
6639
6701
  "corpusVersion": "1.0.0"
@@ -6641,9 +6703,9 @@
6641
6703
  {
6642
6704
  "chunkIndex": 18,
6643
6705
  "chunkKind": "authoring_manifest",
6644
- "content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.0.0\",\"componentId\":\"praxis-crud\",\"ownerPackage\":\"@praxisui/crud\",\"configSchemaId\":\"CrudMetadata\",\"chunkSection\":\"validators\",\"validators\":[{\"validatorId\":\"resource-exists-in-api-metadata\",\"level\":\"error\",\"code\":\"CRUD_RESOURCE_EXISTS\",\"description\":\"Resource must exist in api_metadata or resource discovery.\"},{\"validatorId\":\"resource-path-canonical\",\"level\":\"error\",\"code\":\"CRUD_RESOURCE_PATH_CANONICAL\",\"description\":\"Resource path must be canonical and not a local alias.\"},{\"validatorId\":\"resource-key-stable\",\"level\":\"error\",\"code\":\"CRUD_RESOURCE_KEY_STABLE\",\"description\":\"Resource key must remain stable for surfaces/actions/capabilities.\"},{\"validatorId\":\"id-field-known\",\"level\":\"error\",\"code\":\"CRUD_ID_FIELD_KNOWN\",\"description\":\"CRUD id field must exist for edit/view/delete flows.\"},{\"validatorId\":\"resource-capabilities-resolvable\",\"level\":\"error\",\"code\":\"CRUD_CAPABILITIES_RESOLVABLE\",\"description\":\"Resource capabilities must be resolvable before action enablement is authored.\"},{\"validatorId\":\"domain-catalog-context-resolvable\",\"level\":\"error\",\"code\":\"CRUD_DOMAIN_CATALOG_CONTEXT_RESOLVABLE\",\"description\":\"Domain Catalog governance context must resolve for the resource key and requested probe before authoring uses it.\"},{\"validatorId\":\"domain-catalog-read-only\",\"level\":\"error\",\"code\":\"CRUD_DOMAIN_CATALOG_READ_ONLY\",\"description\":\"Domain Catalog is read-only semantic context; CRUD may reference a probe but must not persist copied catalog rules.\"},{\"validatorId\":\"ai-usage-visibility-respected\",\"level\":\"error\",\"code\":\"CRUD_AI_USAGE_VISIBILITY_RESPECTED\",\"description\":\"Authoring must respect Domain Catalog aiUsage.visibility before exposing field data, prompts or generated patches.\"},{\"validatorId\":\"no-form-config-rule-materialization\",\"level\":\"error\",\"code\":\"CRUD_NO_FORM_RULE_MATERIALIZATION\",\"description\":\"Shared domain governance must not be materialized into FormConfig rules unless a backend policy explicitly authorizes it.\"},{\"validatorId\":\"table-child-operation-delegated\",\"level\":\"error\",\"code\":\"CRUD_TABLE_CHILD_DELEGATED\",\"description\":\"Table semantics must be delegated to praxis-table.\"},{\"validatorId\":\"query-context-valid\",\"level\":\"error\",\"code\":\"CRUD_QUERY_CONTEXT_VALID\",\"description\":\"Query context must be valid for the bound resource.\"},{\"validatorId\":\"filter-criteria-bridge-valid\",\"level\":\"warning\",\"code\":\"CRUD_FILTER_CRITERIA_BRIDGE_VALID\",\"description\":\"filterCriteria is a bridge; prefer queryContext for new remote authoring.\"},{\"validatorId\":\"crud-context-stable\",\"level\":\"error\",\"code\":\"CRUD_CONTEXT_STABLE\",\"description\":\"Authoring must not break stable crudContext references.\"},{\"validatorId\":\"action-exists\",\"level\":\"error\",\"code\":\"CRUD_ACTION_EXISTS\",\"description\":\"Target action must exist or be created through CRUD action orchestration.\"},{\"validatorId\":\"open-mode-binding-complete\",\"level\":\"error\",\"code\":\"CRUD_OPEN_MODE_BINDING_COMPLETE\",\"description\":\"route requires route; modal/drawer require formId unless resolved by governed overrides.\"},{\"validatorId\":\"schema-url-canonical\",\"level\":\"error\",\"code\":\"CRUD_SCHEMA_URL_CANONICAL\",\"description\":\"Schema URLs must be canonical for the bound resource/action.\"},{\"validatorId\":\"submit-url-canonical\",\"level\":\"error\",\"code\":\"CRUD_SUBMIT_URL_CANONICAL\",\"description\":\"Submit URL and method must be canonical and declared together.\"},{\"validatorId\":\"resource-create-supported\",\"level\":\"error\",\"code\":\"CRUD_CREATE_SUPPORTED\",\"description\":\"Create surface requires resource create capability.\"},{\"validatorId\":\"resource-edit-supported\",\"level\":\"error\",\"code\":\"CRUD_EDIT_SUPPORTED\",\"description\":\"Edit surface requires resource edit capability.\"},{\"validatorId\":\"resource-view-supported\",\"level\":\"error\",\"code\":\"CRUD_VIEW_SUPPORTED\",\"description\":\"View surface requires resource view capability.\"},{\"validatorId\":\"form-child-operation-delegated\",\"level\":\"error\",\"code\":\"CRUD_FORM_CHILD_DELEGATED\",\"description\":\"FormConfig and FieldMetadata edits must delegate to dynamic-form/metadata-editor manifests.\"},{\"validatorId\":\"id-param-mapping-valid\",\"level\":\"error\",\"code\":\"CRUD_ID_PARAM_MAPPING_VALID\",\"description\":\"Edit/view actions must map resource id into route/query/input as required.\"},{\"validatorId\":\"readonly-form-delegation-valid\",\"level\":\"error\",\"code\":\"CRUD_READONLY_FORM_DELEGATED\",\"description\":\"Readonly form behavior belongs to the dynamic-form manifest.\"},{\"validatorId\":\"delete-action-exists\",\"level\":\"error\",\"code\":\"CRUD_DELETE_ACTION_EXISTS\",\"description\":\"Delete behavior requires a delete action target.\"},{\"validatorId\":\"resource-delete-supported\",\"level\":\"error\",\"code\":\"CRUD_DELETE_SUPPORTED\",\"description\":\"Delete behavior requires resource delete capability.\"},{\"validatorId\":\"destructive-delete-confirmed\",\"level\":\"error\",\"code\":\"CRUD_DELETE_CONFIRMED\",\"description\":\"Destructive delete behavior requires explicit confirmation.\"},{\"validatorId\":\"permissions-delete-valid\",\"level\":\"error\",\"code\":\"CRUD_DELETE_PERMISSION_VALID\",\"description\":\"Delete permission cannot bypass resource capabilities or confirmation policy.\"},{\"validatorId\":\"open-mode-supported\",\"level\":\"error\",\"code\":\"CRUD_OPEN_MODE_SUPPORTED\",\"description\":\"Open mode must be route, modal or drawer.\"},{\"validatorId\":\"modal-size-valid\",\"level\":\"error\",\"code\":\"CRUD_MODAL_SIZE_VALID\",\"description\":\"Modal sizing defaults must be valid DialogConfig values.\"},{\"validatorId\":\"drawer-runtime-available\",\"level\":\"error\",\"code\":\"CRUD_DRAWER_RUNTIME_AVAILABLE\",\"description\":\"Drawer open mode uses the default dialog-backed drawer runtime with CRUD-owned chrome, responsive right-panel sizing, Escape/backdrop handling and semantic close/save results; a host drawer adapter is optional only for shell-specific presentation.\"},{\"validatorId\":\"back-policy-valid\",\"level\":\"error\",\"code\":\"CRUD_BACK_POLICY_VALID\",\"description\":\"Back policy must be valid for route/modal/drawer behavior.\"},{\"validatorId\":\"settings-panel-shell-compatible\",\"level\":\"warning\",\"code\":\"CRUD_SETTINGS_PANEL_COMPATIBLE\",\"description\":\"Authoring shell must preserve apply/save/reset semantics.\"},{\"validatorId\":\"action-permission-supported\",\"level\":\"error\",\"code\":\"CRUD_ACTION_PERMISSION_SUPPORTED\",\"description\":\"Action permissions must map to supported resource capabilities.\"},{\"validatorId\":\"delete-permission-requires-confirmation\",\"level\":\"error\",\"code\":\"CRUD_DELETE_PERMISSION_CONFIRMATION\",\"description\":\"Delete permission enablement requires confirmation policy.\"},{\"validatorId\":\"permissions-do-not-shadow-backend\",\"level\":\"error\",\"code\":\"CRUD_PERMISSIONS_NO_BACKEND_SHADOW\",\"description\":\"UI permissions must not shadow backend capability denial.\"},{\"validatorId\":\"child-manifest-available\",\"level\":\"error\",\"code\":\"CRUD_CHILD_MANIFEST_AVAILABLE\",\"description\":\"Delegated child manifest must be available.\"},{\"validatorId\":\"child-operation-known\",\"level\":\"error\",\"code\":\"CRUD_CHILD_OPERATION_KNOWN\",\"description\":\"Delegated operation must exist in the child manifest.\"},{\"validatorId\":\"no-local-form-config-write\",\"level\":\"error\",\"code\":\"CRUD_NO_LOCAL_FORM_CONFIG_WRITE\",\"description\":\"CRUD must not locally redefine FormConfig semantics.\"},{\"validatorId\":\"no-local-table-config-write\",\"level\":\"error\",\"code\":\"CRUD_NO_LOCAL_TABLE_CONFIG_WRITE\",\"description\":\"CRUD must not locally redefine TableConfig semantics.\"},{\"validatorId\":\"delegation-target-valid\",\"level\":\"error\",\"code\":\"CRUD_DELEGATION_TARGET_VALID\",\"description\":\"Delegation target must be resolvable by the child manifest.\"}]}",
6706
+ "content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.0.0\",\"componentId\":\"praxis-crud\",\"ownerPackage\":\"@praxisui/crud\",\"configSchemaId\":\"CrudMetadata\",\"chunkSection\":\"validators\",\"validators\":[{\"validatorId\":\"resource-exists-in-api-metadata\",\"level\":\"error\",\"code\":\"CRUD_RESOURCE_EXISTS\",\"description\":\"Resource must exist in api_metadata or resource discovery.\"},{\"validatorId\":\"resource-path-canonical\",\"level\":\"error\",\"code\":\"CRUD_RESOURCE_PATH_CANONICAL\",\"description\":\"Resource path must be canonical and not a local alias.\"},{\"validatorId\":\"resource-key-stable\",\"level\":\"error\",\"code\":\"CRUD_RESOURCE_KEY_STABLE\",\"description\":\"Resource key must remain stable for surfaces/actions/capabilities.\"},{\"validatorId\":\"id-field-known\",\"level\":\"error\",\"code\":\"CRUD_ID_FIELD_KNOWN\",\"description\":\"CRUD id field must exist for edit/view/delete flows.\"},{\"validatorId\":\"resource-capabilities-resolvable\",\"level\":\"error\",\"code\":\"CRUD_CAPABILITIES_RESOLVABLE\",\"description\":\"Resource capabilities must be resolvable before action enablement is authored.\"},{\"validatorId\":\"domain-catalog-context-resolvable\",\"level\":\"error\",\"code\":\"CRUD_DOMAIN_CATALOG_CONTEXT_RESOLVABLE\",\"description\":\"Domain Catalog governance context must resolve for the resource key and requested probe before authoring uses it.\"},{\"validatorId\":\"domain-catalog-read-only\",\"level\":\"error\",\"code\":\"CRUD_DOMAIN_CATALOG_READ_ONLY\",\"description\":\"Domain Catalog is read-only semantic context; CRUD may reference a probe but must not persist copied catalog rules.\"},{\"validatorId\":\"ai-usage-visibility-respected\",\"level\":\"error\",\"code\":\"CRUD_AI_USAGE_VISIBILITY_RESPECTED\",\"description\":\"Authoring must respect Domain Catalog aiUsage.visibility before exposing field data, prompts or generated patches.\"},{\"validatorId\":\"no-form-config-rule-materialization\",\"level\":\"error\",\"code\":\"CRUD_NO_FORM_RULE_MATERIALIZATION\",\"description\":\"Shared domain governance must not be materialized into FormConfig rules unless a backend policy explicitly authorizes it.\"},{\"validatorId\":\"table-child-operation-delegated\",\"level\":\"error\",\"code\":\"CRUD_TABLE_CHILD_DELEGATED\",\"description\":\"Table semantics must be delegated to praxis-table.\"},{\"validatorId\":\"query-context-valid\",\"level\":\"error\",\"code\":\"CRUD_QUERY_CONTEXT_VALID\",\"description\":\"Query context must be valid for the bound resource.\"},{\"validatorId\":\"filter-criteria-bridge-valid\",\"level\":\"warning\",\"code\":\"CRUD_FILTER_CRITERIA_BRIDGE_VALID\",\"description\":\"filterCriteria is a bridge; prefer queryContext for new remote authoring.\"},{\"validatorId\":\"crud-context-stable\",\"level\":\"error\",\"code\":\"CRUD_CONTEXT_STABLE\",\"description\":\"Authoring must not break stable crudContext references.\"},{\"validatorId\":\"action-exists\",\"level\":\"error\",\"code\":\"CRUD_ACTION_EXISTS\",\"description\":\"Target action must exist or be created through CRUD action orchestration.\"},{\"validatorId\":\"open-mode-binding-complete\",\"level\":\"error\",\"code\":\"CRUD_OPEN_MODE_BINDING_COMPLETE\",\"description\":\"route requires route; modal/drawer require formId unless resolved by governed overrides.\"},{\"validatorId\":\"schema-url-canonical\",\"level\":\"error\",\"code\":\"CRUD_SCHEMA_URL_CANONICAL\",\"description\":\"Schema URLs must be canonical for the bound resource/action.\"},{\"validatorId\":\"submit-url-canonical\",\"level\":\"error\",\"code\":\"CRUD_SUBMIT_URL_CANONICAL\",\"description\":\"Submit URL and method must be canonical and declared together.\"},{\"validatorId\":\"resource-create-supported\",\"level\":\"error\",\"code\":\"CRUD_CREATE_SUPPORTED\",\"description\":\"Create surface requires resource create capability.\"},{\"validatorId\":\"resource-edit-supported\",\"level\":\"error\",\"code\":\"CRUD_EDIT_SUPPORTED\",\"description\":\"Edit surface requires resource edit capability.\"},{\"validatorId\":\"resource-view-supported\",\"level\":\"error\",\"code\":\"CRUD_VIEW_SUPPORTED\",\"description\":\"View surface requires resource view capability.\"},{\"validatorId\":\"form-child-operation-delegated\",\"level\":\"error\",\"code\":\"CRUD_FORM_CHILD_DELEGATED\",\"description\":\"FormConfig and FieldMetadata edits must delegate to dynamic-form/metadata-editor manifests.\"},{\"validatorId\":\"id-param-mapping-valid\",\"level\":\"error\",\"code\":\"CRUD_ID_PARAM_MAPPING_VALID\",\"description\":\"Edit/view actions must map resource id into route/query/input as required.\"},{\"validatorId\":\"readonly-form-delegation-valid\",\"level\":\"error\",\"code\":\"CRUD_READONLY_FORM_DELEGATED\",\"description\":\"Readonly form behavior belongs to the dynamic-form manifest.\"},{\"validatorId\":\"delete-action-exists\",\"level\":\"error\",\"code\":\"CRUD_DELETE_ACTION_EXISTS\",\"description\":\"Delete behavior requires a delete action target.\"},{\"validatorId\":\"resource-delete-supported\",\"level\":\"error\",\"code\":\"CRUD_DELETE_SUPPORTED\",\"description\":\"Delete behavior requires resource delete capability.\"},{\"validatorId\":\"destructive-delete-confirmed\",\"level\":\"error\",\"code\":\"CRUD_DELETE_CONFIRMED\",\"description\":\"Destructive delete behavior requires explicit confirmation.\"},{\"validatorId\":\"permissions-delete-valid\",\"level\":\"error\",\"code\":\"CRUD_DELETE_PERMISSION_VALID\",\"description\":\"Delete permission cannot bypass resource capabilities or confirmation policy.\"},{\"validatorId\":\"open-mode-supported\",\"level\":\"error\",\"code\":\"CRUD_OPEN_MODE_SUPPORTED\",\"description\":\"Open mode must be route, modal or drawer.\"},{\"validatorId\":\"modal-size-valid\",\"level\":\"error\",\"code\":\"CRUD_MODAL_SIZE_VALID\",\"description\":\"Modal sizing defaults must be valid DialogConfig values.\"},{\"validatorId\":\"drawer-runtime-available\",\"level\":\"error\",\"code\":\"CRUD_DRAWER_RUNTIME_AVAILABLE\",\"description\":\"Drawer open mode uses the default dialog-backed drawer runtime with CRUD-owned chrome, responsive right-panel sizing, Escape/backdrop handling and semantic close/save results; a host drawer adapter is optional only for shell-specific presentation.\"},{\"validatorId\":\"drawer-position-lifecycle-stable\",\"level\":\"error\",\"code\":\"CRUD_DRAWER_POSITION_LIFECYCLE_STABLE\",\"description\":\"A drawer must preserve its configured viewport edge through open, remembered-size restoration, maximize and restore.\"},{\"validatorId\":\"back-policy-valid\",\"level\":\"error\",\"code\":\"CRUD_BACK_POLICY_VALID\",\"description\":\"Back policy must be valid for route/modal/drawer behavior.\"},{\"validatorId\":\"settings-panel-shell-compatible\",\"level\":\"warning\",\"code\":\"CRUD_SETTINGS_PANEL_COMPATIBLE\",\"description\":\"Authoring shell must preserve apply/save/reset semantics.\"},{\"validatorId\":\"action-permission-supported\",\"level\":\"error\",\"code\":\"CRUD_ACTION_PERMISSION_SUPPORTED\",\"description\":\"Action permissions must map to supported resource capabilities.\"},{\"validatorId\":\"delete-permission-requires-confirmation\",\"level\":\"error\",\"code\":\"CRUD_DELETE_PERMISSION_CONFIRMATION\",\"description\":\"Delete permission enablement requires confirmation policy.\"},{\"validatorId\":\"permissions-do-not-shadow-backend\",\"level\":\"error\",\"code\":\"CRUD_PERMISSIONS_NO_BACKEND_SHADOW\",\"description\":\"UI permissions must not shadow backend capability denial.\"},{\"validatorId\":\"child-manifest-available\",\"level\":\"error\",\"code\":\"CRUD_CHILD_MANIFEST_AVAILABLE\",\"description\":\"Delegated child manifest must be available.\"},{\"validatorId\":\"child-operation-known\",\"level\":\"error\",\"code\":\"CRUD_CHILD_OPERATION_KNOWN\",\"description\":\"Delegated operation must exist in the child manifest.\"},{\"validatorId\":\"no-local-form-config-write\",\"level\":\"error\",\"code\":\"CRUD_NO_LOCAL_FORM_CONFIG_WRITE\",\"description\":\"CRUD must not locally redefine FormConfig semantics.\"},{\"validatorId\":\"no-local-table-config-write\",\"level\":\"error\",\"code\":\"CRUD_NO_LOCAL_TABLE_CONFIG_WRITE\",\"description\":\"CRUD must not locally redefine TableConfig semantics.\"},{\"validatorId\":\"delegation-target-valid\",\"level\":\"error\",\"code\":\"CRUD_DELEGATION_TARGET_VALID\",\"description\":\"Delegation target must be resolvable by the child manifest.\"}]}",
6645
6707
  "sourcePointer": "projects/praxis-crud/src/lib/ai/praxis-crud-authoring-manifest.ts",
6646
- "contentHash": "4ea5b5b005de837468ddc1c7f2adcb088cf3efa83efd8ce78a4081ccec6bcbf9",
6708
+ "contentHash": "696e76f0b2bfe99ce6dec91c073c9aa2b81c1287118b0645db6e8a0485a909f0",
6647
6709
  "sourceKind": "component_definition",
6648
6710
  "sourceId": "praxis-crud",
6649
6711
  "corpusVersion": "1.0.0"
@@ -6651,9 +6713,9 @@
6651
6713
  {
6652
6714
  "chunkIndex": 19,
6653
6715
  "chunkKind": "authoring_manifest",
6654
- "content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.0.0\",\"componentId\":\"praxis-crud\",\"ownerPackage\":\"@praxisui/crud\",\"configSchemaId\":\"CrudMetadata\",\"chunkSection\":\"roundTripRequirements\",\"roundTripRequirements\":[\"CrudMetadata is the canonical CRUD document shape.\",\"CrudAuthoringDocument wraps CrudMetadata without introducing host-local aliases.\",\"Resource path and resource key have separate semantics: path is operational, key is discovery/capability identity.\",\"Domain Catalog context is referenced through resourceKey/release/probe and stays read-only; CRUD may attach queryContext.meta.domainCatalog but must not copy shared governance rules into FormConfig.\",\"Open mode round-trip must preserve route, formId, form contract, params, initialValue and back policy.\",\"FormConfig and FieldMetadata edits must delegate to dynamic-form or metadata-editor manifests.\",\"TableConfig edits must delegate to praxis-table; CRUD owns only shell orchestration and list surface binding.\",\"Delete behavior is destructive and requires explicit confirmation plus backend capability support.\",\"crudContext must remain reference-stable across change detection cycles.\"]}",
6716
+ "content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.0.0\",\"componentId\":\"praxis-crud\",\"ownerPackage\":\"@praxisui/crud\",\"configSchemaId\":\"CrudMetadata\",\"chunkSection\":\"roundTripRequirements\",\"roundTripRequirements\":[\"CrudMetadata is the canonical CRUD document shape.\",\"CrudAuthoringDocument wraps CrudMetadata without introducing host-local aliases.\",\"Resource path and resource key have separate semantics: path is operational, key is discovery/capability identity.\",\"Domain Catalog context is referenced through resourceKey/release/probe and stays read-only; CRUD may attach queryContext.meta.domainCatalog but must not copy shared governance rules into FormConfig.\",\"Open mode round-trip must preserve route, formId, form contract, params, initialValue and back policy.\",\"FormConfig and FieldMetadata edits must delegate to dynamic-form or metadata-editor manifests.\",\"TableConfig edits must delegate to praxis-table; CRUD owns only shell orchestration and list surface binding.\",\"Delete behavior is destructive and requires explicit confirmation plus backend capability support.\",\"Drawer position and edgeGap must round-trip without changing the configured edge during open, remembered-size restoration, maximize or restore.\",\"crudContext must remain reference-stable across change detection cycles.\"]}",
6655
6717
  "sourcePointer": "projects/praxis-crud/src/lib/ai/praxis-crud-authoring-manifest.ts",
6656
- "contentHash": "c0878459368b27710cd0bb3b1e8740f2083fc6c433de740798d8cdc31723e101",
6718
+ "contentHash": "9223b66da0df1282bc6777e827642542a75e6d7487177eed5de185ec4be35716",
6657
6719
  "sourceKind": "component_definition",
6658
6720
  "sourceId": "praxis-crud",
6659
6721
  "corpusVersion": "1.0.0"
@@ -6661,9 +6723,9 @@
6661
6723
  {
6662
6724
  "chunkIndex": 20,
6663
6725
  "chunkKind": "authoring_manifest",
6664
- "content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.0.0\",\"componentId\":\"praxis-crud\",\"ownerPackage\":\"@praxisui/crud\",\"configSchemaId\":\"CrudMetadata\",\"chunkSection\":\"examples\",\"examples\":[{\"id\":\"crud-bind-funcionarios-resource\",\"request\":\"Bind this CRUD to funcionarios using id as the identifier.\",\"operationId\":\"resource.bind\",\"target\":\"resource:funcionarios\",\"params\":{\"resourcePath\":\"/funcionarios\",\"resourceKey\":\"funcionarios\",\"idField\":\"id\"},\"isPositive\":true},{\"id\":\"crud-attach-lgpd-cpf-context\",\"request\":\"Use the Domain Catalog LGPD context for cpf before changing the employee CRUD.\",\"operationId\":\"domain.governanceContext.attach\",\"target\":\"domainCatalog:human-resources.funcionarios:cpf\",\"params\":{\"resourceKey\":\"human-resources.funcionarios\",\"query\":\"cpf\",\"itemTypes\":[\"governance\"],\"intent\":\"authoring\"},\"isPositive\":true},{\"id\":\"crud-list-query-context\",\"request\":\"Filter the list by active employees using the canonical query context.\",\"operationId\":\"list.surface.configure\",\"target\":\"listSurface\",\"params\":{\"queryContext\":{\"filters\":[{\"field\":\"active\",\"operator\":\"eq\",\"value\":true}]}},\"isPositive\":true},{\"id\":\"crud-create-modal\",\"request\":\"Open create in a modal with the employee form.\",\"operationId\":\"surface.create.configure\",\"target\":\"action:create\",\"params\":{\"actionId\":\"create\",\"openMode\":\"modal\",\"formId\":\"employee-create\"},\"isPositive\":true},{\"id\":\"crud-edit-route-with-id\",\"request\":\"Edit should navigate to /employees/:id and map the row id.\",\"operationId\":\"surface.edit.configure\",\"target\":\"action:edit\",\"params\":{\"actionId\":\"edit\",\"openMode\":\"route\",\"route\":\"/employees/:id/edit\",\"params\":[{\"from\":\"id\",\"to\":\"routeParam\",\"name\":\"id\"}]},\"isPositive\":true},{\"id\":\"crud-view-drawer-readonly\",\"request\":\"Open view in a drawer and keep the form readonly.\",\"operationId\":\"surface.view.configure\",\"target\":\"action:view\",\"params\":{\"actionId\":\"view\",\"openMode\":\"drawer\",\"formId\":\"employee-view\"},\"isPositive\":true},{\"id\":\"crud-delete-enable-confirmed\",\"request\":\"Enable delete but require confirmation.\",\"operationId\":\"delete.enabled.set\",\"target\":\"action:delete\",\"params\":{\"enabled\":true,\"actionId\":\"delete\",\"requiresConfirmation\":true,\"autoDelete\":true},\"isPositive\":true},{\"id\":\"crud-dialog-size-medium\",\"request\":\"Make CRUD modals 900px wide and remember their last state.\",\"operationId\":\"dialog.size.set\",\"target\":\"dialogHost\",\"params\":{\"defaultOpenMode\":\"modal\",\"modal\":{\"width\":\"900px\",\"rememberLastState\":true}},\"isPositive\":true},{\"id\":\"crud-permissions-no-delete\",\"request\":\"Hide delete when the resource capabilities do not allow it.\",\"operationId\":\"permissions.set\",\"target\":\"permissions:delete\",\"params\":{\"requiredCapabilities\":[\"delete\"],\"denyWhenMissingCapability\":true},\"isPositive\":true},{\"id\":\"crud-delegate-form-layout\",\"request\":\"Add a new field to the create form.\",\"operationId\":\"form.childOperation.delegate\",\"target\":\"praxis-dynamic-form:field.add\",\"params\":{\"childComponentId\":\"praxis-dynamic-form\",\"childOperationId\":\"field.add\",\"reason\":\"Form field semantics belong to dynamic-form\"},\"isPositive\":true},{\"id\":\"crud-delegate-table-column\",\"request\":\"Add a salary column to the list table.\",\"operationId\":\"form.childOperation.delegate\",\"target\":\"praxis-table:column.add\",\"params\":{\"childComponentId\":\"praxis-table\",\"childOperationId\":\"column.add\",\"reason\":\"Table column semantics belong to praxis-table\"},\"isPositive\":true},{\"id\":\"crud-reject-local-form-rules\",\"request\":\"Put this dynamic-form validation rule directly inside CRUD.\",\"operationId\":\"form.childOperation.delegate\",\"target\":\"praxis-dynamic-form:rules.add\",\"params\":{\"childComponentId\":\"praxis-dynamic-form\",\"childOperationId\":\"rules.add\",\"reason\":\"CRUD must not redefine FormConfig rules\"},\"isPositive\":false},{\"id\":\"crud-reject-copy-domain-rule-to-formconfig\",\"request\":\"Copy the LGPD rule from Domain Catalog directly into the create form JSON.\",\"operationId\":\"domain.governanceContext.attach\",\"target\":\"domainCatalog:human-resources.funcionarios:cpf\",\"params\":{\"resourceKey\":\"human-resources.funcionarios\",\"query\":\"cpf\",\"itemTypes\":[\"governance\"],\"intent\":\"authoring\"},\"isPositive\":false},{\"id\":\"crud-reject-delete-without-capability\",\"request\":\"Force delete even though the backend does not expose delete capability.\",\"operationId\":\"delete.enabled.set\",\"target\":\"action:delete\",\"params\":{\"enabled\":true,\"actionId\":\"delete\",\"requiresConfirmation\":false},\"isPositive\":false}]}",
6726
+ "content": "{\"schemaVersion\":\"1.0.0\",\"manifestVersion\":\"1.0.0\",\"componentId\":\"praxis-crud\",\"ownerPackage\":\"@praxisui/crud\",\"configSchemaId\":\"CrudMetadata\",\"chunkSection\":\"examples\",\"examples\":[{\"id\":\"crud-bind-funcionarios-resource\",\"request\":\"Bind this CRUD to funcionarios using id as the identifier.\",\"operationId\":\"resource.bind\",\"target\":\"resource:funcionarios\",\"params\":{\"resourcePath\":\"/funcionarios\",\"resourceKey\":\"funcionarios\",\"idField\":\"id\"},\"isPositive\":true},{\"id\":\"crud-attach-lgpd-cpf-context\",\"request\":\"Use the Domain Catalog LGPD context for cpf before changing the employee CRUD.\",\"operationId\":\"domain.governanceContext.attach\",\"target\":\"domainCatalog:human-resources.funcionarios:cpf\",\"params\":{\"resourceKey\":\"human-resources.funcionarios\",\"query\":\"cpf\",\"itemTypes\":[\"governance\"],\"intent\":\"authoring\"},\"isPositive\":true},{\"id\":\"crud-list-query-context\",\"request\":\"Filter the list by active employees using the canonical query context.\",\"operationId\":\"list.surface.configure\",\"target\":\"listSurface\",\"params\":{\"queryContext\":{\"filters\":[{\"field\":\"active\",\"operator\":\"eq\",\"value\":true}]}},\"isPositive\":true},{\"id\":\"crud-create-modal\",\"request\":\"Open create in a modal with the employee form.\",\"operationId\":\"surface.create.configure\",\"target\":\"action:create\",\"params\":{\"actionId\":\"create\",\"openMode\":\"modal\",\"formId\":\"employee-create\"},\"isPositive\":true},{\"id\":\"crud-edit-route-with-id\",\"request\":\"Edit should navigate to /employees/:id and map the row id.\",\"operationId\":\"surface.edit.configure\",\"target\":\"action:edit\",\"params\":{\"actionId\":\"edit\",\"openMode\":\"route\",\"route\":\"/employees/:id/edit\",\"params\":[{\"from\":\"id\",\"to\":\"routeParam\",\"name\":\"id\"}]},\"isPositive\":true},{\"id\":\"crud-view-drawer-readonly\",\"request\":\"Open view in a drawer and keep the form readonly.\",\"operationId\":\"surface.view.configure\",\"target\":\"action:view\",\"params\":{\"actionId\":\"view\",\"openMode\":\"drawer\",\"formId\":\"employee-view\"},\"isPositive\":true},{\"id\":\"crud-delete-enable-confirmed\",\"request\":\"Enable delete but require confirmation.\",\"operationId\":\"delete.enabled.set\",\"target\":\"action:delete\",\"params\":{\"enabled\":true,\"actionId\":\"delete\",\"requiresConfirmation\":true,\"autoDelete\":true},\"isPositive\":true},{\"id\":\"crud-dialog-size-medium\",\"request\":\"Make CRUD modals 900px wide and remember their last state.\",\"operationId\":\"dialog.size.set\",\"target\":\"dialogHost\",\"params\":{\"defaultOpenMode\":\"modal\",\"modal\":{\"width\":\"900px\",\"rememberLastState\":true}},\"isPositive\":true},{\"id\":\"crud-drawer-left-edge\",\"request\":\"Open CRUD forms in a left drawer with an 8px maximized inset.\",\"operationId\":\"dialog.size.set\",\"target\":\"dialogHost\",\"params\":{\"defaultOpenMode\":\"drawer\",\"modal\":{\"position\":{\"left\":\"0\",\"top\":\"0\"},\"edgeGap\":8}},\"isPositive\":true},{\"id\":\"crud-permissions-no-delete\",\"request\":\"Hide delete when the resource capabilities do not allow it.\",\"operationId\":\"permissions.set\",\"target\":\"permissions:delete\",\"params\":{\"requiredCapabilities\":[\"delete\"],\"denyWhenMissingCapability\":true},\"isPositive\":true},{\"id\":\"crud-delegate-form-layout\",\"request\":\"Add a new field to the create form.\",\"operationId\":\"form.childOperation.delegate\",\"target\":\"praxis-dynamic-form:field.add\",\"params\":{\"childComponentId\":\"praxis-dynamic-form\",\"childOperationId\":\"field.add\",\"reason\":\"Form field semantics belong to dynamic-form\"},\"isPositive\":true},{\"id\":\"crud-delegate-table-column\",\"request\":\"Add a salary column to the list table.\",\"operationId\":\"form.childOperation.delegate\",\"target\":\"praxis-table:column.add\",\"params\":{\"childComponentId\":\"praxis-table\",\"childOperationId\":\"column.add\",\"reason\":\"Table column semantics belong to praxis-table\"},\"isPositive\":true},{\"id\":\"crud-reject-local-form-rules\",\"request\":\"Put this dynamic-form validation rule directly inside CRUD.\",\"operationId\":\"form.childOperation.delegate\",\"target\":\"praxis-dynamic-form:rules.add\",\"params\":{\"childComponentId\":\"praxis-dynamic-form\",\"childOperationId\":\"rules.add\",\"reason\":\"CRUD must not redefine FormConfig rules\"},\"isPositive\":false},{\"id\":\"crud-reject-copy-domain-rule-to-formconfig\",\"request\":\"Copy the LGPD rule from Domain Catalog directly into the create form JSON.\",\"operationId\":\"domain.governanceContext.attach\",\"target\":\"domainCatalog:human-resources.funcionarios:cpf\",\"params\":{\"resourceKey\":\"human-resources.funcionarios\",\"query\":\"cpf\",\"itemTypes\":[\"governance\"],\"intent\":\"authoring\"},\"isPositive\":false},{\"id\":\"crud-reject-delete-without-capability\",\"request\":\"Force delete even though the backend does not expose delete capability.\",\"operationId\":\"delete.enabled.set\",\"target\":\"action:delete\",\"params\":{\"enabled\":true,\"actionId\":\"delete\",\"requiresConfirmation\":false},\"isPositive\":false}]}",
6665
6727
  "sourcePointer": "projects/praxis-crud/src/lib/ai/praxis-crud-authoring-manifest.ts",
6666
- "contentHash": "6508bda0ac250c6663ba9182bbb8a5af3a09cd5af3a939d1c2233fd27647cdc7",
6728
+ "contentHash": "f2821fa0869a580cc33fbbd1d29f12c791082edab5b40787179d612d187a2990",
6667
6729
  "sourceKind": "component_definition",
6668
6730
  "sourceId": "praxis-crud",
6669
6731
  "corpusVersion": "1.0.0"
@@ -6876,7 +6938,7 @@
6876
6938
  "name": "data.metadata.defaults.modal",
6877
6939
  "type": "DialogConfig",
6878
6940
  "required": false,
6879
- "description": "Dialog defaults (size, behavior)."
6941
+ "description": "Dialog defaults for size, behavior, viewport edge position, and maximized edge gap."
6880
6942
  },
6881
6943
  {
6882
6944
  "name": "data.metadata.defaults.back",
@@ -3765,6 +3765,15 @@ class PraxisCrudComponent {
3765
3765
  }
3766
3766
  await this.onAction(action, row ?? undefined, event);
3767
3767
  }
3768
+ async onToolbarAction(event) {
3769
+ const action = String(event?.action || '').trim();
3770
+ if (!action)
3771
+ return;
3772
+ const row = this.extractSelectedRowFromToolbarAction(event);
3773
+ if (row)
3774
+ this.selectedRow = row;
3775
+ await this.onAction(action, row ?? undefined, event);
3776
+ }
3768
3777
  ngOnChanges(changes) {
3769
3778
  if (!changes['metadata'] && !changes['context']) {
3770
3779
  return;
@@ -4294,6 +4303,20 @@ class PraxisCrudComponent {
4294
4303
  const first = Array.isArray(event?.rows) ? event.rows[0] : null;
4295
4304
  return this.isRecord(first) ? first : null;
4296
4305
  }
4306
+ extractSelectedRowFromToolbarAction(event) {
4307
+ if (this.isRecord(event?.selectedRow))
4308
+ return event.selectedRow;
4309
+ if (this.isRecord(event?.row))
4310
+ return event.row;
4311
+ const first = Array.isArray(event?.selectedRows) ? event.selectedRows[0] : null;
4312
+ return this.isRecord(first) ? first : null;
4313
+ }
4314
+ extractDiscoveryActionConfig(event) {
4315
+ const config = event?.actionConfig;
4316
+ return this.isRecord(config) && typeof config['resourceKey'] === 'string'
4317
+ ? config
4318
+ : null;
4319
+ }
4297
4320
  resolveSelectedRowForAction(action) {
4298
4321
  const normalized = String(action || '').trim().toLowerCase();
4299
4322
  if (!normalized || normalized === 'create' || normalized === 'add' || normalized === 'new') {
@@ -4306,7 +4329,7 @@ class PraxisCrudComponent {
4306
4329
  if (!this.surfaceService) {
4307
4330
  return false;
4308
4331
  }
4309
- const providedSurface = this.resolveProvidedSurface(normalizedAction, runtimeEvent?.actionConfig);
4332
+ const providedSurface = this.resolveProvidedSurface(normalizedAction, this.extractDiscoveryActionConfig(runtimeEvent));
4310
4333
  if (!providedSurface && !this.isDiscoveryManagedCrudAction(normalizedAction)) {
4311
4334
  return false;
4312
4335
  }
@@ -4393,7 +4416,7 @@ class PraxisCrudComponent {
4393
4416
  if (!normalizedAction || this.isDiscoveryManagedCrudAction(normalizedAction) || !this.surfaceService) {
4394
4417
  return false;
4395
4418
  }
4396
- const providedAction = this.resolveProvidedWorkflowAction(normalizedAction, runtimeEvent?.actionConfig);
4419
+ const providedAction = this.resolveProvidedWorkflowAction(normalizedAction, this.extractDiscoveryActionConfig(runtimeEvent));
4397
4420
  const catalog = providedAction ? null : await this.resolveDiscoveredActionCatalog(row);
4398
4421
  const discoveredAction = providedAction || this.selectDiscoveredWorkflowAction(normalizedAction, catalog?.actions || []);
4399
4422
  const resourcePath = String(catalog?.resourcePath || this.resolveResourcePath(this.resolvedMetadata) || '').trim();
@@ -5124,7 +5147,7 @@ class PraxisCrudComponent {
5124
5147
  }
5125
5148
  }
5126
5149
  interpolateTranslationParams(text, params) {
5127
- return Object.entries(params).reduce((current, [name, value]) => current.replaceAll(`{${name}}`, value), text);
5150
+ return Object.entries(params).reduce((current, [name, value]) => current.split(`{${name}}`).join(value), text);
5128
5151
  }
5129
5152
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: PraxisCrudComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5130
5153
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: PraxisCrudComponent, isStandalone: true, selector: "praxis-crud", inputs: { metadata: "metadata", crudId: "crudId", componentInstanceId: "componentInstanceId", context: "context", enableCustomization: "enableCustomization", authoringCapability: "authoringCapability" }, outputs: { configureRequested: "configureRequested", afterOpen: "afterOpen", afterClose: "afterClose", afterSave: "afterSave", afterDelete: "afterDelete", error: "error", rowClick: "rowClick", selectionChange: "selectionChange", tableRuntimeConfigChange: "tableRuntimeConfigChange", crudAuthoringDocumentApplied: "crudAuthoringDocumentApplied", crudAuthoringDocumentSaved: "crudAuthoringDocumentSaved" }, providers: [
@@ -5145,7 +5168,7 @@ class PraxisCrudComponent {
5145
5168
  (rowClick)="onTableRowClick($event)"
5146
5169
  (selectionChange)="onTableSelectionChange($event)"
5147
5170
  (rowAction)="onAction($event.action, $event.row, $event)"
5148
- (toolbarAction)="onAction($event.action)"
5171
+ (toolbarAction)="onToolbarAction($event)"
5149
5172
  (bulkAction)="onBulkAction($event)"
5150
5173
  (collectionLinksChange)="onCollectionLinksChange($event)"
5151
5174
  (reset)="onResetPreferences()"
@@ -5184,7 +5207,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImpo
5184
5207
  (rowClick)="onTableRowClick($event)"
5185
5208
  (selectionChange)="onTableSelectionChange($event)"
5186
5209
  (rowAction)="onAction($event.action, $event.row, $event)"
5187
- (toolbarAction)="onAction($event.action)"
5210
+ (toolbarAction)="onToolbarAction($event)"
5188
5211
  (bulkAction)="onBulkAction($event)"
5189
5212
  (collectionLinksChange)="onCollectionLinksChange($event)"
5190
5213
  (reset)="onResetPreferences()"
@@ -6529,14 +6552,28 @@ const dialogHostSchema = {
6529
6552
  width: { type: 'string' },
6530
6553
  height: { type: 'string' },
6531
6554
  minWidth: { type: 'string' },
6555
+ minHeight: { type: 'string' },
6532
6556
  maxWidth: { type: 'string' },
6557
+ maxHeight: { type: 'string' },
6533
6558
  density: { type: 'string' },
6559
+ backdropStyle: { enum: ['blur', 'dim', 'transparent'] },
6534
6560
  canMaximize: { type: 'boolean' },
6535
6561
  rememberLastState: { type: 'boolean' },
6562
+ closeOnSave: { type: 'boolean' },
6536
6563
  startMaximized: { type: 'boolean' },
6537
6564
  disableCloseOnEsc: { type: 'boolean' },
6538
6565
  disableCloseOnBackdrop: { type: 'boolean' },
6539
- fullscreenBreakpoint: { type: 'string' },
6566
+ fullscreenBreakpoint: { type: 'number' },
6567
+ position: {
6568
+ type: 'object',
6569
+ properties: {
6570
+ top: { type: 'string' },
6571
+ bottom: { type: 'string' },
6572
+ left: { type: 'string' },
6573
+ right: { type: 'string' },
6574
+ },
6575
+ },
6576
+ edgeGap: { type: 'number' },
6540
6577
  },
6541
6578
  },
6542
6579
  back: { type: 'object' },
@@ -6763,10 +6800,10 @@ const PRAXIS_CRUD_AUTHORING_MANIFEST = {
6763
6800
  writes: ['CrudMetadata.defaults.openMode', 'CrudMetadata.defaults.modal', 'CrudMetadata.defaults.back'],
6764
6801
  identityKeys: ['crudId'],
6765
6802
  inputSchema: dialogHostSchema,
6766
- failureModes: ['open-mode-unsupported', 'drawer-runtime-unavailable', 'modal-size-invalid', 'back-policy-invalid'],
6803
+ failureModes: ['open-mode-unsupported', 'drawer-runtime-unavailable', 'modal-size-invalid', 'drawer-position-lifecycle-unstable', 'back-policy-invalid'],
6767
6804
  description: 'Configures CRUD-owned route/modal/drawer defaults consumed by the launcher, default dialog-backed drawer runtime and optional host drawer adapter.',
6768
6805
  } }],
6769
- validators: ['open-mode-supported', 'modal-size-valid', 'drawer-runtime-available', 'back-policy-valid', 'settings-panel-shell-compatible'],
6806
+ validators: ['open-mode-supported', 'modal-size-valid', 'drawer-runtime-available', 'drawer-position-lifecycle-stable', 'back-policy-valid', 'settings-panel-shell-compatible'],
6770
6807
  affectedPaths: ['defaults.openMode', 'defaults.modal', 'defaults.back'],
6771
6808
  submissionImpact: 'config-only',
6772
6809
  preconditions: ['crud-metadata-loaded'],
@@ -6843,6 +6880,7 @@ const PRAXIS_CRUD_AUTHORING_MANIFEST = {
6843
6880
  { validatorId: 'open-mode-supported', level: 'error', code: 'CRUD_OPEN_MODE_SUPPORTED', description: 'Open mode must be route, modal or drawer.' },
6844
6881
  { validatorId: 'modal-size-valid', level: 'error', code: 'CRUD_MODAL_SIZE_VALID', description: 'Modal sizing defaults must be valid DialogConfig values.' },
6845
6882
  { validatorId: 'drawer-runtime-available', level: 'error', code: 'CRUD_DRAWER_RUNTIME_AVAILABLE', description: 'Drawer open mode uses the default dialog-backed drawer runtime with CRUD-owned chrome, responsive right-panel sizing, Escape/backdrop handling and semantic close/save results; a host drawer adapter is optional only for shell-specific presentation.' },
6883
+ { validatorId: 'drawer-position-lifecycle-stable', level: 'error', code: 'CRUD_DRAWER_POSITION_LIFECYCLE_STABLE', description: 'A drawer must preserve its configured viewport edge through open, remembered-size restoration, maximize and restore.' },
6846
6884
  { validatorId: 'back-policy-valid', level: 'error', code: 'CRUD_BACK_POLICY_VALID', description: 'Back policy must be valid for route/modal/drawer behavior.' },
6847
6885
  { validatorId: 'settings-panel-shell-compatible', level: 'warning', code: 'CRUD_SETTINGS_PANEL_COMPATIBLE', description: 'Authoring shell must preserve apply/save/reset semantics.' },
6848
6886
  { validatorId: 'action-permission-supported', level: 'error', code: 'CRUD_ACTION_PERMISSION_SUPPORTED', description: 'Action permissions must map to supported resource capabilities.' },
@@ -6863,6 +6901,7 @@ const PRAXIS_CRUD_AUTHORING_MANIFEST = {
6863
6901
  'FormConfig and FieldMetadata edits must delegate to dynamic-form or metadata-editor manifests.',
6864
6902
  'TableConfig edits must delegate to praxis-table; CRUD owns only shell orchestration and list surface binding.',
6865
6903
  'Delete behavior is destructive and requires explicit confirmation plus backend capability support.',
6904
+ 'Drawer position and edgeGap must round-trip without changing the configured edge during open, remembered-size restoration, maximize or restore.',
6866
6905
  'crudContext must remain reference-stable across change detection cycles.',
6867
6906
  ],
6868
6907
  examples: [
@@ -6874,6 +6913,7 @@ const PRAXIS_CRUD_AUTHORING_MANIFEST = {
6874
6913
  { id: 'crud-view-drawer-readonly', request: 'Open view in a drawer and keep the form readonly.', operationId: 'surface.view.configure', target: 'action:view', params: { actionId: 'view', openMode: 'drawer', formId: 'employee-view' }, isPositive: true },
6875
6914
  { id: 'crud-delete-enable-confirmed', request: 'Enable delete but require confirmation.', operationId: 'delete.enabled.set', target: 'action:delete', params: { enabled: true, actionId: 'delete', requiresConfirmation: true, autoDelete: true }, isPositive: true },
6876
6915
  { id: 'crud-dialog-size-medium', request: 'Make CRUD modals 900px wide and remember their last state.', operationId: 'dialog.size.set', target: 'dialogHost', params: { defaultOpenMode: 'modal', modal: { width: '900px', rememberLastState: true } }, isPositive: true },
6916
+ { id: 'crud-drawer-left-edge', request: 'Open CRUD forms in a left drawer with an 8px maximized inset.', operationId: 'dialog.size.set', target: 'dialogHost', params: { defaultOpenMode: 'drawer', modal: { position: { left: '0', top: '0' }, edgeGap: 8 } }, isPositive: true },
6877
6917
  { id: 'crud-permissions-no-delete', request: 'Hide delete when the resource capabilities do not allow it.', operationId: 'permissions.set', target: 'permissions:delete', params: { requiredCapabilities: ['delete'], denyWhenMissingCapability: true }, isPositive: true },
6878
6918
  { id: 'crud-delegate-form-layout', request: 'Add a new field to the create form.', operationId: 'form.childOperation.delegate', target: 'praxis-dynamic-form:field.add', params: { childComponentId: 'praxis-dynamic-form', childOperationId: 'field.add', reason: 'Form field semantics belong to dynamic-form' }, isPositive: true },
6879
6919
  { id: 'crud-delegate-table-column', request: 'Add a salary column to the list table.', operationId: 'form.childOperation.delegate', target: 'praxis-table:column.add', params: { childComponentId: 'praxis-table', childOperationId: 'column.add', reason: 'Table column semantics belong to praxis-table' }, isPositive: true },
package/package.json CHANGED
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@praxisui/crud",
3
- "version": "9.0.0-beta.80",
3
+ "version": "9.0.0-beta.82",
4
4
  "description": "CRUD building blocks for Praxis UI: integrates dynamic forms and tables with unified configuration and services.",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
7
7
  "@angular/core": "^21.0.0",
8
- "@praxisui/dynamic-form": "^9.0.0-beta.80",
9
- "@praxisui/table": "^9.0.0-beta.80",
10
- "@praxisui/core": "^9.0.0-beta.80",
11
- "@praxisui/dynamic-fields": "^9.0.0-beta.80",
12
- "@praxisui/settings-panel": "^9.0.0-beta.80",
8
+ "@praxisui/dynamic-form": "^9.0.0-beta.82",
9
+ "@praxisui/table": "^9.0.0-beta.82",
10
+ "@praxisui/core": "^9.0.0-beta.82",
11
+ "@praxisui/dynamic-fields": "^9.0.0-beta.82",
12
+ "@praxisui/settings-panel": "^9.0.0-beta.82",
13
13
  "@angular/cdk": "^21.0.0",
14
14
  "@angular/forms": "^21.0.0",
15
15
  "@angular/material": "^21.0.0",
16
16
  "@angular/router": "^21.0.0",
17
- "@praxisui/ai": "^9.0.0-beta.80",
17
+ "@praxisui/ai": "^9.0.0-beta.82",
18
18
  "rxjs": "~7.8.0"
19
19
  },
20
20
  "dependencies": {
@@ -20,14 +20,20 @@ interface DialogConfig<D = any> extends MatDialogConfig<D> {
20
20
  disableCloseOnEsc?: boolean;
21
21
  /** Density hint consumed by CRUD dialog host chrome */
22
22
  density?: 'default' | 'compact';
23
+ /** Praxis backdrop visual preset */
24
+ backdropStyle?: 'blur' | 'dim' | 'transparent';
23
25
  /** Enable maximize button */
24
26
  canMaximize?: boolean;
25
27
  /** Persist last dialog size/state for the same form */
26
28
  rememberLastState?: boolean;
29
+ /** Keep the dialog open after a successful form submission */
30
+ closeOnSave?: boolean;
27
31
  /** Start dialog maximized */
28
32
  startMaximized?: boolean;
29
33
  /** Screen width in px where dialog starts fullscreen */
30
34
  fullscreenBreakpoint?: number;
35
+ /** Viewport inset in px applied while the dialog or drawer is maximized */
36
+ edgeGap?: number;
31
37
  }
32
38
 
33
39
  declare class DialogService {
@@ -131,7 +137,10 @@ interface CrudValidationContext {
131
137
 
132
138
  type CrudActionRuntimeEvent = {
133
139
  action?: string;
134
- actionConfig?: ResourceActionCatalogItem | ResourceSurfaceCatalogItem | null;
140
+ actionConfig?: ToolbarAction | ResourceActionCatalogItem | ResourceSurfaceCatalogItem | null;
141
+ row?: Record<string, unknown>;
142
+ selectedRow?: Record<string, unknown>;
143
+ selectedRows?: Record<string, unknown>[];
135
144
  rows?: Record<string, unknown>[];
136
145
  resourceIdentity?: MaterializedResourceIdentity | null;
137
146
  };
@@ -227,6 +236,7 @@ declare class PraxisCrudComponent implements OnChanges {
227
236
  onTableRowClick(event: unknown): void;
228
237
  onTableSelectionChange(event: unknown): void;
229
238
  onBulkAction(event: CrudActionRuntimeEvent): Promise<void>;
239
+ onToolbarAction(event: CrudActionRuntimeEvent): Promise<void>;
230
240
  ngOnChanges(changes: SimpleChanges): void;
231
241
  onAction(action: string, row?: Record<string, unknown>, runtimeEvent?: CrudActionRuntimeEvent): Promise<void>;
232
242
  private hasExplicitOpenBinding;
@@ -261,6 +271,8 @@ declare class PraxisCrudComponent implements OnChanges {
261
271
  private extractRowFromEvent;
262
272
  private extractSelectedRowFromEvent;
263
273
  private extractSelectedRowFromBulkAction;
274
+ private extractSelectedRowFromToolbarAction;
275
+ private extractDiscoveryActionConfig;
264
276
  private resolveSelectedRowForAction;
265
277
  private tryOpenDiscoveredCrudSurface;
266
278
  private resolveDiscoveredSurfaceCatalog;