@praxisui/page-builder 10.0.0-rc.27 → 10.0.0-rc.29

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.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-10-15T22:42:56.841Z",
3
+ "generatedAt": "2026-10-16T14:27:38.517Z",
4
4
  "packageName": "@praxisui/page-builder",
5
- "packageVersion": "10.0.0-rc.27",
5
+ "packageVersion": "10.0.0-rc.29",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 1,
@@ -7,7 +7,7 @@ import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
7
7
  import * as i2$1 from '@angular/material/icon';
8
8
  import { MatIconModule } from '@angular/material/icon';
9
9
  import * as i2$2 from '@praxisui/core';
10
- import { PraxisIconDirective, providePraxisI18n, BUILTIN_WIDGET_SHELL_PRESETS, SETTINGS_EDITOR_SECTIONS_HOSTED, PraxisI18nService, getWidgetShellActionIdError, WidgetShellComponent, SettingsSectionTabsComponent, PraxisIconButtonComponent, GLOBAL_ACTION_CATALOG, ComponentMetadataRegistry, CompositionValidatorService, getGlobalActionCatalog, PRAXIS_GLOBAL_ACTION_CATALOG, SurfaceOpenActionEditorComponent, BUILTIN_PAGE_LAYOUT_PRESETS, resolveWidgetPageLayoutPreset, BUILTIN_PAGE_THEME_PRESETS, prepareCanvasArrangement, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, WidgetPageDataSourceRuntimeService, WidgetPageStateRuntimeService, validateCanvasArrangement, PraxisJsonLogicService, NestedWidgetConfigAccessor, canonicalJsonSha256, ResourceDiscoveryService, ComponentRuntimeProfileService, buildApiUrl, deepMerge, generateId, ObservabilityDashboardService, PraxisRuntimeComponentObservationRegistryService, PraxisCustomizationModeService, domainKnowledgeTimelineToRichContentDocument, DomainRuleService, DomainKnowledgeService, SETTINGS_PANEL_BRIDGE, DynamicWidgetPageComponent, DYNAMIC_PAGE_SHELL_EDITOR } from '@praxisui/core';
10
+ import { PraxisIconDirective, providePraxisI18n, BUILTIN_WIDGET_SHELL_PRESETS, SETTINGS_EDITOR_SECTIONS_HOSTED, PraxisI18nService, getWidgetShellActionIdError, WidgetShellComponent, SettingsSectionTabsComponent, PraxisIconButtonComponent, NestedWidgetConfigAccessor, GLOBAL_ACTION_CATALOG, ComponentMetadataRegistry, CompositionValidatorService, getGlobalActionCatalog, PRAXIS_GLOBAL_ACTION_CATALOG, SurfaceOpenActionEditorComponent, BUILTIN_PAGE_LAYOUT_PRESETS, resolveWidgetPageLayoutPreset, BUILTIN_PAGE_THEME_PRESETS, prepareCanvasArrangement, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, WidgetPageDataSourceRuntimeService, WidgetPageStateRuntimeService, validateCanvasArrangement, PraxisJsonLogicService, canonicalJsonSha256, ResourceDiscoveryService, ComponentRuntimeProfileService, buildApiUrl, deepMerge, generateId, ObservabilityDashboardService, PraxisRuntimeComponentObservationRegistryService, PraxisCustomizationModeService, domainKnowledgeTimelineToRichContentDocument, DomainRuleService, DomainKnowledgeService, SETTINGS_PANEL_BRIDGE, DynamicWidgetPageComponent, DYNAMIC_PAGE_SHELL_EDITOR } from '@praxisui/core';
11
11
  import * as i3 from '@angular/material/tooltip';
12
12
  import { MatTooltipModule } from '@angular/material/tooltip';
13
13
  import * as i4 from '@angular/material/form-field';
@@ -5489,6 +5489,7 @@ function clone$3(value) {
5489
5489
  return JSON.parse(JSON.stringify(value));
5490
5490
  }
5491
5491
 
5492
+ const nestedWidgetAccessor = new NestedWidgetConfigAccessor();
5492
5493
  const CONNECTION_EDITOR_STATE_NODE_ID = '__page_state__';
5493
5494
  const CONNECTION_EDITOR_GLOBAL_ACTION_NODE_ID = '__global_actions__';
5494
5495
  function deriveConnectionEditorGraph(page, options = {}) {
@@ -5529,11 +5530,27 @@ function deriveConnectionEditorGraph(page, options = {}) {
5529
5530
  const nestedPrefix = nested.definition.childWidgetKey
5530
5531
  ?? nested.path.find((segment) => segment.kind === 'widget')?.key
5531
5532
  ?? 'nested';
5532
- for (const port of Object.keys(nested.definition.outputs ?? {})) {
5533
- outputPorts.push(componentPort(widgetId, port, 'output', nestedComponentType, nested.path, `${nestedPrefix}.${port}`));
5534
- }
5535
- for (const port of Object.keys(nested.definition.inputs ?? {})) {
5536
- inputPorts.push(componentPort(widgetId, port, 'input', nestedComponentType, nested.path, `${nestedPrefix}.${port}`));
5533
+ const nestedMetadata = nestedComponentType ? options.componentMetadata?.(nestedComponentType) : undefined;
5534
+ const nestedContracts = (nestedMetadata?.ports ?? []).filter((port) => port.exposure?.public !== false && !port.exposure?.deprecated);
5535
+ const nestedOutputs = uniqueStrings([
5536
+ ...nestedContracts.filter((port) => port.direction === 'output').map((port) => port.id),
5537
+ ...(nestedContracts.length ? [] : nestedMetadata?.outputs ?? []).map((output) => output.name),
5538
+ ...Object.keys(nested.definition.outputs ?? {}),
5539
+ ]);
5540
+ const nestedInputs = uniqueStrings([
5541
+ ...nestedContracts.filter((port) => port.direction === 'input').map((port) => port.id),
5542
+ ...(nestedContracts.length ? [] : nestedMetadata?.inputs ?? []).map((input) => input.name),
5543
+ ...Object.keys(nested.definition.inputs ?? {}),
5544
+ ]);
5545
+ for (const port of nestedOutputs) {
5546
+ const created = componentPort(widgetId, port, 'output', nestedComponentType, nested.path, `${nestedPrefix}.${port}`);
5547
+ created.contract = nestedContracts.find((contract) => contract.id === port && contract.direction === 'output');
5548
+ outputPorts.push(created);
5549
+ }
5550
+ for (const port of nestedInputs) {
5551
+ const created = componentPort(widgetId, port, 'input', nestedComponentType, nested.path, `${nestedPrefix}.${port}`);
5552
+ created.contract = nestedContracts.find((contract) => contract.id === port && contract.direction === 'input');
5553
+ inputPorts.push(created);
5537
5554
  }
5538
5555
  }
5539
5556
  return {
@@ -5548,7 +5565,15 @@ function deriveConnectionEditorGraph(page, options = {}) {
5548
5565
  states: [],
5549
5566
  };
5550
5567
  });
5551
- const statePaths = collectStatePaths(resolveStateValues(page?.state));
5568
+ const statePaths = [];
5569
+ for (const candidate of [
5570
+ ...collectStatePaths(resolveStateValues(page?.state)),
5571
+ ...collectSchemaStatePaths(page?.state),
5572
+ ]) {
5573
+ if (candidate && !statePaths.includes(candidate)) {
5574
+ statePaths.push(candidate);
5575
+ }
5576
+ }
5552
5577
  const derivedStatePaths = Object.keys(page?.state?.derived ?? {})
5553
5578
  .map((path) => path.trim())
5554
5579
  .filter(Boolean);
@@ -5593,6 +5618,7 @@ function deriveConnectionEditorGraph(page, options = {}) {
5593
5618
  }
5594
5619
  }
5595
5620
  }
5621
+ const openSchemaObjectPaths = collectOpenSchemaObjectPaths(page?.state, declaredStatePaths);
5596
5622
  const valuePorts = (statePaths.length || derivedStatePaths.length ? statePaths : [fallbackStatePath]).map((path) => statePort(path));
5597
5623
  const derivedPorts = derivedStatePaths.map((path) => statePort(path, { path, layer: 'derived', writable: false }));
5598
5624
  const stateNode = {
@@ -5650,8 +5676,8 @@ function deriveConnectionEditorGraph(page, options = {}) {
5650
5676
  const fromPort = connectionPort(link.from, portByEndpointKey);
5651
5677
  const toPort = connectionPort(link.to, portByEndpointKey);
5652
5678
  const diagnostics = [
5653
- ...diagnosticsForEndpoint(link.from, 'from', endpointWasDeclared.get(`${link.id}:from`) ?? false, fromPort, allNodes, declaredStatePaths),
5654
- ...diagnosticsForEndpoint(link.to, 'to', endpointWasDeclared.get(`${link.id}:to`) ?? false, toPort, allNodes, declaredStatePaths),
5679
+ ...diagnosticsForEndpoint(link.from, 'from', endpointWasDeclared.get(`${link.id}:from`) ?? false, fromPort, allNodes, declaredStatePaths, openSchemaObjectPaths),
5680
+ ...diagnosticsForEndpoint(link.to, 'to', endpointWasDeclared.get(`${link.id}:to`) ?? false, toPort, allNodes, declaredStatePaths, openSchemaObjectPaths),
5655
5681
  ];
5656
5682
  connectionToLink.set(link.id, link);
5657
5683
  return {
@@ -5837,7 +5863,7 @@ function inputPortPriority(port) {
5837
5863
  return 100;
5838
5864
  if (normalized === 'filtercriteria')
5839
5865
  return 96;
5840
- if (normalized === 'resourceid')
5866
+ if (normalized === 'resourceid' || normalized === 'parentresourceid')
5841
5867
  return 92;
5842
5868
  if (normalized === 'context')
5843
5869
  return 88;
@@ -5863,6 +5889,8 @@ function iconForComponentType(componentType) {
5863
5889
  return 'dynamic_form';
5864
5890
  if (normalized.includes('tabs'))
5865
5891
  return 'dashboard_customize';
5892
+ if (normalized.includes('related-resource-outlet'))
5893
+ return 'hub';
5866
5894
  if (normalized.includes('rich') || normalized.includes('text') || normalized.includes('document'))
5867
5895
  return 'article';
5868
5896
  if (normalized.includes('source'))
@@ -5982,9 +6010,9 @@ function isTableRowSelectionToFormResourceId(source, target) {
5982
6010
  const sourcePort = source.endpoint.ref.port.toLowerCase();
5983
6011
  const targetPort = target.endpoint.ref.port.toLowerCase();
5984
6012
  return sourceComponent === 'praxis-table'
5985
- && targetComponent === 'praxis-dynamic-form'
5986
- && ['rowclick', 'selectionchange'].includes(sourcePort)
5987
- && targetPort === 'resourceid';
6013
+ && ((targetComponent === 'praxis-dynamic-form' && targetPort === 'resourceid')
6014
+ || (targetComponent === 'praxis-related-resource-outlet' && targetPort === 'parentresourceid'))
6015
+ && ['rowclick', 'selectionchange'].includes(sourcePort);
5988
6016
  }
5989
6017
  function deriveConnectionEditorSuggestions(graph, limit = 5) {
5990
6018
  return buildConnectionEditorSuggestions(graph).slice(0, limit);
@@ -6174,12 +6202,13 @@ function endpointKey(endpoint) {
6174
6202
  : '';
6175
6203
  return `component:${endpoint.ref.widget}:${nested}:${endpoint.ref.direction}:${endpoint.ref.port}`;
6176
6204
  }
6177
- function diagnosticsForEndpoint(endpoint, side, wasDeclared, resolvedPort, nodes, declaredStatePaths) {
6205
+ function diagnosticsForEndpoint(endpoint, side, wasDeclared, resolvedPort, nodes, declaredStatePaths, openSchemaObjectPaths = new Set()) {
6178
6206
  const diagnostics = [];
6179
6207
  if (endpoint.kind === 'state') {
6180
6208
  const layer = endpoint.ref.layer ?? 'values';
6181
6209
  // Transient paths are declared by their links, not initialized in persisted values.
6182
- if (layer !== 'transient' && !declaredStatePaths.has(`${layer}:${endpoint.ref.path}`)) {
6210
+ if (layer !== 'transient' &&
6211
+ !isStatePathDeclared(layer, endpoint.ref.path, declaredStatePaths, openSchemaObjectPaths)) {
6183
6212
  diagnostics.push({
6184
6213
  code: 'state-path-not-declared',
6185
6214
  severity: 'warning',
@@ -6270,49 +6299,113 @@ function uniquePorts$1(ports) {
6270
6299
  });
6271
6300
  }
6272
6301
  function collectNestedWidgets(widget) {
6273
- const config = readRecord(readRecord(widget.definition?.inputs)?.['config']);
6274
- const tabs = readArray(config?.['tabs']);
6275
- const descriptors = [];
6276
- tabs.forEach((tab, tabIndex) => {
6277
- const tabRecord = readRecord(tab);
6278
- const childWidgets = readArray(tabRecord?.['widgets']);
6279
- childWidgets.forEach((child) => {
6280
- const definition = readRecord(child);
6281
- if (!definition)
6282
- return;
6283
- const childWidgetKey = readString(definition['childWidgetKey']) ?? readString(definition['key']);
6284
- const componentType = readString(definition['id']) ?? readString(definition['componentType']);
6285
- descriptors.push({
6286
- path: [
6287
- { kind: 'tab', id: readString(tabRecord?.['id']), index: tabIndex },
6288
- { kind: 'widget', key: childWidgetKey, componentType },
6289
- ],
6290
- definition: {
6291
- id: componentType,
6292
- childWidgetKey,
6293
- inputs: readRecord(definition['inputs']),
6294
- outputs: readRecord(definition['outputs']),
6295
- },
6296
- });
6297
- });
6298
- });
6299
- return descriptors;
6302
+ return nestedWidgetAccessor.listNestedWidgets(widget).map((item) => ({
6303
+ path: item.nestedPath,
6304
+ definition: {
6305
+ id: item.componentId,
6306
+ ...(item.childWidgetKey ? { childWidgetKey: item.childWidgetKey } : {}),
6307
+ inputs: readRecord(item.widget.inputs),
6308
+ outputs: readRecord(item.widget.outputs),
6309
+ },
6310
+ }));
6311
+ }
6312
+ function isStructuredPageState(state) {
6313
+ if (!state || typeof state !== 'object' || Array.isArray(state))
6314
+ return false;
6315
+ return 'values' in state || 'schema' in state || 'derived' in state || 'persistence' in state;
6300
6316
  }
6301
6317
  function resolveStateValues(state) {
6302
6318
  if (!state || typeof state !== 'object' || Array.isArray(state))
6303
6319
  return undefined;
6304
- if ('values' in state) {
6320
+ if (isStructuredPageState(state)) {
6305
6321
  return readRecord(state.values);
6306
6322
  }
6307
6323
  return state;
6308
6324
  }
6325
+ function collectSchemaStatePaths(state) {
6326
+ if (!state || typeof state !== 'object' || Array.isArray(state) || !('schema' in state)) {
6327
+ return [];
6328
+ }
6329
+ const schemaRecord = readRecord(state.schema);
6330
+ if (!schemaRecord)
6331
+ return [];
6332
+ const paths = [];
6333
+ const pushUnique = (candidate) => {
6334
+ const trimmed = candidate.trim();
6335
+ if (trimmed && !paths.includes(trimmed)) {
6336
+ paths.push(trimmed);
6337
+ }
6338
+ };
6339
+ for (const [rawKey, rawDescriptor] of Object.entries(schemaRecord)) {
6340
+ const key = rawKey.trim();
6341
+ if (!key)
6342
+ continue;
6343
+ pushUnique(key);
6344
+ const descriptor = readRecord(rawDescriptor);
6345
+ const initial = descriptor?.['initial'];
6346
+ if (initial &&
6347
+ typeof initial === 'object' &&
6348
+ !Array.isArray(initial) &&
6349
+ Object.keys(initial).length > 0) {
6350
+ for (const nestedPath of collectStatePaths(initial, key)) {
6351
+ pushUnique(nestedPath);
6352
+ }
6353
+ }
6354
+ }
6355
+ return paths;
6356
+ }
6357
+ function collectOpenSchemaObjectPaths(state, declaredStatePaths) {
6358
+ const openRoots = new Set();
6359
+ if (!state || typeof state !== 'object' || Array.isArray(state) || !('schema' in state)) {
6360
+ return openRoots;
6361
+ }
6362
+ const schemaRecord = readRecord(state.schema);
6363
+ if (!schemaRecord)
6364
+ return openRoots;
6365
+ for (const [rawKey, rawDescriptor] of Object.entries(schemaRecord)) {
6366
+ const key = rawKey.trim();
6367
+ if (!key)
6368
+ continue;
6369
+ const descriptor = readRecord(rawDescriptor);
6370
+ const declaredType = readString(descriptor?.['type'])?.toLowerCase();
6371
+ const initial = descriptor?.['initial'];
6372
+ const isEmptyPlainObjectInitial = Boolean(initial && typeof initial === 'object' && !Array.isArray(initial)) &&
6373
+ Object.keys(initial).length === 0;
6374
+ const isObjectSchema = declaredType === 'object' || isEmptyPlainObjectInitial;
6375
+ const hasExplicitLeafChildren = [...declaredStatePaths].some((entry) => entry.startsWith(`values:${key}.`));
6376
+ if (isObjectSchema && !hasExplicitLeafChildren) {
6377
+ openRoots.add(key);
6378
+ }
6379
+ }
6380
+ return openRoots;
6381
+ }
6382
+ function isStatePathDeclared(layer, path, declaredStatePaths, openSchemaObjectPaths) {
6383
+ if (declaredStatePaths.has(`${layer}:${path}`)) {
6384
+ return true;
6385
+ }
6386
+ const childPrefix = `${layer}:${path}.`;
6387
+ if ([...declaredStatePaths].some((entry) => entry.startsWith(childPrefix))) {
6388
+ return true;
6389
+ }
6390
+ if (layer === 'values') {
6391
+ for (const openRoot of openSchemaObjectPaths) {
6392
+ if (path.startsWith(`${openRoot}.`)) {
6393
+ return true;
6394
+ }
6395
+ }
6396
+ }
6397
+ return false;
6398
+ }
6309
6399
  function collectStatePaths(values, prefix = '') {
6310
6400
  if (!values || typeof values !== 'object' || Array.isArray(values))
6311
6401
  return [];
6312
6402
  const paths = [];
6313
6403
  for (const [key, value] of Object.entries(values)) {
6314
6404
  const path = prefix ? `${prefix}.${key}` : key;
6315
- if (value && typeof value === 'object' && !Array.isArray(value)) {
6405
+ if (value &&
6406
+ typeof value === 'object' &&
6407
+ !Array.isArray(value) &&
6408
+ Object.keys(value).length > 0) {
6316
6409
  paths.push(...collectStatePaths(value, path));
6317
6410
  }
6318
6411
  else {
@@ -6341,9 +6434,6 @@ function readRecord(value) {
6341
6434
  ? value
6342
6435
  : undefined;
6343
6436
  }
6344
- function readArray(value) {
6345
- return Array.isArray(value) ? value : [];
6346
- }
6347
6437
  function readString(value) {
6348
6438
  return typeof value === 'string' && value.trim() ? value : undefined;
6349
6439
  }
@@ -9083,13 +9173,23 @@ class ConnectionEditorComponent {
9083
9173
  if (port.endpoint.kind === 'state') {
9084
9174
  return this.authoredStateName(port) || port.label;
9085
9175
  }
9086
- return this.editorialPort(port)?.label || port.contract?.label || port.label;
9176
+ const baseLabel = this.editorialPort(port)?.label || port.contract?.label;
9177
+ if (!baseLabel)
9178
+ return port.label;
9179
+ if (port.nestedPath?.length) {
9180
+ const terminal = [...port.nestedPath].reverse().find((segment) => segment.kind === 'widget');
9181
+ const prefix = terminal?.key?.trim();
9182
+ if (prefix && !baseLabel.startsWith(`${prefix}.`) && !baseLabel.startsWith(`${prefix} ·`)) {
9183
+ return `${prefix} · ${baseLabel}`;
9184
+ }
9185
+ }
9186
+ return baseLabel;
9087
9187
  }
9088
9188
  authoredStateName(port) {
9089
9189
  if (port.endpoint.kind !== 'state')
9090
9190
  return '';
9091
9191
  const { path, layer = 'values' } = port.endpoint.ref;
9092
- const state = this.page()?.state;
9192
+ const state = this.workingPage()?.state;
9093
9193
  if (layer === 'derived')
9094
9194
  return state?.derived?.[path]?.description ?? '';
9095
9195
  if (layer === 'values')
@@ -9101,7 +9201,7 @@ class ConnectionEditorComponent {
9101
9201
  if (port?.endpoint.kind !== 'state')
9102
9202
  return [];
9103
9203
  const { path, layer = 'values' } = port.endpoint.ref;
9104
- return layer === 'values' || (layer === 'derived' && this.page()?.state?.derived?.[path]) ? [port] : [];
9204
+ return layer === 'values' || (layer === 'derived' && this.workingPage()?.state?.derived?.[path]) ? [port] : [];
9105
9205
  }
9106
9206
  applyStateName(port, name) {
9107
9207
  const current = this.page();
@@ -9177,27 +9277,28 @@ class ConnectionEditorComponent {
9177
9277
  return false;
9178
9278
  if (source.kind !== 'output' || target.kind !== 'input')
9179
9279
  return false;
9180
- if (target.label !== 'resourceId')
9280
+ const sourcePort = source.endpoint.kind === 'component-port' ? source.endpoint.ref.port : source.label;
9281
+ const targetPort = target.endpoint.kind === 'component-port' ? target.endpoint.ref.port : target.label;
9282
+ if (!['rowClick', 'selectionChange'].includes(sourcePort))
9181
9283
  return false;
9182
- if (!['rowClick', 'selectionChange'].includes(source.label))
9183
- return false;
9184
- const sourceComponent = source.endpoint.kind === 'component-port'
9185
- ? String(source.endpoint.ref.componentType ?? '').toLowerCase()
9186
- : '';
9187
- const targetComponent = target.endpoint.kind === 'component-port'
9188
- ? String(target.endpoint.ref.componentType ?? '').toLowerCase()
9189
- : '';
9190
- return sourceComponent === 'praxis-table' && targetComponent === 'praxis-dynamic-form';
9284
+ const sourceComponent = this.portComponentType(source);
9285
+ const targetComponent = this.portComponentType(target);
9286
+ return (sourceComponent === 'praxis-table' &&
9287
+ ((targetComponent === 'praxis-dynamic-form' && targetPort === 'resourceId') ||
9288
+ (targetComponent === 'praxis-related-resource-outlet' && targetPort === 'parentResourceId')));
9191
9289
  }
9192
9290
  isTableSelectionOutput(port) {
9291
+ const portName = port.endpoint.kind === 'component-port' ? port.endpoint.ref.port : port.label;
9193
9292
  return port.kind === 'output'
9194
- && ['rowClick', 'selectionChange'].includes(port.label)
9293
+ && ['rowClick', 'selectionChange'].includes(portName)
9195
9294
  && this.portComponentType(port) === 'praxis-table';
9196
9295
  }
9197
9296
  isFormResourceIdInput(port) {
9297
+ const portName = port.endpoint.kind === 'component-port' ? port.endpoint.ref.port : port.label;
9298
+ const componentType = this.portComponentType(port);
9198
9299
  return port.kind === 'input'
9199
- && port.label === 'resourceId'
9200
- && this.portComponentType(port) === 'praxis-dynamic-form';
9300
+ && ((componentType === 'praxis-dynamic-form' && portName === 'resourceId')
9301
+ || (componentType === 'praxis-related-resource-outlet' && portName === 'parentResourceId'));
9201
9302
  }
9202
9303
  portComponentType(port) {
9203
9304
  return port.endpoint.kind === 'component-port'
@@ -9686,11 +9787,42 @@ class ConnectionEditorComponent {
9686
9787
  if (source.endpoint.kind !== 'component-port')
9687
9788
  return;
9688
9789
  const ref = source.endpoint.ref;
9689
- if (ref.direction !== 'output' || ref.nestedPath?.length)
9790
+ if (ref.direction !== 'output')
9690
9791
  return;
9691
9792
  const widget = page.widgets?.find((candidate) => candidate.key === ref.widget);
9692
9793
  if (!widget)
9693
9794
  return;
9795
+ if (ref.nestedPath?.length) {
9796
+ const tabSegment = ref.nestedPath.find((segment) => segment.kind === 'tab');
9797
+ const widgetSegment = [...ref.nestedPath].reverse().find((segment) => segment.kind === 'widget');
9798
+ const inputs = widget.definition?.inputs;
9799
+ const config = inputs?.['config'];
9800
+ const tabs = Array.isArray(config?.['tabs']) ? config['tabs'] : null;
9801
+ if (!tabs || !widgetSegment)
9802
+ return;
9803
+ const targetTab = tabs.find((tab, index) => (tabSegment?.id && tab?.['id'] === tabSegment.id) ||
9804
+ (typeof tabSegment?.index === 'number' && index === tabSegment.index));
9805
+ const childWidgets = Array.isArray(targetTab?.['widgets'])
9806
+ ? targetTab['widgets']
9807
+ : null;
9808
+ if (!childWidgets)
9809
+ return;
9810
+ const targetChild = childWidgets.find((child, index) => (widgetSegment.key &&
9811
+ (child?.['childWidgetKey'] === widgetSegment.key || child?.['key'] === widgetSegment.key)) ||
9812
+ (typeof widgetSegment.index === 'number' && index === widgetSegment.index));
9813
+ if (!targetChild)
9814
+ return;
9815
+ const currentNestedOutputs = targetChild['outputs'] && typeof targetChild['outputs'] === 'object' && !Array.isArray(targetChild['outputs'])
9816
+ ? targetChild['outputs']
9817
+ : {};
9818
+ if (Object.prototype.hasOwnProperty.call(currentNestedOutputs, ref.port))
9819
+ return;
9820
+ targetChild['outputs'] = {
9821
+ ...currentNestedOutputs,
9822
+ [ref.port]: 'emit',
9823
+ };
9824
+ return;
9825
+ }
9694
9826
  const currentOutputs = widget.definition.outputs ?? {};
9695
9827
  if (Object.prototype.hasOwnProperty.call(currentOutputs, ref.port))
9696
9828
  return;
@@ -14777,6 +14909,14 @@ class DynamicPageConfigEditorComponent {
14777
14909
  tokens.add(token);
14778
14910
  }
14779
14911
  }
14912
+ if (this.isPlainObject(state['schema'])) {
14913
+ for (const [key, descriptor] of Object.entries(state['schema'])) {
14914
+ const initialVal = this.isPlainObject(descriptor) ? descriptor['initial'] : undefined;
14915
+ for (const token of this.collectStateTokensForValue(key, initialVal)) {
14916
+ tokens.add(token);
14917
+ }
14918
+ }
14919
+ }
14780
14920
  if (this.isPlainObject(state['derived'])) {
14781
14921
  for (const key of Object.keys(state['derived'])) {
14782
14922
  tokens.add(`{{state.derived.${key}}}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praxisui/page-builder",
3
- "version": "10.0.0-rc.27",
3
+ "version": "10.0.0-rc.29",
4
4
  "description": "Page and widget builder utilities for Praxis UI (grid, dynamic widgets, editors).",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
@@ -8,9 +8,9 @@
8
8
  "@angular/forms": "^21.0.0",
9
9
  "@angular/cdk": "^21.0.0",
10
10
  "@angular/material": "^21.0.0",
11
- "@praxisui/ai": "^10.0.0-rc.27",
12
- "@praxisui/core": "^10.0.0-rc.27",
13
- "@praxisui/settings-panel": "^10.0.0-rc.27",
11
+ "@praxisui/ai": "^10.0.0-rc.29",
12
+ "@praxisui/core": "^10.0.0-rc.29",
13
+ "@praxisui/settings-panel": "^10.0.0-rc.29",
14
14
  "rxjs": "~7.8.0"
15
15
  },
16
16
  "dependencies": {