@masterteam/flowplus-workflow 0.0.2 → 0.0.3

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.
@@ -27,7 +27,7 @@ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
27
27
  import { DateField } from '@masterteam/components/date-field';
28
28
  import { ToastService } from '@masterteam/components/toast';
29
29
  import * as i1$2 from '@foblex/flow';
30
- import { FExternalItem, FExternalItemPreview, FFlowModule, EFMarkerType, provideFFlow, withReflowOnResize, EFReflowDeltaSource, EFReflowCollision } from '@foblex/flow';
30
+ import { FExternalItem, FExternalItemPreview, EFResizeHandleType, FFlowModule, EFMarkerType, EFCanvasLayer, provideFFlow, withReflowOnResize, EFReflowDeltaSource, EFReflowCollision } from '@foblex/flow';
31
31
  import { Avatar } from '@masterteam/components/avatar';
32
32
  import { RadioCardsField } from '@masterteam/components/radio-cards-field';
33
33
  import { DynamicForm } from '@masterteam/forms/dynamic-form';
@@ -1244,16 +1244,16 @@ function defaultApprovalDecisionValues(routeOutputKeys) {
1244
1244
  function dynamicRouteOutputKeysForConfig$2(nodeType, config) {
1245
1245
  if (nodeType === 'ParallelStart') {
1246
1246
  return uniqueStrings$2(readArrayRecords$3(config['branches'])
1247
- .map((branch) => readString$d(branch, 'key'))
1247
+ .map((branch) => readString$e(branch, 'key'))
1248
1248
  .filter((key) => !!key?.trim()));
1249
1249
  }
1250
1250
  if (nodeType === 'Switch') {
1251
1251
  const caseKeys = readArrayRecords$3(config['cases'])
1252
- .map((item) => readString$d(item, 'key'))
1252
+ .map((item) => readString$e(item, 'key'))
1253
1253
  .filter((key) => !!key?.trim())
1254
1254
  .map((key) => switchCaseRouteKey$3(key));
1255
- const defaultKey = readString$d(config, 'defaultOutputKey') ??
1256
- readString$d(config, 'defaultCaseKey') ??
1255
+ const defaultKey = readString$e(config, 'defaultOutputKey') ??
1256
+ readString$e(config, 'defaultCaseKey') ??
1257
1257
  'default';
1258
1258
  return uniqueStrings$2([
1259
1259
  ...caseKeys,
@@ -1397,7 +1397,7 @@ function readRecord$3(value) {
1397
1397
  ? value
1398
1398
  : null;
1399
1399
  }
1400
- function readString$d(record, key) {
1400
+ function readString$e(record, key) {
1401
1401
  const value = record?.[key];
1402
1402
  return typeof value === 'string' ? value : undefined;
1403
1403
  }
@@ -1593,19 +1593,19 @@ function automationBuilderCatalogToWorkflowCatalog(catalog) {
1593
1593
  const triggerTypes = (catalog.triggerTypes ?? []).map((item) => {
1594
1594
  const raw = asRecord$7(item);
1595
1595
  const type = catalogTriggerType$2(item);
1596
- const key = readString$c(raw, 'key') ?? type;
1596
+ const key = readString$d(raw, 'key') ?? type;
1597
1597
  return {
1598
1598
  key,
1599
1599
  value: type,
1600
1600
  label: primaryText(item.displayName) ?? type,
1601
1601
  displayName: item.displayName ?? type,
1602
1602
  description: item.description ?? null,
1603
- icon: readString$c(raw, 'icon') ?? readString$c(raw, 'iconKey') ?? null,
1603
+ icon: readString$d(raw, 'icon') ?? readString$d(raw, 'iconKey') ?? null,
1604
1604
  metadata: {
1605
1605
  automationTriggerType: true,
1606
1606
  type,
1607
1607
  triggerType: type,
1608
- category: readString$c(raw, 'category') ?? null,
1608
+ category: readString$d(raw, 'category') ?? null,
1609
1609
  configSchema: item.configSchema ?? null,
1610
1610
  authPolicySchema: item.authPolicySchema ?? null,
1611
1611
  payloadSchema: item.payloadSchema ?? null,
@@ -1776,8 +1776,8 @@ function workflowMetadataRequestToAutomationMetadata(request) {
1776
1776
  name: primaryText(request.name) ?? 'Untitled automation',
1777
1777
  };
1778
1778
  const description = primaryText(request.description);
1779
- const ownerId = readString$c(metadata, 'ownerId');
1780
- const projectId = readString$c(metadata, 'projectId');
1779
+ const ownerId = readString$d(metadata, 'ownerId');
1780
+ const projectId = readString$d(metadata, 'projectId');
1781
1781
  if (description)
1782
1782
  payload.description = description;
1783
1783
  if (ownerId)
@@ -1793,17 +1793,17 @@ function workflowStepToAutomationNodeRequest(step, existing) {
1793
1793
  };
1794
1794
  const key = step.key ??
1795
1795
  existing?.key ??
1796
- readString$c(metadata, 'nodeKey') ??
1796
+ readString$d(metadata, 'nodeKey') ??
1797
1797
  keyFromText(step.name ?? existing?.name ?? 'node');
1798
1798
  const type = String(step.type ?? existing?.type ?? 'SetFields');
1799
1799
  const mergedConfig = {
1800
1800
  ...readConfigFromStep(existing),
1801
1801
  ...readConfigFromStep(step),
1802
1802
  };
1803
- const configJson = readString$c(metadata, 'configJson') ?? stringifyJson$1(mergedConfig) ?? '{}';
1803
+ const configJson = readString$d(metadata, 'configJson') ?? stringifyJson$1(mergedConfig) ?? '{}';
1804
1804
  const positionJson = layoutToJson(step.layout) ??
1805
1805
  layoutToJson(existing?.layout) ??
1806
- readString$c(metadata, 'positionJson') ??
1806
+ readString$d(metadata, 'positionJson') ??
1807
1807
  stringifyJson$1(readRecord$2(metadata, 'position')) ??
1808
1808
  '{}';
1809
1809
  const nodeType = String(step.type ?? existing?.type ?? 'SetFields');
@@ -1817,7 +1817,7 @@ function workflowStepToAutomationNodeRequest(step, existing) {
1817
1817
  timeoutPolicyJson: jsonField(metadata, 'timeoutPolicyJson', '{}'),
1818
1818
  retryPolicyJson: jsonField(metadata, 'retryPolicyJson', '{}'),
1819
1819
  errorPolicyJson: jsonField(metadata, 'errorPolicyJson', '{}'),
1820
- sideEffectPolicy: readString$c(metadata, 'sideEffectPolicy') ??
1820
+ sideEffectPolicy: readString$d(metadata, 'sideEffectPolicy') ??
1821
1821
  defaultSideEffectPolicyForNodeType(nodeType),
1822
1822
  };
1823
1823
  }
@@ -1850,7 +1850,7 @@ function workflowTriggerToAutomationTriggerRequest(trigger, existing) {
1850
1850
  ...(asRecord$7(trigger.metadata) ?? {}),
1851
1851
  };
1852
1852
  const type = String(trigger.type ?? existing?.type ?? 'ManualTrigger');
1853
- const key = readString$c(metadata, 'triggerKey') ??
1853
+ const key = readString$d(metadata, 'triggerKey') ??
1854
1854
  trigger.webhookKey ??
1855
1855
  existing?.webhookKey ??
1856
1856
  keyFromText(`${type}_trigger`);
@@ -1860,7 +1860,7 @@ function workflowTriggerToAutomationTriggerRequest(trigger, existing) {
1860
1860
  ...parseJsonObject$4(trigger.configJson),
1861
1861
  ...readRecord$2(metadata, 'config'),
1862
1862
  };
1863
- const startNodeKey = readString$c(metadata, 'startNodeKey') ?? readString$c(config, 'startNodeKey');
1863
+ const startNodeKey = readString$d(metadata, 'startNodeKey') ?? readString$d(config, 'startNodeKey');
1864
1864
  if (startNodeKey)
1865
1865
  config['startNodeKey'] = startNodeKey;
1866
1866
  return {
@@ -1897,37 +1897,37 @@ function workflowConnectionToAutomationRouteRequest(connection, index, existing)
1897
1897
  const targetStepId = readNumber$5(connectionRecord, 'targetStepId') ??
1898
1898
  existing?.targetStepId ??
1899
1899
  0;
1900
- const sourceNodeKey = readString$c(metadata, 'sourceNodeKey') ??
1900
+ const sourceNodeKey = readString$d(metadata, 'sourceNodeKey') ??
1901
1901
  index.nodeKeyByStepId.get(sourceStepId) ??
1902
1902
  existing?.sourceStepKey ??
1903
1903
  '';
1904
- const targetNodeKey = readString$c(metadata, 'targetNodeKey') ??
1904
+ const targetNodeKey = readString$d(metadata, 'targetNodeKey') ??
1905
1905
  index.nodeKeyByStepId.get(targetStepId) ??
1906
1906
  existing?.targetStepKey ??
1907
1907
  '';
1908
- const sourcePortKey = readString$c(connectionRecord, 'sourcePortKey');
1908
+ const sourcePortKey = readString$d(connectionRecord, 'sourcePortKey');
1909
1909
  const sourceOutputChanged = sourcePortKey != null &&
1910
1910
  existing?.sourcePortKey != null &&
1911
1911
  normalizeRouteKey(sourcePortKey) !== normalizeRouteKey(existing.sourcePortKey);
1912
- const sourceOutputKey = readString$c(connectionMetadata, 'sourceOutputKey') ??
1912
+ const sourceOutputKey = readString$d(connectionMetadata, 'sourceOutputKey') ??
1913
1913
  sourcePortKey ??
1914
1914
  existing?.sourcePortKey ??
1915
1915
  readSelectedActionKey(connection) ??
1916
1916
  '';
1917
- const explicitConditionJson = readString$c(connectionMetadata, 'conditionJson') ??
1917
+ const explicitConditionJson = readString$d(connectionMetadata, 'conditionJson') ??
1918
1918
  readExpressionText(connection);
1919
1919
  const preservedConditionJson = sourceOutputChanged
1920
1920
  ? null
1921
- : (readString$c(existingMetadata, 'conditionJson') ??
1921
+ : (readString$d(existingMetadata, 'conditionJson') ??
1922
1922
  existing?.expressionText ??
1923
1923
  null);
1924
1924
  const conditionJson = explicitConditionJson ??
1925
1925
  preservedConditionJson ??
1926
1926
  defaultConditionJsonForRoute(sourceOutputKey);
1927
- const routeType = normalizeAutomationRouteType(readString$c(connectionMetadata, 'routeType') ??
1928
- (sourceOutputChanged ? null : readString$c(existingMetadata, 'routeType')), sourceOutputKey, conditionJson);
1927
+ const routeType = normalizeAutomationRouteType(readString$d(connectionMetadata, 'routeType') ??
1928
+ (sourceOutputChanged ? null : readString$d(existingMetadata, 'routeType')), sourceOutputKey, conditionJson);
1929
1929
  return {
1930
- routeId: readString$c(metadata, 'routeId') ??
1930
+ routeId: readString$d(metadata, 'routeId') ??
1931
1931
  index.routeIdByConnectionId.get(readNumber$5(connectionRecord, 'id') ?? 0) ??
1932
1932
  null,
1933
1933
  sourceNodeKey,
@@ -1942,11 +1942,11 @@ function findWorkflowStepByNodeKey(builder, nodeKey) {
1942
1942
  return builder.steps.find((step) => step.key === nodeKey) ?? null;
1943
1943
  }
1944
1944
  function findWorkflowTriggerByKey(builder, triggerKey) {
1945
- return (builder.triggers.find((trigger) => readString$c(asRecord$7(trigger.metadata), 'triggerKey') === triggerKey) ?? null);
1945
+ return (builder.triggers.find((trigger) => readString$d(asRecord$7(trigger.metadata), 'triggerKey') === triggerKey) ?? null);
1946
1946
  }
1947
1947
  function findWorkflowConnectionForRoute(builder, route, routeId) {
1948
1948
  if (routeId != null) {
1949
- const exact = builder.connections.find((connection) => readString$c(asRecord$7(connection.metadata), 'routeId') ===
1949
+ const exact = builder.connections.find((connection) => readString$d(asRecord$7(connection.metadata), 'routeId') ===
1950
1950
  String(routeId));
1951
1951
  if (exact)
1952
1952
  return exact;
@@ -1958,7 +1958,7 @@ function findWorkflowConnectionForRoute(builder, route, routeId) {
1958
1958
  function automationNodeToWorkflowStep(node, workflowId, index, triggerStartKeys, routeOutputsByType) {
1959
1959
  const raw = asRecord$7(node);
1960
1960
  const nodeKey = node.key || keyFromText(node.name ?? `node_${index}`);
1961
- const nodeType = readString$c(raw, 'type') ?? readString$c(raw, 'nodeType') ?? 'SetFields';
1961
+ const nodeType = readString$d(raw, 'type') ?? readString$d(raw, 'nodeType') ?? 'SetFields';
1962
1962
  const id = readNumber$5(raw, 'nodeId') ??
1963
1963
  readNumber$5(raw, 'id') ??
1964
1964
  toWorkflowEntityId(nodeKey, `node:${workflowId}`);
@@ -2013,11 +2013,11 @@ function automationNodeToWorkflowStep(node, workflowId, index, triggerStartKeys,
2013
2013
  }
2014
2014
  function automationTriggerToWorkflowTrigger(trigger, workflowId, index) {
2015
2015
  const raw = asRecord$7(trigger);
2016
- const triggerType = readString$c(raw, 'type') ??
2017
- readString$c(raw, 'triggerType') ??
2016
+ const triggerType = readString$d(raw, 'type') ??
2017
+ readString$d(raw, 'triggerType') ??
2018
2018
  'ManualTrigger';
2019
2019
  const triggerKey = trigger.key ||
2020
- readString$c(raw, 'key') ||
2020
+ readString$d(raw, 'key') ||
2021
2021
  keyFromText(`${triggerType}_trigger`);
2022
2022
  const id = readNumber$5(raw, 'triggerId') ??
2023
2023
  readNumber$5(raw, 'id') ??
@@ -2040,7 +2040,7 @@ function automationTriggerToWorkflowTrigger(trigger, workflowId, index) {
2040
2040
  name: toTranslatableText$1(trigger.name || triggerKey),
2041
2041
  enabled,
2042
2042
  isEnabled: enabled,
2043
- webhookKey: readString$c(config, 'webhookKey') ?? triggerKey,
2043
+ webhookKey: readString$d(config, 'webhookKey') ?? triggerKey,
2044
2044
  workflowKey: triggerKey,
2045
2045
  triggerId: id,
2046
2046
  payloadSchema: parseJsonValue$2(trigger.schemaJson ?? null),
@@ -2058,7 +2058,7 @@ function automationTriggerToWorkflowTrigger(trigger, workflowId, index) {
2058
2058
  layout,
2059
2059
  schemaJson: trigger.schemaJson ?? null,
2060
2060
  authenticationPolicyJson: trigger.authenticationPolicyJson ?? null,
2061
- startNodeKey: readString$c(config, 'startNodeKey') ?? null,
2061
+ startNodeKey: readString$d(config, 'startNodeKey') ?? null,
2062
2062
  },
2063
2063
  };
2064
2064
  }
@@ -2218,7 +2218,7 @@ function automationDetailToWorkflowLayout(workflowId, steps, connections, trigge
2218
2218
  function automationNodeTypeToWorkflowStepType(item) {
2219
2219
  const raw = asRecord$7(item);
2220
2220
  const type = catalogNodeType$1(item);
2221
- const key = readString$c(raw, 'key') ?? type;
2221
+ const key = readString$d(raw, 'key') ?? type;
2222
2222
  const routeOutputKeys = readStringArray$7(raw, 'routeOutputKeys');
2223
2223
  const supportsRetry = ['HTTP', 'FlowPlusCommit', 'CallAutomation'].includes(type) ||
2224
2224
  !!item.defaultRetryPolicy;
@@ -2244,9 +2244,9 @@ function automationNodeTypeToWorkflowStepType(item) {
2244
2244
  typeKey: type,
2245
2245
  displayName: item.displayName ?? type,
2246
2246
  description: item.description ?? null,
2247
- category: readString$c(raw, 'category') ?? categoryForNodeType(type),
2248
- icon: readString$c(raw, 'icon') ??
2249
- readString$c(raw, 'iconKey') ??
2247
+ category: readString$d(raw, 'category') ?? categoryForNodeType(type),
2248
+ icon: readString$d(raw, 'icon') ??
2249
+ readString$d(raw, 'iconKey') ??
2250
2250
  iconForNodeType(type),
2251
2251
  colorToken: item.colorToken ?? type,
2252
2252
  tags: [type],
@@ -2322,7 +2322,7 @@ function routeOutputKeysByNodeType(catalog) {
2322
2322
  for (const item of catalog?.nodeTypes ?? []) {
2323
2323
  const raw = asRecord$7(item);
2324
2324
  const type = catalogNodeType$1(item);
2325
- const key = readString$c(raw, 'key') ?? type;
2325
+ const key = readString$d(raw, 'key') ?? type;
2326
2326
  const keys = readStringArray$7(raw, 'routeOutputKeys');
2327
2327
  out.set(type, keys);
2328
2328
  out.set(key, keys);
@@ -2349,17 +2349,17 @@ function dynamicRouteOutputKeysForConfig$1(nodeType, config) {
2349
2349
  if (nodeType === 'ParallelStart') {
2350
2350
  const branches = arrayRecords(config['branches']);
2351
2351
  return uniqueStrings$1(branches
2352
- .map((branch) => readString$c(branch, 'key'))
2352
+ .map((branch) => readString$d(branch, 'key'))
2353
2353
  .filter((key) => !!key?.trim()));
2354
2354
  }
2355
2355
  if (nodeType === 'Switch') {
2356
2356
  const cases = arrayRecords(config['cases']);
2357
2357
  const caseKeys = cases
2358
- .map((item) => readString$c(item, 'key'))
2358
+ .map((item) => readString$d(item, 'key'))
2359
2359
  .filter((key) => !!key?.trim())
2360
2360
  .map((key) => switchCaseRouteKey$2(key));
2361
- const defaultKey = readString$c(config, 'defaultOutputKey') ??
2362
- readString$c(config, 'defaultCaseKey') ??
2361
+ const defaultKey = readString$d(config, 'defaultOutputKey') ??
2362
+ readString$d(config, 'defaultCaseKey') ??
2363
2363
  'default';
2364
2364
  return uniqueStrings$1([
2365
2365
  ...caseKeys,
@@ -2400,7 +2400,7 @@ function readConfigFromStep(step) {
2400
2400
  return {};
2401
2401
  const metadata = asRecord$7(step.metadata);
2402
2402
  return stripUndefined({
2403
- ...parseJsonObject$4(readString$c(metadata, 'configJson')),
2403
+ ...parseJsonObject$4(readString$d(metadata, 'configJson')),
2404
2404
  ...readRecord$2(metadata, 'config'),
2405
2405
  automated: step.automated ?? undefined,
2406
2406
  plugin: step.plugin ?? undefined,
@@ -2465,7 +2465,7 @@ function readTriggerStartNodeKey(trigger) {
2465
2465
  ...parseJsonObject$4(trigger.configJson),
2466
2466
  ...readRecord$2(asRecord$7(trigger), 'config'),
2467
2467
  };
2468
- return readString$c(config, 'startNodeKey') ?? null;
2468
+ return readString$d(config, 'startNodeKey') ?? null;
2469
2469
  }
2470
2470
  function positionToLayout(workflowId, stepId, stepKey, position, index) {
2471
2471
  return {
@@ -2520,7 +2520,7 @@ function isWorkflowNodeLayout(layout) {
2520
2520
  return !!layout;
2521
2521
  }
2522
2522
  function jsonField(record, jsonKey, fallback) {
2523
- const existing = readString$c(record, jsonKey);
2523
+ const existing = readString$d(record, jsonKey);
2524
2524
  if (existing != null)
2525
2525
  return existing;
2526
2526
  const valueKey = jsonKey.replace(/Json$/, '');
@@ -2531,14 +2531,14 @@ function readSelectedActionKey(value) {
2531
2531
  const selected = raw['selectedActions'];
2532
2532
  if (!Array.isArray(selected) || selected.length === 0)
2533
2533
  return null;
2534
- return readString$c(asRecord$7(selected[0]), 'actionKey') ?? null;
2534
+ return readString$d(asRecord$7(selected[0]), 'actionKey') ?? null;
2535
2535
  }
2536
2536
  function readExpressionText(value) {
2537
2537
  const raw = asRecord$7(value);
2538
- return (readString$c(raw, 'expressionText') ??
2539
- readString$c(raw, 'expression') ??
2540
- readString$c(raw, 'formulaRaw') ??
2541
- readString$c(asRecord$7(raw['formula']), 'expression') ??
2538
+ return (readString$d(raw, 'expressionText') ??
2539
+ readString$d(raw, 'expression') ??
2540
+ readString$d(raw, 'formulaRaw') ??
2541
+ readString$d(asRecord$7(raw['formula']), 'expression') ??
2542
2542
  null);
2543
2543
  }
2544
2544
  function categoryForNodeType(type) {
@@ -2599,23 +2599,23 @@ function defaultSideEffectPolicyForNodeType(type) {
2599
2599
  }
2600
2600
  function catalogNodeType$1(item) {
2601
2601
  const raw = asRecord$7(item);
2602
- return (readString$c(raw, 'type') ??
2603
- readString$c(raw, 'nodeType') ??
2604
- readString$c(raw, 'key') ??
2602
+ return (readString$d(raw, 'type') ??
2603
+ readString$d(raw, 'nodeType') ??
2604
+ readString$d(raw, 'key') ??
2605
2605
  'SetFields');
2606
2606
  }
2607
2607
  function catalogTriggerType$2(item) {
2608
2608
  const raw = asRecord$7(item);
2609
- return (readString$c(raw, 'type') ??
2610
- readString$c(raw, 'triggerType') ??
2611
- readString$c(raw, 'key') ??
2609
+ return (readString$d(raw, 'type') ??
2610
+ readString$d(raw, 'triggerType') ??
2611
+ readString$d(raw, 'key') ??
2612
2612
  'ManualTrigger');
2613
2613
  }
2614
2614
  function namespaceKey(namespace) {
2615
2615
  const raw = asRecord$7(namespace);
2616
- return (readString$c(raw, 'key') ??
2617
- readString$c(raw, 'name') ??
2618
- readString$c(raw, 'label') ??
2616
+ return (readString$d(raw, 'key') ??
2617
+ readString$d(raw, 'name') ??
2618
+ readString$d(raw, 'label') ??
2619
2619
  '$json');
2620
2620
  }
2621
2621
  function namespacePaths(namespace) {
@@ -2647,9 +2647,9 @@ function primaryText(value) {
2647
2647
  return value || null;
2648
2648
  if (value && typeof value === 'object') {
2649
2649
  const raw = value;
2650
- return (readString$c(raw, 'en') ??
2651
- readString$c(raw, 'display') ??
2652
- readString$c(raw, 'ar') ??
2650
+ return (readString$d(raw, 'en') ??
2651
+ readString$d(raw, 'display') ??
2652
+ readString$d(raw, 'ar') ??
2653
2653
  null);
2654
2654
  }
2655
2655
  return null;
@@ -2657,9 +2657,9 @@ function primaryText(value) {
2657
2657
  function toTranslatableText$1(value) {
2658
2658
  if (value && typeof value === 'object' && !Array.isArray(value)) {
2659
2659
  const raw = value;
2660
- const en = readString$c(raw, 'en');
2661
- const ar = readString$c(raw, 'ar');
2662
- const display = readString$c(raw, 'display');
2660
+ const en = readString$d(raw, 'en');
2661
+ const ar = readString$d(raw, 'ar');
2662
+ const display = readString$d(raw, 'display');
2663
2663
  return {
2664
2664
  ...raw,
2665
2665
  en: en ?? ar ?? display ?? '',
@@ -2738,7 +2738,7 @@ function asRecord$7(value) {
2738
2738
  return {};
2739
2739
  return value;
2740
2740
  }
2741
- function readString$c(record, key) {
2741
+ function readString$d(record, key) {
2742
2742
  const value = record?.[key];
2743
2743
  return typeof value === 'string' ? value : undefined;
2744
2744
  }
@@ -2752,31 +2752,31 @@ function readBoolean$6(record, key) {
2752
2752
 
2753
2753
  function normalizeWorkflowDefinitionSummaryDto(value) {
2754
2754
  const raw = asRecord$6(value);
2755
- const moduleType = readString$b(raw, 'moduleType');
2756
- const primaryModuleKey = readString$b(raw, 'primaryModuleKey');
2755
+ const moduleType = readString$c(raw, 'moduleType');
2756
+ const primaryModuleKey = readString$c(raw, 'primaryModuleKey');
2757
2757
  const isPublished = readBoolean$5(raw, 'isPublished');
2758
2758
  const isValid = readBoolean$5(raw, 'isValid');
2759
2759
  return {
2760
2760
  ...spreadAs(raw),
2761
2761
  id: readNumber$4(raw, 'id'),
2762
- key: readString$b(raw, 'key'),
2762
+ key: readString$c(raw, 'key'),
2763
2763
  name: toTranslatableText(raw['name']),
2764
2764
  description: toTranslatableText(raw['description']),
2765
- status: readString$b(raw, 'status') ?? (isPublished ? 'Published' : 'Draft'),
2765
+ status: readString$c(raw, 'status') ?? (isPublished ? 'Published' : 'Draft'),
2766
2766
  isPublished,
2767
2767
  isValid,
2768
2768
  primaryModuleId: readNullableNumber(raw, 'primaryModuleId'),
2769
2769
  primaryModuleKey,
2770
2770
  moduleType,
2771
- primaryModuleType: readString$b(raw, 'primaryModuleType') ?? moduleType ?? primaryModuleKey,
2772
- operationType: readString$b(raw, 'operationType'),
2773
- commandName: readString$b(raw, 'commandName'),
2771
+ primaryModuleType: readString$c(raw, 'primaryModuleType') ?? moduleType ?? primaryModuleKey,
2772
+ operationType: readString$c(raw, 'operationType'),
2773
+ commandName: readString$c(raw, 'commandName'),
2774
2774
  hasUnpublishedChanges: readNullableBoolean(raw, 'hasUnpublishedChanges'),
2775
2775
  version: readNullableNumber(raw, 'version'),
2776
2776
  draftVersion: readNullableNumber(raw, 'draftVersion'),
2777
2777
  publishedVersion: readNullableNumber(raw, 'publishedVersion'),
2778
- rowVersion: readString$b(raw, 'rowVersion'),
2779
- triggerType: readString$b(raw, 'triggerType'),
2778
+ rowVersion: readString$c(raw, 'rowVersion'),
2779
+ triggerType: readString$c(raw, 'triggerType'),
2780
2780
  };
2781
2781
  }
2782
2782
  function normalizeWorkflowDefinitionDto(value) {
@@ -2784,9 +2784,9 @@ function normalizeWorkflowDefinitionDto(value) {
2784
2784
  }
2785
2785
  function normalizeWorkflowTriggerDto(value) {
2786
2786
  const raw = asRecord$6(value);
2787
- const payloadSchemaJson = readString$b(raw, 'payloadSchemaJson') ?? readString$b(raw, 'payloadSchema');
2787
+ const payloadSchemaJson = readString$c(raw, 'payloadSchemaJson') ?? readString$c(raw, 'payloadSchema');
2788
2788
  const metadata = readObject$3(raw, 'metadata') ?? {};
2789
- const parsedConfig = parseJsonObject$3(readString$b(raw, 'configJson'));
2789
+ const parsedConfig = parseJsonObject$3(readString$c(raw, 'configJson'));
2790
2790
  if (parsedConfig && !metadata['config'])
2791
2791
  metadata['config'] = parsedConfig;
2792
2792
  return {
@@ -2794,8 +2794,8 @@ function normalizeWorkflowTriggerDto(value) {
2794
2794
  id: readNumber$4(raw, 'id'),
2795
2795
  workflowId: readNumber$4(raw, 'workflowId', null) ?? readNumber$4(raw, 'requestSchemaId'),
2796
2796
  requestSchemaId: readNullableNumber(raw, 'requestSchemaId'),
2797
- type: (readString$b(raw, 'type') ?? 'Manual'),
2798
- typeKey: readString$b(raw, 'typeKey'),
2797
+ type: (readString$c(raw, 'type') ?? 'Manual'),
2798
+ typeKey: readString$c(raw, 'typeKey'),
2799
2799
  name: toTranslatableText(raw['name']),
2800
2800
  enabled: readNullableBoolean(raw, 'enabled') ??
2801
2801
  readNullableBoolean(raw, 'isEnabled') ??
@@ -2806,24 +2806,24 @@ function normalizeWorkflowTriggerDto(value) {
2806
2806
  payloadSchema: parseJsonSchema(payloadSchemaJson) ??
2807
2807
  payloadSchemaJson,
2808
2808
  payloadSchemaJson,
2809
- initiationPropertyMappings: readString$b(raw, 'initiationPropertyMappings'),
2809
+ initiationPropertyMappings: readString$c(raw, 'initiationPropertyMappings'),
2810
2810
  targetProcessSchemaId: readNullableNumber(raw, 'targetProcessSchemaId'),
2811
2811
  targetProcessSchemaName: toTranslatableText(raw['targetProcessSchemaName']),
2812
- expectedHttpMethod: readString$b(raw, 'expectedHttpMethod'),
2813
- workflowKey: readString$b(raw, 'workflowKey'),
2812
+ expectedHttpMethod: readString$c(raw, 'expectedHttpMethod'),
2813
+ workflowKey: readString$c(raw, 'workflowKey'),
2814
2814
  triggerId: readNullableNumber(raw, 'triggerId') ?? readNullableNumber(raw, 'id'),
2815
2815
  allowedContentTypes: readStringArray$6(raw, 'allowedContentTypes'),
2816
2816
  examplePayload: raw['examplePayload'],
2817
- exampleCurl: readString$b(raw, 'exampleCurl'),
2817
+ exampleCurl: readString$c(raw, 'exampleCurl'),
2818
2818
  secretConfigured: readNullableBoolean(raw, 'secretConfigured') ??
2819
2819
  readNullableBoolean(raw, 'hasSecret') ??
2820
2820
  false,
2821
2821
  hasSecret: readNullableBoolean(raw, 'hasSecret') ??
2822
2822
  readNullableBoolean(raw, 'secretConfigured') ??
2823
2823
  false,
2824
- configJson: readString$b(raw, 'configJson'),
2825
- schemaJson: readString$b(raw, 'schemaJson'),
2826
- authenticationPolicyJson: readString$b(raw, 'authenticationPolicyJson'),
2824
+ configJson: readString$c(raw, 'configJson'),
2825
+ schemaJson: readString$c(raw, 'schemaJson'),
2826
+ authenticationPolicyJson: readString$c(raw, 'authenticationPolicyJson'),
2827
2827
  metadata,
2828
2828
  };
2829
2829
  }
@@ -2836,18 +2836,18 @@ function normalizeWorkflowStepDto(value) {
2836
2836
  id: readNumber$4(raw, 'id'),
2837
2837
  requestSchemaId: readNumber$4(raw, 'requestSchemaId', null) ??
2838
2838
  readNumber$4(raw, 'requestSchemaId'),
2839
- key: readString$b(raw, 'key') ?? `step_${readNumber$4(raw, 'id')}`,
2839
+ key: readString$c(raw, 'key') ?? `step_${readNumber$4(raw, 'id')}`,
2840
2840
  name: toTranslatableText(raw['name']),
2841
2841
  description: toTranslatableText(raw['description']),
2842
- type: (readString$b(raw, 'type') ?? 'UserInput'),
2843
- typeKey: readString$b(raw, 'typeKey'),
2842
+ type: (readString$c(raw, 'type') ?? 'UserInput'),
2843
+ typeKey: readString$c(raw, 'typeKey'),
2844
2844
  isInitial: readBoolean$5(raw, 'isInitial'),
2845
2845
  isSystem: readBoolean$5(raw, 'isSystem'),
2846
2846
  isLocked: readBoolean$5(raw, 'isLocked'),
2847
- systemKind: readString$b(raw, 'systemKind'),
2848
- targetType: readString$b(raw, 'targetType'),
2849
- targetValue: readString$b(raw, 'targetValue'),
2850
- groupSelection: readString$b(raw, 'groupSelection'),
2847
+ systemKind: readString$c(raw, 'systemKind'),
2848
+ targetType: readString$c(raw, 'targetType'),
2849
+ targetValue: readString$c(raw, 'targetValue'),
2850
+ groupSelection: readString$c(raw, 'groupSelection'),
2851
2851
  sla: readNullableNumber(raw, 'sla') ?? 0,
2852
2852
  slaHours: readNullableNumber(raw, 'slaHours'),
2853
2853
  metadata: readObject$3(raw, 'metadata'),
@@ -2862,7 +2862,7 @@ function normalizeWorkflowStepDto(value) {
2862
2862
  joinParallel: normalizeJoinParallelConfig(raw['joinParallel']),
2863
2863
  layout: normalizeStepLayout(raw['layout']),
2864
2864
  validationState: raw['validationState'] ?? null,
2865
- formBindingMode: readString$b(raw, 'formBindingMode'),
2865
+ formBindingMode: readString$c(raw, 'formBindingMode'),
2866
2866
  formId: readNullableNumber(raw, 'formId'),
2867
2867
  tags: Array.isArray(raw['tags']) ? raw['tags'] : undefined,
2868
2868
  };
@@ -2873,9 +2873,9 @@ function normalizeWorkflowConnectionDto(value, steps = [], fallbackSelectedActio
2873
2873
  const targetStepId = readNumber$4(raw, 'targetStepId', null) ?? readNumber$4(raw, 'target');
2874
2874
  const sourceStep = steps.find((step) => step.id === sourceStepId);
2875
2875
  const selectedActions = normalizeSelectedActions(raw['selectedActions'], sourceStep, fallbackSelectedActions);
2876
- const expressionText = readString$b(raw, 'expressionText') ??
2877
- readString$b(raw, 'expression') ??
2878
- readString$b(raw, 'formulaRaw');
2876
+ const expressionText = readString$c(raw, 'expressionText') ??
2877
+ readString$c(raw, 'expression') ??
2878
+ readString$c(raw, 'formulaRaw');
2879
2879
  return {
2880
2880
  ...spreadAs(raw),
2881
2881
  id: readNumber$4(raw, 'id'),
@@ -2885,19 +2885,19 @@ function normalizeWorkflowConnectionDto(value, steps = [], fallbackSelectedActio
2885
2885
  targetStepId,
2886
2886
  source: readNullableNumber(raw, 'source'),
2887
2887
  target: readNullableNumber(raw, 'target'),
2888
- sourceStepKey: readString$b(raw, 'sourceStepKey'),
2889
- targetStepKey: readString$b(raw, 'targetStepKey'),
2890
- sourcePortKey: readString$b(raw, 'sourcePortKey') ??
2888
+ sourceStepKey: readString$c(raw, 'sourceStepKey'),
2889
+ targetStepKey: readString$c(raw, 'targetStepKey'),
2890
+ sourcePortKey: readString$c(raw, 'sourcePortKey') ??
2891
2891
  selectedActions[0]?.actionKey ??
2892
2892
  undefined,
2893
- targetPortKey: readString$b(raw, 'targetPortKey') ?? 'in',
2893
+ targetPortKey: readString$c(raw, 'targetPortKey') ?? 'in',
2894
2894
  priority: readNullableNumber(raw, 'priority') ?? 0,
2895
2895
  label: toTranslatableText(raw['label']),
2896
2896
  formula: expressionText
2897
2897
  ? { expression: expressionText }
2898
2898
  : null,
2899
- formulaRaw: readString$b(raw, 'formulaRaw'),
2900
- expression: readString$b(raw, 'expression'),
2899
+ formulaRaw: readString$c(raw, 'formulaRaw'),
2900
+ expression: readString$c(raw, 'expression'),
2901
2901
  expressionText,
2902
2902
  selectedActions,
2903
2903
  metadata: readObject$3(raw, 'metadata'),
@@ -2939,24 +2939,24 @@ function normalizeWorkflowBuilderDto(value) {
2939
2939
  supports: Array.isArray(raw['supports'])
2940
2940
  ? raw['supports']
2941
2941
  : undefined,
2942
- rowVersion: readString$b(raw, 'rowVersion') ?? definition.rowVersion ?? null,
2942
+ rowVersion: readString$c(raw, 'rowVersion') ?? definition.rowVersion ?? null,
2943
2943
  };
2944
2944
  }
2945
2945
  function normalizeWorkflowPluginDescriptorDto(value) {
2946
2946
  const raw = asRecord$6(value);
2947
- const inputSchema = parseJsonSchema(readString$b(raw, 'inputSchemaJson')) ??
2947
+ const inputSchema = parseJsonSchema(readString$c(raw, 'inputSchemaJson')) ??
2948
2948
  schemaFromPluginFields(raw['inputFields']);
2949
- const outputSchema = parseJsonSchema(readString$b(raw, 'outputSchemaJson')) ??
2949
+ const outputSchema = parseJsonSchema(readString$c(raw, 'outputSchemaJson')) ??
2950
2950
  schemaFromPluginFields(raw['outputFields']);
2951
2951
  return {
2952
2952
  ...spreadAs(raw),
2953
- pluginId: readString$b(raw, 'pluginId') ?? '',
2953
+ pluginId: readString$c(raw, 'pluginId') ?? '',
2954
2954
  displayName: toTranslatableText(raw['displayName']),
2955
2955
  description: toTranslatableText(raw['description']),
2956
2956
  inputSchema,
2957
2957
  outputSchema,
2958
- inputSchemaJson: readString$b(raw, 'inputSchemaJson'),
2959
- outputSchemaJson: readString$b(raw, 'outputSchemaJson'),
2958
+ inputSchemaJson: readString$c(raw, 'inputSchemaJson'),
2959
+ outputSchemaJson: readString$c(raw, 'outputSchemaJson'),
2960
2960
  inputFields: Array.isArray(raw['inputFields'])
2961
2961
  ? raw['inputFields']
2962
2962
  : [],
@@ -2964,14 +2964,14 @@ function normalizeWorkflowPluginDescriptorDto(value) {
2964
2964
  ? raw['outputFields']
2965
2965
  : [],
2966
2966
  isAvailable: readNullableBoolean(raw, 'isAvailable'),
2967
- errorMessage: readString$b(raw, 'errorMessage'),
2967
+ errorMessage: readString$c(raw, 'errorMessage'),
2968
2968
  };
2969
2969
  }
2970
2970
  function normalizeWorkflowAppDescriptorDto(value) {
2971
2971
  const raw = asRecord$6(value);
2972
2972
  return {
2973
2973
  ...spreadAs(raw),
2974
- appCode: readString$b(raw, 'appCode') ?? '',
2974
+ appCode: readString$c(raw, 'appCode') ?? '',
2975
2975
  displayName: toTranslatableText(raw['displayName']),
2976
2976
  description: toTranslatableText(raw['description']),
2977
2977
  actionCount: readNullableNumber(raw, 'actionCount'),
@@ -2982,16 +2982,16 @@ function normalizeWorkflowAppActionDescriptorDto(value) {
2982
2982
  const inputSchema = schemaFromAppInputSchema(raw['inputSchema']);
2983
2983
  const outputSchema = schemaFromAppOutputSchema(raw['outputSchema']);
2984
2984
  const configSchema = readObject$3(raw, 'configSchema')?.['schema'] ??
2985
- parseJsonSchema(readString$b(raw, 'configSchemaJson'));
2985
+ parseJsonSchema(readString$c(raw, 'configSchemaJson'));
2986
2986
  return {
2987
2987
  ...spreadAs(raw),
2988
- appCode: readString$b(raw, 'appCode'),
2989
- actionKey: readString$b(raw, 'actionKey') ?? '',
2988
+ appCode: readString$c(raw, 'appCode'),
2989
+ actionKey: readString$c(raw, 'actionKey') ?? '',
2990
2990
  displayName: toTranslatableText(raw['displayName']),
2991
2991
  description: toTranslatableText(raw['description']),
2992
- category: readString$b(raw, 'category'),
2992
+ category: readString$c(raw, 'category'),
2993
2993
  configSchema: configSchema ?? null,
2994
- configSchemaJson: readString$b(raw, 'configSchemaJson'),
2994
+ configSchemaJson: readString$c(raw, 'configSchemaJson'),
2995
2995
  runtimeInputsSchema: inputSchema,
2996
2996
  outputsSchema: outputSchema,
2997
2997
  inputSchema: Array.isArray(raw['inputSchema'])
@@ -3028,9 +3028,9 @@ function normalizeWorkflowTestRunResultDto(value) {
3028
3028
  return {
3029
3029
  ...spreadAs(raw),
3030
3030
  workflowId: readNullableNumber(raw, 'workflowId'),
3031
- testRunId: readString$b(raw, 'testRunId') ??
3031
+ testRunId: readString$c(raw, 'testRunId') ??
3032
3032
  `workflow_${readNullableNumber(raw, 'workflowId') ?? 'test'}`,
3033
- status: readString$b(raw, 'status') ??
3033
+ status: readString$c(raw, 'status') ??
3034
3034
  (readNullableBoolean(raw, 'success') === false ? 'Failed' : 'Succeeded'),
3035
3035
  success: readNullableBoolean(raw, 'success'),
3036
3036
  canStart: readNullableBoolean(raw, 'canStart'),
@@ -3248,10 +3248,10 @@ function normalizeWorkflowStepActions(value) {
3248
3248
  return [];
3249
3249
  return value.map((item) => {
3250
3250
  const raw = asRecord$6(item);
3251
- const key = readString$b(raw, 'key') ??
3252
- readString$b(raw, 'typeKey') ??
3253
- readString$b(raw, 'type') ??
3254
- readString$b(raw, 'statusKey') ??
3251
+ const key = readString$c(raw, 'key') ??
3252
+ readString$c(raw, 'typeKey') ??
3253
+ readString$c(raw, 'type') ??
3254
+ readString$c(raw, 'statusKey') ??
3255
3255
  '';
3256
3256
  return {
3257
3257
  ...spreadAs(raw),
@@ -3262,25 +3262,25 @@ function normalizeWorkflowStepActions(value) {
3262
3262
  stepSchemaId: readNullableNumber(raw, 'stepSchemaId') ?? undefined,
3263
3263
  order: readNullableNumber(raw, 'order') ?? undefined,
3264
3264
  key,
3265
- type: readString$b(raw, 'type'),
3266
- typeKey: readString$b(raw, 'typeKey'),
3265
+ type: readString$c(raw, 'type'),
3266
+ typeKey: readString$c(raw, 'typeKey'),
3267
3267
  label: toTranslatableText(raw['label'] ?? raw['name'] ?? key),
3268
3268
  name: toTranslatableText(raw['name'] ?? raw['label'] ?? key),
3269
- statusKey: readString$b(raw, 'statusKey'),
3270
- color: readString$b(raw, 'color'),
3271
- icon: readString$b(raw, 'icon'),
3269
+ statusKey: readString$c(raw, 'statusKey'),
3270
+ color: readString$c(raw, 'color'),
3271
+ icon: readString$c(raw, 'icon'),
3272
3272
  requireConfirmation: readNullableBoolean(raw, 'requireConfirmation') ?? undefined,
3273
3273
  confirmationMessage: toTranslatableText(raw['confirmationMessage']),
3274
3274
  requireSignature: readNullableBoolean(raw, 'requireSignature') ?? undefined,
3275
- routingBehavior: readString$b(raw, 'routingBehavior'),
3275
+ routingBehavior: readString$c(raw, 'routingBehavior'),
3276
3276
  routesThroughSelectedActionConnection: readNullableBoolean(raw, 'routesThroughSelectedActionConnection') ??
3277
3277
  undefined,
3278
3278
  isTerminal: readNullableBoolean(raw, 'isTerminal') ??
3279
- readString$b(raw, 'routingBehavior') === 'Terminal',
3279
+ readString$c(raw, 'routingBehavior') === 'Terminal',
3280
3280
  isRouteable: readNullableBoolean(raw, 'isRouteable') ??
3281
3281
  readNullableBoolean(raw, 'canRoute') ??
3282
3282
  readNullableBoolean(raw, 'routesThroughSelectedActionConnection') ??
3283
- readString$b(raw, 'routingBehavior') === 'SelectedActionConnection',
3283
+ readString$c(raw, 'routingBehavior') === 'SelectedActionConnection',
3284
3284
  canRoute: readNullableBoolean(raw, 'canRoute') ??
3285
3285
  readNullableBoolean(raw, 'isRouteable') ??
3286
3286
  undefined,
@@ -3300,9 +3300,9 @@ function normalizeWorkflowStepProperties(value) {
3300
3300
  readNullableNumber(property, 'id') ??
3301
3301
  0,
3302
3302
  refId: readNullableNumber(raw, 'refId') ?? undefined,
3303
- refType: readString$b(raw, 'refType'),
3304
- type: readString$b(raw, 'type'),
3305
- propertyKey: readString$b(raw, 'propertyKey') ?? readString$b(property, 'key'),
3303
+ refType: readString$c(raw, 'refType'),
3304
+ type: readString$c(raw, 'type'),
3305
+ propertyKey: readString$c(raw, 'propertyKey') ?? readString$c(property, 'key'),
3306
3306
  read: readNullableBoolean(raw, 'read') ??
3307
3307
  readNullableBoolean(raw, 'isRead') ??
3308
3308
  false,
@@ -3314,9 +3314,9 @@ function normalizeWorkflowStepProperties(value) {
3314
3314
  property: property
3315
3315
  ? {
3316
3316
  id: readNullableNumber(property, 'id'),
3317
- key: readString$b(property, 'key'),
3317
+ key: readString$c(property, 'key'),
3318
3318
  name: toTranslatableText(property['name']),
3319
- viewType: readString$b(property, 'viewType'),
3319
+ viewType: readString$c(property, 'viewType'),
3320
3320
  isRequired: readNullableBoolean(property, 'isRequired') ?? undefined,
3321
3321
  isTranslatable: readNullableBoolean(property, 'isTranslatable') ?? undefined,
3322
3322
  }
@@ -3330,25 +3330,25 @@ function normalizeAutomatedConfig(value) {
3330
3330
  const raw = asRecord$6(value);
3331
3331
  return {
3332
3332
  ...spreadAs(raw),
3333
- method: readString$b(raw, 'method') ?? 'POST',
3334
- serviceUrl: readString$b(raw, 'serviceUrl') ?? readString$b(raw, 'url'),
3335
- url: readString$b(raw, 'url') ?? readString$b(raw, 'serviceUrl'),
3333
+ method: readString$c(raw, 'method') ?? 'POST',
3334
+ serviceUrl: readString$c(raw, 'serviceUrl') ?? readString$c(raw, 'url'),
3335
+ url: readString$c(raw, 'url') ?? readString$c(raw, 'serviceUrl'),
3336
3336
  timeoutSeconds: readNullableNumber(raw, 'timeoutSeconds'),
3337
3337
  retryCount: readNullableNumber(raw, 'retryCount'),
3338
3338
  retryDelaySeconds: readNullableNumber(raw, 'retryDelaySeconds'),
3339
- onFailureBehavior: readString$b(raw, 'onFailureBehavior') ??
3340
- readString$b(raw, 'failureBehavior'),
3341
- failureBehavior: readString$b(raw, 'failureBehavior') ??
3342
- readString$b(raw, 'onFailureBehavior'),
3339
+ onFailureBehavior: readString$c(raw, 'onFailureBehavior') ??
3340
+ readString$c(raw, 'failureBehavior'),
3341
+ failureBehavior: readString$c(raw, 'failureBehavior') ??
3342
+ readString$c(raw, 'onFailureBehavior'),
3343
3343
  headers: pairsFromUnknown(raw['headers']),
3344
3344
  headersMap: readStringRecord(raw['headers']),
3345
3345
  query: pairsFromUnknown(raw['query'] ?? raw['queryParameters'] ?? raw['queryParams']),
3346
3346
  queryParameters: readStringRecord(raw['queryParameters'] ?? raw['queryParams']),
3347
3347
  queryParams: readStringRecord(raw['queryParams'] ?? raw['queryParameters']),
3348
- jsonBody: readString$b(raw, 'jsonBody') ?? readString$b(raw, 'body'),
3349
- body: readString$b(raw, 'body') ?? readString$b(raw, 'jsonBody'),
3350
- bodyType: readString$b(raw, 'bodyType'),
3351
- bodyKind: readString$b(raw, 'bodyKind'),
3348
+ jsonBody: readString$c(raw, 'jsonBody') ?? readString$c(raw, 'body'),
3349
+ body: readString$c(raw, 'body') ?? readString$c(raw, 'jsonBody'),
3350
+ bodyType: readString$c(raw, 'bodyType'),
3351
+ bodyKind: readString$c(raw, 'bodyKind'),
3352
3352
  outputs: normalizeAutomatedOutputMappings(raw['outputs'] ?? raw['outputMappings']),
3353
3353
  outputMappings: normalizeAutomatedOutputMappings(raw['outputMappings'] ?? raw['outputs']),
3354
3354
  placeholders: readStringRecord(raw['placeholders']),
@@ -3367,17 +3367,17 @@ function normalizePluginConfig(value) {
3367
3367
  : [];
3368
3368
  return {
3369
3369
  ...spreadAs(raw),
3370
- pluginId: readString$b(raw, 'pluginId'),
3371
- inputSchemaJson: readString$b(raw, 'inputSchemaJson'),
3372
- outputSchemaJson: readString$b(raw, 'outputSchemaJson'),
3370
+ pluginId: readString$c(raw, 'pluginId'),
3371
+ inputSchemaJson: readString$c(raw, 'inputSchemaJson'),
3372
+ outputSchemaJson: readString$c(raw, 'outputSchemaJson'),
3373
3373
  waitForCompletion: readNullableBoolean(raw, 'waitForCompletion') ?? true,
3374
3374
  timeoutSeconds: readNullableNumber(raw, 'timeoutSeconds'),
3375
3375
  retryCount: readNullableNumber(raw, 'retryCount'),
3376
3376
  retryDelaySeconds: readNullableNumber(raw, 'retryDelaySeconds'),
3377
- onFailureBehavior: readString$b(raw, 'onFailureBehavior') ??
3378
- readString$b(raw, 'failureBehavior'),
3379
- failureBehavior: readString$b(raw, 'failureBehavior') ??
3380
- readString$b(raw, 'onFailureBehavior'),
3377
+ onFailureBehavior: readString$c(raw, 'onFailureBehavior') ??
3378
+ readString$c(raw, 'failureBehavior'),
3379
+ failureBehavior: readString$c(raw, 'failureBehavior') ??
3380
+ readString$c(raw, 'onFailureBehavior'),
3381
3381
  skipInputValidation: readNullableBoolean(raw, 'skipInputValidation') ?? undefined,
3382
3382
  inputs: Array.isArray(raw['inputs']) && raw['inputs'].length > 0
3383
3383
  ? raw['inputs']
@@ -3404,10 +3404,10 @@ function normalizeAppActionConfig(value) {
3404
3404
  : [];
3405
3405
  return {
3406
3406
  ...spreadAs(raw),
3407
- appCode: readString$b(raw, 'appCode'),
3408
- actionKey: readString$b(raw, 'actionKey') ?? '',
3409
- configJson: readString$b(raw, 'configJson'),
3410
- config: parseJsonObject$3(readString$b(raw, 'configJson')) ??
3407
+ appCode: readString$c(raw, 'appCode'),
3408
+ actionKey: readString$c(raw, 'actionKey') ?? '',
3409
+ configJson: readString$c(raw, 'configJson'),
3410
+ config: parseJsonObject$3(readString$c(raw, 'configJson')) ??
3411
3411
  readObject$3(raw, 'config') ??
3412
3412
  {},
3413
3413
  configMappings,
@@ -3419,7 +3419,7 @@ function normalizeAppActionConfig(value) {
3419
3419
  ? raw['outputs']
3420
3420
  : outputMappings.map((mapping) => normalizeAppOutput(mapping)),
3421
3421
  outputMappings,
3422
- failureBehavior: readString$b(raw, 'failureBehavior'),
3422
+ failureBehavior: readString$c(raw, 'failureBehavior'),
3423
3423
  timeoutSeconds: readNullableNumber(raw, 'timeoutSeconds'),
3424
3424
  };
3425
3425
  }
@@ -3470,7 +3470,7 @@ function normalizeJoinParallelConfig(value) {
3470
3470
  const anyBranchApprove = readNullableBoolean(raw, 'anyBranchApprove') ?? false;
3471
3471
  return {
3472
3472
  ...spreadAs(raw),
3473
- policy: readString$b(raw, 'policy') ?? (anyBranchApprove ? 'Any' : 'All'),
3473
+ policy: readString$c(raw, 'policy') ?? (anyBranchApprove ? 'Any' : 'All'),
3474
3474
  anyBranchApprove,
3475
3475
  threshold: readNullableNumber(raw, 'threshold'),
3476
3476
  customCondition: raw['customCondition'] ??
@@ -3501,12 +3501,12 @@ function normalizeSelectedActions(value, sourceStep, fallbackSelectedActions = [
3501
3501
  return {
3502
3502
  stepActionId: stepActionId ?? undefined,
3503
3503
  actionId: stepActionId ?? undefined,
3504
- actionKey: readString$b(raw, 'actionKey') ??
3505
- readString$b(raw, 'key') ??
3506
- readString$b(raw, 'typeKey') ??
3507
- readString$b(raw, 'statusKey') ??
3504
+ actionKey: readString$c(raw, 'actionKey') ??
3505
+ readString$c(raw, 'key') ??
3506
+ readString$c(raw, 'typeKey') ??
3507
+ readString$c(raw, 'statusKey') ??
3508
3508
  '',
3509
- key: readString$b(raw, 'key'),
3509
+ key: readString$c(raw, 'key'),
3510
3510
  };
3511
3511
  })
3512
3512
  .filter((item) => item.actionKey);
@@ -3564,12 +3564,12 @@ function normalizeAutomatedOutputMappings(value) {
3564
3564
  return value.map((item) => {
3565
3565
  const raw = asRecord$6(item);
3566
3566
  return {
3567
- source: readString$b(raw, 'source') ?? readString$b(raw, 'jsonPath') ?? 'response',
3568
- targetPath: readString$b(raw, 'targetPath') ?? readString$b(raw, 'contextPath') ?? '',
3569
- jsonPath: readString$b(raw, 'jsonPath'),
3570
- contextPath: readString$b(raw, 'contextPath'),
3571
- writePolicy: readString$b(raw, 'writePolicy'),
3572
- valueType: readString$b(raw, 'valueType'),
3567
+ source: readString$c(raw, 'source') ?? readString$c(raw, 'jsonPath') ?? 'response',
3568
+ targetPath: readString$c(raw, 'targetPath') ?? readString$c(raw, 'contextPath') ?? '',
3569
+ jsonPath: readString$c(raw, 'jsonPath'),
3570
+ contextPath: readString$c(raw, 'contextPath'),
3571
+ writePolicy: readString$c(raw, 'writePolicy'),
3572
+ valueType: readString$c(raw, 'valueType'),
3573
3573
  isSecret: readNullableBoolean(raw, 'isSecret') ?? undefined,
3574
3574
  };
3575
3575
  });
@@ -3578,27 +3578,27 @@ function normalizePluginInput(value) {
3578
3578
  const raw = asRecord$6(value);
3579
3579
  if (readNullableNumber(raw, 'requestPropertyId') != null) {
3580
3580
  return {
3581
- field: readString$b(raw, 'pluginInputFieldName') ?? '',
3581
+ field: readString$c(raw, 'pluginInputFieldName') ?? '',
3582
3582
  source: 'Property',
3583
3583
  value: readNullableNumber(raw, 'requestPropertyId'),
3584
3584
  };
3585
3585
  }
3586
- if (readString$b(raw, 'contextPath')) {
3586
+ if (readString$c(raw, 'contextPath')) {
3587
3587
  return {
3588
- field: readString$b(raw, 'pluginInputFieldName') ?? '',
3588
+ field: readString$c(raw, 'pluginInputFieldName') ?? '',
3589
3589
  source: 'Context',
3590
- value: readString$b(raw, 'contextPath') ?? '',
3590
+ value: readString$c(raw, 'contextPath') ?? '',
3591
3591
  };
3592
3592
  }
3593
- if (readString$b(raw, 'transformExpression')) {
3593
+ if (readString$c(raw, 'transformExpression')) {
3594
3594
  return {
3595
- field: readString$b(raw, 'pluginInputFieldName') ?? '',
3595
+ field: readString$c(raw, 'pluginInputFieldName') ?? '',
3596
3596
  source: 'Expression',
3597
- value: readString$b(raw, 'transformExpression') ?? '',
3597
+ value: readString$c(raw, 'transformExpression') ?? '',
3598
3598
  };
3599
3599
  }
3600
3600
  return {
3601
- field: readString$b(raw, 'pluginInputFieldName') ?? '',
3601
+ field: readString$c(raw, 'pluginInputFieldName') ?? '',
3602
3602
  source: 'Static',
3603
3603
  value: raw['staticValue'],
3604
3604
  };
@@ -3606,35 +3606,35 @@ function normalizePluginInput(value) {
3606
3606
  function normalizePluginOutput(value) {
3607
3607
  const raw = asRecord$6(value);
3608
3608
  return {
3609
- field: readString$b(raw, 'pluginOutputFieldName') ?? '',
3610
- targetPath: readString$b(raw, 'contextPath') ?? readString$b(raw, 'targetPath') ?? '',
3609
+ field: readString$c(raw, 'pluginOutputFieldName') ?? '',
3610
+ targetPath: readString$c(raw, 'contextPath') ?? readString$c(raw, 'targetPath') ?? '',
3611
3611
  };
3612
3612
  }
3613
3613
  function normalizeAppRuntimeInput(value) {
3614
3614
  const raw = asRecord$6(value);
3615
3615
  if (readNullableNumber(raw, 'requestPropertyId') != null) {
3616
3616
  return {
3617
- field: readString$b(raw, 'fieldKey') ?? '',
3617
+ field: readString$c(raw, 'fieldKey') ?? '',
3618
3618
  source: 'Property',
3619
3619
  value: readNullableNumber(raw, 'requestPropertyId'),
3620
3620
  };
3621
3621
  }
3622
- if (readString$b(raw, 'contextPath')) {
3622
+ if (readString$c(raw, 'contextPath')) {
3623
3623
  return {
3624
- field: readString$b(raw, 'fieldKey') ?? '',
3624
+ field: readString$c(raw, 'fieldKey') ?? '',
3625
3625
  source: 'Context',
3626
- value: readString$b(raw, 'contextPath') ?? '',
3626
+ value: readString$c(raw, 'contextPath') ?? '',
3627
3627
  };
3628
3628
  }
3629
- if (readString$b(raw, 'transformExpression')) {
3629
+ if (readString$c(raw, 'transformExpression')) {
3630
3630
  return {
3631
- field: readString$b(raw, 'fieldKey') ?? '',
3631
+ field: readString$c(raw, 'fieldKey') ?? '',
3632
3632
  source: 'Expression',
3633
- value: readString$b(raw, 'transformExpression') ?? '',
3633
+ value: readString$c(raw, 'transformExpression') ?? '',
3634
3634
  };
3635
3635
  }
3636
3636
  return {
3637
- field: readString$b(raw, 'fieldKey') ?? '',
3637
+ field: readString$c(raw, 'fieldKey') ?? '',
3638
3638
  source: 'Static',
3639
3639
  value: raw['staticValue'],
3640
3640
  };
@@ -3642,8 +3642,8 @@ function normalizeAppRuntimeInput(value) {
3642
3642
  function normalizeAppOutput(value) {
3643
3643
  const raw = asRecord$6(value);
3644
3644
  return {
3645
- field: readString$b(raw, 'fieldKey') ?? '',
3646
- targetPath: readString$b(raw, 'contextPath') ?? readString$b(raw, 'targetPath') ?? '',
3645
+ field: readString$c(raw, 'fieldKey') ?? '',
3646
+ targetPath: readString$c(raw, 'contextPath') ?? readString$c(raw, 'targetPath') ?? '',
3647
3647
  };
3648
3648
  }
3649
3649
  function normalizeSubprocessPropertyMapping(value) {
@@ -3654,23 +3654,23 @@ function normalizeSubprocessPropertyMapping(value) {
3654
3654
  parentRequestPropertyId: readNullableNumber(raw, 'parentRequestPropertyId') ?? undefined,
3655
3655
  subRequestPropertyId: readNullableNumber(raw, 'subRequestPropertyId') ?? undefined,
3656
3656
  reverseMapping: readNullableBoolean(raw, 'reverseMapping') ?? undefined,
3657
- parentPath: readString$b(raw, 'parentPath') ??
3657
+ parentPath: readString$c(raw, 'parentPath') ??
3658
3658
  String(readNullableNumber(raw, 'parentRequestPropertyId') ?? ''),
3659
- childPath: readString$b(raw, 'childPath') ??
3659
+ childPath: readString$c(raw, 'childPath') ??
3660
3660
  String(readNullableNumber(raw, 'subRequestPropertyId') ?? ''),
3661
- direction: readString$b(raw, 'direction') ??
3661
+ direction: readString$c(raw, 'direction') ??
3662
3662
  (readNullableBoolean(raw, 'reverseMapping') ? 'Both' : 'In'),
3663
3663
  required: readNullableBoolean(raw, 'required') ?? undefined,
3664
- transform: readString$b(raw, 'transform'),
3664
+ transform: readString$c(raw, 'transform'),
3665
3665
  };
3666
3666
  }
3667
3667
  function normalizeSubprocessContextMapping(value) {
3668
3668
  const raw = asRecord$6(value);
3669
3669
  return {
3670
- parentPath: readString$b(raw, 'parentPath') ?? '',
3671
- childPath: readString$b(raw, 'childPath') ?? '',
3672
- direction: readString$b(raw, 'direction') ?? 'In',
3673
- transform: readString$b(raw, 'transform'),
3670
+ parentPath: readString$c(raw, 'parentPath') ?? '',
3671
+ childPath: readString$c(raw, 'childPath') ?? '',
3672
+ direction: readString$c(raw, 'direction') ?? 'In',
3673
+ transform: readString$c(raw, 'transform'),
3674
3674
  };
3675
3675
  }
3676
3676
  function normalizeActionsForPayload(actions) {
@@ -3721,14 +3721,14 @@ function normalizePluginInputsForPayload(mappings) {
3721
3721
  if ('pluginInputFieldName' in raw || 'contextPath' in raw) {
3722
3722
  return { ...raw };
3723
3723
  }
3724
- const source = readString$b(raw, 'source') ?? 'Context';
3724
+ const source = readString$c(raw, 'source') ?? 'Context';
3725
3725
  return {
3726
3726
  id: 0,
3727
- pluginInputFieldName: readString$b(raw, 'field') ?? '',
3727
+ pluginInputFieldName: readString$c(raw, 'field') ?? '',
3728
3728
  requestPropertyId: source === 'Property' ? readNullableNumber(raw, 'value') : undefined,
3729
- contextPath: source === 'Context' ? (readString$b(raw, 'value') ?? '') : undefined,
3729
+ contextPath: source === 'Context' ? (readString$c(raw, 'value') ?? '') : undefined,
3730
3730
  staticValue: source === 'Static' ? raw['value'] : undefined,
3731
- transformExpression: source === 'Expression' ? (readString$b(raw, 'value') ?? '') : undefined,
3731
+ transformExpression: source === 'Expression' ? (readString$c(raw, 'value') ?? '') : undefined,
3732
3732
  };
3733
3733
  });
3734
3734
  }
@@ -3742,9 +3742,9 @@ function normalizePluginOutputsForPayload(mappings) {
3742
3742
  }
3743
3743
  return {
3744
3744
  id: 0,
3745
- pluginOutputFieldName: readString$b(raw, 'field') ?? '',
3746
- contextPath: readString$b(raw, 'targetPath') ?? readString$b(raw, 'contextPath') ?? '',
3747
- transformExpression: readString$b(raw, 'transformExpression') ?? undefined,
3745
+ pluginOutputFieldName: readString$c(raw, 'field') ?? '',
3746
+ contextPath: readString$c(raw, 'targetPath') ?? readString$c(raw, 'contextPath') ?? '',
3747
+ transformExpression: readString$c(raw, 'transformExpression') ?? undefined,
3748
3748
  };
3749
3749
  });
3750
3750
  }
@@ -3760,17 +3760,17 @@ function normalizeAppActionMappingsForPayload(mappings, kind) {
3760
3760
  }
3761
3761
  if (kind === 'output') {
3762
3762
  return {
3763
- fieldKey: readString$b(raw, 'field') ?? '',
3764
- contextPath: readString$b(raw, 'targetPath') ?? readString$b(raw, 'contextPath') ?? '',
3763
+ fieldKey: readString$c(raw, 'field') ?? '',
3764
+ contextPath: readString$c(raw, 'targetPath') ?? readString$c(raw, 'contextPath') ?? '',
3765
3765
  };
3766
3766
  }
3767
- const source = readString$b(raw, 'source') ?? 'Context';
3767
+ const source = readString$c(raw, 'source') ?? 'Context';
3768
3768
  return {
3769
- fieldKey: readString$b(raw, 'field') ?? '',
3769
+ fieldKey: readString$c(raw, 'field') ?? '',
3770
3770
  requestPropertyId: source === 'Property' ? readNullableNumber(raw, 'value') : undefined,
3771
- contextPath: source === 'Context' ? (readString$b(raw, 'value') ?? '') : undefined,
3771
+ contextPath: source === 'Context' ? (readString$c(raw, 'value') ?? '') : undefined,
3772
3772
  staticValue: source === 'Static' ? raw['value'] : undefined,
3773
- transformExpression: source === 'Expression' ? (readString$b(raw, 'value') ?? '') : undefined,
3773
+ transformExpression: source === 'Expression' ? (readString$c(raw, 'value') ?? '') : undefined,
3774
3774
  };
3775
3775
  });
3776
3776
  }
@@ -3808,7 +3808,7 @@ function normalizeContextSnapshot(value) {
3808
3808
  const snapshot = {};
3809
3809
  for (const item of value) {
3810
3810
  const raw = asRecord$6(item);
3811
- const path = readString$b(raw, 'path');
3811
+ const path = readString$c(raw, 'path');
3812
3812
  if (!path)
3813
3813
  continue;
3814
3814
  snapshot[path] = raw['value'];
@@ -3822,12 +3822,12 @@ function schemaFromPluginFields(value) {
3822
3822
  const required = [];
3823
3823
  for (const item of value) {
3824
3824
  const raw = asRecord$6(item);
3825
- const key = readString$b(raw, 'name');
3825
+ const key = readString$c(raw, 'name');
3826
3826
  if (!key)
3827
3827
  continue;
3828
3828
  properties[key] = {
3829
- type: readString$b(raw, 'type') ?? 'string',
3830
- description: readString$b(raw, 'description') ?? '',
3829
+ type: readString$c(raw, 'type') ?? 'string',
3830
+ description: readString$c(raw, 'description') ?? '',
3831
3831
  };
3832
3832
  if (readNullableBoolean(raw, 'required'))
3833
3833
  required.push(key);
@@ -3841,12 +3841,12 @@ function schemaFromAppInputSchema(value) {
3841
3841
  const required = [];
3842
3842
  for (const item of value) {
3843
3843
  const raw = asRecord$6(item);
3844
- const key = readString$b(raw, 'key');
3844
+ const key = readString$c(raw, 'key');
3845
3845
  if (!key)
3846
3846
  continue;
3847
3847
  properties[key] = {
3848
3848
  type: 'string',
3849
- description: readString$b(raw, 'source') ?? '',
3849
+ description: readString$c(raw, 'source') ?? '',
3850
3850
  };
3851
3851
  if (readNullableBoolean(raw, 'required'))
3852
3852
  required.push(key);
@@ -3859,12 +3859,12 @@ function schemaFromAppOutputSchema(value) {
3859
3859
  const properties = {};
3860
3860
  for (const item of value) {
3861
3861
  const raw = asRecord$6(item);
3862
- const key = readString$b(raw, 'key');
3862
+ const key = readString$c(raw, 'key');
3863
3863
  if (!key)
3864
3864
  continue;
3865
3865
  properties[key] = {
3866
- type: readString$b(raw, 'type') ?? 'object',
3867
- description: readString$b(raw, 'description') ?? '',
3866
+ type: readString$c(raw, 'type') ?? 'object',
3867
+ description: readString$c(raw, 'description') ?? '',
3868
3868
  };
3869
3869
  }
3870
3870
  return { type: 'object', properties };
@@ -3872,9 +3872,9 @@ function schemaFromAppOutputSchema(value) {
3872
3872
  function toTranslatableText(value) {
3873
3873
  if (value && typeof value === 'object') {
3874
3874
  const raw = value;
3875
- const en = readString$b(raw, 'en');
3876
- const ar = readString$b(raw, 'ar');
3877
- const display = readString$b(raw, 'display');
3875
+ const en = readString$c(raw, 'en');
3876
+ const ar = readString$c(raw, 'ar');
3877
+ const display = readString$c(raw, 'display');
3878
3878
  return {
3879
3879
  en: en ?? ar ?? display ?? '',
3880
3880
  ar: ar ?? en ?? display ?? '',
@@ -3889,9 +3889,9 @@ function toPrimaryText(value) {
3889
3889
  return value || undefined;
3890
3890
  if (value && typeof value === 'object') {
3891
3891
  const raw = value;
3892
- return (readString$b(raw, 'en') ??
3893
- readString$b(raw, 'display') ??
3894
- readString$b(raw, 'ar') ??
3892
+ return (readString$c(raw, 'en') ??
3893
+ readString$c(raw, 'display') ??
3894
+ readString$c(raw, 'ar') ??
3895
3895
  undefined);
3896
3896
  }
3897
3897
  return undefined;
@@ -3913,8 +3913,8 @@ function pairsFromUnknown(value) {
3913
3913
  return value
3914
3914
  .map((item) => asRecord$6(item))
3915
3915
  .map((item) => ({
3916
- key: readString$b(item, 'key') ?? '',
3917
- value: readString$b(item, 'value') ?? '',
3916
+ key: readString$c(item, 'key') ?? '',
3917
+ value: readString$c(item, 'value') ?? '',
3918
3918
  }));
3919
3919
  }
3920
3920
  return Object.entries(readStringRecord(value) ?? {}).map(([key, recordValue]) => ({
@@ -3981,7 +3981,7 @@ function readObject$3(record, key) {
3981
3981
  return null;
3982
3982
  return value;
3983
3983
  }
3984
- function readString$b(record, key) {
3984
+ function readString$c(record, key) {
3985
3985
  const value = record[key];
3986
3986
  return typeof value === 'string' ? value : undefined;
3987
3987
  }
@@ -4918,7 +4918,7 @@ function normalizePathValidation(paths, response) {
4918
4918
  }
4919
4919
  return {
4920
4920
  results: raw.map((item, index) => {
4921
- const path = readString$a(item, 'path') ?? readString$a(item, 'expression');
4921
+ const path = readString$b(item, 'path') ?? readString$b(item, 'expression');
4922
4922
  const valid = readBoolean$4(item, 'isValid') ??
4923
4923
  readBoolean$4(item, 'valid') ??
4924
4924
  readBoolean$4(item, 'success') ??
@@ -4930,8 +4930,8 @@ function normalizePathValidation(paths, response) {
4930
4930
  path: path ?? paths[index] ?? '',
4931
4931
  isValid: valid,
4932
4932
  isAvailable: available,
4933
- type: readString$a(item, 'type'),
4934
- message: readString$a(item, 'message') ?? readString$a(item, 'error'),
4933
+ type: readString$b(item, 'type'),
4934
+ message: readString$b(item, 'message') ?? readString$b(item, 'error'),
4935
4935
  };
4936
4936
  }),
4937
4937
  };
@@ -4950,7 +4950,7 @@ function asRecordArray(value) {
4950
4950
  return null;
4951
4951
  return value.filter((item) => !!item && typeof item === 'object' && !Array.isArray(item));
4952
4952
  }
4953
- function readString$a(source, key) {
4953
+ function readString$b(source, key) {
4954
4954
  const value = source[key];
4955
4955
  return typeof value === 'string' ? value : null;
4956
4956
  }
@@ -5101,8 +5101,8 @@ function normalizeExecutionListResult(result) {
5101
5101
  ...result,
5102
5102
  items,
5103
5103
  hasMore: readBoolean$3(raw, 'hasMore') ?? result.hasMore,
5104
- nextCursor: readString$9(raw, 'nextCursor') ??
5105
- buildCursor(readString$9(raw, 'nextCursorStartedAtUtc'), readId$3(raw, 'nextCursorExecutionId')),
5104
+ nextCursor: readString$a(raw, 'nextCursor') ??
5105
+ buildCursor(readString$a(raw, 'nextCursorStartedAtUtc'), readId$3(raw, 'nextCursorExecutionId')),
5106
5106
  };
5107
5107
  }
5108
5108
  function normalizeExecutionDetail(detail) {
@@ -5116,11 +5116,11 @@ function normalizeExecutionDetail(detail) {
5116
5116
  ? readArray$1(raw, 'events')
5117
5117
  : readArray$1(raw, 'timeline')).map(normalizeExecutionEvent);
5118
5118
  const triggerSummary = raw['triggerSummary'] ??
5119
- parseJsonValue$1(readString$9(raw, 'triggerSummaryJson'));
5119
+ parseJsonValue$1(readString$a(raw, 'triggerSummaryJson'));
5120
5120
  return {
5121
5121
  ...detail,
5122
5122
  ...summary,
5123
- triggerSummaryJson: readString$9(raw, 'triggerSummaryJson') ?? null,
5123
+ triggerSummaryJson: readString$a(raw, 'triggerSummaryJson') ?? null,
5124
5124
  triggerSummary,
5125
5125
  nodeRuns,
5126
5126
  waits,
@@ -5131,7 +5131,7 @@ function normalizeExecutionDetail(detail) {
5131
5131
  flowPlusCommitAudits: readArray$1(raw, 'flowPlusCommitAudits'),
5132
5132
  approvalDecisions: readArray$1(raw, 'approvalDecisions'),
5133
5133
  timelineHasMore: readBoolean$3(raw, 'timelineHasMore') ?? false,
5134
- timelineNextAfterUtc: readString$9(raw, 'timelineNextAfterUtc') ?? null,
5134
+ timelineNextAfterUtc: readString$a(raw, 'timelineNextAfterUtc') ?? null,
5135
5135
  };
5136
5136
  }
5137
5137
  function normalizeExecutionSummary(value) {
@@ -5143,18 +5143,18 @@ function normalizeExecutionSummary(value) {
5143
5143
  automationId: readId$3(raw, 'automationId') ?? '',
5144
5144
  revisionId,
5145
5145
  automationRevisionId: readId$3(raw, 'automationRevisionId') ?? revisionId,
5146
- automationName: readString$9(raw, 'automationName') ?? null,
5146
+ automationName: readString$a(raw, 'automationName') ?? null,
5147
5147
  revisionNumber: readNumber$2(raw, 'revisionNumber') ?? null,
5148
- status: String(readString$9(raw, 'status') ?? ''),
5149
- triggerType: readString$9(raw, 'triggerType') ?? null,
5150
- triggerKey: readString$9(raw, 'triggerKey') ?? null,
5151
- createdAtUtc: readString$9(raw, 'createdAtUtc') ?? null,
5152
- startedAtUtc: readString$9(raw, 'startedAtUtc') ??
5153
- readString$9(raw, 'createdAtUtc') ??
5148
+ status: String(readString$a(raw, 'status') ?? ''),
5149
+ triggerType: readString$a(raw, 'triggerType') ?? null,
5150
+ triggerKey: readString$a(raw, 'triggerKey') ?? null,
5151
+ createdAtUtc: readString$a(raw, 'createdAtUtc') ?? null,
5152
+ startedAtUtc: readString$a(raw, 'startedAtUtc') ??
5153
+ readString$a(raw, 'createdAtUtc') ??
5154
5154
  null,
5155
- completedAtUtc: readString$9(raw, 'completedAtUtc') ?? null,
5155
+ completedAtUtc: readString$a(raw, 'completedAtUtc') ?? null,
5156
5156
  durationMs: readNumber$2(raw, 'durationMs') ?? null,
5157
- correlationId: readString$9(raw, 'correlationId') ?? null,
5157
+ correlationId: readString$a(raw, 'correlationId') ?? null,
5158
5158
  };
5159
5159
  }
5160
5160
  function normalizeNodeRun(value) {
@@ -5162,9 +5162,9 @@ function normalizeNodeRun(value) {
5162
5162
  return {
5163
5163
  ...raw,
5164
5164
  nodeRunId: readId$3(raw, 'nodeRunId') ?? '',
5165
- nodeKey: readString$9(raw, 'nodeKey') ?? '',
5166
- nodeType: String(readString$9(raw, 'nodeType') ?? ''),
5167
- status: String(readString$9(raw, 'status') ?? ''),
5165
+ nodeKey: readString$a(raw, 'nodeKey') ?? '',
5166
+ nodeType: String(readString$a(raw, 'nodeType') ?? ''),
5167
+ status: String(readString$a(raw, 'status') ?? ''),
5168
5168
  attempts: readArray$1(raw, 'attempts').map((attempt) => {
5169
5169
  const attemptRaw = asRecord$5(attempt);
5170
5170
  return {
@@ -5173,9 +5173,9 @@ function normalizeNodeRun(value) {
5173
5173
  readId$3(attemptRaw, 'nodeAttemptId') ??
5174
5174
  '',
5175
5175
  nodeAttemptId: readId$3(attemptRaw, 'nodeAttemptId'),
5176
- status: String(readString$9(attemptRaw, 'status') ?? ''),
5177
- errorMessage: readString$9(attemptRaw, 'errorMessage') ??
5178
- readString$9(attemptRaw, 'errorJson') ??
5176
+ status: String(readString$a(attemptRaw, 'status') ?? ''),
5177
+ errorMessage: readString$a(attemptRaw, 'errorMessage') ??
5178
+ readString$a(attemptRaw, 'errorJson') ??
5179
5179
  null,
5180
5180
  };
5181
5181
  }),
@@ -5187,9 +5187,9 @@ function normalizeRuntimeWait(value) {
5187
5187
  ...raw,
5188
5188
  runtimeWaitId: readId$3(raw, 'runtimeWaitId') ?? '',
5189
5189
  nodeRunId: readId$3(raw, 'nodeRunId') ?? null,
5190
- nodeKey: readString$9(raw, 'nodeKey') ?? null,
5191
- waitType: readString$9(raw, 'waitType') ?? '',
5192
- status: readString$9(raw, 'status') ?? '',
5190
+ nodeKey: readString$a(raw, 'nodeKey') ?? null,
5191
+ waitType: readString$a(raw, 'waitType') ?? '',
5192
+ status: readString$a(raw, 'status') ?? '',
5193
5193
  };
5194
5194
  }
5195
5195
  function normalizeExecutionEvent(value) {
@@ -5197,27 +5197,27 @@ function normalizeExecutionEvent(value) {
5197
5197
  return {
5198
5198
  ...raw,
5199
5199
  eventId: readId$3(raw, 'eventId') ?? '',
5200
- eventType: readString$9(raw, 'eventType') ?? '',
5201
- severity: readString$9(raw, 'severity') ?? 'Info',
5202
- occurredAtUtc: readString$9(raw, 'occurredAtUtc') ?? '',
5200
+ eventType: readString$a(raw, 'eventType') ?? '',
5201
+ severity: readString$a(raw, 'severity') ?? 'Info',
5202
+ occurredAtUtc: readString$a(raw, 'occurredAtUtc') ?? '',
5203
5203
  nodeRunId: readId$3(raw, 'nodeRunId') ?? null,
5204
5204
  nodeAttemptId: readId$3(raw, 'nodeAttemptId') ?? null,
5205
- correlationId: readString$9(raw, 'correlationId') ?? null,
5206
- data: raw['data'] ?? parseJsonValue$1(readString$9(raw, 'dataJson')),
5207
- dataJson: readString$9(raw, 'dataJson') ?? null,
5205
+ correlationId: readString$a(raw, 'correlationId') ?? null,
5206
+ data: raw['data'] ?? parseJsonValue$1(readString$a(raw, 'dataJson')),
5207
+ dataJson: readString$a(raw, 'dataJson') ?? null,
5208
5208
  };
5209
5209
  }
5210
5210
  function normalizeNodeDataDetail(detail) {
5211
5211
  const raw = asRecord$5(detail);
5212
5212
  const input = normalizeDataBlob(raw['input']);
5213
5213
  const output = normalizeDataBlob(raw['output']);
5214
- const error = parseJsonValue$1(readString$9(raw, 'errorJson'));
5214
+ const error = parseJsonValue$1(readString$a(raw, 'errorJson'));
5215
5215
  const summary = raw['summary'] ?? {
5216
5216
  input,
5217
5217
  output,
5218
5218
  error,
5219
5219
  replayAvailable: readBoolean$3(raw, 'replayAvailable') ?? false,
5220
- replayAvailabilityReason: readString$9(raw, 'replayAvailabilityReason') ?? null,
5220
+ replayAvailabilityReason: readString$a(raw, 'replayAvailabilityReason') ?? null,
5221
5221
  };
5222
5222
  return {
5223
5223
  ...detail,
@@ -5225,7 +5225,7 @@ function normalizeNodeDataDetail(detail) {
5225
5225
  summary,
5226
5226
  input: raw['input'],
5227
5227
  output: raw['output'],
5228
- errorJson: readString$9(raw, 'errorJson') ?? null,
5228
+ errorJson: readString$a(raw, 'errorJson') ?? null,
5229
5229
  attempts: readArray$1(raw, 'attempts').map((attempt) => {
5230
5230
  const attemptRaw = asRecord$5(attempt);
5231
5231
  return {
@@ -5233,7 +5233,7 @@ function normalizeNodeDataDetail(detail) {
5233
5233
  attemptId: readId$3(attemptRaw, 'attemptId') ??
5234
5234
  readId$3(attemptRaw, 'nodeAttemptId') ??
5235
5235
  '',
5236
- status: String(readString$9(attemptRaw, 'status') ?? ''),
5236
+ status: String(readString$a(attemptRaw, 'status') ?? ''),
5237
5237
  };
5238
5238
  }),
5239
5239
  logs: readArray$1(raw, 'logs').map(normalizeExecutionEvent),
@@ -5252,8 +5252,8 @@ function normalizeDataBlob(value) {
5252
5252
  return {
5253
5253
  ...raw,
5254
5254
  content: raw['content'] ??
5255
- parseJsonValue$1(readString$9(raw, 'contentJson')) ??
5256
- readString$9(raw, 'contentJson') ??
5255
+ parseJsonValue$1(readString$a(raw, 'contentJson')) ??
5256
+ readString$a(raw, 'contentJson') ??
5257
5257
  null,
5258
5258
  };
5259
5259
  }
@@ -5281,7 +5281,7 @@ function readArray$1(record, key) {
5281
5281
  const value = record[key];
5282
5282
  return Array.isArray(value) ? value : [];
5283
5283
  }
5284
- function readString$9(record, key) {
5284
+ function readString$a(record, key) {
5285
5285
  const value = record[key];
5286
5286
  return typeof value === 'string' ? value : undefined;
5287
5287
  }
@@ -6720,6 +6720,43 @@ class SetLayoutAutosavePaused {
6720
6720
  this.paused = paused;
6721
6721
  }
6722
6722
  }
6723
+ class AddCanvasNote {
6724
+ note;
6725
+ static type = '[FlowplusWorkflow] Add Canvas Note';
6726
+ constructor(note) {
6727
+ this.note = note;
6728
+ }
6729
+ }
6730
+ class UpdateCanvasNote {
6731
+ noteId;
6732
+ patch;
6733
+ static type = '[FlowplusWorkflow] Update Canvas Note';
6734
+ constructor(noteId, patch) {
6735
+ this.noteId = noteId;
6736
+ this.patch = patch;
6737
+ }
6738
+ }
6739
+ class DeleteCanvasNote {
6740
+ noteId;
6741
+ static type = '[FlowplusWorkflow] Delete Canvas Note';
6742
+ constructor(noteId) {
6743
+ this.noteId = noteId;
6744
+ }
6745
+ }
6746
+ class DuplicateCanvasNote {
6747
+ noteId;
6748
+ static type = '[FlowplusWorkflow] Duplicate Canvas Note';
6749
+ constructor(noteId) {
6750
+ this.noteId = noteId;
6751
+ }
6752
+ }
6753
+ class SelectCanvasNote {
6754
+ noteId;
6755
+ static type = '[FlowplusWorkflow] Select Canvas Note';
6756
+ constructor(noteId) {
6757
+ this.noteId = noteId;
6758
+ }
6759
+ }
6723
6760
  /* ============================================================
6724
6761
  * Validation / publish
6725
6762
  * ============================================================ */
@@ -6762,10 +6799,12 @@ class ClearSelection {
6762
6799
  class SetSelectionFromCanvas {
6763
6800
  stepIds;
6764
6801
  connectionIds;
6802
+ canvasNoteIds;
6765
6803
  static type = '[FlowplusWorkflow] Set Selection From Canvas';
6766
- constructor(stepIds, connectionIds) {
6804
+ constructor(stepIds, connectionIds, canvasNoteIds = []) {
6767
6805
  this.stepIds = stepIds;
6768
6806
  this.connectionIds = connectionIds;
6807
+ this.canvasNoteIds = canvasNoteIds;
6769
6808
  }
6770
6809
  }
6771
6810
  class SetActiveInspectorTab {
@@ -6950,6 +6989,188 @@ class RunWorkflowTest {
6950
6989
  }
6951
6990
  }
6952
6991
 
6992
+ const CANVAS_NOTE_METADATA_KEY = 'canvasNotes';
6993
+ const CANVAS_NOTE_GROUP_PREFIX = 'note:';
6994
+ const CANVAS_NOTE_DEFAULT_WIDTH = 360;
6995
+ const CANVAS_NOTE_DEFAULT_HEIGHT = 220;
6996
+ const CANVAS_NOTE_MIN_WIDTH = 220;
6997
+ const CANVAS_NOTE_MIN_HEIGHT = 140;
6998
+ const CANVAS_NOTE_DEFAULT_TEXT = "## I'm a note\nDouble click to edit. **Guide**";
6999
+ const CANVAS_NOTE_COLORS = [
7000
+ {
7001
+ key: 'amber',
7002
+ label: 'Amber',
7003
+ background: '#733e0a',
7004
+ border: '#a16207',
7005
+ text: '#fff7ed',
7006
+ },
7007
+ {
7008
+ key: 'slate',
7009
+ label: 'Slate',
7010
+ background: '#1f2937',
7011
+ border: '#475569',
7012
+ text: '#f8fafc',
7013
+ },
7014
+ {
7015
+ key: 'rose',
7016
+ label: 'Rose',
7017
+ background: '#881337',
7018
+ border: '#be123c',
7019
+ text: '#fff1f2',
7020
+ },
7021
+ {
7022
+ key: 'emerald',
7023
+ label: 'Emerald',
7024
+ background: '#064e3b',
7025
+ border: '#059669',
7026
+ text: '#ecfdf5',
7027
+ },
7028
+ {
7029
+ key: 'blue',
7030
+ label: 'Blue',
7031
+ background: '#1e3a8a',
7032
+ border: '#2563eb',
7033
+ text: '#eff6ff',
7034
+ },
7035
+ {
7036
+ key: 'violet',
7037
+ label: 'Violet',
7038
+ background: '#4c1d95',
7039
+ border: '#7c3aed',
7040
+ text: '#f5f3ff',
7041
+ },
7042
+ {
7043
+ key: 'neutral',
7044
+ label: 'Neutral',
7045
+ background: '#262626',
7046
+ border: '#737373',
7047
+ text: '#fafafa',
7048
+ },
7049
+ ];
7050
+ const DEFAULT_COLOR = CANVAS_NOTE_COLORS[0];
7051
+ function canvasNoteGroupId(noteId) {
7052
+ return `${CANVAS_NOTE_GROUP_PREFIX}${noteId}`;
7053
+ }
7054
+ function parseCanvasNoteGroupId(value) {
7055
+ if (!value?.startsWith(CANVAS_NOTE_GROUP_PREFIX))
7056
+ return null;
7057
+ const id = value.slice(CANVAS_NOTE_GROUP_PREFIX.length).trim();
7058
+ return id || null;
7059
+ }
7060
+ function resolveCanvasNoteColor(color) {
7061
+ return (CANVAS_NOTE_COLORS.find((item) => item.key === color) ?? DEFAULT_COLOR);
7062
+ }
7063
+ function createCanvasNote(position, now = new Date().toISOString()) {
7064
+ return {
7065
+ id: createCanvasNoteId(),
7066
+ text: CANVAS_NOTE_DEFAULT_TEXT,
7067
+ color: DEFAULT_COLOR.key,
7068
+ x: Math.round(position.x),
7069
+ y: Math.round(position.y),
7070
+ width: CANVAS_NOTE_DEFAULT_WIDTH,
7071
+ height: CANVAS_NOTE_DEFAULT_HEIGHT,
7072
+ createdAt: now,
7073
+ updatedAt: now,
7074
+ };
7075
+ }
7076
+ function readCanvasNotesFromLayout(layout) {
7077
+ return normalizeCanvasNotes(layout?.metadata?.[CANVAS_NOTE_METADATA_KEY]);
7078
+ }
7079
+ function writeCanvasNotesToLayout(layout, workflowId, notes) {
7080
+ const metadata = { ...(layout?.metadata ?? {}) };
7081
+ metadata[CANVAS_NOTE_METADATA_KEY] = notes.map((note) => ({ ...note }));
7082
+ return {
7083
+ workflowId,
7084
+ version: layout?.version ?? null,
7085
+ nodes: layout?.nodes ?? [],
7086
+ connections: layout?.connections ?? [],
7087
+ viewport: layout?.viewport ?? null,
7088
+ metadata,
7089
+ };
7090
+ }
7091
+ function upsertCanvasNote(notes, note) {
7092
+ const index = notes.findIndex((item) => item.id === note.id);
7093
+ if (index < 0)
7094
+ return [...notes, note];
7095
+ const next = notes.slice();
7096
+ next[index] = note;
7097
+ return next;
7098
+ }
7099
+ function patchCanvasNote(note, patch, now = new Date().toISOString()) {
7100
+ return {
7101
+ ...note,
7102
+ text: patch.text ?? note.text,
7103
+ color: resolveCanvasNoteColor(patch.color ?? note.color).key,
7104
+ x: normalizeNumber(patch.x, note.x),
7105
+ y: normalizeNumber(patch.y, note.y),
7106
+ width: Math.max(CANVAS_NOTE_MIN_WIDTH, normalizeNumber(patch.width, note.width)),
7107
+ height: Math.max(CANVAS_NOTE_MIN_HEIGHT, normalizeNumber(patch.height, note.height)),
7108
+ updatedAt: now,
7109
+ };
7110
+ }
7111
+ function duplicateCanvasNote(note, now = new Date().toISOString()) {
7112
+ return {
7113
+ ...note,
7114
+ id: createCanvasNoteId(),
7115
+ x: note.x + 32,
7116
+ y: note.y + 32,
7117
+ createdAt: now,
7118
+ updatedAt: now,
7119
+ };
7120
+ }
7121
+ function normalizeCanvasNotes(value) {
7122
+ if (!Array.isArray(value))
7123
+ return [];
7124
+ return value
7125
+ .map((item) => normalizeCanvasNote(item))
7126
+ .filter((item) => item != null);
7127
+ }
7128
+ function normalizeCanvasNote(value) {
7129
+ if (!isRecord(value))
7130
+ return null;
7131
+ const id = readString$9(value, 'id');
7132
+ if (!id)
7133
+ return null;
7134
+ return {
7135
+ id,
7136
+ text: readString$9(value, 'text') ?? CANVAS_NOTE_DEFAULT_TEXT,
7137
+ color: resolveCanvasNoteColor(readString$9(value, 'color')).key,
7138
+ x: normalizeNumber(value['x'], 120),
7139
+ y: normalizeNumber(value['y'], 120),
7140
+ width: Math.max(CANVAS_NOTE_MIN_WIDTH, normalizeNumber(value['width'], CANVAS_NOTE_DEFAULT_WIDTH)),
7141
+ height: Math.max(CANVAS_NOTE_MIN_HEIGHT, normalizeNumber(value['height'], CANVAS_NOTE_DEFAULT_HEIGHT)),
7142
+ createdAt: readString$9(value, 'createdAt'),
7143
+ updatedAt: readString$9(value, 'updatedAt'),
7144
+ };
7145
+ }
7146
+ function createCanvasNoteId() {
7147
+ const cryptoApi = globalThis.crypto;
7148
+ if (cryptoApi && typeof cryptoApi.randomUUID === 'function') {
7149
+ return cryptoApi.randomUUID();
7150
+ }
7151
+ return `note_${Date.now().toString(36)}_${Math.random()
7152
+ .toString(36)
7153
+ .slice(2, 8)}`;
7154
+ }
7155
+ function normalizeNumber(value, fallback) {
7156
+ if (typeof value === 'number' && Number.isFinite(value)) {
7157
+ return Math.round(value);
7158
+ }
7159
+ if (typeof value === 'string' && value.trim()) {
7160
+ const parsed = Number(value);
7161
+ if (Number.isFinite(parsed))
7162
+ return Math.round(parsed);
7163
+ }
7164
+ return fallback;
7165
+ }
7166
+ function readString$9(record, key) {
7167
+ const value = record[key];
7168
+ return typeof value === 'string' && value.trim() ? value : null;
7169
+ }
7170
+ function isRecord(value) {
7171
+ return value != null && typeof value === 'object' && !Array.isArray(value);
7172
+ }
7173
+
6953
7174
  /**
6954
7175
  * NGXS state model for the FlowPlus design-time workflow builder.
6955
7176
  *
@@ -7031,6 +7252,7 @@ const FLOWPLUS_WORKFLOW_DEFAULT_STATE = {
7031
7252
  selection: {
7032
7253
  stepIds: [],
7033
7254
  connectionIds: [],
7255
+ canvasNoteIds: [],
7034
7256
  triggerId: null,
7035
7257
  isWorkflowSelected: true,
7036
7258
  activeTab: 'overview',
@@ -7841,6 +8063,7 @@ let FlowplusWorkflowState = class FlowplusWorkflowState {
7841
8063
  ? Array.from(new Set([...cur.stepIds, action.stepId]))
7842
8064
  : [action.stepId],
7843
8065
  connectionIds: action.append ? cur.connectionIds : [],
8066
+ canvasNoteIds: [],
7844
8067
  triggerId: null,
7845
8068
  isWorkflowSelected: false,
7846
8069
  activeTab: cur.activeTab || 'overview',
@@ -8044,6 +8267,7 @@ let FlowplusWorkflowState = class FlowplusWorkflowState {
8044
8267
  selection: {
8045
8268
  stepIds: [],
8046
8269
  connectionIds: [action.connectionId],
8270
+ canvasNoteIds: [],
8047
8271
  triggerId: null,
8048
8272
  isWorkflowSelected: false,
8049
8273
  activeTab: 'configure',
@@ -8059,6 +8283,7 @@ let FlowplusWorkflowState = class FlowplusWorkflowState {
8059
8283
  selection: {
8060
8284
  stepIds: [],
8061
8285
  connectionIds: [],
8286
+ canvasNoteIds: [],
8062
8287
  triggerId: action.triggerId,
8063
8288
  isWorkflowSelected: false,
8064
8289
  activeTab: 'configure',
@@ -8370,6 +8595,95 @@ let FlowplusWorkflowState = class FlowplusWorkflowState {
8370
8595
  }
8371
8596
  return undefined;
8372
8597
  }
8598
+ addCanvasNote(ctx, action) {
8599
+ const state = ctx.getState();
8600
+ const workflowId = state.builder.workflowId;
8601
+ if (!workflowId)
8602
+ return;
8603
+ const notes = upsertCanvasNote(readCanvasNotesFromLayout(state.builder.layout), action.note);
8604
+ this.patchCanvasNotes(ctx, workflowId, notes, [action.note.id]);
8605
+ return ctx.dispatch(new MarkLayoutDirty());
8606
+ }
8607
+ updateCanvasNote(ctx, action) {
8608
+ const state = ctx.getState();
8609
+ const workflowId = state.builder.workflowId;
8610
+ if (!workflowId)
8611
+ return;
8612
+ const notes = readCanvasNotesFromLayout(state.builder.layout);
8613
+ const next = notes.map((note) => note.id === action.noteId ? patchCanvasNote(note, action.patch) : note);
8614
+ if (!next.some((note) => note.id === action.noteId))
8615
+ return;
8616
+ this.patchCanvasNotes(ctx, workflowId, next);
8617
+ return ctx.dispatch(new MarkLayoutDirty());
8618
+ }
8619
+ deleteCanvasNote(ctx, action) {
8620
+ const state = ctx.getState();
8621
+ const workflowId = state.builder.workflowId;
8622
+ if (!workflowId)
8623
+ return;
8624
+ const notes = readCanvasNotesFromLayout(state.builder.layout);
8625
+ const next = notes.filter((note) => note.id !== action.noteId);
8626
+ if (next.length === notes.length)
8627
+ return;
8628
+ const selected = state.builder.selection.canvasNoteIds.includes(action.noteId)
8629
+ ? []
8630
+ : state.builder.selection.canvasNoteIds;
8631
+ this.patchCanvasNotes(ctx, workflowId, next, selected);
8632
+ return ctx.dispatch(new MarkLayoutDirty());
8633
+ }
8634
+ duplicateCanvasNote(ctx, action) {
8635
+ const state = ctx.getState();
8636
+ const workflowId = state.builder.workflowId;
8637
+ if (!workflowId)
8638
+ return;
8639
+ const notes = readCanvasNotesFromLayout(state.builder.layout);
8640
+ const note = notes.find((item) => item.id === action.noteId);
8641
+ if (!note)
8642
+ return;
8643
+ const copy = duplicateCanvasNote(note);
8644
+ this.patchCanvasNotes(ctx, workflowId, [...notes, copy], [copy.id]);
8645
+ return ctx.dispatch(new MarkLayoutDirty());
8646
+ }
8647
+ selectCanvasNote(ctx, action) {
8648
+ const state = ctx.getState();
8649
+ ctx.patchState({
8650
+ builder: {
8651
+ ...state.builder,
8652
+ selection: {
8653
+ stepIds: [],
8654
+ connectionIds: [],
8655
+ canvasNoteIds: action.noteId ? [action.noteId] : [],
8656
+ triggerId: null,
8657
+ isWorkflowSelected: false,
8658
+ activeTab: 'overview',
8659
+ },
8660
+ },
8661
+ ui: { ...state.ui, isInspectorOpen: false },
8662
+ });
8663
+ }
8664
+ patchCanvasNotes(ctx, workflowId, notes, selectedNoteIds) {
8665
+ const state = ctx.getState();
8666
+ const selection = selectedNoteIds === undefined
8667
+ ? state.builder.selection
8668
+ : {
8669
+ stepIds: [],
8670
+ connectionIds: [],
8671
+ canvasNoteIds: selectedNoteIds,
8672
+ triggerId: null,
8673
+ isWorkflowSelected: false,
8674
+ activeTab: selectedNoteIds.length > 1 ? 'multi' : 'overview',
8675
+ };
8676
+ ctx.patchState({
8677
+ builder: {
8678
+ ...state.builder,
8679
+ layout: writeCanvasNotesToLayout(state.builder.layout, workflowId, notes),
8680
+ selection,
8681
+ layoutSaveStatus: 'dirty',
8682
+ dirty: { ...state.builder.dirty, layout: true },
8683
+ },
8684
+ ui: { ...state.ui, isInspectorOpen: false },
8685
+ });
8686
+ }
8373
8687
  saveLayout(ctx) {
8374
8688
  if (this.layoutTimer) {
8375
8689
  clearTimeout(this.layoutTimer);
@@ -8483,8 +8797,13 @@ let FlowplusWorkflowState = class FlowplusWorkflowState {
8483
8797
  setSelectionFromCanvas(ctx, action) {
8484
8798
  const state = ctx.getState();
8485
8799
  const cur = state.builder.selection;
8486
- const nothing = action.stepIds.length === 0 && action.connectionIds.length === 0;
8487
- const single = action.stepIds.length + action.connectionIds.length === 1;
8800
+ const nothing = action.stepIds.length === 0 &&
8801
+ action.connectionIds.length === 0 &&
8802
+ action.canvasNoteIds.length === 0;
8803
+ const single = action.stepIds.length +
8804
+ action.connectionIds.length +
8805
+ action.canvasNoteIds.length ===
8806
+ 1;
8488
8807
  let activeTab = cur.activeTab;
8489
8808
  if (nothing)
8490
8809
  activeTab = 'overview';
@@ -8497,12 +8816,16 @@ let FlowplusWorkflowState = class FlowplusWorkflowState {
8497
8816
  else if (action.connectionIds.length === 1) {
8498
8817
  activeTab = 'configure';
8499
8818
  }
8819
+ else if (action.canvasNoteIds.length === 1) {
8820
+ activeTab = 'overview';
8821
+ }
8500
8822
  ctx.patchState({
8501
8823
  builder: {
8502
8824
  ...state.builder,
8503
8825
  selection: {
8504
8826
  stepIds: action.stepIds,
8505
8827
  connectionIds: action.connectionIds,
8828
+ canvasNoteIds: action.canvasNoteIds,
8506
8829
  triggerId: null,
8507
8830
  // Clicking empty canvas no longer auto-selects the workflow root —
8508
8831
  // the canvas-first shell expects the inspector to close on empty
@@ -8519,7 +8842,9 @@ let FlowplusWorkflowState = class FlowplusWorkflowState {
8519
8842
  // empty canvas still closes whatever was open.
8520
8843
  ui: {
8521
8844
  ...state.ui,
8522
- isInspectorOpen: nothing ? false : state.ui.isInspectorOpen,
8845
+ isInspectorOpen: nothing || action.canvasNoteIds.length > 0
8846
+ ? false
8847
+ : state.ui.isInspectorOpen,
8523
8848
  },
8524
8849
  });
8525
8850
  }
@@ -9226,6 +9551,21 @@ __decorate([
9226
9551
  __decorate([
9227
9552
  Action(SetLayoutAutosavePaused)
9228
9553
  ], FlowplusWorkflowState.prototype, "setLayoutAutosavePaused", null);
9554
+ __decorate([
9555
+ Action(AddCanvasNote)
9556
+ ], FlowplusWorkflowState.prototype, "addCanvasNote", null);
9557
+ __decorate([
9558
+ Action(UpdateCanvasNote)
9559
+ ], FlowplusWorkflowState.prototype, "updateCanvasNote", null);
9560
+ __decorate([
9561
+ Action(DeleteCanvasNote)
9562
+ ], FlowplusWorkflowState.prototype, "deleteCanvasNote", null);
9563
+ __decorate([
9564
+ Action(DuplicateCanvasNote)
9565
+ ], FlowplusWorkflowState.prototype, "duplicateCanvasNote", null);
9566
+ __decorate([
9567
+ Action(SelectCanvasNote)
9568
+ ], FlowplusWorkflowState.prototype, "selectCanvasNote", null);
9229
9569
  __decorate([
9230
9570
  Action(SaveLayout)
9231
9571
  ], FlowplusWorkflowState.prototype, "saveLayout", null);
@@ -9438,7 +9778,7 @@ FlowplusWorkflowState = FlowplusWorkflowState_1 = __decorate([
9438
9778
  ], FlowplusWorkflowState);
9439
9779
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FlowplusWorkflowState, decorators: [{
9440
9780
  type: Injectable
9441
- }], propDecorators: { loadWorkflowList: [], setStudioFilter: [], createWorkflow: [], duplicateWorkflow: [], deleteWorkflow: [], loadWorkflowBuilder: [], applyBuilderSnapshot: [], reloadWorkflowBuilder: [], clearWorkflowBuilder: [], loadWorkflowCatalog: [], setWorkflowCatalog: [], loadContextCatalog: [], loadContextCatalogForStep: [], setContextCatalog: [], updateWorkflowMetadata: [], commitWorkflowMetadata: [], setWorkflowDefinition: [], updateWorkflowResources: [], updateWorkflowVariables: [], selectStep: [], createStep: [], updateStep: [], deleteStep: [], moveStep: [], selectConnection: [], selectTrigger: [], createConnection: [], updateConnection: [], deleteConnection: [], loadTriggers: [], createTrigger: [], updateTrigger: [], deleteTrigger: [], loadLayout: [], markLayoutDirty: [], setLayoutAutosavePaused: [], saveLayout: [], validateWorkflow: [], setValidation: [], setLayout: [], publishWorkflow: [], unpublishWorkflow: [], setSelection: [], setSelectionFromCanvas: [], setActiveInspectorTab: [], setBottomPanelTab: [], setBottomPanelOpen: [], setMinimap: [], setPaletteOpen: [], setInspectorOpen: [], setCreateDialogOpen: [], setPaletteSearch: [], setCanvasViewport: [], setReadonly: [], setPendingOperation: [], clearPendingOperation: [], clearConflict: [], undoBuilderCommand: [], redoBuilderCommand: [], clearCommandHistory: [], setSelectedRuntimeTrigger: [], runAutomationTrigger: [], pollAutomationExecution: [], loadLatestAutomationExecution: [], loadAutomationExecution: [], applyAutomationExecutionDetail: [], clearAutomationRuntimeState: [], selectRuntimeNodeRun: [], runWorkflowTest: [] } });
9781
+ }], propDecorators: { loadWorkflowList: [], setStudioFilter: [], createWorkflow: [], duplicateWorkflow: [], deleteWorkflow: [], loadWorkflowBuilder: [], applyBuilderSnapshot: [], reloadWorkflowBuilder: [], clearWorkflowBuilder: [], loadWorkflowCatalog: [], setWorkflowCatalog: [], loadContextCatalog: [], loadContextCatalogForStep: [], setContextCatalog: [], updateWorkflowMetadata: [], commitWorkflowMetadata: [], setWorkflowDefinition: [], updateWorkflowResources: [], updateWorkflowVariables: [], selectStep: [], createStep: [], updateStep: [], deleteStep: [], moveStep: [], selectConnection: [], selectTrigger: [], createConnection: [], updateConnection: [], deleteConnection: [], loadTriggers: [], createTrigger: [], updateTrigger: [], deleteTrigger: [], loadLayout: [], markLayoutDirty: [], setLayoutAutosavePaused: [], addCanvasNote: [], updateCanvasNote: [], deleteCanvasNote: [], duplicateCanvasNote: [], selectCanvasNote: [], saveLayout: [], validateWorkflow: [], setValidation: [], setLayout: [], publishWorkflow: [], unpublishWorkflow: [], setSelection: [], setSelectionFromCanvas: [], setActiveInspectorTab: [], setBottomPanelTab: [], setBottomPanelOpen: [], setMinimap: [], setPaletteOpen: [], setInspectorOpen: [], setCreateDialogOpen: [], setPaletteSearch: [], setCanvasViewport: [], setReadonly: [], setPendingOperation: [], clearPendingOperation: [], clearConflict: [], undoBuilderCommand: [], redoBuilderCommand: [], clearCommandHistory: [], setSelectedRuntimeTrigger: [], runAutomationTrigger: [], pollAutomationExecution: [], loadLatestAutomationExecution: [], loadAutomationExecution: [], applyAutomationExecutionDetail: [], clearAutomationRuntimeState: [], selectRuntimeNodeRun: [], runWorkflowTest: [] } });
9442
9782
  function collectRuntimeFacts(detail) {
9443
9783
  const nodeKeyByRunId = new Map((detail.nodeRuns ?? []).map((nodeRun) => [
9444
9784
  String(nodeRun.nodeRunId),
@@ -9831,6 +10171,8 @@ class FlowplusWorkflowFacade {
9831
10171
  selection = select(FlowplusWorkflowState.selection);
9832
10172
  viewport = select(FlowplusWorkflowState.viewport);
9833
10173
  layout = select(FlowplusWorkflowState.layout);
10174
+ canvasNotes = computed(() => readCanvasNotesFromLayout(this.layout()), ...(ngDevMode ? [{ debugName: "canvasNotes" }] : /* istanbul ignore next */ []));
10175
+ selectedCanvasNoteIds = computed(() => this.selection().canvasNoteIds, ...(ngDevMode ? [{ debugName: "selectedCanvasNoteIds" }] : /* istanbul ignore next */ []));
9834
10176
  saveStatus = select(FlowplusWorkflowState.saveStatus);
9835
10177
  layoutSaveStatus = select(FlowplusWorkflowState.layoutSaveStatus);
9836
10178
  dirtyFlags = select(FlowplusWorkflowState.dirtyFlags);
@@ -10244,20 +10586,24 @@ class FlowplusWorkflowFacade {
10244
10586
  return this.store.dispatch(new SetSelection({
10245
10587
  stepIds: [],
10246
10588
  connectionIds: [],
10589
+ canvasNoteIds: [],
10247
10590
  triggerId: null,
10248
10591
  isWorkflowSelected: true,
10249
10592
  activeTab: 'overview',
10250
10593
  }));
10251
10594
  }
10252
10595
  /** Set selection from a Foblex `fSelectionChange` payload. */
10253
- setSelectionFromCanvas(stepIds, connectionIds) {
10254
- return this.store.dispatch(new SetSelectionFromCanvas(stepIds, connectionIds));
10596
+ setSelectionFromCanvas(stepIds, connectionIds, canvasNoteIds = []) {
10597
+ return this.store.dispatch(new SetSelectionFromCanvas(stepIds, connectionIds, canvasNoteIds));
10255
10598
  }
10256
10599
  /** Select every step (Ctrl/Cmd+A). */
10257
10600
  selectAll() {
10258
10601
  const ids = this.steps().map((s) => s.id);
10259
10602
  return this.store.dispatch(new SetSelectionFromCanvas(ids, []));
10260
10603
  }
10604
+ selectCanvasNote(noteId) {
10605
+ return this.store.dispatch(new SelectCanvasNote(noteId));
10606
+ }
10261
10607
  setInspectorTab(tab) {
10262
10608
  return this.store.dispatch(new SetActiveInspectorTab(tab));
10263
10609
  }
@@ -10301,6 +10647,21 @@ class FlowplusWorkflowFacade {
10301
10647
  deleteTrigger(triggerId) {
10302
10648
  return this.store.dispatch(new DeleteTrigger(triggerId));
10303
10649
  }
10650
+ /* -------- canvas notes -------- */
10651
+ addCanvasNote(position) {
10652
+ const note = createCanvasNote(position);
10653
+ this.store.dispatch(new AddCanvasNote(note));
10654
+ return note;
10655
+ }
10656
+ updateCanvasNote(noteId, patch) {
10657
+ return this.store.dispatch(new UpdateCanvasNote(noteId, patch));
10658
+ }
10659
+ deleteCanvasNote(noteId) {
10660
+ return this.store.dispatch(new DeleteCanvasNote(noteId));
10661
+ }
10662
+ duplicateCanvasNote(noteId) {
10663
+ return this.store.dispatch(new DuplicateCanvasNote(noteId));
10664
+ }
10304
10665
  /* -------- layout -------- */
10305
10666
  loadLayout() {
10306
10667
  return this.store.dispatch(new LoadLayout());
@@ -10346,6 +10707,28 @@ class FlowplusWorkflowFacade {
10346
10707
  y: trigger.y,
10347
10708
  };
10348
10709
  }
10710
+ let layoutForNotes = state.layout;
10711
+ if ((args.notes?.length ?? 0) > 0) {
10712
+ const notes = readCanvasNotesFromLayout(state.layout);
10713
+ let notesChanged = false;
10714
+ const nextNotes = notes.map((note) => {
10715
+ const update = args.notes?.find((item) => item.noteId === note.id);
10716
+ if (!update)
10717
+ return note;
10718
+ const next = patchCanvasNote(note, update);
10719
+ if (next.x !== note.x ||
10720
+ next.y !== note.y ||
10721
+ next.width !== note.width ||
10722
+ next.height !== note.height) {
10723
+ notesChanged = true;
10724
+ }
10725
+ return next;
10726
+ });
10727
+ if (notesChanged) {
10728
+ changed = true;
10729
+ layoutForNotes = writeCanvasNotesToLayout(state.layout, state.workflowId, nextNotes);
10730
+ }
10731
+ }
10349
10732
  if (!changed)
10350
10733
  return false;
10351
10734
  if ((args.triggers?.length ?? 0) > 0) {
@@ -10353,11 +10736,17 @@ class FlowplusWorkflowFacade {
10353
10736
  }
10354
10737
  this.store.dispatch(new SetLayout({
10355
10738
  workflowId: state.workflowId,
10356
- version: state.layout?.version ?? null,
10739
+ version: layoutForNotes?.version ?? null,
10357
10740
  nodes: Array.from(nodesByStepId.values()),
10358
- connections: state.layout?.connections ?? [],
10359
- viewport: state.layout?.viewport ?? null,
10360
- metadata,
10741
+ connections: layoutForNotes?.connections ?? [],
10742
+ viewport: layoutForNotes?.viewport ?? null,
10743
+ metadata: {
10744
+ ...metadata,
10745
+ ...(layoutForNotes?.metadata ?? {}),
10746
+ ...((args.triggers?.length ?? 0) > 0
10747
+ ? { triggerPositions }
10748
+ : {}),
10749
+ },
10361
10750
  }));
10362
10751
  if (options.scheduleSave !== false) {
10363
10752
  this.store.dispatch(new MarkLayoutDirty());
@@ -18290,6 +18679,135 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
18290
18679
  type: Output
18291
18680
  }] } });
18292
18681
 
18682
+ class CanvasNoteComponent {
18683
+ host = inject((ElementRef));
18684
+ note = input.required(...(ngDevMode ? [{ debugName: "note" }] : /* istanbul ignore next */ []));
18685
+ selected = input(false, ...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
18686
+ editing = input(false, ...(ngDevMode ? [{ debugName: "editing" }] : /* istanbul ignore next */ []));
18687
+ colors = input(CANVAS_NOTE_COLORS, ...(ngDevMode ? [{ debugName: "colors" }] : /* istanbul ignore next */ []));
18688
+ contentChange = output();
18689
+ colorChange = output();
18690
+ duplicate = output();
18691
+ remove = output();
18692
+ editStart = output();
18693
+ editEnd = output();
18694
+ resizeHandle = EFResizeHandleType;
18695
+ draft = signal('', ...(ngDevMode ? [{ debugName: "draft" }] : /* istanbul ignore next */ []));
18696
+ textareaInputs = {
18697
+ spellcheck: true,
18698
+ };
18699
+ color = computed(() => resolveCanvasNoteColor(this.note().color), ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
18700
+ noteLabel = computed(() => `Canvas note ${this.note().text.split(/\r?\n/)[0] ?? ''}`.trim(), ...(ngDevMode ? [{ debugName: "noteLabel" }] : /* istanbul ignore next */ []));
18701
+ markdownLines = computed(() => parseMarkdown(this.note().text), ...(ngDevMode ? [{ debugName: "markdownLines" }] : /* istanbul ignore next */ []));
18702
+ constructor() {
18703
+ effect(() => {
18704
+ if (this.editing())
18705
+ return;
18706
+ this.draft.set(this.note().text);
18707
+ });
18708
+ effect(() => {
18709
+ if (!this.editing())
18710
+ return;
18711
+ requestAnimationFrame(() => {
18712
+ this.host.nativeElement.querySelector('textarea')?.focus();
18713
+ });
18714
+ });
18715
+ }
18716
+ startEdit(event) {
18717
+ event?.stopPropagation();
18718
+ this.draft.set(this.note().text);
18719
+ this.editStart.emit({ noteId: this.note().id });
18720
+ }
18721
+ updateDraft(value) {
18722
+ this.draft.set(value ?? '');
18723
+ }
18724
+ finishEdit(event) {
18725
+ event?.stopPropagation();
18726
+ const text = this.draft().trimEnd();
18727
+ if (text !== this.note().text) {
18728
+ this.contentChange.emit({ noteId: this.note().id, text });
18729
+ }
18730
+ this.editEnd.emit({ noteId: this.note().id });
18731
+ }
18732
+ finishEditFromKeyboard(event) {
18733
+ event.preventDefault();
18734
+ this.finishEdit(event);
18735
+ }
18736
+ iconForColor(color) {
18737
+ return color.key === this.note().color ? 'general.check' : undefined;
18738
+ }
18739
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: CanvasNoteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
18740
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: CanvasNoteComponent, isStandalone: true, selector: "fp-canvas-note", inputs: { note: { classPropertyName: "note", publicName: "note", isSignal: true, isRequired: true, transformFunction: null }, selected: { classPropertyName: "selected", publicName: "selected", isSignal: true, isRequired: false, transformFunction: null }, editing: { classPropertyName: "editing", publicName: "editing", isSignal: true, isRequired: false, transformFunction: null }, colors: { classPropertyName: "colors", publicName: "colors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { contentChange: "contentChange", colorChange: "colorChange", duplicate: "duplicate", remove: "remove", editStart: "editStart", editEnd: "editEnd" }, host: { attributes: { "tabindex": "0" }, properties: { "class.is-selected": "selected()", "style.width.px": "note().width", "style.height.px": "note().height", "style.--fp-note-bg": "color().background", "style.--fp-note-border": "color().border", "style.--fp-note-text": "color().text", "attr.aria-label": "noteLabel()" }, classAttribute: "fp-canvas-note-host group/note block touch-none select-none outline-none" }, ngImport: i0, template: "@if (selected()) {\n <div\n class=\"absolute -top-10 start-2 z-10 flex items-center gap-1.5 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background)/95 px-1.5 py-1 shadow-lg backdrop-blur\"\n role=\"toolbar\"\n [attr.aria-label]=\"'flowplus.canvas.noteActions' | transloco\"\n fDragBlocker\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\"\n >\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.edit-05\"\n styleClass=\"fp-note-tool-button\"\n [tooltip]=\"'flowplus.canvas.editNote' | transloco\"\n (onClick)=\"startEdit($event)\"\n />\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.copy-05\"\n styleClass=\"fp-note-tool-button\"\n [tooltip]=\"'flowplus.canvas.duplicateNote' | transloco\"\n (onClick)=\"duplicate.emit({ noteId: note().id })\"\n />\n <mt-button\n variant=\"text\"\n severity=\"danger\"\n size=\"small\"\n icon=\"general.trash-01\"\n styleClass=\"fp-note-tool-button\"\n [tooltip]=\"'flowplus.canvas.deleteNote' | transloco\"\n (onClick)=\"remove.emit({ noteId: note().id })\"\n />\n <span class=\"mx-1 h-4 w-px bg-(--p-content-border-color)\"></span>\n @for (option of colors(); track option.key) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n [icon]=\"iconForColor(option)\"\n styleClass=\"fp-note-swatch\"\n [style.--fp-note-swatch]=\"option.background\"\n [attr.aria-label]=\"option.label\"\n [tooltip]=\"option.label\"\n (onClick)=\"colorChange.emit({ noteId: note().id, color: option.key })\"\n />\n }\n </div>\n}\n\n<article\n class=\"fp-canvas-note-card relative flex h-full w-full overflow-hidden rounded-[9px] border-2 backdrop-blur-[1px] transition-[border-color,box-shadow,transform] duration-150\"\n (dblclick)=\"startEdit($event)\"\n>\n @if (editing()) {\n <div\n class=\"flex h-full w-full flex-col p-3\"\n fDragBlocker\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\"\n (focusout)=\"finishEdit($event)\"\n >\n <mt-textarea-field\n class=\"fp-note-editor min-h-0 flex-1\"\n [field]=\"false\"\n [rows]=\"'8'\"\n [maxLength]=\"5000\"\n [pInputs]=\"textareaInputs\"\n [placeholder]=\"'flowplus.canvas.notePlaceholder' | transloco\"\n [ngModel]=\"draft()\"\n (ngModelChange)=\"updateDraft($event)\"\n (keydown.control.enter)=\"finishEditFromKeyboard($event)\"\n (keydown.meta.enter)=\"finishEditFromKeyboard($event)\"\n />\n </div>\n } @else {\n <div class=\"fp-scroll min-h-0 flex-1 overflow-auto px-5 py-4\">\n @for (line of markdownLines(); track $index) {\n @if (line.kind === \"empty\") {\n <div class=\"h-3\"></div>\n } @else {\n <p\n class=\"m-0 max-w-full break-words\"\n [class.text-[28px]]=\"line.kind === 'h1'\"\n [class.text-[24px]]=\"line.kind === 'h2'\"\n [class.text-[15px]]=\"line.kind === 'body'\"\n [class.font-bold]=\"line.kind !== 'body'\"\n [class.leading-tight]=\"line.kind !== 'body'\"\n [class.leading-6]=\"line.kind === 'body'\"\n [class.mb-2]=\"line.kind !== 'body'\"\n [class.mb-1]=\"line.kind === 'body'\"\n >\n @for (segment of line.segments; track $index) {\n <span [class.font-bold]=\"segment.bold\">{{ segment.text }}</span>\n }\n </p>\n }\n }\n </div>\n }\n\n <span\n fResizeHandle\n [fResizeHandleType]=\"resizeHandle.RIGHT_BOTTOM\"\n class=\"fp-note-resize-handle absolute end-1.5 bottom-1.5 h-4 w-4 cursor-nwse-resize rounded-sm border-e-2 border-b-2 opacity-0 transition-opacity\"\n [attr.aria-label]=\"'flowplus.canvas.resizeNote' | transloco\"\n ></span>\n</article>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: FFlowModule }, { kind: "directive", type: i1$2.FResizeHandleDirective, selector: "[fResizeHandle]", inputs: ["fResizeHandleType"] }, { kind: "directive", type: i1$2.FDragBlockerDirective, selector: "[fDragBlocker]" }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: TextareaField, selector: "mt-textarea-field", inputs: ["field", "hint", "label", "placeholder", "class", "readonly", "noErrorStyle", "pInputs", "rows", "required", "maxLength"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
18741
+ }
18742
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: CanvasNoteComponent, decorators: [{
18743
+ type: Component,
18744
+ args: [{ selector: 'fp-canvas-note', standalone: true, imports: [
18745
+ CommonModule,
18746
+ FormsModule,
18747
+ FFlowModule,
18748
+ TranslocoModule,
18749
+ Button,
18750
+ TextareaField,
18751
+ Tooltip,
18752
+ Icon,
18753
+ ], host: {
18754
+ class: 'fp-canvas-note-host group/note block touch-none select-none outline-none',
18755
+ '[class.is-selected]': 'selected()',
18756
+ '[style.width.px]': 'note().width',
18757
+ '[style.height.px]': 'note().height',
18758
+ '[style.--fp-note-bg]': 'color().background',
18759
+ '[style.--fp-note-border]': 'color().border',
18760
+ '[style.--fp-note-text]': 'color().text',
18761
+ '[attr.aria-label]': 'noteLabel()',
18762
+ tabindex: '0',
18763
+ }, template: "@if (selected()) {\n <div\n class=\"absolute -top-10 start-2 z-10 flex items-center gap-1.5 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background)/95 px-1.5 py-1 shadow-lg backdrop-blur\"\n role=\"toolbar\"\n [attr.aria-label]=\"'flowplus.canvas.noteActions' | transloco\"\n fDragBlocker\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\"\n >\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.edit-05\"\n styleClass=\"fp-note-tool-button\"\n [tooltip]=\"'flowplus.canvas.editNote' | transloco\"\n (onClick)=\"startEdit($event)\"\n />\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.copy-05\"\n styleClass=\"fp-note-tool-button\"\n [tooltip]=\"'flowplus.canvas.duplicateNote' | transloco\"\n (onClick)=\"duplicate.emit({ noteId: note().id })\"\n />\n <mt-button\n variant=\"text\"\n severity=\"danger\"\n size=\"small\"\n icon=\"general.trash-01\"\n styleClass=\"fp-note-tool-button\"\n [tooltip]=\"'flowplus.canvas.deleteNote' | transloco\"\n (onClick)=\"remove.emit({ noteId: note().id })\"\n />\n <span class=\"mx-1 h-4 w-px bg-(--p-content-border-color)\"></span>\n @for (option of colors(); track option.key) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n [icon]=\"iconForColor(option)\"\n styleClass=\"fp-note-swatch\"\n [style.--fp-note-swatch]=\"option.background\"\n [attr.aria-label]=\"option.label\"\n [tooltip]=\"option.label\"\n (onClick)=\"colorChange.emit({ noteId: note().id, color: option.key })\"\n />\n }\n </div>\n}\n\n<article\n class=\"fp-canvas-note-card relative flex h-full w-full overflow-hidden rounded-[9px] border-2 backdrop-blur-[1px] transition-[border-color,box-shadow,transform] duration-150\"\n (dblclick)=\"startEdit($event)\"\n>\n @if (editing()) {\n <div\n class=\"flex h-full w-full flex-col p-3\"\n fDragBlocker\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\"\n (focusout)=\"finishEdit($event)\"\n >\n <mt-textarea-field\n class=\"fp-note-editor min-h-0 flex-1\"\n [field]=\"false\"\n [rows]=\"'8'\"\n [maxLength]=\"5000\"\n [pInputs]=\"textareaInputs\"\n [placeholder]=\"'flowplus.canvas.notePlaceholder' | transloco\"\n [ngModel]=\"draft()\"\n (ngModelChange)=\"updateDraft($event)\"\n (keydown.control.enter)=\"finishEditFromKeyboard($event)\"\n (keydown.meta.enter)=\"finishEditFromKeyboard($event)\"\n />\n </div>\n } @else {\n <div class=\"fp-scroll min-h-0 flex-1 overflow-auto px-5 py-4\">\n @for (line of markdownLines(); track $index) {\n @if (line.kind === \"empty\") {\n <div class=\"h-3\"></div>\n } @else {\n <p\n class=\"m-0 max-w-full break-words\"\n [class.text-[28px]]=\"line.kind === 'h1'\"\n [class.text-[24px]]=\"line.kind === 'h2'\"\n [class.text-[15px]]=\"line.kind === 'body'\"\n [class.font-bold]=\"line.kind !== 'body'\"\n [class.leading-tight]=\"line.kind !== 'body'\"\n [class.leading-6]=\"line.kind === 'body'\"\n [class.mb-2]=\"line.kind !== 'body'\"\n [class.mb-1]=\"line.kind === 'body'\"\n >\n @for (segment of line.segments; track $index) {\n <span [class.font-bold]=\"segment.bold\">{{ segment.text }}</span>\n }\n </p>\n }\n }\n </div>\n }\n\n <span\n fResizeHandle\n [fResizeHandleType]=\"resizeHandle.RIGHT_BOTTOM\"\n class=\"fp-note-resize-handle absolute end-1.5 bottom-1.5 h-4 w-4 cursor-nwse-resize rounded-sm border-e-2 border-b-2 opacity-0 transition-opacity\"\n [attr.aria-label]=\"'flowplus.canvas.resizeNote' | transloco\"\n ></span>\n</article>\n" }]
18764
+ }], ctorParameters: () => [], propDecorators: { note: [{ type: i0.Input, args: [{ isSignal: true, alias: "note", required: true }] }], selected: [{ type: i0.Input, args: [{ isSignal: true, alias: "selected", required: false }] }], editing: [{ type: i0.Input, args: [{ isSignal: true, alias: "editing", required: false }] }], colors: [{ type: i0.Input, args: [{ isSignal: true, alias: "colors", required: false }] }], contentChange: [{ type: i0.Output, args: ["contentChange"] }], colorChange: [{ type: i0.Output, args: ["colorChange"] }], duplicate: [{ type: i0.Output, args: ["duplicate"] }], remove: [{ type: i0.Output, args: ["remove"] }], editStart: [{ type: i0.Output, args: ["editStart"] }], editEnd: [{ type: i0.Output, args: ["editEnd"] }] } });
18765
+ function parseMarkdown(text) {
18766
+ const rawLines = text.split(/\r?\n/);
18767
+ const lines = rawLines.length ? rawLines : [''];
18768
+ return lines.map((line) => {
18769
+ const trimmed = line.trim();
18770
+ if (!trimmed)
18771
+ return { kind: 'empty', segments: [] };
18772
+ if (trimmed.startsWith('## ')) {
18773
+ return {
18774
+ kind: 'h2',
18775
+ segments: parseInline(trimmed.slice(3).trim()),
18776
+ };
18777
+ }
18778
+ if (trimmed.startsWith('# ')) {
18779
+ return {
18780
+ kind: 'h1',
18781
+ segments: parseInline(trimmed.slice(2).trim()),
18782
+ };
18783
+ }
18784
+ return { kind: 'body', segments: parseInline(line) };
18785
+ });
18786
+ }
18787
+ function parseInline(text) {
18788
+ const parts = [];
18789
+ let rest = text;
18790
+ while (rest.length) {
18791
+ const start = rest.indexOf('**');
18792
+ if (start < 0) {
18793
+ parts.push({ text: rest, bold: false });
18794
+ break;
18795
+ }
18796
+ if (start > 0) {
18797
+ parts.push({ text: rest.slice(0, start), bold: false });
18798
+ }
18799
+ const afterStart = rest.slice(start + 2);
18800
+ const end = afterStart.indexOf('**');
18801
+ if (end < 0) {
18802
+ parts.push({ text: rest.slice(start), bold: false });
18803
+ break;
18804
+ }
18805
+ parts.push({ text: afterStart.slice(0, end), bold: true });
18806
+ rest = afterStart.slice(end + 2);
18807
+ }
18808
+ return parts;
18809
+ }
18810
+
18293
18811
  function canvasIssueSeverity(issues) {
18294
18812
  const list = issues ?? [];
18295
18813
  if (list.some((issue) => issue.severity === 'Error'))
@@ -19240,12 +19758,23 @@ class FlowCanvasComponent {
19240
19758
  canvasBackgroundClick = new EventEmitter();
19241
19759
  /** Add affordance → page opens the left palette (no floating popover). */
19242
19760
  requestAddStep = new EventEmitter();
19761
+ noteUpdate = new EventEmitter();
19762
+ noteDuplicate = new EventEmitter();
19763
+ noteDelete = new EventEmitter();
19243
19764
  /* -------- VM signals -------- */
19765
+ canvasLayers = [
19766
+ EFCanvasLayer.GROUPS,
19767
+ EFCanvasLayer.CONNECTIONS,
19768
+ EFCanvasLayer.NODES,
19769
+ ];
19244
19770
  nodes = computed(() => this.store.nodeVms(), ...(ngDevMode ? [{ debugName: "nodes" }] : /* istanbul ignore next */ []));
19771
+ canvasNotes = computed(() => this.store.canvasNotes(), ...(ngDevMode ? [{ debugName: "canvasNotes" }] : /* istanbul ignore next */ []));
19245
19772
  edges = computed(() => this.store.edgeVms(), ...(ngDevMode ? [{ debugName: "edges" }] : /* istanbul ignore next */ []));
19246
19773
  triggerNodes = computed(() => this.store.triggerNodeVms(), ...(ngDevMode ? [{ debugName: "triggerNodes" }] : /* istanbul ignore next */ []));
19247
19774
  branchLanes = computed(() => this.store.branchLaneVms(), ...(ngDevMode ? [{ debugName: "branchLanes" }] : /* istanbul ignore next */ []));
19248
19775
  viewport = computed(() => this.store.viewport(), ...(ngDevMode ? [{ debugName: "viewport" }] : /* istanbul ignore next */ []));
19776
+ selectedCanvasNoteIds = computed(() => this.store.selectedCanvasNoteIds(), ...(ngDevMode ? [{ debugName: "selectedCanvasNoteIds" }] : /* istanbul ignore next */ []));
19777
+ editingNoteId = signal(null, ...(ngDevMode ? [{ debugName: "editingNoteId" }] : /* istanbul ignore next */ []));
19249
19778
  /**
19250
19779
  * Minimum world size the minimap renders. A very small value (Foblex's
19251
19780
  * default-ish 80) makes one or two nodes fill the whole minimap, which
@@ -19410,7 +19939,7 @@ class FlowCanvasComponent {
19410
19939
  }
19411
19940
  /** Real Foblex nodes on the canvas: steps + virtual trigger nodes. */
19412
19941
  totalNodeCount() {
19413
- return this.nodes().length + this.triggerNodes().length;
19942
+ return (this.nodes().length + this.triggerNodes().length + this.canvasNotes().length);
19414
19943
  }
19415
19944
  /**
19416
19945
  * Keep the toolbar zoom label and the persisted viewport in sync with
@@ -19452,6 +19981,11 @@ class FlowCanvasComponent {
19452
19981
  focusTrigger(triggerId) {
19453
19982
  this.requestFocus(`trigger:${triggerId}`, false);
19454
19983
  }
19984
+ /** Public: center + select a sticky canvas note. */
19985
+ focusNote(noteId) {
19986
+ this.requestFocus(canvasNoteGroupId(noteId), false);
19987
+ this.store.selectCanvasNote(noteId);
19988
+ }
19455
19989
  /**
19456
19990
  * Public: center + select a connection. Foblex centers nodes/groups, not
19457
19991
  * edges, so we center on the connection's source node and highlight the
@@ -19492,6 +20026,10 @@ class FlowCanvasComponent {
19492
20026
  });
19493
20027
  }
19494
20028
  canvasNodeExists(canvasNodeId) {
20029
+ const noteId = parseCanvasNoteGroupId(canvasNodeId);
20030
+ if (noteId) {
20031
+ return this.canvasNotes().some((note) => note.id === noteId);
20032
+ }
19495
20033
  if (canvasNodeId.startsWith('trigger:')) {
19496
20034
  const tid = Number(canvasNodeId.slice('trigger:'.length));
19497
20035
  return this.triggerNodes().some((t) => t.triggerId === tid);
@@ -19935,7 +20473,17 @@ class FlowCanvasComponent {
19935
20473
  onMoveNodes(event) {
19936
20474
  const nodePositions = [];
19937
20475
  const triggerPositions = [];
20476
+ const notePositions = [];
19938
20477
  for (const move of event.nodes) {
20478
+ const noteId = parseCanvasNoteGroupId(move.id);
20479
+ if (noteId) {
20480
+ notePositions.push({
20481
+ noteId,
20482
+ x: move.position.x,
20483
+ y: move.position.y,
20484
+ });
20485
+ continue;
20486
+ }
19939
20487
  const parsed = parseNodeId(move.id);
19940
20488
  if (parsed.kind === 'step' && parsed.stepId != null) {
19941
20489
  nodePositions.push({
@@ -19952,18 +20500,49 @@ class FlowCanvasComponent {
19952
20500
  });
19953
20501
  }
19954
20502
  }
19955
- this.commitCanvasPositions(nodePositions, triggerPositions);
20503
+ this.commitCanvasPositions(nodePositions, triggerPositions, notePositions);
19956
20504
  }
19957
20505
  onStepPositionChange(stepId, position) {
19958
- this.commitCanvasPositions([{ stepId, x: position.x, y: position.y }], []);
20506
+ this.commitCanvasPositions([{ stepId, x: position.x, y: position.y }], [], []);
19959
20507
  }
19960
20508
  onTriggerPositionChange(triggerId, position) {
19961
- this.commitCanvasPositions([], [{ triggerId, x: position.x, y: position.y }]);
20509
+ this.commitCanvasPositions([], [{ triggerId, x: position.x, y: position.y }], []);
20510
+ }
20511
+ onNotePositionChange(noteId, position) {
20512
+ this.commitCanvasPositions([], [], [{ noteId, x: position.x, y: position.y }]);
20513
+ }
20514
+ onNoteSizeChange(noteId, size) {
20515
+ this.noteUpdate.emit({
20516
+ noteId,
20517
+ patch: { width: size.width, height: size.height },
20518
+ });
20519
+ }
20520
+ onNoteContentChange(event) {
20521
+ this.noteUpdate.emit({
20522
+ noteId: event.noteId,
20523
+ patch: { text: event.text },
20524
+ });
20525
+ }
20526
+ onNoteColorChange(event) {
20527
+ this.noteUpdate.emit({
20528
+ noteId: event.noteId,
20529
+ patch: { color: event.color },
20530
+ });
20531
+ }
20532
+ onNoteDuplicate(event) {
20533
+ this.noteDuplicate.emit(event);
20534
+ }
20535
+ onNoteDelete(event) {
20536
+ this.noteDelete.emit(event);
19962
20537
  }
19963
- commitCanvasPositions(nodes, triggers) {
19964
- if (!nodes.length && !triggers.length)
20538
+ onNoteEditStart(noteId) {
20539
+ this.store.selectCanvasNote(noteId);
20540
+ this.editingNoteId.set(noteId);
20541
+ }
20542
+ commitCanvasPositions(nodes, triggers, notes = []) {
20543
+ if (!nodes.length && !triggers.length && !notes.length)
19965
20544
  return false;
19966
- const changed = this.store.setLayoutPositions({ nodes, triggers }, { scheduleSave: !this.positionDragActive });
20545
+ const changed = this.store.setLayoutPositions({ nodes, triggers, notes }, { scheduleSave: !this.positionDragActive });
19967
20546
  if (this.positionDragActive && changed) {
19968
20547
  this.positionChangedDuringDrag = true;
19969
20548
  }
@@ -19971,7 +20550,7 @@ class FlowCanvasComponent {
19971
20550
  }
19972
20551
  syncCanvasPositionsFromDom() {
19973
20552
  const positions = this.readCanvasPositionSnapshot();
19974
- return this.commitCanvasPositions(positions.nodes, positions.triggers);
20553
+ return this.commitCanvasPositions(positions.nodes, positions.triggers, positions.notes);
19975
20554
  }
19976
20555
  syncStableCanvasPositions() {
19977
20556
  if (this.isPositionSyncSuppressed())
@@ -19982,7 +20561,7 @@ class FlowCanvasComponent {
19982
20561
  this.lastObservedPositionSignature = signature;
19983
20562
  return;
19984
20563
  }
19985
- const changed = this.commitCanvasPositions(positions.nodes, positions.triggers);
20564
+ const changed = this.commitCanvasPositions(positions.nodes, positions.triggers, positions.notes);
19986
20565
  if (this.positionDragActive) {
19987
20566
  const shouldSave = changed || this.positionChangedDuringDrag;
19988
20567
  this.positionDragActive = false;
@@ -20015,7 +20594,16 @@ class FlowCanvasComponent {
20015
20594
  : null;
20016
20595
  })
20017
20596
  .filter((item) => item != null);
20018
- return { nodes, triggers };
20597
+ const notes = Array.from(this.hostEl.querySelectorAll('fp-canvas-note[data-fp-note]'))
20598
+ .map((el) => {
20599
+ const noteId = el.dataset['fpNote'];
20600
+ const position = readTranslatePosition(el);
20601
+ return noteId && position
20602
+ ? { noteId, x: position.x, y: position.y }
20603
+ : null;
20604
+ })
20605
+ .filter((item) => item != null);
20606
+ return { nodes, triggers, notes };
20019
20607
  }
20020
20608
  schedulePointerPositionSync(delayMs) {
20021
20609
  if (this.isPositionSyncSuppressed())
@@ -20135,8 +20723,18 @@ class FlowCanvasComponent {
20135
20723
  if (c != null)
20136
20724
  connectionIds.push(c);
20137
20725
  }
20138
- this.store.setSelectionFromCanvas(stepIds, connectionIds);
20139
- if (stepIds.length === 0 && connectionIds.length === 0) {
20726
+ const canvasNoteIds = [];
20727
+ for (const id of event.fGroupIds) {
20728
+ const noteId = parseCanvasNoteGroupId(id);
20729
+ if (noteId)
20730
+ canvasNoteIds.push(noteId);
20731
+ }
20732
+ this.store.setSelectionFromCanvas(stepIds, connectionIds, canvasNoteIds);
20733
+ if (!canvasNoteIds.length)
20734
+ this.editingNoteId.set(null);
20735
+ if (stepIds.length === 0 &&
20736
+ connectionIds.length === 0 &&
20737
+ canvasNoteIds.length === 0) {
20140
20738
  this.canvasBackgroundClick.emit();
20141
20739
  }
20142
20740
  }
@@ -20194,6 +20792,9 @@ class FlowCanvasComponent {
20194
20792
  isConnectionSelected(connectionId) {
20195
20793
  return this.selectionConnectionIds().includes(connectionId);
20196
20794
  }
20795
+ canvasNoteGroupId(noteId) {
20796
+ return canvasNoteGroupId(noteId);
20797
+ }
20197
20798
  /* -------- node hover-bar forwards -------- */
20198
20799
  onNodeQuickAdd(e) {
20199
20800
  this.nodeQuickAdd.emit(e);
@@ -20247,7 +20848,7 @@ class FlowCanvasComponent {
20247
20848
  this.triggerStartDisconnect.emit(e);
20248
20849
  }
20249
20850
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FlowCanvasComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20250
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: FlowCanvasComponent, isStandalone: true, selector: "fp-flow-canvas", outputs: { paletteDrop: "paletteDrop", connectionCreate: "connectionCreate", connectionReassign: "connectionReassign", triggerStartConnect: "triggerStartConnect", triggerStartReassign: "triggerStartReassign", triggerStartDisconnect: "triggerStartDisconnect", connectionQuickAdd: "connectionQuickAdd", quickAddPick: "quickAddPick", autoLayoutRequested: "autoLayoutRequested", loaded: "loaded", nodeQuickAdd: "nodeQuickAdd", nodePortPlusClick: "nodePortPlusClick", nodeDuplicate: "nodeDuplicate", nodeRemove: "nodeRemove", nodeOpenDetails: "nodeOpenDetails", edgeInsertStep: "edgeInsertStep", edgeRemove: "edgeRemove", edgeEditFormula: "edgeEditFormula", edgeOpenDetails: "edgeOpenDetails", assignNodeToConnection: "assignNodeToConnection", openChildWorkflow: "openChildWorkflow", starterAddTrigger: "starterAddTrigger", triggerOpenDetails: "triggerOpenDetails", triggerExecute: "triggerExecute", triggerToggleEnabled: "triggerToggleEnabled", triggerDelete: "triggerDelete", canvasBackgroundClick: "canvasBackgroundClick", requestAddStep: "requestAddStep" }, providers: [
20851
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: FlowCanvasComponent, isStandalone: true, selector: "fp-flow-canvas", outputs: { paletteDrop: "paletteDrop", connectionCreate: "connectionCreate", connectionReassign: "connectionReassign", triggerStartConnect: "triggerStartConnect", triggerStartReassign: "triggerStartReassign", triggerStartDisconnect: "triggerStartDisconnect", connectionQuickAdd: "connectionQuickAdd", quickAddPick: "quickAddPick", autoLayoutRequested: "autoLayoutRequested", loaded: "loaded", nodeQuickAdd: "nodeQuickAdd", nodePortPlusClick: "nodePortPlusClick", nodeDuplicate: "nodeDuplicate", nodeRemove: "nodeRemove", nodeOpenDetails: "nodeOpenDetails", edgeInsertStep: "edgeInsertStep", edgeRemove: "edgeRemove", edgeEditFormula: "edgeEditFormula", edgeOpenDetails: "edgeOpenDetails", assignNodeToConnection: "assignNodeToConnection", openChildWorkflow: "openChildWorkflow", starterAddTrigger: "starterAddTrigger", triggerOpenDetails: "triggerOpenDetails", triggerExecute: "triggerExecute", triggerToggleEnabled: "triggerToggleEnabled", triggerDelete: "triggerDelete", canvasBackgroundClick: "canvasBackgroundClick", requestAddStep: "requestAddStep", noteUpdate: "noteUpdate", noteDuplicate: "noteDuplicate", noteDelete: "noteDelete" }, providers: [
20251
20852
  provideFFlow({ id: 'flowplus-builder' }, withReflowOnResize({
20252
20853
  collision: EFReflowCollision.STOP,
20253
20854
  deltaSource: EFReflowDeltaSource.EDGE_BASED,
@@ -20255,7 +20856,7 @@ class FlowCanvasComponent {
20255
20856
  maxCascadeDepth: 8,
20256
20857
  maxAbsoluteShiftPerPlan: 10000,
20257
20858
  })),
20258
- ], viewQueries: [{ propertyName: "flow", first: true, predicate: ["flow"], descendants: true, isSignal: true }, { propertyName: "canvas", first: true, predicate: ["canvas"], descendants: true, isSignal: true }], ngImport: i0, template: "<f-flow\n #flow\n fDraggable\n [vCellSize]=\"gridCellSize\"\n [hCellSize]=\"gridCellSize\"\n [fCellSizeWhileDragging]=\"false\"\n (fFullRendered)=\"onFullRendered()\"\n (fNodesRendered)=\"onNodesRendered()\"\n (fCreateNode)=\"onCreateNode($event)\"\n (fCreateConnection)=\"onCreateConnection($event)\"\n (fReassignConnection)=\"onReassignConnection($event)\"\n (fDragStarted)=\"onDragStarted($event)\"\n (fDragEnded)=\"onDragEnded()\"\n (fMoveNodes)=\"onMoveNodes($event)\"\n (fSelectionChange)=\"onSelectionChange($event)\"\n>\n <!-- Background dot grid \u2014 its spacing is locked to the drag cell size so\n nodes snap exactly onto the visible dots (Foblex grid-system). -->\n <f-background>\n <f-circle-pattern [radius]=\"gridCellSize\" />\n </f-background>\n\n <!-- Alignment guides + marquee selection are `f-flow` children (NOT\n `f-canvas` children \u2014 `f-canvas` only projects connections/nodes/groups,\n so helpers placed inside it are silently dropped). This mirrors the\n Foblex call-center reference layout. -->\n <f-line-alignment [fAlignThreshold]=\"20\" />\n <f-selection-area />\n\n <!-- Auto-pan: when dragging a node / connection near the viewport edge, the\n canvas pans to follow, so you can wire across off-screen nodes without\n letting go. (Foblex `f-auto-pan` plugin.) -->\n <f-auto-pan [fEdgeThreshold]=\"36\" [fSpeed]=\"8\" />\n\n <f-canvas\n #canvas\n fZoom\n [debounceTime]=\"200\"\n (fCanvasChange)=\"onCanvasChange($event)\"\n >\n <!-- Floating behavior anchors the preview at the connector boundary along\n the center\u2192pointer axis, so a connection dragged from the \"+\" outlet\n leaves it cleanly (NOT from its bottom edge \u2014 `fixed` defaults an AUTO\n connectable side to bottom) and snaps to the target's nearest edge. -->\n <f-connection-for-create\n [fBehavior]=\"'floating'\"\n fType=\"adaptive-curve\"\n [fOffset]=\"0\"\n >\n <f-connection-marker-arrow />\n </f-connection-for-create>\n\n <!-- Auto-snap: while dragging a new/reassigned connection, snap to the\n nearest connector within the threshold (forgiving connect UX). -->\n <f-snap-connection\n [fBehavior]=\"'floating'\"\n fType=\"adaptive-curve\"\n [fSnapThreshold]=\"40\"\n [fOffset]=\"0\"\n >\n <f-connection-marker-arrow />\n </f-snap-connection>\n\n <!-- ngProjectAs is REQUIRED: `f-canvas` distributes projected content\n into its layer containers via selective `<ng-content select=\"...\">`\n with NO catch-all slot. A wrapper component (`fp-flow-node`, etc.)\n does not match `[fNode]` / `f-connection` on its host, so without\n `ngProjectAs` Foblex silently drops it and the canvas renders empty.\n The actual Foblex directive lives on the wrapper's inner element and\n self-registers via the mediator, so routing the wrapper into the\n right layer is all that's needed. -->\n @for (lane of branchLanes(); track lane.id) {\n <div\n ngProjectAs=\"[fGroup]\"\n class=\"pointer-events-none absolute z-0 rounded-[14px] border border-dashed border-[color-mix(in_srgb,rgb(var(--fp-parallel))_55%,transparent)] bg-[color-mix(in_srgb,rgb(var(--fp-parallel))_8%,transparent)]\"\n [attr.data-color-token]=\"lane.colorToken\"\n [style.left.px]=\"lane.x\"\n [style.top.px]=\"lane.y\"\n [style.width.px]=\"lane.width\"\n [style.height.px]=\"lane.height\"\n >\n <span\n class=\"pointer-events-auto absolute -top-2.5 start-3 rounded-full border border-[color-mix(in_srgb,rgb(var(--fp-parallel))_40%,transparent)] bg-(--p-content-background) px-2 py-px text-[10.5px] font-semibold text-[rgb(var(--fp-parallel))]\"\n >{{ lane.label }}</span\n >\n </div>\n }\n\n <!-- Connections are inlined here (NOT wrapped in a component) so each\n <f-connection> is a DIRECT child of Foblex's connections container.\n A wrapper would make `hostElement.parentElement` the wrapper instead\n of the container, which makes Foblex's select layer-raise throw\n \"Unknown container\" the moment an edge is selected. Same call-center\n pattern used for nodes/triggers. -->\n @for (edge of edges(); track edge.id) {\n <f-connection\n class=\"group\"\n [attr.data-fp-edge]=\"edge.connectionId\"\n [fConnectionId]=\"edge.id\"\n [fOutputId]=\"edge.sourcePortId\"\n [fInputId]=\"edge.targetPortId\"\n [fBehavior]=\"'fixed'\"\n [fType]=\"'adaptive-curve'\"\n [fOffset]=\"32\"\n fInputSide=\"calculate\"\n [fReassignableStart]=\"edge.edgeKind !== 'triggerStart'\"\n [fReassignDisabled]=\"edge.edgeKind === 'triggerStart'\"\n [class.fp-edge]=\"true\"\n [class.is-trigger-start]=\"edge.edgeKind === 'triggerStart'\"\n [class.is-drop-target]=\"isDropTargetEdge(edge.connectionId)\"\n [class.is-selected]=\"isConnectionSelected(edge.connectionId)\"\n [class.is-error]=\"edge.isInvalid\"\n [class.is-formula]=\"edge.isFormula\"\n [class.is-runtime-active]=\"edge.runtimeState === 'active'\"\n [class.is-runtime-completed]=\"edge.runtimeState === 'completed'\"\n [class.is-runtime-failed]=\"edge.runtimeState === 'failed'\"\n [class.is-runtime-waiting]=\"edge.runtimeState === 'waiting'\"\n (dblclick)=\"\n edge.edgeKind === 'triggerStart' && edge.triggerId != null\n ? onTriggerOpenDetails({ triggerId: edge.triggerId })\n : onEdgeOpenDetails({ connectionId: edge.connectionId })\n \"\n >\n <!-- Arrow marker at the target \u2014 the connection carries the arrowhead;\n the input lives on the node host (no separate input element). -->\n <f-connection-marker-arrow [type]=\"markerEnd\" />\n\n <!-- Editable waypoints are intentionally NOT rendered: there is no\n backend persistence wired for manual bends, so exposing draggable\n waypoints would silently lose the user's edits on reload. -->\n\n <!-- Label / formula badge \u2014 pinned to the CENTER of the line\n (connection-content position 0.5). Fades out on hover/selected so\n the centered action box can take its place. -->\n @if (edge.label) {\n <div\n fConnectionContent\n [position]=\"0.5\"\n class=\"pointer-events-none opacity-100 transition-opacity duration-150 group-hover:opacity-0 group-[.is-selected]:opacity-0\"\n >\n <span\n class=\"inline-flex items-center gap-1.5 whitespace-nowrap rounded-full border border-[rgb(var(--fp-connector))] bg-(--p-content-background) py-1 pe-2.5 ps-2 text-[11px] font-semibold text-(--p-text-color) shadow-[0_1px_3px_rgba(15,23,42,0.1)] transition-colors group-[.is-selected]:border-(--p-primary-color) group-[.is-selected]:text-(--p-primary-color)\"\n >\n <span\n class=\"size-1.5 flex-none rounded-full bg-[rgb(var(--fp-connector))] group-[.is-selected]:bg-(--p-primary-color)\"\n ></span>\n {{ edge.label }}\n </span>\n </div>\n } @else if (edge.isFormula) {\n <div\n fConnectionContent\n [position]=\"0.5\"\n class=\"pointer-events-none opacity-100 transition-opacity duration-150 group-hover:opacity-0 group-[.is-selected]:opacity-0\"\n >\n <span\n class=\"grid h-5 w-5 place-items-center rounded-full border border-(--p-content-border-color) bg-(--p-content-background) font-mono text-[12px] italic text-(--p-primary-color) shadow-sm\"\n title=\"Conditional route\"\n >\u0192</span\n >\n </div>\n }\n\n <!-- Centered action box \u2014 edit (opens the connection MODAL) + delete.\n Same content anchor (position 0.5), revealed on hover/selected and\n overlapping the label so the actions stay centered ON the line. -->\n @if (edgeIssueCount(edge) > 0) {\n <div\n fConnectionContent\n [position]=\"0.38\"\n class=\"pointer-events-auto\"\n >\n <span\n class=\"fp-node-badge grid h-[18px] w-[18px] cursor-help place-items-center rounded-sm bg-(--p-content-background) shadow-sm outline-none transition-transform focus-visible:ring-2 focus-visible:ring-(--p-primary-color)\"\n [style.color]=\"edgeIssueColor(edge)\"\n tabindex=\"0\"\n role=\"img\"\n [attr.aria-label]=\"edgeIssueTooltip(edge)\"\n [mtTooltip]=\"edgeIssueTooltip(edge)\"\n [tooltipStyleClass]=\"edgeIssueTooltipClass(edge)\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [escape]=\"true\"\n [showDelay]=\"180\"\n [hideDelay]=\"80\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"\n $event.stopPropagation();\n edge.edgeKind === 'triggerStart' && edge.triggerId != null\n ? onTriggerOpenDetails({ triggerId: edge.triggerId })\n : onEdgeOpenDetails({ connectionId: edge.connectionId })\n \"\n (dblclick)=\"$event.stopPropagation()\"\n >\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M10 3.4 18 16.8 2 16.8 Z\"\n fill=\"currentColor\"\n stroke=\"currentColor\"\n stroke-width=\"2.6\"\n stroke-linejoin=\"round\"\n />\n <rect x=\"9.05\" y=\"8\" width=\"1.9\" height=\"4.6\" rx=\"0.95\" fill=\"#fff\" />\n <circle cx=\"10\" cy=\"14.6\" r=\"1.05\" fill=\"#fff\" />\n </svg>\n </span>\n </div>\n }\n\n @if (edge.runtimeState && edge.runtimeTooltip) {\n <div\n fConnectionContent\n [position]=\"0.62\"\n class=\"pointer-events-auto\"\n >\n <span\n class=\"inline-flex h-[18px] w-[18px] cursor-help items-center justify-center rounded-full border bg-(--p-content-background) shadow-sm outline-none focus-visible:ring-2 focus-visible:ring-(--p-primary-color)\"\n [class.animate-pulse]=\"edge.runtimeState === 'active'\"\n [style.borderColor]=\"edgeRuntimeColor(edge)\"\n [style.color]=\"edgeRuntimeColor(edge)\"\n tabindex=\"0\"\n role=\"status\"\n [attr.aria-label]=\"edge.runtimeTooltip\"\n [mtTooltip]=\"edge.runtimeTooltip\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [escape]=\"true\"\n [showDelay]=\"180\"\n [hideDelay]=\"80\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\"\n (dblclick)=\"$event.stopPropagation()\"\n >\n <mt-icon [icon]=\"edgeRuntimeIcon(edge)\" class=\"[&_svg]:size-3\" />\n </span>\n </div>\n }\n\n <div\n fConnectionContent\n [position]=\"0.5\"\n class=\"pointer-events-none opacity-0 transition-opacity duration-150 group-hover:pointer-events-auto group-hover:opacity-100 group-[.is-selected]:pointer-events-auto group-[.is-selected]:opacity-100\"\n >\n <div\n class=\"flex items-center gap-0.5 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) p-1 shadow-md\"\n role=\"toolbar\"\n aria-label=\"Connection actions\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\"\n >\n @if (edge.edgeKind === \"triggerStart\" && edge.triggerId != null) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.settings-01\"\n [tooltip]=\"'flowplus.trigger.node.configure' | transloco\"\n (onClick)=\"onTriggerOpenDetails({ triggerId: edge.triggerId })\"\n />\n <mt-button\n variant=\"text\"\n severity=\"danger\"\n size=\"small\"\n icon=\"general.link-broken-01\"\n [tooltip]=\"'flowplus.trigger.node.unlink' | transloco\"\n (onClick)=\"\n onTriggerStartDisconnect({ triggerId: edge.triggerId })\n \"\n />\n } @else {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.edit-05\"\n [tooltip]=\"'flowplus.inspector.connection.title' | transloco\"\n (onClick)=\"\n onEdgeOpenDetails({ connectionId: edge.connectionId })\n \"\n />\n <mt-button\n variant=\"text\"\n severity=\"danger\"\n size=\"small\"\n icon=\"general.trash-01\"\n [tooltip]=\"'flowplus.edge.delete' | transloco\"\n (onClick)=\"onEdgeRemove({ connectionId: edge.connectionId })\"\n />\n }\n </div>\n </div>\n </f-connection>\n }\n\n <!-- The Foblex `fNode` directive lives on the component element itself\n (not an inner div). This keeps the node element a DIRECT child of\n Foblex's nodes container, so `hostElement.parentElement` IS that\n container \u2014 required by the select/move layer-raise (otherwise it\n throws \"Unknown container\"). This is the Foblex call-center pattern;\n the `fNode` attribute also routes it into the `[fNode]` projection\n slot, so `ngProjectAs` is not needed. -->\n @for (node of nodes(); track node.id) {\n <fp-flow-node\n fNode\n fDragHandle\n fNodeInput\n [attr.data-fp-node]=\"node.stepId\"\n [fInputId]=\"node.inputs[0]?.id\"\n [fInputMultiple]=\"node.inputs[0]?.allowMultiple ?? true\"\n [fInputDisabled]=\"!node.inputs.length\"\n [fNodeId]=\"node.id\"\n [fNodePosition]=\"{ x: node.x, y: node.y }\"\n (fNodePositionChange)=\"onStepPositionChange(node.stepId, $event)\"\n [node]=\"node\"\n [connectedOutputKeys]=\"connectedOutputKeys(node.stepId)\"\n (quickAdd)=\"onNodeQuickAdd($event)\"\n (portPlusClick)=\"onNodePortPlus($event)\"\n (duplicate)=\"onNodeDuplicate($event)\"\n (remove)=\"onNodeRemove($event)\"\n (openDetails)=\"onNodeOpenDetails($event)\"\n (openChild)=\"onOpenChild($event)\"\n />\n }\n\n @for (t of triggerNodes(); track t.triggerId) {\n <fp-trigger-node\n fNode\n fDragHandle\n [attr.data-fp-trigger]=\"t.triggerId\"\n [fNodeId]=\"'trigger:' + t.triggerId\"\n [fNodePosition]=\"{ x: t.x, y: t.y }\"\n (fNodePositionChange)=\"onTriggerPositionChange(t.triggerId, $event)\"\n [trigger]=\"t\"\n [noFirstStep]=\"!t.startStepId\"\n (addFirstStep)=\"onTriggerAddFirstStep($event)\"\n (configure)=\"onTriggerOpenDetails($event)\"\n (execute)=\"onTriggerExecute($event)\"\n (toggleEnabled)=\"onTriggerToggleEnabled($event)\"\n (remove)=\"onTriggerDelete($event)\"\n />\n }\n </f-canvas>\n\n <!-- Minimap \u2014 `f-flow` child (NOT inside `f-canvas`). Toggled from the\n canvas controls; `fMinSize` keeps a couple of nodes zoomed-out enough\n to give real spatial context. -->\n @if (minimapVisible()) {\n <f-minimap [fMinSize]=\"minimapMinSize\" />\n }\n\n @if (showStarter() && !store.loading()) {\n <fp-starter-card\n (addTrigger)=\"onStarterAddTrigger($event)\"\n (directPick)=\"onStarterDirectPick($event)\"\n />\n }\n\n <fp-canvas-controls\n [zoom]=\"zoom()\"\n [minimapVisible]=\"minimapVisible()\"\n (zoomIn)=\"zoomIn()\"\n (zoomOut)=\"zoomOut()\"\n (fitView)=\"fitView()\"\n (resetView)=\"resetView()\"\n (toggleMinimap)=\"toggleMinimap()\"\n (autoLayout)=\"autoLayout()\"\n />\n</f-flow>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FFlowModule }, { kind: "component", type: i1$2.FFlowComponent, selector: "f-flow", inputs: ["fFlowId", "fCache"], outputs: ["fNodesRendered", "fFullRendered", "fLoaded"] }, { kind: "component", type: i1$2.FCanvasComponent, selector: "f-canvas", inputs: ["position", "scale", "debounceTime", "fLayers"], outputs: ["fCanvasChange"] }, { kind: "component", type: i1$2.FBackgroundComponent, selector: "f-background" }, { kind: "component", type: i1$2.FCirclePatternComponent, selector: "f-circle-pattern", inputs: ["id", "color", "radius"] }, { kind: "component", type: i1$2.FAutoPan, selector: "f-auto-pan", inputs: ["fEdgeThreshold", "fSpeed", "fAcceleration"] }, { kind: "directive", type: i1$2.FZoomDirective, selector: "f-canvas[fZoom]", inputs: ["fZoom", "fWheelTrigger", "fDblClickTrigger", "fZoomMinimum", "fZoomMaximum", "fZoomStep", "fZoomDblClickStep"] }, { kind: "component", type: i1$2.FSelectionArea, selector: "f-selection-area", inputs: ["fTrigger"] }, { kind: "directive", type: i1$2.FConnectionContent, selector: "[fConnectionContent]", inputs: ["position", "offset", "align"] }, { kind: "component", type: i1$2.FConnectionMarkerArrow, selector: "f-connection-marker-arrow", inputs: ["type"] }, { kind: "component", type: i1$2.FConnectionComponent, selector: "f-connection", inputs: ["fConnectionId", "fOutputId", "fInputId", "fRadius", "fOffset", "fBehavior", "fType", "fSelectionDisabled", "fReassignableStart", "fReassignDisabled", "fInputSide", "fOutputSide"], exportAs: ["fComponent"] }, { kind: "component", type: i1$2.FConnectionForCreateComponent, selector: "f-connection-for-create", inputs: ["fRadius", "fOffset", "fBehavior", "fType", "fInputSide", "fOutputSide"] }, { kind: "component", type: i1$2.FSnapConnectionComponent, selector: "f-snap-connection", inputs: ["fSnapThreshold", "fRadius", "fOffset", "fBehavior", "fType", "fInputSide", "fOutputSide"] }, { kind: "directive", type: i1$2.FNodeInputDirective, selector: "[fNodeInput]", inputs: ["fInputId", "fInputCategory", "fInputMultiple", "fInputDisabled", "fInputConnectableSide"], exportAs: ["fNodeInput"] }, { kind: "component", type: i1$2.FLineAlignmentComponent, selector: "f-line-alignment", inputs: ["fAlignThreshold"], exportAs: ["fComponent"] }, { kind: "component", type: i1$2.FMinimapComponent, selector: "f-minimap", inputs: ["fMinSize", "fNodeRenderLimit"], exportAs: ["fComponent"] }, { kind: "directive", type: i1$2.FNodeDirective, selector: "[fNode]", inputs: ["fNodeId", "fNodeParentId", "fNodePosition", "fNodeSize", "fNodeRotate", "fConnectOnNode", "fMinimapClass", "fNodeDraggingDisabled", "fNodeSelectionDisabled", "fIncludePadding", "fAutoExpandOnChildHit", "fAutoSizeToFitChildren"], outputs: ["fNodePositionChange", "fNodeSizeChange", "fNodeRotateChange"], exportAs: ["fComponent"] }, { kind: "directive", type: i1$2.FDragHandleDirective, selector: "[fDragHandle]" }, { kind: "directive", type: i1$2.FDraggableDirective, selector: "f-flow[fDraggable]", inputs: ["fDraggableDisabled", "fMultiSelectTrigger", "fReassignConnectionTrigger", "fCreateConnectionTrigger", "fConnectionWaypointsTrigger", "fMoveControlPointTrigger", "fNodeResizeTrigger", "fNodeRotateTrigger", "fNodeMoveTrigger", "fCanvasMoveTrigger", "fExternalItemTrigger", "fEmitOnNodeIntersect", "vCellSize", "hCellSize", "fCellSizeWhileDragging"], outputs: ["fSelectionChange", "fNodeIntersectedWithConnections", "fNodeConnectionsIntersection", "fCreateNode", "fMoveNodes", "fReassignConnection", "fCreateConnection", "fConnectionWaypointsChanged", "fDropToGroup", "fDragStarted", "fDragEnded"], exportAs: ["fDraggable"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: Tooltip, selector: "[mtTooltip]" }, { kind: "component", type: FlowNodeComponent, selector: "fp-flow-node", inputs: ["node", "connectedOutputKeys"], outputs: ["positionChange", "nodeClick", "quickAdd", "portPlusClick", "duplicate", "remove", "testStep", "openChild", "openDetails"] }, { kind: "component", type: CanvasControlsComponent, selector: "fp-canvas-controls", inputs: ["zoom", "minimapVisible"], outputs: ["zoomIn", "zoomOut", "fitView", "resetView", "toggleMinimap", "autoLayout"] }, { kind: "component", type: StarterCardComponent, selector: "fp-starter-card", outputs: ["addTrigger", "directPick"] }, { kind: "component", type: TriggerNodeComponent, selector: "fp-trigger-node", inputs: ["trigger", "noFirstStep"], outputs: ["addFirstStep", "configure", "execute", "toggleEnabled", "remove", "positionChange"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
20859
+ ], viewQueries: [{ propertyName: "flow", first: true, predicate: ["flow"], descendants: true, isSignal: true }, { propertyName: "canvas", first: true, predicate: ["canvas"], descendants: true, isSignal: true }], ngImport: i0, template: "<f-flow\n #flow\n fDraggable\n [vCellSize]=\"gridCellSize\"\n [hCellSize]=\"gridCellSize\"\n [fCellSizeWhileDragging]=\"false\"\n (fFullRendered)=\"onFullRendered()\"\n (fNodesRendered)=\"onNodesRendered()\"\n (fCreateNode)=\"onCreateNode($event)\"\n (fCreateConnection)=\"onCreateConnection($event)\"\n (fReassignConnection)=\"onReassignConnection($event)\"\n (fDragStarted)=\"onDragStarted($event)\"\n (fDragEnded)=\"onDragEnded()\"\n (fMoveNodes)=\"onMoveNodes($event)\"\n (fSelectionChange)=\"onSelectionChange($event)\"\n>\n <!-- Background dot grid \u2014 its spacing is locked to the drag cell size so\n nodes snap exactly onto the visible dots (Foblex grid-system). -->\n <f-background>\n <f-circle-pattern [radius]=\"gridCellSize\" />\n </f-background>\n\n <!-- Alignment guides + marquee selection are `f-flow` children (NOT\n `f-canvas` children \u2014 `f-canvas` only projects connections/nodes/groups,\n so helpers placed inside it are silently dropped). This mirrors the\n Foblex call-center reference layout. -->\n <f-line-alignment [fAlignThreshold]=\"20\" />\n <f-selection-area />\n\n <!-- Auto-pan: when dragging a node / connection near the viewport edge, the\n canvas pans to follow, so you can wire across off-screen nodes without\n letting go. (Foblex `f-auto-pan` plugin.) -->\n <f-auto-pan [fEdgeThreshold]=\"36\" [fSpeed]=\"8\" />\n\n <f-canvas\n #canvas\n fZoom\n [debounceTime]=\"200\"\n [fLayers]=\"canvasLayers\"\n (fCanvasChange)=\"onCanvasChange($event)\"\n >\n <!-- Floating behavior anchors the preview at the connector boundary along\n the center\u2192pointer axis, so a connection dragged from the \"+\" outlet\n leaves it cleanly (NOT from its bottom edge \u2014 `fixed` defaults an AUTO\n connectable side to bottom) and snaps to the target's nearest edge. -->\n <f-connection-for-create\n [fBehavior]=\"'floating'\"\n fType=\"adaptive-curve\"\n [fOffset]=\"0\"\n >\n <f-connection-marker-arrow />\n </f-connection-for-create>\n\n <!-- Auto-snap: while dragging a new/reassigned connection, snap to the\n nearest connector within the threshold (forgiving connect UX). -->\n <f-snap-connection\n [fBehavior]=\"'floating'\"\n fType=\"adaptive-curve\"\n [fSnapThreshold]=\"40\"\n [fOffset]=\"0\"\n >\n <f-connection-marker-arrow />\n </f-snap-connection>\n\n <!-- ngProjectAs is REQUIRED: `f-canvas` distributes projected content\n into its layer containers via selective `<ng-content select=\"...\">`\n with NO catch-all slot. A wrapper component (`fp-flow-node`, etc.)\n does not match `[fNode]` / `f-connection` on its host, so without\n `ngProjectAs` Foblex silently drops it and the canvas renders empty.\n The actual Foblex directive lives on the wrapper's inner element and\n self-registers via the mediator, so routing the wrapper into the\n right layer is all that's needed. -->\n @for (note of canvasNotes(); track note.id) {\n <fp-canvas-note\n fGroup\n fDragHandle\n [attr.data-fp-note]=\"note.id\"\n [fGroupId]=\"canvasNoteGroupId(note.id)\"\n [fGroupPosition]=\"{ x: note.x, y: note.y }\"\n [fGroupSize]=\"{ width: note.width, height: note.height }\"\n [fGroupDraggingDisabled]=\"editingNoteId() === note.id\"\n [note]=\"note\"\n [selected]=\"selectedCanvasNoteIds().includes(note.id)\"\n [editing]=\"editingNoteId() === note.id\"\n (fGroupPositionChange)=\"onNotePositionChange(note.id, $event)\"\n (fGroupSizeChange)=\"onNoteSizeChange(note.id, $event)\"\n (contentChange)=\"onNoteContentChange($event)\"\n (colorChange)=\"onNoteColorChange($event)\"\n (duplicate)=\"onNoteDuplicate($event)\"\n (remove)=\"onNoteDelete($event)\"\n (editStart)=\"onNoteEditStart($event.noteId)\"\n (editEnd)=\"editingNoteId.set(null)\"\n />\n }\n\n @for (lane of branchLanes(); track lane.id) {\n <div\n ngProjectAs=\"[fGroup]\"\n class=\"pointer-events-none absolute z-0 rounded-[14px] border border-dashed border-[color-mix(in_srgb,rgb(var(--fp-parallel))_55%,transparent)] bg-[color-mix(in_srgb,rgb(var(--fp-parallel))_8%,transparent)]\"\n [attr.data-color-token]=\"lane.colorToken\"\n [style.left.px]=\"lane.x\"\n [style.top.px]=\"lane.y\"\n [style.width.px]=\"lane.width\"\n [style.height.px]=\"lane.height\"\n >\n <span\n class=\"pointer-events-auto absolute -top-2.5 start-3 rounded-full border border-[color-mix(in_srgb,rgb(var(--fp-parallel))_40%,transparent)] bg-(--p-content-background) px-2 py-px text-[10.5px] font-semibold text-[rgb(var(--fp-parallel))]\"\n >{{ lane.label }}</span\n >\n </div>\n }\n\n <!-- Connections are inlined here (NOT wrapped in a component) so each\n <f-connection> is a DIRECT child of Foblex's connections container.\n A wrapper would make `hostElement.parentElement` the wrapper instead\n of the container, which makes Foblex's select layer-raise throw\n \"Unknown container\" the moment an edge is selected. Same call-center\n pattern used for nodes/triggers. -->\n @for (edge of edges(); track edge.id) {\n <f-connection\n class=\"group\"\n [attr.data-fp-edge]=\"edge.connectionId\"\n [fConnectionId]=\"edge.id\"\n [fOutputId]=\"edge.sourcePortId\"\n [fInputId]=\"edge.targetPortId\"\n [fBehavior]=\"'fixed'\"\n [fType]=\"'adaptive-curve'\"\n [fOffset]=\"32\"\n fInputSide=\"calculate\"\n [fReassignableStart]=\"edge.edgeKind !== 'triggerStart'\"\n [fReassignDisabled]=\"edge.edgeKind === 'triggerStart'\"\n [class.fp-edge]=\"true\"\n [class.is-trigger-start]=\"edge.edgeKind === 'triggerStart'\"\n [class.is-drop-target]=\"isDropTargetEdge(edge.connectionId)\"\n [class.is-selected]=\"isConnectionSelected(edge.connectionId)\"\n [class.is-error]=\"edge.isInvalid\"\n [class.is-formula]=\"edge.isFormula\"\n [class.is-runtime-active]=\"edge.runtimeState === 'active'\"\n [class.is-runtime-completed]=\"edge.runtimeState === 'completed'\"\n [class.is-runtime-failed]=\"edge.runtimeState === 'failed'\"\n [class.is-runtime-waiting]=\"edge.runtimeState === 'waiting'\"\n (dblclick)=\"\n edge.edgeKind === 'triggerStart' && edge.triggerId != null\n ? onTriggerOpenDetails({ triggerId: edge.triggerId })\n : onEdgeOpenDetails({ connectionId: edge.connectionId })\n \"\n >\n <!-- Arrow marker at the target \u2014 the connection carries the arrowhead;\n the input lives on the node host (no separate input element). -->\n <f-connection-marker-arrow [type]=\"markerEnd\" />\n\n <!-- Editable waypoints are intentionally NOT rendered: there is no\n backend persistence wired for manual bends, so exposing draggable\n waypoints would silently lose the user's edits on reload. -->\n\n <!-- Label / formula badge \u2014 pinned to the CENTER of the line\n (connection-content position 0.5). Fades out on hover/selected so\n the centered action box can take its place. -->\n @if (edge.label) {\n <div\n fConnectionContent\n [position]=\"0.5\"\n class=\"pointer-events-none opacity-100 transition-opacity duration-150 group-hover:opacity-0 group-[.is-selected]:opacity-0\"\n >\n <span\n class=\"inline-flex items-center gap-1.5 whitespace-nowrap rounded-full border border-[rgb(var(--fp-connector))] bg-(--p-content-background) py-1 pe-2.5 ps-2 text-[11px] font-semibold text-(--p-text-color) shadow-[0_1px_3px_rgba(15,23,42,0.1)] transition-colors group-[.is-selected]:border-(--p-primary-color) group-[.is-selected]:text-(--p-primary-color)\"\n >\n <span\n class=\"size-1.5 flex-none rounded-full bg-[rgb(var(--fp-connector))] group-[.is-selected]:bg-(--p-primary-color)\"\n ></span>\n {{ edge.label }}\n </span>\n </div>\n } @else if (edge.isFormula) {\n <div\n fConnectionContent\n [position]=\"0.5\"\n class=\"pointer-events-none opacity-100 transition-opacity duration-150 group-hover:opacity-0 group-[.is-selected]:opacity-0\"\n >\n <span\n class=\"grid h-5 w-5 place-items-center rounded-full border border-(--p-content-border-color) bg-(--p-content-background) font-mono text-[12px] italic text-(--p-primary-color) shadow-sm\"\n title=\"Conditional route\"\n >\u0192</span\n >\n </div>\n }\n\n <!-- Centered action box \u2014 edit (opens the connection MODAL) + delete.\n Same content anchor (position 0.5), revealed on hover/selected and\n overlapping the label so the actions stay centered ON the line. -->\n @if (edgeIssueCount(edge) > 0) {\n <div\n fConnectionContent\n [position]=\"0.38\"\n class=\"pointer-events-auto\"\n >\n <span\n class=\"fp-node-badge grid h-[18px] w-[18px] cursor-help place-items-center rounded-sm bg-(--p-content-background) shadow-sm outline-none transition-transform focus-visible:ring-2 focus-visible:ring-(--p-primary-color)\"\n [style.color]=\"edgeIssueColor(edge)\"\n tabindex=\"0\"\n role=\"img\"\n [attr.aria-label]=\"edgeIssueTooltip(edge)\"\n [mtTooltip]=\"edgeIssueTooltip(edge)\"\n [tooltipStyleClass]=\"edgeIssueTooltipClass(edge)\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [escape]=\"true\"\n [showDelay]=\"180\"\n [hideDelay]=\"80\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"\n $event.stopPropagation();\n edge.edgeKind === 'triggerStart' && edge.triggerId != null\n ? onTriggerOpenDetails({ triggerId: edge.triggerId })\n : onEdgeOpenDetails({ connectionId: edge.connectionId })\n \"\n (dblclick)=\"$event.stopPropagation()\"\n >\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M10 3.4 18 16.8 2 16.8 Z\"\n fill=\"currentColor\"\n stroke=\"currentColor\"\n stroke-width=\"2.6\"\n stroke-linejoin=\"round\"\n />\n <rect x=\"9.05\" y=\"8\" width=\"1.9\" height=\"4.6\" rx=\"0.95\" fill=\"#fff\" />\n <circle cx=\"10\" cy=\"14.6\" r=\"1.05\" fill=\"#fff\" />\n </svg>\n </span>\n </div>\n }\n\n @if (edge.runtimeState && edge.runtimeTooltip) {\n <div\n fConnectionContent\n [position]=\"0.62\"\n class=\"pointer-events-auto\"\n >\n <span\n class=\"inline-flex h-[18px] w-[18px] cursor-help items-center justify-center rounded-full border bg-(--p-content-background) shadow-sm outline-none focus-visible:ring-2 focus-visible:ring-(--p-primary-color)\"\n [class.animate-pulse]=\"edge.runtimeState === 'active'\"\n [style.borderColor]=\"edgeRuntimeColor(edge)\"\n [style.color]=\"edgeRuntimeColor(edge)\"\n tabindex=\"0\"\n role=\"status\"\n [attr.aria-label]=\"edge.runtimeTooltip\"\n [mtTooltip]=\"edge.runtimeTooltip\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [escape]=\"true\"\n [showDelay]=\"180\"\n [hideDelay]=\"80\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\"\n (dblclick)=\"$event.stopPropagation()\"\n >\n <mt-icon [icon]=\"edgeRuntimeIcon(edge)\" class=\"[&_svg]:size-3\" />\n </span>\n </div>\n }\n\n <div\n fConnectionContent\n [position]=\"0.5\"\n class=\"pointer-events-none opacity-0 transition-opacity duration-150 group-hover:pointer-events-auto group-hover:opacity-100 group-[.is-selected]:pointer-events-auto group-[.is-selected]:opacity-100\"\n >\n <div\n class=\"flex items-center gap-0.5 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) p-1 shadow-md\"\n role=\"toolbar\"\n aria-label=\"Connection actions\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\"\n >\n @if (edge.edgeKind === \"triggerStart\" && edge.triggerId != null) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.settings-01\"\n [tooltip]=\"'flowplus.trigger.node.configure' | transloco\"\n (onClick)=\"onTriggerOpenDetails({ triggerId: edge.triggerId })\"\n />\n <mt-button\n variant=\"text\"\n severity=\"danger\"\n size=\"small\"\n icon=\"general.link-broken-01\"\n [tooltip]=\"'flowplus.trigger.node.unlink' | transloco\"\n (onClick)=\"\n onTriggerStartDisconnect({ triggerId: edge.triggerId })\n \"\n />\n } @else {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.edit-05\"\n [tooltip]=\"'flowplus.inspector.connection.title' | transloco\"\n (onClick)=\"\n onEdgeOpenDetails({ connectionId: edge.connectionId })\n \"\n />\n <mt-button\n variant=\"text\"\n severity=\"danger\"\n size=\"small\"\n icon=\"general.trash-01\"\n [tooltip]=\"'flowplus.edge.delete' | transloco\"\n (onClick)=\"onEdgeRemove({ connectionId: edge.connectionId })\"\n />\n }\n </div>\n </div>\n </f-connection>\n }\n\n <!-- The Foblex `fNode` directive lives on the component element itself\n (not an inner div). This keeps the node element a DIRECT child of\n Foblex's nodes container, so `hostElement.parentElement` IS that\n container \u2014 required by the select/move layer-raise (otherwise it\n throws \"Unknown container\"). This is the Foblex call-center pattern;\n the `fNode` attribute also routes it into the `[fNode]` projection\n slot, so `ngProjectAs` is not needed. -->\n @for (node of nodes(); track node.id) {\n <fp-flow-node\n fNode\n fDragHandle\n fNodeInput\n [attr.data-fp-node]=\"node.stepId\"\n [fInputId]=\"node.inputs[0]?.id\"\n [fInputMultiple]=\"node.inputs[0]?.allowMultiple ?? true\"\n [fInputDisabled]=\"!node.inputs.length\"\n [fNodeId]=\"node.id\"\n [fNodePosition]=\"{ x: node.x, y: node.y }\"\n (fNodePositionChange)=\"onStepPositionChange(node.stepId, $event)\"\n [node]=\"node\"\n [connectedOutputKeys]=\"connectedOutputKeys(node.stepId)\"\n (quickAdd)=\"onNodeQuickAdd($event)\"\n (portPlusClick)=\"onNodePortPlus($event)\"\n (duplicate)=\"onNodeDuplicate($event)\"\n (remove)=\"onNodeRemove($event)\"\n (openDetails)=\"onNodeOpenDetails($event)\"\n (openChild)=\"onOpenChild($event)\"\n />\n }\n\n @for (t of triggerNodes(); track t.triggerId) {\n <fp-trigger-node\n fNode\n fDragHandle\n [attr.data-fp-trigger]=\"t.triggerId\"\n [fNodeId]=\"'trigger:' + t.triggerId\"\n [fNodePosition]=\"{ x: t.x, y: t.y }\"\n (fNodePositionChange)=\"onTriggerPositionChange(t.triggerId, $event)\"\n [trigger]=\"t\"\n [noFirstStep]=\"!t.startStepId\"\n (addFirstStep)=\"onTriggerAddFirstStep($event)\"\n (configure)=\"onTriggerOpenDetails($event)\"\n (execute)=\"onTriggerExecute($event)\"\n (toggleEnabled)=\"onTriggerToggleEnabled($event)\"\n (remove)=\"onTriggerDelete($event)\"\n />\n }\n </f-canvas>\n\n <!-- Minimap \u2014 `f-flow` child (NOT inside `f-canvas`). Toggled from the\n canvas controls; `fMinSize` keeps a couple of nodes zoomed-out enough\n to give real spatial context. -->\n @if (minimapVisible()) {\n <f-minimap [fMinSize]=\"minimapMinSize\" />\n }\n\n @if (showStarter() && !store.loading()) {\n <fp-starter-card\n (addTrigger)=\"onStarterAddTrigger($event)\"\n (directPick)=\"onStarterDirectPick($event)\"\n />\n }\n\n <fp-canvas-controls\n [zoom]=\"zoom()\"\n [minimapVisible]=\"minimapVisible()\"\n (zoomIn)=\"zoomIn()\"\n (zoomOut)=\"zoomOut()\"\n (fitView)=\"fitView()\"\n (resetView)=\"resetView()\"\n (toggleMinimap)=\"toggleMinimap()\"\n (autoLayout)=\"autoLayout()\"\n />\n</f-flow>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FFlowModule }, { kind: "component", type: i1$2.FFlowComponent, selector: "f-flow", inputs: ["fFlowId", "fCache"], outputs: ["fNodesRendered", "fFullRendered", "fLoaded"] }, { kind: "component", type: i1$2.FCanvasComponent, selector: "f-canvas", inputs: ["position", "scale", "debounceTime", "fLayers"], outputs: ["fCanvasChange"] }, { kind: "component", type: i1$2.FBackgroundComponent, selector: "f-background" }, { kind: "component", type: i1$2.FCirclePatternComponent, selector: "f-circle-pattern", inputs: ["id", "color", "radius"] }, { kind: "component", type: i1$2.FAutoPan, selector: "f-auto-pan", inputs: ["fEdgeThreshold", "fSpeed", "fAcceleration"] }, { kind: "directive", type: i1$2.FZoomDirective, selector: "f-canvas[fZoom]", inputs: ["fZoom", "fWheelTrigger", "fDblClickTrigger", "fZoomMinimum", "fZoomMaximum", "fZoomStep", "fZoomDblClickStep"] }, { kind: "component", type: i1$2.FSelectionArea, selector: "f-selection-area", inputs: ["fTrigger"] }, { kind: "directive", type: i1$2.FConnectionContent, selector: "[fConnectionContent]", inputs: ["position", "offset", "align"] }, { kind: "component", type: i1$2.FConnectionMarkerArrow, selector: "f-connection-marker-arrow", inputs: ["type"] }, { kind: "component", type: i1$2.FConnectionComponent, selector: "f-connection", inputs: ["fConnectionId", "fOutputId", "fInputId", "fRadius", "fOffset", "fBehavior", "fType", "fSelectionDisabled", "fReassignableStart", "fReassignDisabled", "fInputSide", "fOutputSide"], exportAs: ["fComponent"] }, { kind: "component", type: i1$2.FConnectionForCreateComponent, selector: "f-connection-for-create", inputs: ["fRadius", "fOffset", "fBehavior", "fType", "fInputSide", "fOutputSide"] }, { kind: "component", type: i1$2.FSnapConnectionComponent, selector: "f-snap-connection", inputs: ["fSnapThreshold", "fRadius", "fOffset", "fBehavior", "fType", "fInputSide", "fOutputSide"] }, { kind: "directive", type: i1$2.FNodeInputDirective, selector: "[fNodeInput]", inputs: ["fInputId", "fInputCategory", "fInputMultiple", "fInputDisabled", "fInputConnectableSide"], exportAs: ["fNodeInput"] }, { kind: "component", type: i1$2.FLineAlignmentComponent, selector: "f-line-alignment", inputs: ["fAlignThreshold"], exportAs: ["fComponent"] }, { kind: "component", type: i1$2.FMinimapComponent, selector: "f-minimap", inputs: ["fMinSize", "fNodeRenderLimit"], exportAs: ["fComponent"] }, { kind: "directive", type: i1$2.FGroupDirective, selector: "[fGroup]", inputs: ["fGroupId", "fGroupParentId", "fGroupPosition", "fGroupSize", "fGroupRotate", "fConnectOnNode", "fMinimapClass", "fGroupDraggingDisabled", "fGroupSelectionDisabled", "fIncludePadding", "fAutoExpandOnChildHit", "fAutoSizeToFitChildren"], outputs: ["fGroupPositionChange", "fGroupSizeChange", "fGroupRotateChange"], exportAs: ["fComponent"] }, { kind: "directive", type: i1$2.FNodeDirective, selector: "[fNode]", inputs: ["fNodeId", "fNodeParentId", "fNodePosition", "fNodeSize", "fNodeRotate", "fConnectOnNode", "fMinimapClass", "fNodeDraggingDisabled", "fNodeSelectionDisabled", "fIncludePadding", "fAutoExpandOnChildHit", "fAutoSizeToFitChildren"], outputs: ["fNodePositionChange", "fNodeSizeChange", "fNodeRotateChange"], exportAs: ["fComponent"] }, { kind: "directive", type: i1$2.FDragHandleDirective, selector: "[fDragHandle]" }, { kind: "directive", type: i1$2.FDraggableDirective, selector: "f-flow[fDraggable]", inputs: ["fDraggableDisabled", "fMultiSelectTrigger", "fReassignConnectionTrigger", "fCreateConnectionTrigger", "fConnectionWaypointsTrigger", "fMoveControlPointTrigger", "fNodeResizeTrigger", "fNodeRotateTrigger", "fNodeMoveTrigger", "fCanvasMoveTrigger", "fExternalItemTrigger", "fEmitOnNodeIntersect", "vCellSize", "hCellSize", "fCellSizeWhileDragging"], outputs: ["fSelectionChange", "fNodeIntersectedWithConnections", "fNodeConnectionsIntersection", "fCreateNode", "fMoveNodes", "fReassignConnection", "fCreateConnection", "fConnectionWaypointsChanged", "fDropToGroup", "fDragStarted", "fDragEnded"], exportAs: ["fDraggable"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "directive", type: Tooltip, selector: "[mtTooltip]" }, { kind: "component", type: FlowNodeComponent, selector: "fp-flow-node", inputs: ["node", "connectedOutputKeys"], outputs: ["positionChange", "nodeClick", "quickAdd", "portPlusClick", "duplicate", "remove", "testStep", "openChild", "openDetails"] }, { kind: "component", type: CanvasControlsComponent, selector: "fp-canvas-controls", inputs: ["zoom", "minimapVisible"], outputs: ["zoomIn", "zoomOut", "fitView", "resetView", "toggleMinimap", "autoLayout"] }, { kind: "component", type: CanvasNoteComponent, selector: "fp-canvas-note", inputs: ["note", "selected", "editing", "colors"], outputs: ["contentChange", "colorChange", "duplicate", "remove", "editStart", "editEnd"] }, { kind: "component", type: StarterCardComponent, selector: "fp-starter-card", outputs: ["addTrigger", "directPick"] }, { kind: "component", type: TriggerNodeComponent, selector: "fp-trigger-node", inputs: ["trigger", "noFirstStep"], outputs: ["addFirstStep", "configure", "execute", "toggleEnabled", "remove", "positionChange"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
20259
20860
  }
20260
20861
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: FlowCanvasComponent, decorators: [{
20261
20862
  type: Component,
@@ -20267,6 +20868,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
20267
20868
  Tooltip,
20268
20869
  FlowNodeComponent,
20269
20870
  CanvasControlsComponent,
20871
+ CanvasNoteComponent,
20270
20872
  StarterCardComponent,
20271
20873
  TriggerNodeComponent,
20272
20874
  ], providers: [
@@ -20277,7 +20879,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
20277
20879
  maxCascadeDepth: 8,
20278
20880
  maxAbsoluteShiftPerPlan: 10000,
20279
20881
  })),
20280
- ], template: "<f-flow\n #flow\n fDraggable\n [vCellSize]=\"gridCellSize\"\n [hCellSize]=\"gridCellSize\"\n [fCellSizeWhileDragging]=\"false\"\n (fFullRendered)=\"onFullRendered()\"\n (fNodesRendered)=\"onNodesRendered()\"\n (fCreateNode)=\"onCreateNode($event)\"\n (fCreateConnection)=\"onCreateConnection($event)\"\n (fReassignConnection)=\"onReassignConnection($event)\"\n (fDragStarted)=\"onDragStarted($event)\"\n (fDragEnded)=\"onDragEnded()\"\n (fMoveNodes)=\"onMoveNodes($event)\"\n (fSelectionChange)=\"onSelectionChange($event)\"\n>\n <!-- Background dot grid \u2014 its spacing is locked to the drag cell size so\n nodes snap exactly onto the visible dots (Foblex grid-system). -->\n <f-background>\n <f-circle-pattern [radius]=\"gridCellSize\" />\n </f-background>\n\n <!-- Alignment guides + marquee selection are `f-flow` children (NOT\n `f-canvas` children \u2014 `f-canvas` only projects connections/nodes/groups,\n so helpers placed inside it are silently dropped). This mirrors the\n Foblex call-center reference layout. -->\n <f-line-alignment [fAlignThreshold]=\"20\" />\n <f-selection-area />\n\n <!-- Auto-pan: when dragging a node / connection near the viewport edge, the\n canvas pans to follow, so you can wire across off-screen nodes without\n letting go. (Foblex `f-auto-pan` plugin.) -->\n <f-auto-pan [fEdgeThreshold]=\"36\" [fSpeed]=\"8\" />\n\n <f-canvas\n #canvas\n fZoom\n [debounceTime]=\"200\"\n (fCanvasChange)=\"onCanvasChange($event)\"\n >\n <!-- Floating behavior anchors the preview at the connector boundary along\n the center\u2192pointer axis, so a connection dragged from the \"+\" outlet\n leaves it cleanly (NOT from its bottom edge \u2014 `fixed` defaults an AUTO\n connectable side to bottom) and snaps to the target's nearest edge. -->\n <f-connection-for-create\n [fBehavior]=\"'floating'\"\n fType=\"adaptive-curve\"\n [fOffset]=\"0\"\n >\n <f-connection-marker-arrow />\n </f-connection-for-create>\n\n <!-- Auto-snap: while dragging a new/reassigned connection, snap to the\n nearest connector within the threshold (forgiving connect UX). -->\n <f-snap-connection\n [fBehavior]=\"'floating'\"\n fType=\"adaptive-curve\"\n [fSnapThreshold]=\"40\"\n [fOffset]=\"0\"\n >\n <f-connection-marker-arrow />\n </f-snap-connection>\n\n <!-- ngProjectAs is REQUIRED: `f-canvas` distributes projected content\n into its layer containers via selective `<ng-content select=\"...\">`\n with NO catch-all slot. A wrapper component (`fp-flow-node`, etc.)\n does not match `[fNode]` / `f-connection` on its host, so without\n `ngProjectAs` Foblex silently drops it and the canvas renders empty.\n The actual Foblex directive lives on the wrapper's inner element and\n self-registers via the mediator, so routing the wrapper into the\n right layer is all that's needed. -->\n @for (lane of branchLanes(); track lane.id) {\n <div\n ngProjectAs=\"[fGroup]\"\n class=\"pointer-events-none absolute z-0 rounded-[14px] border border-dashed border-[color-mix(in_srgb,rgb(var(--fp-parallel))_55%,transparent)] bg-[color-mix(in_srgb,rgb(var(--fp-parallel))_8%,transparent)]\"\n [attr.data-color-token]=\"lane.colorToken\"\n [style.left.px]=\"lane.x\"\n [style.top.px]=\"lane.y\"\n [style.width.px]=\"lane.width\"\n [style.height.px]=\"lane.height\"\n >\n <span\n class=\"pointer-events-auto absolute -top-2.5 start-3 rounded-full border border-[color-mix(in_srgb,rgb(var(--fp-parallel))_40%,transparent)] bg-(--p-content-background) px-2 py-px text-[10.5px] font-semibold text-[rgb(var(--fp-parallel))]\"\n >{{ lane.label }}</span\n >\n </div>\n }\n\n <!-- Connections are inlined here (NOT wrapped in a component) so each\n <f-connection> is a DIRECT child of Foblex's connections container.\n A wrapper would make `hostElement.parentElement` the wrapper instead\n of the container, which makes Foblex's select layer-raise throw\n \"Unknown container\" the moment an edge is selected. Same call-center\n pattern used for nodes/triggers. -->\n @for (edge of edges(); track edge.id) {\n <f-connection\n class=\"group\"\n [attr.data-fp-edge]=\"edge.connectionId\"\n [fConnectionId]=\"edge.id\"\n [fOutputId]=\"edge.sourcePortId\"\n [fInputId]=\"edge.targetPortId\"\n [fBehavior]=\"'fixed'\"\n [fType]=\"'adaptive-curve'\"\n [fOffset]=\"32\"\n fInputSide=\"calculate\"\n [fReassignableStart]=\"edge.edgeKind !== 'triggerStart'\"\n [fReassignDisabled]=\"edge.edgeKind === 'triggerStart'\"\n [class.fp-edge]=\"true\"\n [class.is-trigger-start]=\"edge.edgeKind === 'triggerStart'\"\n [class.is-drop-target]=\"isDropTargetEdge(edge.connectionId)\"\n [class.is-selected]=\"isConnectionSelected(edge.connectionId)\"\n [class.is-error]=\"edge.isInvalid\"\n [class.is-formula]=\"edge.isFormula\"\n [class.is-runtime-active]=\"edge.runtimeState === 'active'\"\n [class.is-runtime-completed]=\"edge.runtimeState === 'completed'\"\n [class.is-runtime-failed]=\"edge.runtimeState === 'failed'\"\n [class.is-runtime-waiting]=\"edge.runtimeState === 'waiting'\"\n (dblclick)=\"\n edge.edgeKind === 'triggerStart' && edge.triggerId != null\n ? onTriggerOpenDetails({ triggerId: edge.triggerId })\n : onEdgeOpenDetails({ connectionId: edge.connectionId })\n \"\n >\n <!-- Arrow marker at the target \u2014 the connection carries the arrowhead;\n the input lives on the node host (no separate input element). -->\n <f-connection-marker-arrow [type]=\"markerEnd\" />\n\n <!-- Editable waypoints are intentionally NOT rendered: there is no\n backend persistence wired for manual bends, so exposing draggable\n waypoints would silently lose the user's edits on reload. -->\n\n <!-- Label / formula badge \u2014 pinned to the CENTER of the line\n (connection-content position 0.5). Fades out on hover/selected so\n the centered action box can take its place. -->\n @if (edge.label) {\n <div\n fConnectionContent\n [position]=\"0.5\"\n class=\"pointer-events-none opacity-100 transition-opacity duration-150 group-hover:opacity-0 group-[.is-selected]:opacity-0\"\n >\n <span\n class=\"inline-flex items-center gap-1.5 whitespace-nowrap rounded-full border border-[rgb(var(--fp-connector))] bg-(--p-content-background) py-1 pe-2.5 ps-2 text-[11px] font-semibold text-(--p-text-color) shadow-[0_1px_3px_rgba(15,23,42,0.1)] transition-colors group-[.is-selected]:border-(--p-primary-color) group-[.is-selected]:text-(--p-primary-color)\"\n >\n <span\n class=\"size-1.5 flex-none rounded-full bg-[rgb(var(--fp-connector))] group-[.is-selected]:bg-(--p-primary-color)\"\n ></span>\n {{ edge.label }}\n </span>\n </div>\n } @else if (edge.isFormula) {\n <div\n fConnectionContent\n [position]=\"0.5\"\n class=\"pointer-events-none opacity-100 transition-opacity duration-150 group-hover:opacity-0 group-[.is-selected]:opacity-0\"\n >\n <span\n class=\"grid h-5 w-5 place-items-center rounded-full border border-(--p-content-border-color) bg-(--p-content-background) font-mono text-[12px] italic text-(--p-primary-color) shadow-sm\"\n title=\"Conditional route\"\n >\u0192</span\n >\n </div>\n }\n\n <!-- Centered action box \u2014 edit (opens the connection MODAL) + delete.\n Same content anchor (position 0.5), revealed on hover/selected and\n overlapping the label so the actions stay centered ON the line. -->\n @if (edgeIssueCount(edge) > 0) {\n <div\n fConnectionContent\n [position]=\"0.38\"\n class=\"pointer-events-auto\"\n >\n <span\n class=\"fp-node-badge grid h-[18px] w-[18px] cursor-help place-items-center rounded-sm bg-(--p-content-background) shadow-sm outline-none transition-transform focus-visible:ring-2 focus-visible:ring-(--p-primary-color)\"\n [style.color]=\"edgeIssueColor(edge)\"\n tabindex=\"0\"\n role=\"img\"\n [attr.aria-label]=\"edgeIssueTooltip(edge)\"\n [mtTooltip]=\"edgeIssueTooltip(edge)\"\n [tooltipStyleClass]=\"edgeIssueTooltipClass(edge)\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [escape]=\"true\"\n [showDelay]=\"180\"\n [hideDelay]=\"80\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"\n $event.stopPropagation();\n edge.edgeKind === 'triggerStart' && edge.triggerId != null\n ? onTriggerOpenDetails({ triggerId: edge.triggerId })\n : onEdgeOpenDetails({ connectionId: edge.connectionId })\n \"\n (dblclick)=\"$event.stopPropagation()\"\n >\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M10 3.4 18 16.8 2 16.8 Z\"\n fill=\"currentColor\"\n stroke=\"currentColor\"\n stroke-width=\"2.6\"\n stroke-linejoin=\"round\"\n />\n <rect x=\"9.05\" y=\"8\" width=\"1.9\" height=\"4.6\" rx=\"0.95\" fill=\"#fff\" />\n <circle cx=\"10\" cy=\"14.6\" r=\"1.05\" fill=\"#fff\" />\n </svg>\n </span>\n </div>\n }\n\n @if (edge.runtimeState && edge.runtimeTooltip) {\n <div\n fConnectionContent\n [position]=\"0.62\"\n class=\"pointer-events-auto\"\n >\n <span\n class=\"inline-flex h-[18px] w-[18px] cursor-help items-center justify-center rounded-full border bg-(--p-content-background) shadow-sm outline-none focus-visible:ring-2 focus-visible:ring-(--p-primary-color)\"\n [class.animate-pulse]=\"edge.runtimeState === 'active'\"\n [style.borderColor]=\"edgeRuntimeColor(edge)\"\n [style.color]=\"edgeRuntimeColor(edge)\"\n tabindex=\"0\"\n role=\"status\"\n [attr.aria-label]=\"edge.runtimeTooltip\"\n [mtTooltip]=\"edge.runtimeTooltip\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [escape]=\"true\"\n [showDelay]=\"180\"\n [hideDelay]=\"80\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\"\n (dblclick)=\"$event.stopPropagation()\"\n >\n <mt-icon [icon]=\"edgeRuntimeIcon(edge)\" class=\"[&_svg]:size-3\" />\n </span>\n </div>\n }\n\n <div\n fConnectionContent\n [position]=\"0.5\"\n class=\"pointer-events-none opacity-0 transition-opacity duration-150 group-hover:pointer-events-auto group-hover:opacity-100 group-[.is-selected]:pointer-events-auto group-[.is-selected]:opacity-100\"\n >\n <div\n class=\"flex items-center gap-0.5 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) p-1 shadow-md\"\n role=\"toolbar\"\n aria-label=\"Connection actions\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\"\n >\n @if (edge.edgeKind === \"triggerStart\" && edge.triggerId != null) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.settings-01\"\n [tooltip]=\"'flowplus.trigger.node.configure' | transloco\"\n (onClick)=\"onTriggerOpenDetails({ triggerId: edge.triggerId })\"\n />\n <mt-button\n variant=\"text\"\n severity=\"danger\"\n size=\"small\"\n icon=\"general.link-broken-01\"\n [tooltip]=\"'flowplus.trigger.node.unlink' | transloco\"\n (onClick)=\"\n onTriggerStartDisconnect({ triggerId: edge.triggerId })\n \"\n />\n } @else {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.edit-05\"\n [tooltip]=\"'flowplus.inspector.connection.title' | transloco\"\n (onClick)=\"\n onEdgeOpenDetails({ connectionId: edge.connectionId })\n \"\n />\n <mt-button\n variant=\"text\"\n severity=\"danger\"\n size=\"small\"\n icon=\"general.trash-01\"\n [tooltip]=\"'flowplus.edge.delete' | transloco\"\n (onClick)=\"onEdgeRemove({ connectionId: edge.connectionId })\"\n />\n }\n </div>\n </div>\n </f-connection>\n }\n\n <!-- The Foblex `fNode` directive lives on the component element itself\n (not an inner div). This keeps the node element a DIRECT child of\n Foblex's nodes container, so `hostElement.parentElement` IS that\n container \u2014 required by the select/move layer-raise (otherwise it\n throws \"Unknown container\"). This is the Foblex call-center pattern;\n the `fNode` attribute also routes it into the `[fNode]` projection\n slot, so `ngProjectAs` is not needed. -->\n @for (node of nodes(); track node.id) {\n <fp-flow-node\n fNode\n fDragHandle\n fNodeInput\n [attr.data-fp-node]=\"node.stepId\"\n [fInputId]=\"node.inputs[0]?.id\"\n [fInputMultiple]=\"node.inputs[0]?.allowMultiple ?? true\"\n [fInputDisabled]=\"!node.inputs.length\"\n [fNodeId]=\"node.id\"\n [fNodePosition]=\"{ x: node.x, y: node.y }\"\n (fNodePositionChange)=\"onStepPositionChange(node.stepId, $event)\"\n [node]=\"node\"\n [connectedOutputKeys]=\"connectedOutputKeys(node.stepId)\"\n (quickAdd)=\"onNodeQuickAdd($event)\"\n (portPlusClick)=\"onNodePortPlus($event)\"\n (duplicate)=\"onNodeDuplicate($event)\"\n (remove)=\"onNodeRemove($event)\"\n (openDetails)=\"onNodeOpenDetails($event)\"\n (openChild)=\"onOpenChild($event)\"\n />\n }\n\n @for (t of triggerNodes(); track t.triggerId) {\n <fp-trigger-node\n fNode\n fDragHandle\n [attr.data-fp-trigger]=\"t.triggerId\"\n [fNodeId]=\"'trigger:' + t.triggerId\"\n [fNodePosition]=\"{ x: t.x, y: t.y }\"\n (fNodePositionChange)=\"onTriggerPositionChange(t.triggerId, $event)\"\n [trigger]=\"t\"\n [noFirstStep]=\"!t.startStepId\"\n (addFirstStep)=\"onTriggerAddFirstStep($event)\"\n (configure)=\"onTriggerOpenDetails($event)\"\n (execute)=\"onTriggerExecute($event)\"\n (toggleEnabled)=\"onTriggerToggleEnabled($event)\"\n (remove)=\"onTriggerDelete($event)\"\n />\n }\n </f-canvas>\n\n <!-- Minimap \u2014 `f-flow` child (NOT inside `f-canvas`). Toggled from the\n canvas controls; `fMinSize` keeps a couple of nodes zoomed-out enough\n to give real spatial context. -->\n @if (minimapVisible()) {\n <f-minimap [fMinSize]=\"minimapMinSize\" />\n }\n\n @if (showStarter() && !store.loading()) {\n <fp-starter-card\n (addTrigger)=\"onStarterAddTrigger($event)\"\n (directPick)=\"onStarterDirectPick($event)\"\n />\n }\n\n <fp-canvas-controls\n [zoom]=\"zoom()\"\n [minimapVisible]=\"minimapVisible()\"\n (zoomIn)=\"zoomIn()\"\n (zoomOut)=\"zoomOut()\"\n (fitView)=\"fitView()\"\n (resetView)=\"resetView()\"\n (toggleMinimap)=\"toggleMinimap()\"\n (autoLayout)=\"autoLayout()\"\n />\n</f-flow>\n" }]
20882
+ ], template: "<f-flow\n #flow\n fDraggable\n [vCellSize]=\"gridCellSize\"\n [hCellSize]=\"gridCellSize\"\n [fCellSizeWhileDragging]=\"false\"\n (fFullRendered)=\"onFullRendered()\"\n (fNodesRendered)=\"onNodesRendered()\"\n (fCreateNode)=\"onCreateNode($event)\"\n (fCreateConnection)=\"onCreateConnection($event)\"\n (fReassignConnection)=\"onReassignConnection($event)\"\n (fDragStarted)=\"onDragStarted($event)\"\n (fDragEnded)=\"onDragEnded()\"\n (fMoveNodes)=\"onMoveNodes($event)\"\n (fSelectionChange)=\"onSelectionChange($event)\"\n>\n <!-- Background dot grid \u2014 its spacing is locked to the drag cell size so\n nodes snap exactly onto the visible dots (Foblex grid-system). -->\n <f-background>\n <f-circle-pattern [radius]=\"gridCellSize\" />\n </f-background>\n\n <!-- Alignment guides + marquee selection are `f-flow` children (NOT\n `f-canvas` children \u2014 `f-canvas` only projects connections/nodes/groups,\n so helpers placed inside it are silently dropped). This mirrors the\n Foblex call-center reference layout. -->\n <f-line-alignment [fAlignThreshold]=\"20\" />\n <f-selection-area />\n\n <!-- Auto-pan: when dragging a node / connection near the viewport edge, the\n canvas pans to follow, so you can wire across off-screen nodes without\n letting go. (Foblex `f-auto-pan` plugin.) -->\n <f-auto-pan [fEdgeThreshold]=\"36\" [fSpeed]=\"8\" />\n\n <f-canvas\n #canvas\n fZoom\n [debounceTime]=\"200\"\n [fLayers]=\"canvasLayers\"\n (fCanvasChange)=\"onCanvasChange($event)\"\n >\n <!-- Floating behavior anchors the preview at the connector boundary along\n the center\u2192pointer axis, so a connection dragged from the \"+\" outlet\n leaves it cleanly (NOT from its bottom edge \u2014 `fixed` defaults an AUTO\n connectable side to bottom) and snaps to the target's nearest edge. -->\n <f-connection-for-create\n [fBehavior]=\"'floating'\"\n fType=\"adaptive-curve\"\n [fOffset]=\"0\"\n >\n <f-connection-marker-arrow />\n </f-connection-for-create>\n\n <!-- Auto-snap: while dragging a new/reassigned connection, snap to the\n nearest connector within the threshold (forgiving connect UX). -->\n <f-snap-connection\n [fBehavior]=\"'floating'\"\n fType=\"adaptive-curve\"\n [fSnapThreshold]=\"40\"\n [fOffset]=\"0\"\n >\n <f-connection-marker-arrow />\n </f-snap-connection>\n\n <!-- ngProjectAs is REQUIRED: `f-canvas` distributes projected content\n into its layer containers via selective `<ng-content select=\"...\">`\n with NO catch-all slot. A wrapper component (`fp-flow-node`, etc.)\n does not match `[fNode]` / `f-connection` on its host, so without\n `ngProjectAs` Foblex silently drops it and the canvas renders empty.\n The actual Foblex directive lives on the wrapper's inner element and\n self-registers via the mediator, so routing the wrapper into the\n right layer is all that's needed. -->\n @for (note of canvasNotes(); track note.id) {\n <fp-canvas-note\n fGroup\n fDragHandle\n [attr.data-fp-note]=\"note.id\"\n [fGroupId]=\"canvasNoteGroupId(note.id)\"\n [fGroupPosition]=\"{ x: note.x, y: note.y }\"\n [fGroupSize]=\"{ width: note.width, height: note.height }\"\n [fGroupDraggingDisabled]=\"editingNoteId() === note.id\"\n [note]=\"note\"\n [selected]=\"selectedCanvasNoteIds().includes(note.id)\"\n [editing]=\"editingNoteId() === note.id\"\n (fGroupPositionChange)=\"onNotePositionChange(note.id, $event)\"\n (fGroupSizeChange)=\"onNoteSizeChange(note.id, $event)\"\n (contentChange)=\"onNoteContentChange($event)\"\n (colorChange)=\"onNoteColorChange($event)\"\n (duplicate)=\"onNoteDuplicate($event)\"\n (remove)=\"onNoteDelete($event)\"\n (editStart)=\"onNoteEditStart($event.noteId)\"\n (editEnd)=\"editingNoteId.set(null)\"\n />\n }\n\n @for (lane of branchLanes(); track lane.id) {\n <div\n ngProjectAs=\"[fGroup]\"\n class=\"pointer-events-none absolute z-0 rounded-[14px] border border-dashed border-[color-mix(in_srgb,rgb(var(--fp-parallel))_55%,transparent)] bg-[color-mix(in_srgb,rgb(var(--fp-parallel))_8%,transparent)]\"\n [attr.data-color-token]=\"lane.colorToken\"\n [style.left.px]=\"lane.x\"\n [style.top.px]=\"lane.y\"\n [style.width.px]=\"lane.width\"\n [style.height.px]=\"lane.height\"\n >\n <span\n class=\"pointer-events-auto absolute -top-2.5 start-3 rounded-full border border-[color-mix(in_srgb,rgb(var(--fp-parallel))_40%,transparent)] bg-(--p-content-background) px-2 py-px text-[10.5px] font-semibold text-[rgb(var(--fp-parallel))]\"\n >{{ lane.label }}</span\n >\n </div>\n }\n\n <!-- Connections are inlined here (NOT wrapped in a component) so each\n <f-connection> is a DIRECT child of Foblex's connections container.\n A wrapper would make `hostElement.parentElement` the wrapper instead\n of the container, which makes Foblex's select layer-raise throw\n \"Unknown container\" the moment an edge is selected. Same call-center\n pattern used for nodes/triggers. -->\n @for (edge of edges(); track edge.id) {\n <f-connection\n class=\"group\"\n [attr.data-fp-edge]=\"edge.connectionId\"\n [fConnectionId]=\"edge.id\"\n [fOutputId]=\"edge.sourcePortId\"\n [fInputId]=\"edge.targetPortId\"\n [fBehavior]=\"'fixed'\"\n [fType]=\"'adaptive-curve'\"\n [fOffset]=\"32\"\n fInputSide=\"calculate\"\n [fReassignableStart]=\"edge.edgeKind !== 'triggerStart'\"\n [fReassignDisabled]=\"edge.edgeKind === 'triggerStart'\"\n [class.fp-edge]=\"true\"\n [class.is-trigger-start]=\"edge.edgeKind === 'triggerStart'\"\n [class.is-drop-target]=\"isDropTargetEdge(edge.connectionId)\"\n [class.is-selected]=\"isConnectionSelected(edge.connectionId)\"\n [class.is-error]=\"edge.isInvalid\"\n [class.is-formula]=\"edge.isFormula\"\n [class.is-runtime-active]=\"edge.runtimeState === 'active'\"\n [class.is-runtime-completed]=\"edge.runtimeState === 'completed'\"\n [class.is-runtime-failed]=\"edge.runtimeState === 'failed'\"\n [class.is-runtime-waiting]=\"edge.runtimeState === 'waiting'\"\n (dblclick)=\"\n edge.edgeKind === 'triggerStart' && edge.triggerId != null\n ? onTriggerOpenDetails({ triggerId: edge.triggerId })\n : onEdgeOpenDetails({ connectionId: edge.connectionId })\n \"\n >\n <!-- Arrow marker at the target \u2014 the connection carries the arrowhead;\n the input lives on the node host (no separate input element). -->\n <f-connection-marker-arrow [type]=\"markerEnd\" />\n\n <!-- Editable waypoints are intentionally NOT rendered: there is no\n backend persistence wired for manual bends, so exposing draggable\n waypoints would silently lose the user's edits on reload. -->\n\n <!-- Label / formula badge \u2014 pinned to the CENTER of the line\n (connection-content position 0.5). Fades out on hover/selected so\n the centered action box can take its place. -->\n @if (edge.label) {\n <div\n fConnectionContent\n [position]=\"0.5\"\n class=\"pointer-events-none opacity-100 transition-opacity duration-150 group-hover:opacity-0 group-[.is-selected]:opacity-0\"\n >\n <span\n class=\"inline-flex items-center gap-1.5 whitespace-nowrap rounded-full border border-[rgb(var(--fp-connector))] bg-(--p-content-background) py-1 pe-2.5 ps-2 text-[11px] font-semibold text-(--p-text-color) shadow-[0_1px_3px_rgba(15,23,42,0.1)] transition-colors group-[.is-selected]:border-(--p-primary-color) group-[.is-selected]:text-(--p-primary-color)\"\n >\n <span\n class=\"size-1.5 flex-none rounded-full bg-[rgb(var(--fp-connector))] group-[.is-selected]:bg-(--p-primary-color)\"\n ></span>\n {{ edge.label }}\n </span>\n </div>\n } @else if (edge.isFormula) {\n <div\n fConnectionContent\n [position]=\"0.5\"\n class=\"pointer-events-none opacity-100 transition-opacity duration-150 group-hover:opacity-0 group-[.is-selected]:opacity-0\"\n >\n <span\n class=\"grid h-5 w-5 place-items-center rounded-full border border-(--p-content-border-color) bg-(--p-content-background) font-mono text-[12px] italic text-(--p-primary-color) shadow-sm\"\n title=\"Conditional route\"\n >\u0192</span\n >\n </div>\n }\n\n <!-- Centered action box \u2014 edit (opens the connection MODAL) + delete.\n Same content anchor (position 0.5), revealed on hover/selected and\n overlapping the label so the actions stay centered ON the line. -->\n @if (edgeIssueCount(edge) > 0) {\n <div\n fConnectionContent\n [position]=\"0.38\"\n class=\"pointer-events-auto\"\n >\n <span\n class=\"fp-node-badge grid h-[18px] w-[18px] cursor-help place-items-center rounded-sm bg-(--p-content-background) shadow-sm outline-none transition-transform focus-visible:ring-2 focus-visible:ring-(--p-primary-color)\"\n [style.color]=\"edgeIssueColor(edge)\"\n tabindex=\"0\"\n role=\"img\"\n [attr.aria-label]=\"edgeIssueTooltip(edge)\"\n [mtTooltip]=\"edgeIssueTooltip(edge)\"\n [tooltipStyleClass]=\"edgeIssueTooltipClass(edge)\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [escape]=\"true\"\n [showDelay]=\"180\"\n [hideDelay]=\"80\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"\n $event.stopPropagation();\n edge.edgeKind === 'triggerStart' && edge.triggerId != null\n ? onTriggerOpenDetails({ triggerId: edge.triggerId })\n : onEdgeOpenDetails({ connectionId: edge.connectionId })\n \"\n (dblclick)=\"$event.stopPropagation()\"\n >\n <svg width=\"15\" height=\"15\" viewBox=\"0 0 20 20\" aria-hidden=\"true\">\n <path\n d=\"M10 3.4 18 16.8 2 16.8 Z\"\n fill=\"currentColor\"\n stroke=\"currentColor\"\n stroke-width=\"2.6\"\n stroke-linejoin=\"round\"\n />\n <rect x=\"9.05\" y=\"8\" width=\"1.9\" height=\"4.6\" rx=\"0.95\" fill=\"#fff\" />\n <circle cx=\"10\" cy=\"14.6\" r=\"1.05\" fill=\"#fff\" />\n </svg>\n </span>\n </div>\n }\n\n @if (edge.runtimeState && edge.runtimeTooltip) {\n <div\n fConnectionContent\n [position]=\"0.62\"\n class=\"pointer-events-auto\"\n >\n <span\n class=\"inline-flex h-[18px] w-[18px] cursor-help items-center justify-center rounded-full border bg-(--p-content-background) shadow-sm outline-none focus-visible:ring-2 focus-visible:ring-(--p-primary-color)\"\n [class.animate-pulse]=\"edge.runtimeState === 'active'\"\n [style.borderColor]=\"edgeRuntimeColor(edge)\"\n [style.color]=\"edgeRuntimeColor(edge)\"\n tabindex=\"0\"\n role=\"status\"\n [attr.aria-label]=\"edge.runtimeTooltip\"\n [mtTooltip]=\"edge.runtimeTooltip\"\n tooltipPosition=\"top\"\n appendTo=\"body\"\n [escape]=\"true\"\n [showDelay]=\"180\"\n [hideDelay]=\"80\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\"\n (dblclick)=\"$event.stopPropagation()\"\n >\n <mt-icon [icon]=\"edgeRuntimeIcon(edge)\" class=\"[&_svg]:size-3\" />\n </span>\n </div>\n }\n\n <div\n fConnectionContent\n [position]=\"0.5\"\n class=\"pointer-events-none opacity-0 transition-opacity duration-150 group-hover:pointer-events-auto group-hover:opacity-100 group-[.is-selected]:pointer-events-auto group-[.is-selected]:opacity-100\"\n >\n <div\n class=\"flex items-center gap-0.5 rounded-lg border border-(--p-content-border-color) bg-(--p-content-background) p-1 shadow-md\"\n role=\"toolbar\"\n aria-label=\"Connection actions\"\n (pointerdown)=\"$event.stopPropagation()\"\n (click)=\"$event.stopPropagation()\"\n >\n @if (edge.edgeKind === \"triggerStart\" && edge.triggerId != null) {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.settings-01\"\n [tooltip]=\"'flowplus.trigger.node.configure' | transloco\"\n (onClick)=\"onTriggerOpenDetails({ triggerId: edge.triggerId })\"\n />\n <mt-button\n variant=\"text\"\n severity=\"danger\"\n size=\"small\"\n icon=\"general.link-broken-01\"\n [tooltip]=\"'flowplus.trigger.node.unlink' | transloco\"\n (onClick)=\"\n onTriggerStartDisconnect({ triggerId: edge.triggerId })\n \"\n />\n } @else {\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.edit-05\"\n [tooltip]=\"'flowplus.inspector.connection.title' | transloco\"\n (onClick)=\"\n onEdgeOpenDetails({ connectionId: edge.connectionId })\n \"\n />\n <mt-button\n variant=\"text\"\n severity=\"danger\"\n size=\"small\"\n icon=\"general.trash-01\"\n [tooltip]=\"'flowplus.edge.delete' | transloco\"\n (onClick)=\"onEdgeRemove({ connectionId: edge.connectionId })\"\n />\n }\n </div>\n </div>\n </f-connection>\n }\n\n <!-- The Foblex `fNode` directive lives on the component element itself\n (not an inner div). This keeps the node element a DIRECT child of\n Foblex's nodes container, so `hostElement.parentElement` IS that\n container \u2014 required by the select/move layer-raise (otherwise it\n throws \"Unknown container\"). This is the Foblex call-center pattern;\n the `fNode` attribute also routes it into the `[fNode]` projection\n slot, so `ngProjectAs` is not needed. -->\n @for (node of nodes(); track node.id) {\n <fp-flow-node\n fNode\n fDragHandle\n fNodeInput\n [attr.data-fp-node]=\"node.stepId\"\n [fInputId]=\"node.inputs[0]?.id\"\n [fInputMultiple]=\"node.inputs[0]?.allowMultiple ?? true\"\n [fInputDisabled]=\"!node.inputs.length\"\n [fNodeId]=\"node.id\"\n [fNodePosition]=\"{ x: node.x, y: node.y }\"\n (fNodePositionChange)=\"onStepPositionChange(node.stepId, $event)\"\n [node]=\"node\"\n [connectedOutputKeys]=\"connectedOutputKeys(node.stepId)\"\n (quickAdd)=\"onNodeQuickAdd($event)\"\n (portPlusClick)=\"onNodePortPlus($event)\"\n (duplicate)=\"onNodeDuplicate($event)\"\n (remove)=\"onNodeRemove($event)\"\n (openDetails)=\"onNodeOpenDetails($event)\"\n (openChild)=\"onOpenChild($event)\"\n />\n }\n\n @for (t of triggerNodes(); track t.triggerId) {\n <fp-trigger-node\n fNode\n fDragHandle\n [attr.data-fp-trigger]=\"t.triggerId\"\n [fNodeId]=\"'trigger:' + t.triggerId\"\n [fNodePosition]=\"{ x: t.x, y: t.y }\"\n (fNodePositionChange)=\"onTriggerPositionChange(t.triggerId, $event)\"\n [trigger]=\"t\"\n [noFirstStep]=\"!t.startStepId\"\n (addFirstStep)=\"onTriggerAddFirstStep($event)\"\n (configure)=\"onTriggerOpenDetails($event)\"\n (execute)=\"onTriggerExecute($event)\"\n (toggleEnabled)=\"onTriggerToggleEnabled($event)\"\n (remove)=\"onTriggerDelete($event)\"\n />\n }\n </f-canvas>\n\n <!-- Minimap \u2014 `f-flow` child (NOT inside `f-canvas`). Toggled from the\n canvas controls; `fMinSize` keeps a couple of nodes zoomed-out enough\n to give real spatial context. -->\n @if (minimapVisible()) {\n <f-minimap [fMinSize]=\"minimapMinSize\" />\n }\n\n @if (showStarter() && !store.loading()) {\n <fp-starter-card\n (addTrigger)=\"onStarterAddTrigger($event)\"\n (directPick)=\"onStarterDirectPick($event)\"\n />\n }\n\n <fp-canvas-controls\n [zoom]=\"zoom()\"\n [minimapVisible]=\"minimapVisible()\"\n (zoomIn)=\"zoomIn()\"\n (zoomOut)=\"zoomOut()\"\n (fitView)=\"fitView()\"\n (resetView)=\"resetView()\"\n (toggleMinimap)=\"toggleMinimap()\"\n (autoLayout)=\"autoLayout()\"\n />\n</f-flow>\n" }]
20281
20883
  }], ctorParameters: () => [], propDecorators: { flow: [{ type: i0.ViewChild, args: ['flow', { isSignal: true }] }], canvas: [{ type: i0.ViewChild, args: ['canvas', { isSignal: true }] }], paletteDrop: [{
20282
20884
  type: Output
20283
20885
  }], connectionCreate: [{
@@ -20334,6 +20936,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
20334
20936
  type: Output
20335
20937
  }], requestAddStep: [{
20336
20938
  type: Output
20939
+ }], noteUpdate: [{
20940
+ type: Output
20941
+ }], noteDuplicate: [{
20942
+ type: Output
20943
+ }], noteDelete: [{
20944
+ type: Output
20337
20945
  }] } });
20338
20946
  function triggerStarterKindMatchesCatalogItem(kind, item) {
20339
20947
  const metadata = item.metadata && typeof item.metadata === 'object'
@@ -20359,7 +20967,8 @@ function readTranslatePosition(el) {
20359
20967
  function isCanvasPositionElement(target) {
20360
20968
  return (target instanceof HTMLElement &&
20361
20969
  (target.matches('fp-flow-node[data-fp-node]') ||
20362
- target.matches('fp-trigger-node[data-fp-trigger]')));
20970
+ target.matches('fp-trigger-node[data-fp-trigger]') ||
20971
+ target.matches('fp-canvas-note[data-fp-note]')));
20363
20972
  }
20364
20973
  function normalizeOutputPortKey(portKey, fallback) {
20365
20974
  const key = portKey || fallback;
@@ -20526,6 +21135,8 @@ class PaletteComponent {
20526
21135
  add = new EventEmitter();
20527
21136
  /** Add a trigger from the "Add another trigger" section. */
20528
21137
  addTrigger = new EventEmitter();
21138
+ /** Add a sticky canvas note directly from the rail. */
21139
+ addNote = new EventEmitter();
20529
21140
  searchModel = '';
20530
21141
  /** Two-level navigation state. */
20531
21142
  view = signal('root', ...(ngDevMode ? [{ debugName: "view" }] : /* istanbul ignore next */ []));
@@ -20624,6 +21235,9 @@ class PaletteComponent {
20624
21235
  openTriggers() {
20625
21236
  this.view.set('triggers');
20626
21237
  }
21238
+ addCanvasNote() {
21239
+ this.addNote.emit();
21240
+ }
20627
21241
  back() {
20628
21242
  this.view.set('root');
20629
21243
  this.activeCategory.set(null);
@@ -20729,7 +21343,7 @@ class PaletteComponent {
20729
21343
  return v && v !== key ? v : fallback;
20730
21344
  }
20731
21345
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: PaletteComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
20732
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: PaletteComponent, isStandalone: true, selector: "fp-palette", outputs: { closeRequested: "closeRequested", openRequested: "openRequested", add: "add", addTrigger: "addTrigger" }, host: { listeners: { "document:pointerdown": "onDocumentPointerDown($event)" }, classAttribute: "block absolute top-0 bottom-0 start-0 w-11 z-[5] overflow-visible bg-(--p-surface-200) dark:bg-(--p-surface-800) border-e border-(--p-content-border-color)" }, ngImport: i0, template: "<div\r\n class=\"flex h-full w-11 flex-col items-center gap-2 bg-transparent py-3\"\r\n role=\"toolbar\"\r\n [attr.aria-label]=\"'flowplus.palette.rail' | transloco\"\r\n>\r\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n [icon]=\"isExpanded() ? 'general.x-close' : 'general.plus'\"\n [styleClass]=\"\n isExpanded()\n ? 'fp-palette-rail-button is-active'\n : 'fp-palette-rail-button'\n \"\n [attr.aria-label]=\"'flowplus.palette.toggle' | transloco\"\n [mtTooltip]=\"'flowplus.palette.toggle' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n (onClick)=\"isExpanded() ? closeDrawer() : openDrawer()\"\n />\n\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.search-md\"\n [styleClass]=\"\n isExpanded()\n ? 'fp-palette-rail-button is-active'\n : 'fp-palette-rail-button'\n \"\n [attr.aria-label]=\"'flowplus.palette.search' | transloco\"\n [mtTooltip]=\"'flowplus.palette.search' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n (onClick)=\"openDrawer()\"\n />\n</div>\r\n\r\n@if (isExpanded()) {\r\n <aside\r\n class=\"absolute start-11 top-0 bottom-10 z-[6] flex w-80 min-h-0 flex-col overflow-hidden border-e border-(--p-content-border-color) bg-(--p-surface-50) shadow-lg animate-[fp-palette-slide-in_280ms_cubic-bezier(0,0,0.2,1)_both] rtl:animate-[fp-palette-slide-in-rtl_280ms_cubic-bezier(0,0,0.2,1)_both]\"\r\n role=\"complementary\"\r\n [attr.aria-label]=\"'flowplus.palette.title' | transloco\"\r\n >\r\n <header class=\"flex flex-col gap-2.5 px-3 pt-3 pb-2.5\">\r\n <div class=\"flex items-center gap-2\">\r\n @if (!searching() && view() !== \"root\") {\r\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"arrow.chevron-left\"\n styleClass=\"fp-palette-back-button\"\n (onClick)=\"back()\"\n [attr.aria-label]=\"'flowplus.palette.back' | transloco\"\n [mtTooltip]=\"'flowplus.palette.back' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n />\n }\r\n <h3\r\n class=\"min-w-0 flex-1 truncate text-[13.5px] font-bold text-(--p-text-color)\"\r\n >\r\n {{ headerTitle() }}\r\n </h3>\r\n </div>\r\n\r\n <mt-text-field\r\n [(ngModel)]=\"searchModel\"\r\n (ngModelChange)=\"onSearchChange($event)\"\r\n [placeholder]=\"'flowplus.palette.search' | transloco\"\r\n icon=\"general.search-md\"\r\n />\r\n </header>\r\n\r\n <div\r\n class=\"fp-scroll fp-pal-list flex flex-1 flex-col gap-0.5 overflow-y-auto px-2.5 pt-1 pb-6\"\r\n role=\"list\"\r\n >\r\n @if (searching() && view() !== \"triggers\") {\n @for (item of searchResults(); track paletteItemKey(item, $index)) {\r\n <div\r\n fExternalItem\n [fData]=\"item\"\n [fExternalItemId]=\"paletteItemKey(item, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\r\n tabindex=\"0\"\r\n [attr.title]=\"\r\n resolve(item.description) || resolve(item.displayName)\r\n \"\r\n (click)=\"pickStep(item)\"\n (keydown.enter)=\"pickStep(item)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n stepDragPreview;\n context: { $implicit: item }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"iconFor(item)\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\r\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\r\n />\r\n <div class=\"min-w-0 flex-1\">\r\n <div\r\n class=\"truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >\r\n {{ resolve(item.displayName) }}\r\n </div>\r\n @if (item.description) {\r\n <div\r\n class=\"line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >\r\n {{ resolve(item.description) }}\r\n </div>\r\n }\r\n </div>\r\n <mt-icon\r\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\r\n icon=\"general.dots-grid\"\r\n />\r\n </div>\r\n }\r\n @if (searchResults().length === 0) {\r\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\r\n }\r\n } @else if (view() === \"triggers\") {\n @for (option of visibleTriggerOptions(); track option.key) {\n <div\n fExternalItem\n [fData]=\"triggerPaletteItem(option)\"\n [fExternalItemId]=\"triggerPaletteItemKey(option, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\n tabindex=\"0\"\n [attr.title]=\"\n resolve(option.description) || resolve(option.displayName)\n \"\n (click)=\"pickTrigger(option)\"\n (keydown.enter)=\"pickTrigger(option)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n triggerDragPreview;\n context: { $implicit: option }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"triggerIconFor(option)\"\n [style.--fp-avatar-color]=\"triggerOptionVars(option).color\"\n [style.--fp-avatar-bg]=\"triggerOptionVars(option).bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ resolve(option.displayName) }}</span\r\n >\r\n @if (option.description) {\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{ resolve(option.description) }}</span\r\n >\n }\n </span>\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\n icon=\"general.dots-grid\"\n />\n </div>\n }\n @if (visibleTriggerOptions().length === 0) {\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\n }\n } @else if (view() === \"category\") {\n @for (item of activeItems(); track paletteItemKey(item, $index)) {\r\n <div\r\n fExternalItem\n [fData]=\"item\"\n [fExternalItemId]=\"paletteItemKey(item, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\r\n tabindex=\"0\"\r\n [attr.title]=\"\r\n resolve(item.description) || resolve(item.displayName)\r\n \"\r\n (click)=\"pickStep(item)\"\n (keydown.enter)=\"pickStep(item)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n stepDragPreview;\n context: { $implicit: item }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"iconFor(item)\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\r\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\r\n />\r\n <div class=\"min-w-0 flex-1\">\r\n <div\r\n class=\"truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >\r\n {{ resolve(item.displayName) }}\r\n </div>\r\n @if (item.description) {\r\n <div\r\n class=\"line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >\r\n {{ resolve(item.description) }}\r\n </div>\r\n }\r\n </div>\r\n <mt-icon\r\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\r\n icon=\"general.dots-grid\"\r\n />\r\n </div>\r\n }\r\n } @else {\r\n @for (group of groups(); track group.category) {\r\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"flex w-full cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n (click)=\"openCategory(group.category)\"\n (keydown.enter)=\"openCategory(group.category)\"\n (keydown.space)=\"$event.preventDefault(); openCategory(group.category)\"\n >\n <mt-avatar\r\n shape=\"square\"\r\n [icon]=\"group.icon\"\r\n [style.--fp-avatar-color]=\"group.color\"\r\n [style.--fp-avatar-bg]=\"group.bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ group.label }}</span\r\n >\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{ group.description }}</span\r\n >\r\n </span>\r\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color) [&_svg]:size-4 rtl:rotate-180\"\n icon=\"arrow.chevron-right\"\n />\n </div>\n }\n\n @if (groups().length === 0) {\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\n } @else {\n <div class=\"mx-2.5 my-1.5 h-px bg-(--p-content-border-color)\"></div>\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"flex w-full cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n (click)=\"openTriggers()\"\n (keydown.enter)=\"openTriggers()\"\n (keydown.space)=\"$event.preventDefault(); openTriggers()\"\n >\n <mt-avatar\r\n shape=\"square\"\r\n icon=\"general.zap-fast\"\r\n [style.--fp-avatar-color]=\"triggerVars().color\"\r\n [style.--fp-avatar-bg]=\"triggerVars().bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ \"flowplus.palette.addAnotherTrigger\" | transloco }}</span\r\n >\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{\r\n \"flowplus.palette.addAnotherTriggerDesc\" | transloco\r\n }}</span\r\n >\r\n </span>\r\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color) [&_svg]:size-4 rtl:rotate-180\"\n icon=\"arrow.chevron-right\"\n />\n </div>\n }\n }\r\n </div>\r\n </aside>\r\n}\r\n\r\n<ng-template #emptyRow>\r\n <div\r\n class=\"flex flex-col items-center justify-center px-6 py-8 text-center text-(--p-text-muted-color)\"\r\n >\r\n <p>{{ \"flowplus.common.empty\" | transloco }}</p>\r\n </div>\n</ng-template>\n\n<ng-template #stepDragPreview let-item>\n <div\n class=\"pointer-events-none w-[280px] overflow-hidden rounded-xl border border-[color-mix(in_srgb,var(--fp-avatar-color)_28%,var(--p-content-border-color))] bg-(--p-content-background)/95 text-(--p-text-color) shadow-[0_18px_36px_rgba(15,23,42,0.18),0_2px_8px_rgba(15,23,42,0.08)] ring-1 ring-white/60 backdrop-blur-md dark:ring-white/10\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\n >\n <div class=\"flex items-start gap-3 p-3\">\n <span\n class=\"grid h-11 w-11 flex-none place-items-center rounded-xl bg-[var(--fp-avatar-bg)] text-[var(--fp-avatar-color)] shadow-inner ring-1 ring-[color-mix(in_srgb,var(--fp-avatar-color)_24%,transparent)] [&_.p-avatar]:bg-transparent [&_.p-avatar]:text-inherit [&_mt-icon]:size-5 [&_svg]:size-5\"\n >\n <mt-avatar size=\"normal\" shape=\"square\" [icon]=\"iconFor(item)\" />\n </span>\n <span class=\"min-w-0 flex-1\">\n <span class=\"block truncate text-[13.5px] font-bold leading-5\">\n {{ resolve(item.displayName) }}\n </span>\n @if (item.description) {\n <span\n class=\"mt-0.5 block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\n >\n {{ resolve(item.description) }}\n </span>\n }\n </span>\n </div>\n <div\n class=\"flex items-center justify-between border-t border-(--p-content-border-color)/70 bg-[color-mix(in_srgb,var(--fp-avatar-color)_7%,transparent)] px-3 py-2\"\n >\n <span\n class=\"max-w-[150px] truncate text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n {{ item.category || item.type }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-content-background) px-2 py-0.5 text-[10.5px] font-semibold text-[var(--fp-avatar-color)] shadow-sm\"\n >\n <mt-icon icon=\"general.plus\" class=\"[&_svg]:size-3\" />\n Drop to add\n </span>\n </div>\n </div>\n</ng-template>\n\n<ng-template #triggerDragPreview let-option>\n <div\n class=\"pointer-events-none w-[280px] overflow-hidden rounded-xl border border-[color-mix(in_srgb,rgb(var(--fp-app-action))_30%,var(--p-content-border-color))] bg-(--p-content-background)/95 text-(--p-text-color) shadow-[0_18px_36px_rgba(245,158,11,0.20),0_2px_8px_rgba(15,23,42,0.08)] ring-1 ring-white/60 backdrop-blur-md dark:ring-white/10\"\n >\n <div class=\"flex items-start gap-3 p-3\">\n <span\n class=\"grid h-11 w-11 flex-none place-items-center rounded-xl bg-[color-mix(in_srgb,rgb(var(--fp-app-action))_16%,transparent)] text-[rgb(var(--fp-app-action))] shadow-inner ring-1 ring-[color-mix(in_srgb,rgb(var(--fp-app-action))_24%,transparent)] [&_.p-avatar]:bg-transparent [&_.p-avatar]:text-inherit [&_mt-icon]:size-5 [&_svg]:size-5\"\n >\n <mt-avatar\n size=\"normal\"\n shape=\"square\"\n [icon]=\"triggerIconFor(option)\"\n />\n </span>\n <span class=\"min-w-0 flex-1\">\n <span class=\"block truncate text-[13.5px] font-bold leading-5\">\n {{ resolve(option.displayName) }}\n </span>\n @if (option.description) {\n <span\n class=\"mt-0.5 block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\n >\n {{ resolve(option.description) }}\n </span>\n }\n </span>\n </div>\n <div\n class=\"flex items-center justify-between border-t border-(--p-content-border-color)/70 bg-[color-mix(in_srgb,rgb(var(--fp-app-action))_8%,transparent)] px-3 py-2\"\n >\n <span\n class=\"max-w-[150px] truncate text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n Trigger\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-content-background) px-2 py-0.5 text-[10.5px] font-semibold text-[rgb(var(--fp-app-action))] shadow-sm\"\n >\n <mt-icon icon=\"general.plus\" class=\"[&_svg]:size-3\" />\n Drop to add\n </span>\n </div>\n </div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "directive", type: Tooltip, selector: "[mtTooltip]" }, { kind: "directive", type:
21346
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: PaletteComponent, isStandalone: true, selector: "fp-palette", outputs: { closeRequested: "closeRequested", openRequested: "openRequested", add: "add", addTrigger: "addTrigger", addNote: "addNote" }, host: { listeners: { "document:pointerdown": "onDocumentPointerDown($event)" }, classAttribute: "block absolute top-0 bottom-0 start-0 w-11 z-[5] overflow-visible bg-(--p-surface-200) dark:bg-(--p-surface-800) border-e border-(--p-content-border-color)" }, ngImport: i0, template: "<div\r\n class=\"flex h-full w-11 flex-col items-center gap-2 bg-transparent py-3\"\r\n role=\"toolbar\"\r\n [attr.aria-label]=\"'flowplus.palette.rail' | transloco\"\r\n>\r\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n [icon]=\"isExpanded() ? 'general.x-close' : 'general.plus'\"\n [styleClass]=\"\n isExpanded()\n ? 'fp-palette-rail-button is-active'\n : 'fp-palette-rail-button'\n \"\n [attr.aria-label]=\"'flowplus.palette.toggle' | transloco\"\n [mtTooltip]=\"'flowplus.palette.toggle' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n (onClick)=\"isExpanded() ? closeDrawer() : openDrawer()\"\n />\n\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.search-md\"\n [styleClass]=\"\n isExpanded()\n ? 'fp-palette-rail-button is-active'\n : 'fp-palette-rail-button'\n \"\n [attr.aria-label]=\"'flowplus.palette.search' | transloco\"\n [mtTooltip]=\"'flowplus.palette.search' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n (onClick)=\"openDrawer()\"\n />\n\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"communication.annotation-plus\"\n styleClass=\"fp-palette-rail-button\"\n [attr.aria-label]=\"'flowplus.canvas.addNote' | transloco\"\n [mtTooltip]=\"'flowplus.canvas.addNote' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n (onClick)=\"addCanvasNote()\"\n />\n</div>\n\r\n@if (isExpanded()) {\r\n <aside\r\n class=\"absolute start-11 top-0 bottom-10 z-[6] flex w-80 min-h-0 flex-col overflow-hidden border-e border-(--p-content-border-color) bg-(--p-surface-50) shadow-lg animate-[fp-palette-slide-in_280ms_cubic-bezier(0,0,0.2,1)_both] rtl:animate-[fp-palette-slide-in-rtl_280ms_cubic-bezier(0,0,0.2,1)_both]\"\r\n role=\"complementary\"\r\n [attr.aria-label]=\"'flowplus.palette.title' | transloco\"\r\n >\r\n <header class=\"flex flex-col gap-2.5 px-3 pt-3 pb-2.5\">\r\n <div class=\"flex items-center gap-2\">\r\n @if (!searching() && view() !== \"root\") {\r\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"arrow.chevron-left\"\n styleClass=\"fp-palette-back-button\"\n (onClick)=\"back()\"\n [attr.aria-label]=\"'flowplus.palette.back' | transloco\"\n [mtTooltip]=\"'flowplus.palette.back' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n />\n }\r\n <h3\r\n class=\"min-w-0 flex-1 truncate text-[13.5px] font-bold text-(--p-text-color)\"\r\n >\r\n {{ headerTitle() }}\r\n </h3>\r\n </div>\r\n\r\n <mt-text-field\r\n [(ngModel)]=\"searchModel\"\r\n (ngModelChange)=\"onSearchChange($event)\"\r\n [placeholder]=\"'flowplus.palette.search' | transloco\"\r\n icon=\"general.search-md\"\r\n />\r\n </header>\r\n\r\n <div\r\n class=\"fp-scroll fp-pal-list flex flex-1 flex-col gap-0.5 overflow-y-auto px-2.5 pt-1 pb-6\"\r\n role=\"list\"\r\n >\r\n @if (searching() && view() !== \"triggers\") {\n @for (item of searchResults(); track paletteItemKey(item, $index)) {\r\n <div\r\n fExternalItem\n [fData]=\"item\"\n [fExternalItemId]=\"paletteItemKey(item, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\r\n tabindex=\"0\"\r\n [attr.title]=\"\r\n resolve(item.description) || resolve(item.displayName)\r\n \"\r\n (click)=\"pickStep(item)\"\n (keydown.enter)=\"pickStep(item)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n stepDragPreview;\n context: { $implicit: item }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"iconFor(item)\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\r\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\r\n />\r\n <div class=\"min-w-0 flex-1\">\r\n <div\r\n class=\"truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >\r\n {{ resolve(item.displayName) }}\r\n </div>\r\n @if (item.description) {\r\n <div\r\n class=\"line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >\r\n {{ resolve(item.description) }}\r\n </div>\r\n }\r\n </div>\r\n <mt-icon\r\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\r\n icon=\"general.dots-grid\"\r\n />\r\n </div>\r\n }\r\n @if (searchResults().length === 0) {\r\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\r\n }\r\n } @else if (view() === \"triggers\") {\n @for (option of visibleTriggerOptions(); track option.key) {\n <div\n fExternalItem\n [fData]=\"triggerPaletteItem(option)\"\n [fExternalItemId]=\"triggerPaletteItemKey(option, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\n tabindex=\"0\"\n [attr.title]=\"\n resolve(option.description) || resolve(option.displayName)\n \"\n (click)=\"pickTrigger(option)\"\n (keydown.enter)=\"pickTrigger(option)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n triggerDragPreview;\n context: { $implicit: option }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"triggerIconFor(option)\"\n [style.--fp-avatar-color]=\"triggerOptionVars(option).color\"\n [style.--fp-avatar-bg]=\"triggerOptionVars(option).bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ resolve(option.displayName) }}</span\r\n >\r\n @if (option.description) {\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{ resolve(option.description) }}</span\r\n >\n }\n </span>\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\n icon=\"general.dots-grid\"\n />\n </div>\n }\n @if (visibleTriggerOptions().length === 0) {\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\n }\n } @else if (view() === \"category\") {\n @for (item of activeItems(); track paletteItemKey(item, $index)) {\r\n <div\r\n fExternalItem\n [fData]=\"item\"\n [fExternalItemId]=\"paletteItemKey(item, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\r\n tabindex=\"0\"\r\n [attr.title]=\"\r\n resolve(item.description) || resolve(item.displayName)\r\n \"\r\n (click)=\"pickStep(item)\"\n (keydown.enter)=\"pickStep(item)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n stepDragPreview;\n context: { $implicit: item }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"iconFor(item)\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\r\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\r\n />\r\n <div class=\"min-w-0 flex-1\">\r\n <div\r\n class=\"truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >\r\n {{ resolve(item.displayName) }}\r\n </div>\r\n @if (item.description) {\r\n <div\r\n class=\"line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >\r\n {{ resolve(item.description) }}\r\n </div>\r\n }\r\n </div>\r\n <mt-icon\r\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\r\n icon=\"general.dots-grid\"\r\n />\r\n </div>\r\n }\r\n } @else {\r\n @for (group of groups(); track group.category) {\r\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"flex w-full cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n (click)=\"openCategory(group.category)\"\n (keydown.enter)=\"openCategory(group.category)\"\n (keydown.space)=\"$event.preventDefault(); openCategory(group.category)\"\n >\n <mt-avatar\r\n shape=\"square\"\r\n [icon]=\"group.icon\"\r\n [style.--fp-avatar-color]=\"group.color\"\r\n [style.--fp-avatar-bg]=\"group.bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ group.label }}</span\r\n >\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{ group.description }}</span\r\n >\r\n </span>\r\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color) [&_svg]:size-4 rtl:rotate-180\"\n icon=\"arrow.chevron-right\"\n />\n </div>\n }\n\n @if (groups().length === 0) {\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\n } @else {\n <div class=\"mx-2.5 my-1.5 h-px bg-(--p-content-border-color)\"></div>\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"flex w-full cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n (click)=\"openTriggers()\"\n (keydown.enter)=\"openTriggers()\"\n (keydown.space)=\"$event.preventDefault(); openTriggers()\"\n >\n <mt-avatar\r\n shape=\"square\"\r\n icon=\"general.zap-fast\"\r\n [style.--fp-avatar-color]=\"triggerVars().color\"\r\n [style.--fp-avatar-bg]=\"triggerVars().bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ \"flowplus.palette.addAnotherTrigger\" | transloco }}</span\r\n >\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{\r\n \"flowplus.palette.addAnotherTriggerDesc\" | transloco\r\n }}</span\r\n >\r\n </span>\r\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color) [&_svg]:size-4 rtl:rotate-180\"\n icon=\"arrow.chevron-right\"\n />\n </div>\n }\n }\r\n </div>\r\n </aside>\r\n}\r\n\r\n<ng-template #emptyRow>\r\n <div\r\n class=\"flex flex-col items-center justify-center px-6 py-8 text-center text-(--p-text-muted-color)\"\r\n >\r\n <p>{{ \"flowplus.common.empty\" | transloco }}</p>\r\n </div>\n</ng-template>\n\n<ng-template #stepDragPreview let-item>\n <div\n class=\"pointer-events-none w-[280px] overflow-hidden rounded-xl border border-[color-mix(in_srgb,var(--fp-avatar-color)_28%,var(--p-content-border-color))] bg-(--p-content-background)/95 text-(--p-text-color) shadow-[0_18px_36px_rgba(15,23,42,0.18),0_2px_8px_rgba(15,23,42,0.08)] ring-1 ring-white/60 backdrop-blur-md dark:ring-white/10\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\n >\n <div class=\"flex items-start gap-3 p-3\">\n <span\n class=\"grid h-11 w-11 flex-none place-items-center rounded-xl bg-[var(--fp-avatar-bg)] text-[var(--fp-avatar-color)] shadow-inner ring-1 ring-[color-mix(in_srgb,var(--fp-avatar-color)_24%,transparent)] [&_.p-avatar]:bg-transparent [&_.p-avatar]:text-inherit [&_mt-icon]:size-5 [&_svg]:size-5\"\n >\n <mt-avatar size=\"normal\" shape=\"square\" [icon]=\"iconFor(item)\" />\n </span>\n <span class=\"min-w-0 flex-1\">\n <span class=\"block truncate text-[13.5px] font-bold leading-5\">\n {{ resolve(item.displayName) }}\n </span>\n @if (item.description) {\n <span\n class=\"mt-0.5 block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\n >\n {{ resolve(item.description) }}\n </span>\n }\n </span>\n </div>\n <div\n class=\"flex items-center justify-between border-t border-(--p-content-border-color)/70 bg-[color-mix(in_srgb,var(--fp-avatar-color)_7%,transparent)] px-3 py-2\"\n >\n <span\n class=\"max-w-[150px] truncate text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n {{ item.category || item.type }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-content-background) px-2 py-0.5 text-[10.5px] font-semibold text-[var(--fp-avatar-color)] shadow-sm\"\n >\n <mt-icon icon=\"general.plus\" class=\"[&_svg]:size-3\" />\n Drop to add\n </span>\n </div>\n </div>\n</ng-template>\n\n<ng-template #triggerDragPreview let-option>\n <div\n class=\"pointer-events-none w-[280px] overflow-hidden rounded-xl border border-[color-mix(in_srgb,rgb(var(--fp-app-action))_30%,var(--p-content-border-color))] bg-(--p-content-background)/95 text-(--p-text-color) shadow-[0_18px_36px_rgba(245,158,11,0.20),0_2px_8px_rgba(15,23,42,0.08)] ring-1 ring-white/60 backdrop-blur-md dark:ring-white/10\"\n >\n <div class=\"flex items-start gap-3 p-3\">\n <span\n class=\"grid h-11 w-11 flex-none place-items-center rounded-xl bg-[color-mix(in_srgb,rgb(var(--fp-app-action))_16%,transparent)] text-[rgb(var(--fp-app-action))] shadow-inner ring-1 ring-[color-mix(in_srgb,rgb(var(--fp-app-action))_24%,transparent)] [&_.p-avatar]:bg-transparent [&_.p-avatar]:text-inherit [&_mt-icon]:size-5 [&_svg]:size-5\"\n >\n <mt-avatar\n size=\"normal\"\n shape=\"square\"\n [icon]=\"triggerIconFor(option)\"\n />\n </span>\n <span class=\"min-w-0 flex-1\">\n <span class=\"block truncate text-[13.5px] font-bold leading-5\">\n {{ resolve(option.displayName) }}\n </span>\n @if (option.description) {\n <span\n class=\"mt-0.5 block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\n >\n {{ resolve(option.description) }}\n </span>\n }\n </span>\n </div>\n <div\n class=\"flex items-center justify-between border-t border-(--p-content-border-color)/70 bg-[color-mix(in_srgb,rgb(var(--fp-app-action))_8%,transparent)] px-3 py-2\"\n >\n <span\n class=\"max-w-[150px] truncate text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n Trigger\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-content-background) px-2 py-0.5 text-[10.5px] font-semibold text-[rgb(var(--fp-app-action))] shadow-sm\"\n >\n <mt-icon icon=\"general.plus\" class=\"[&_svg]:size-3\" />\n Drop to add\n </span>\n </div>\n </div>\n</ng-template>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: Avatar, selector: "mt-avatar", inputs: ["label", "icon", "image", "styleClass", "size", "shape", "badge", "badgeSize", "badgeSeverity"], outputs: ["onImageError"] }, { kind: "component", type: Button, selector: "mt-button", inputs: ["icon", "label", "tooltip", "class", "type", "styleClass", "severity", "badge", "variant", "badgeSeverity", "size", "iconPos", "autofocus", "fluid", "raised", "rounded", "text", "plain", "outlined", "link", "disabled", "loading", "pInputs"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: Icon, selector: "mt-icon", inputs: ["icon"] }, { kind: "component", type: TextField, selector: "mt-text-field", inputs: ["field", "hint", "label", "placeholder", "class", "type", "readonly", "pInputs", "required", "maxLength", "icon", "iconPosition"] }, { kind: "directive", type: Tooltip, selector: "[mtTooltip]" }, { kind: "directive", type:
20733
21347
  // Native Foblex external-item drag (clone preview, like the official
20734
21348
  // add-node-from-palette example).
20735
21349
  FExternalItem, selector: "[fExternalItem]", inputs: ["fExternalItemId", "fData", "fDisabled", "fPreview", "fPreviewMatchSize", "fPlaceholder"], outputs: ["fPreviewChange", "fPlaceholderChange"] }, { kind: "directive", type: FExternalItemPreview, selector: "ng-template[fExternalItemPreview]" }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
@@ -20751,7 +21365,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
20751
21365
  FExternalItemPreview,
20752
21366
  ], host: {
20753
21367
  class: 'block absolute top-0 bottom-0 start-0 w-11 z-[5] overflow-visible bg-(--p-surface-200) dark:bg-(--p-surface-800) border-e border-(--p-content-border-color)',
20754
- }, template: "<div\r\n class=\"flex h-full w-11 flex-col items-center gap-2 bg-transparent py-3\"\r\n role=\"toolbar\"\r\n [attr.aria-label]=\"'flowplus.palette.rail' | transloco\"\r\n>\r\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n [icon]=\"isExpanded() ? 'general.x-close' : 'general.plus'\"\n [styleClass]=\"\n isExpanded()\n ? 'fp-palette-rail-button is-active'\n : 'fp-palette-rail-button'\n \"\n [attr.aria-label]=\"'flowplus.palette.toggle' | transloco\"\n [mtTooltip]=\"'flowplus.palette.toggle' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n (onClick)=\"isExpanded() ? closeDrawer() : openDrawer()\"\n />\n\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.search-md\"\n [styleClass]=\"\n isExpanded()\n ? 'fp-palette-rail-button is-active'\n : 'fp-palette-rail-button'\n \"\n [attr.aria-label]=\"'flowplus.palette.search' | transloco\"\n [mtTooltip]=\"'flowplus.palette.search' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n (onClick)=\"openDrawer()\"\n />\n</div>\r\n\r\n@if (isExpanded()) {\r\n <aside\r\n class=\"absolute start-11 top-0 bottom-10 z-[6] flex w-80 min-h-0 flex-col overflow-hidden border-e border-(--p-content-border-color) bg-(--p-surface-50) shadow-lg animate-[fp-palette-slide-in_280ms_cubic-bezier(0,0,0.2,1)_both] rtl:animate-[fp-palette-slide-in-rtl_280ms_cubic-bezier(0,0,0.2,1)_both]\"\r\n role=\"complementary\"\r\n [attr.aria-label]=\"'flowplus.palette.title' | transloco\"\r\n >\r\n <header class=\"flex flex-col gap-2.5 px-3 pt-3 pb-2.5\">\r\n <div class=\"flex items-center gap-2\">\r\n @if (!searching() && view() !== \"root\") {\r\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"arrow.chevron-left\"\n styleClass=\"fp-palette-back-button\"\n (onClick)=\"back()\"\n [attr.aria-label]=\"'flowplus.palette.back' | transloco\"\n [mtTooltip]=\"'flowplus.palette.back' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n />\n }\r\n <h3\r\n class=\"min-w-0 flex-1 truncate text-[13.5px] font-bold text-(--p-text-color)\"\r\n >\r\n {{ headerTitle() }}\r\n </h3>\r\n </div>\r\n\r\n <mt-text-field\r\n [(ngModel)]=\"searchModel\"\r\n (ngModelChange)=\"onSearchChange($event)\"\r\n [placeholder]=\"'flowplus.palette.search' | transloco\"\r\n icon=\"general.search-md\"\r\n />\r\n </header>\r\n\r\n <div\r\n class=\"fp-scroll fp-pal-list flex flex-1 flex-col gap-0.5 overflow-y-auto px-2.5 pt-1 pb-6\"\r\n role=\"list\"\r\n >\r\n @if (searching() && view() !== \"triggers\") {\n @for (item of searchResults(); track paletteItemKey(item, $index)) {\r\n <div\r\n fExternalItem\n [fData]=\"item\"\n [fExternalItemId]=\"paletteItemKey(item, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\r\n tabindex=\"0\"\r\n [attr.title]=\"\r\n resolve(item.description) || resolve(item.displayName)\r\n \"\r\n (click)=\"pickStep(item)\"\n (keydown.enter)=\"pickStep(item)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n stepDragPreview;\n context: { $implicit: item }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"iconFor(item)\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\r\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\r\n />\r\n <div class=\"min-w-0 flex-1\">\r\n <div\r\n class=\"truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >\r\n {{ resolve(item.displayName) }}\r\n </div>\r\n @if (item.description) {\r\n <div\r\n class=\"line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >\r\n {{ resolve(item.description) }}\r\n </div>\r\n }\r\n </div>\r\n <mt-icon\r\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\r\n icon=\"general.dots-grid\"\r\n />\r\n </div>\r\n }\r\n @if (searchResults().length === 0) {\r\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\r\n }\r\n } @else if (view() === \"triggers\") {\n @for (option of visibleTriggerOptions(); track option.key) {\n <div\n fExternalItem\n [fData]=\"triggerPaletteItem(option)\"\n [fExternalItemId]=\"triggerPaletteItemKey(option, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\n tabindex=\"0\"\n [attr.title]=\"\n resolve(option.description) || resolve(option.displayName)\n \"\n (click)=\"pickTrigger(option)\"\n (keydown.enter)=\"pickTrigger(option)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n triggerDragPreview;\n context: { $implicit: option }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"triggerIconFor(option)\"\n [style.--fp-avatar-color]=\"triggerOptionVars(option).color\"\n [style.--fp-avatar-bg]=\"triggerOptionVars(option).bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ resolve(option.displayName) }}</span\r\n >\r\n @if (option.description) {\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{ resolve(option.description) }}</span\r\n >\n }\n </span>\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\n icon=\"general.dots-grid\"\n />\n </div>\n }\n @if (visibleTriggerOptions().length === 0) {\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\n }\n } @else if (view() === \"category\") {\n @for (item of activeItems(); track paletteItemKey(item, $index)) {\r\n <div\r\n fExternalItem\n [fData]=\"item\"\n [fExternalItemId]=\"paletteItemKey(item, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\r\n tabindex=\"0\"\r\n [attr.title]=\"\r\n resolve(item.description) || resolve(item.displayName)\r\n \"\r\n (click)=\"pickStep(item)\"\n (keydown.enter)=\"pickStep(item)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n stepDragPreview;\n context: { $implicit: item }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"iconFor(item)\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\r\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\r\n />\r\n <div class=\"min-w-0 flex-1\">\r\n <div\r\n class=\"truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >\r\n {{ resolve(item.displayName) }}\r\n </div>\r\n @if (item.description) {\r\n <div\r\n class=\"line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >\r\n {{ resolve(item.description) }}\r\n </div>\r\n }\r\n </div>\r\n <mt-icon\r\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\r\n icon=\"general.dots-grid\"\r\n />\r\n </div>\r\n }\r\n } @else {\r\n @for (group of groups(); track group.category) {\r\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"flex w-full cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n (click)=\"openCategory(group.category)\"\n (keydown.enter)=\"openCategory(group.category)\"\n (keydown.space)=\"$event.preventDefault(); openCategory(group.category)\"\n >\n <mt-avatar\r\n shape=\"square\"\r\n [icon]=\"group.icon\"\r\n [style.--fp-avatar-color]=\"group.color\"\r\n [style.--fp-avatar-bg]=\"group.bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ group.label }}</span\r\n >\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{ group.description }}</span\r\n >\r\n </span>\r\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color) [&_svg]:size-4 rtl:rotate-180\"\n icon=\"arrow.chevron-right\"\n />\n </div>\n }\n\n @if (groups().length === 0) {\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\n } @else {\n <div class=\"mx-2.5 my-1.5 h-px bg-(--p-content-border-color)\"></div>\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"flex w-full cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n (click)=\"openTriggers()\"\n (keydown.enter)=\"openTriggers()\"\n (keydown.space)=\"$event.preventDefault(); openTriggers()\"\n >\n <mt-avatar\r\n shape=\"square\"\r\n icon=\"general.zap-fast\"\r\n [style.--fp-avatar-color]=\"triggerVars().color\"\r\n [style.--fp-avatar-bg]=\"triggerVars().bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ \"flowplus.palette.addAnotherTrigger\" | transloco }}</span\r\n >\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{\r\n \"flowplus.palette.addAnotherTriggerDesc\" | transloco\r\n }}</span\r\n >\r\n </span>\r\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color) [&_svg]:size-4 rtl:rotate-180\"\n icon=\"arrow.chevron-right\"\n />\n </div>\n }\n }\r\n </div>\r\n </aside>\r\n}\r\n\r\n<ng-template #emptyRow>\r\n <div\r\n class=\"flex flex-col items-center justify-center px-6 py-8 text-center text-(--p-text-muted-color)\"\r\n >\r\n <p>{{ \"flowplus.common.empty\" | transloco }}</p>\r\n </div>\n</ng-template>\n\n<ng-template #stepDragPreview let-item>\n <div\n class=\"pointer-events-none w-[280px] overflow-hidden rounded-xl border border-[color-mix(in_srgb,var(--fp-avatar-color)_28%,var(--p-content-border-color))] bg-(--p-content-background)/95 text-(--p-text-color) shadow-[0_18px_36px_rgba(15,23,42,0.18),0_2px_8px_rgba(15,23,42,0.08)] ring-1 ring-white/60 backdrop-blur-md dark:ring-white/10\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\n >\n <div class=\"flex items-start gap-3 p-3\">\n <span\n class=\"grid h-11 w-11 flex-none place-items-center rounded-xl bg-[var(--fp-avatar-bg)] text-[var(--fp-avatar-color)] shadow-inner ring-1 ring-[color-mix(in_srgb,var(--fp-avatar-color)_24%,transparent)] [&_.p-avatar]:bg-transparent [&_.p-avatar]:text-inherit [&_mt-icon]:size-5 [&_svg]:size-5\"\n >\n <mt-avatar size=\"normal\" shape=\"square\" [icon]=\"iconFor(item)\" />\n </span>\n <span class=\"min-w-0 flex-1\">\n <span class=\"block truncate text-[13.5px] font-bold leading-5\">\n {{ resolve(item.displayName) }}\n </span>\n @if (item.description) {\n <span\n class=\"mt-0.5 block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\n >\n {{ resolve(item.description) }}\n </span>\n }\n </span>\n </div>\n <div\n class=\"flex items-center justify-between border-t border-(--p-content-border-color)/70 bg-[color-mix(in_srgb,var(--fp-avatar-color)_7%,transparent)] px-3 py-2\"\n >\n <span\n class=\"max-w-[150px] truncate text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n {{ item.category || item.type }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-content-background) px-2 py-0.5 text-[10.5px] font-semibold text-[var(--fp-avatar-color)] shadow-sm\"\n >\n <mt-icon icon=\"general.plus\" class=\"[&_svg]:size-3\" />\n Drop to add\n </span>\n </div>\n </div>\n</ng-template>\n\n<ng-template #triggerDragPreview let-option>\n <div\n class=\"pointer-events-none w-[280px] overflow-hidden rounded-xl border border-[color-mix(in_srgb,rgb(var(--fp-app-action))_30%,var(--p-content-border-color))] bg-(--p-content-background)/95 text-(--p-text-color) shadow-[0_18px_36px_rgba(245,158,11,0.20),0_2px_8px_rgba(15,23,42,0.08)] ring-1 ring-white/60 backdrop-blur-md dark:ring-white/10\"\n >\n <div class=\"flex items-start gap-3 p-3\">\n <span\n class=\"grid h-11 w-11 flex-none place-items-center rounded-xl bg-[color-mix(in_srgb,rgb(var(--fp-app-action))_16%,transparent)] text-[rgb(var(--fp-app-action))] shadow-inner ring-1 ring-[color-mix(in_srgb,rgb(var(--fp-app-action))_24%,transparent)] [&_.p-avatar]:bg-transparent [&_.p-avatar]:text-inherit [&_mt-icon]:size-5 [&_svg]:size-5\"\n >\n <mt-avatar\n size=\"normal\"\n shape=\"square\"\n [icon]=\"triggerIconFor(option)\"\n />\n </span>\n <span class=\"min-w-0 flex-1\">\n <span class=\"block truncate text-[13.5px] font-bold leading-5\">\n {{ resolve(option.displayName) }}\n </span>\n @if (option.description) {\n <span\n class=\"mt-0.5 block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\n >\n {{ resolve(option.description) }}\n </span>\n }\n </span>\n </div>\n <div\n class=\"flex items-center justify-between border-t border-(--p-content-border-color)/70 bg-[color-mix(in_srgb,rgb(var(--fp-app-action))_8%,transparent)] px-3 py-2\"\n >\n <span\n class=\"max-w-[150px] truncate text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n Trigger\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-content-background) px-2 py-0.5 text-[10.5px] font-semibold text-[rgb(var(--fp-app-action))] shadow-sm\"\n >\n <mt-icon icon=\"general.plus\" class=\"[&_svg]:size-3\" />\n Drop to add\n </span>\n </div>\n </div>\n</ng-template>\n" }]
21368
+ }, template: "<div\r\n class=\"flex h-full w-11 flex-col items-center gap-2 bg-transparent py-3\"\r\n role=\"toolbar\"\r\n [attr.aria-label]=\"'flowplus.palette.rail' | transloco\"\r\n>\r\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n [icon]=\"isExpanded() ? 'general.x-close' : 'general.plus'\"\n [styleClass]=\"\n isExpanded()\n ? 'fp-palette-rail-button is-active'\n : 'fp-palette-rail-button'\n \"\n [attr.aria-label]=\"'flowplus.palette.toggle' | transloco\"\n [mtTooltip]=\"'flowplus.palette.toggle' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n (onClick)=\"isExpanded() ? closeDrawer() : openDrawer()\"\n />\n\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"general.search-md\"\n [styleClass]=\"\n isExpanded()\n ? 'fp-palette-rail-button is-active'\n : 'fp-palette-rail-button'\n \"\n [attr.aria-label]=\"'flowplus.palette.search' | transloco\"\n [mtTooltip]=\"'flowplus.palette.search' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n (onClick)=\"openDrawer()\"\n />\n\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"communication.annotation-plus\"\n styleClass=\"fp-palette-rail-button\"\n [attr.aria-label]=\"'flowplus.canvas.addNote' | transloco\"\n [mtTooltip]=\"'flowplus.canvas.addNote' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n (onClick)=\"addCanvasNote()\"\n />\n</div>\n\r\n@if (isExpanded()) {\r\n <aside\r\n class=\"absolute start-11 top-0 bottom-10 z-[6] flex w-80 min-h-0 flex-col overflow-hidden border-e border-(--p-content-border-color) bg-(--p-surface-50) shadow-lg animate-[fp-palette-slide-in_280ms_cubic-bezier(0,0,0.2,1)_both] rtl:animate-[fp-palette-slide-in-rtl_280ms_cubic-bezier(0,0,0.2,1)_both]\"\r\n role=\"complementary\"\r\n [attr.aria-label]=\"'flowplus.palette.title' | transloco\"\r\n >\r\n <header class=\"flex flex-col gap-2.5 px-3 pt-3 pb-2.5\">\r\n <div class=\"flex items-center gap-2\">\r\n @if (!searching() && view() !== \"root\") {\r\n <mt-button\n variant=\"text\"\n severity=\"secondary\"\n size=\"small\"\n icon=\"arrow.chevron-left\"\n styleClass=\"fp-palette-back-button\"\n (onClick)=\"back()\"\n [attr.aria-label]=\"'flowplus.palette.back' | transloco\"\n [mtTooltip]=\"'flowplus.palette.back' | transloco\"\n tooltipPosition=\"right\"\n appendTo=\"body\"\n [showDelay]=\"220\"\n [hideDelay]=\"80\"\n />\n }\r\n <h3\r\n class=\"min-w-0 flex-1 truncate text-[13.5px] font-bold text-(--p-text-color)\"\r\n >\r\n {{ headerTitle() }}\r\n </h3>\r\n </div>\r\n\r\n <mt-text-field\r\n [(ngModel)]=\"searchModel\"\r\n (ngModelChange)=\"onSearchChange($event)\"\r\n [placeholder]=\"'flowplus.palette.search' | transloco\"\r\n icon=\"general.search-md\"\r\n />\r\n </header>\r\n\r\n <div\r\n class=\"fp-scroll fp-pal-list flex flex-1 flex-col gap-0.5 overflow-y-auto px-2.5 pt-1 pb-6\"\r\n role=\"list\"\r\n >\r\n @if (searching() && view() !== \"triggers\") {\n @for (item of searchResults(); track paletteItemKey(item, $index)) {\r\n <div\r\n fExternalItem\n [fData]=\"item\"\n [fExternalItemId]=\"paletteItemKey(item, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\r\n tabindex=\"0\"\r\n [attr.title]=\"\r\n resolve(item.description) || resolve(item.displayName)\r\n \"\r\n (click)=\"pickStep(item)\"\n (keydown.enter)=\"pickStep(item)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n stepDragPreview;\n context: { $implicit: item }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"iconFor(item)\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\r\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\r\n />\r\n <div class=\"min-w-0 flex-1\">\r\n <div\r\n class=\"truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >\r\n {{ resolve(item.displayName) }}\r\n </div>\r\n @if (item.description) {\r\n <div\r\n class=\"line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >\r\n {{ resolve(item.description) }}\r\n </div>\r\n }\r\n </div>\r\n <mt-icon\r\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\r\n icon=\"general.dots-grid\"\r\n />\r\n </div>\r\n }\r\n @if (searchResults().length === 0) {\r\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\r\n }\r\n } @else if (view() === \"triggers\") {\n @for (option of visibleTriggerOptions(); track option.key) {\n <div\n fExternalItem\n [fData]=\"triggerPaletteItem(option)\"\n [fExternalItemId]=\"triggerPaletteItemKey(option, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\n tabindex=\"0\"\n [attr.title]=\"\n resolve(option.description) || resolve(option.displayName)\n \"\n (click)=\"pickTrigger(option)\"\n (keydown.enter)=\"pickTrigger(option)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n triggerDragPreview;\n context: { $implicit: option }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"triggerIconFor(option)\"\n [style.--fp-avatar-color]=\"triggerOptionVars(option).color\"\n [style.--fp-avatar-bg]=\"triggerOptionVars(option).bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ resolve(option.displayName) }}</span\r\n >\r\n @if (option.description) {\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{ resolve(option.description) }}</span\r\n >\n }\n </span>\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\n icon=\"general.dots-grid\"\n />\n </div>\n }\n @if (visibleTriggerOptions().length === 0) {\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\n }\n } @else if (view() === \"category\") {\n @for (item of activeItems(); track paletteItemKey(item, $index)) {\r\n <div\r\n fExternalItem\n [fData]=\"item\"\n [fExternalItemId]=\"paletteItemKey(item, $index)\"\n [fPreviewMatchSize]=\"false\"\n class=\"group/item !flex w-full cursor-grab touch-none select-none items-center gap-3 rounded-lg px-2.5 py-2.5 transition-colors hover:bg-(--p-surface-100) active:cursor-grabbing focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n role=\"button\"\r\n tabindex=\"0\"\r\n [attr.title]=\"\r\n resolve(item.description) || resolve(item.displayName)\r\n \"\r\n (click)=\"pickStep(item)\"\n (keydown.enter)=\"pickStep(item)\"\n >\n <ng-template fExternalItemPreview>\n <ng-container\n *ngTemplateOutlet=\"\n stepDragPreview;\n context: { $implicit: item }\n \"\n />\n </ng-template>\n <mt-avatar\n shape=\"square\"\n [icon]=\"iconFor(item)\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\r\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\r\n />\r\n <div class=\"min-w-0 flex-1\">\r\n <div\r\n class=\"truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >\r\n {{ resolve(item.displayName) }}\r\n </div>\r\n @if (item.description) {\r\n <div\r\n class=\"line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >\r\n {{ resolve(item.description) }}\r\n </div>\r\n }\r\n </div>\r\n <mt-icon\r\n class=\"flex-none text-(--p-text-muted-color)/40 transition-colors group-hover/item:text-(--p-text-muted-color) [&_svg]:size-4\"\r\n icon=\"general.dots-grid\"\r\n />\r\n </div>\r\n }\r\n } @else {\r\n @for (group of groups(); track group.category) {\r\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"flex w-full cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n (click)=\"openCategory(group.category)\"\n (keydown.enter)=\"openCategory(group.category)\"\n (keydown.space)=\"$event.preventDefault(); openCategory(group.category)\"\n >\n <mt-avatar\r\n shape=\"square\"\r\n [icon]=\"group.icon\"\r\n [style.--fp-avatar-color]=\"group.color\"\r\n [style.--fp-avatar-bg]=\"group.bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ group.label }}</span\r\n >\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{ group.description }}</span\r\n >\r\n </span>\r\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color) [&_svg]:size-4 rtl:rotate-180\"\n icon=\"arrow.chevron-right\"\n />\n </div>\n }\n\n @if (groups().length === 0) {\n <ng-container *ngTemplateOutlet=\"emptyRow\" />\n } @else {\n <div class=\"mx-2.5 my-1.5 h-px bg-(--p-content-border-color)\"></div>\n <div\n role=\"button\"\n tabindex=\"0\"\n class=\"flex w-full cursor-pointer items-center gap-3 rounded-lg px-2.5 py-2.5 text-start transition-colors hover:bg-(--p-surface-100) focus-visible:bg-(--p-surface-100) focus-visible:outline-none\"\n (click)=\"openTriggers()\"\n (keydown.enter)=\"openTriggers()\"\n (keydown.space)=\"$event.preventDefault(); openTriggers()\"\n >\n <mt-avatar\r\n shape=\"square\"\r\n icon=\"general.zap-fast\"\r\n [style.--fp-avatar-color]=\"triggerVars().color\"\r\n [style.--fp-avatar-bg]=\"triggerVars().bg\"\r\n />\r\n <span class=\"min-w-0 flex-1\">\r\n <span\r\n class=\"block truncate text-[13px] font-semibold text-(--p-text-color)\"\r\n >{{ \"flowplus.palette.addAnotherTrigger\" | transloco }}</span\r\n >\r\n <span\r\n class=\"block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\r\n >{{\r\n \"flowplus.palette.addAnotherTriggerDesc\" | transloco\r\n }}</span\r\n >\r\n </span>\r\n <mt-icon\n class=\"flex-none text-(--p-text-muted-color) [&_svg]:size-4 rtl:rotate-180\"\n icon=\"arrow.chevron-right\"\n />\n </div>\n }\n }\r\n </div>\r\n </aside>\r\n}\r\n\r\n<ng-template #emptyRow>\r\n <div\r\n class=\"flex flex-col items-center justify-center px-6 py-8 text-center text-(--p-text-muted-color)\"\r\n >\r\n <p>{{ \"flowplus.common.empty\" | transloco }}</p>\r\n </div>\n</ng-template>\n\n<ng-template #stepDragPreview let-item>\n <div\n class=\"pointer-events-none w-[280px] overflow-hidden rounded-xl border border-[color-mix(in_srgb,var(--fp-avatar-color)_28%,var(--p-content-border-color))] bg-(--p-content-background)/95 text-(--p-text-color) shadow-[0_18px_36px_rgba(15,23,42,0.18),0_2px_8px_rgba(15,23,42,0.08)] ring-1 ring-white/60 backdrop-blur-md dark:ring-white/10\"\n [style.--fp-avatar-color]=\"severityFor(item).color\"\n [style.--fp-avatar-bg]=\"severityFor(item).bg\"\n >\n <div class=\"flex items-start gap-3 p-3\">\n <span\n class=\"grid h-11 w-11 flex-none place-items-center rounded-xl bg-[var(--fp-avatar-bg)] text-[var(--fp-avatar-color)] shadow-inner ring-1 ring-[color-mix(in_srgb,var(--fp-avatar-color)_24%,transparent)] [&_.p-avatar]:bg-transparent [&_.p-avatar]:text-inherit [&_mt-icon]:size-5 [&_svg]:size-5\"\n >\n <mt-avatar size=\"normal\" shape=\"square\" [icon]=\"iconFor(item)\" />\n </span>\n <span class=\"min-w-0 flex-1\">\n <span class=\"block truncate text-[13.5px] font-bold leading-5\">\n {{ resolve(item.displayName) }}\n </span>\n @if (item.description) {\n <span\n class=\"mt-0.5 block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\n >\n {{ resolve(item.description) }}\n </span>\n }\n </span>\n </div>\n <div\n class=\"flex items-center justify-between border-t border-(--p-content-border-color)/70 bg-[color-mix(in_srgb,var(--fp-avatar-color)_7%,transparent)] px-3 py-2\"\n >\n <span\n class=\"max-w-[150px] truncate text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n {{ item.category || item.type }}\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-content-background) px-2 py-0.5 text-[10.5px] font-semibold text-[var(--fp-avatar-color)] shadow-sm\"\n >\n <mt-icon icon=\"general.plus\" class=\"[&_svg]:size-3\" />\n Drop to add\n </span>\n </div>\n </div>\n</ng-template>\n\n<ng-template #triggerDragPreview let-option>\n <div\n class=\"pointer-events-none w-[280px] overflow-hidden rounded-xl border border-[color-mix(in_srgb,rgb(var(--fp-app-action))_30%,var(--p-content-border-color))] bg-(--p-content-background)/95 text-(--p-text-color) shadow-[0_18px_36px_rgba(245,158,11,0.20),0_2px_8px_rgba(15,23,42,0.08)] ring-1 ring-white/60 backdrop-blur-md dark:ring-white/10\"\n >\n <div class=\"flex items-start gap-3 p-3\">\n <span\n class=\"grid h-11 w-11 flex-none place-items-center rounded-xl bg-[color-mix(in_srgb,rgb(var(--fp-app-action))_16%,transparent)] text-[rgb(var(--fp-app-action))] shadow-inner ring-1 ring-[color-mix(in_srgb,rgb(var(--fp-app-action))_24%,transparent)] [&_.p-avatar]:bg-transparent [&_.p-avatar]:text-inherit [&_mt-icon]:size-5 [&_svg]:size-5\"\n >\n <mt-avatar\n size=\"normal\"\n shape=\"square\"\n [icon]=\"triggerIconFor(option)\"\n />\n </span>\n <span class=\"min-w-0 flex-1\">\n <span class=\"block truncate text-[13.5px] font-bold leading-5\">\n {{ resolve(option.displayName) }}\n </span>\n @if (option.description) {\n <span\n class=\"mt-0.5 block line-clamp-2 text-[11.5px] leading-[1.45] text-(--p-text-muted-color)\"\n >\n {{ resolve(option.description) }}\n </span>\n }\n </span>\n </div>\n <div\n class=\"flex items-center justify-between border-t border-(--p-content-border-color)/70 bg-[color-mix(in_srgb,rgb(var(--fp-app-action))_8%,transparent)] px-3 py-2\"\n >\n <span\n class=\"max-w-[150px] truncate text-[10.5px] font-semibold text-(--p-text-muted-color)\"\n >\n Trigger\n </span>\n <span\n class=\"inline-flex items-center gap-1 rounded-full bg-(--p-content-background) px-2 py-0.5 text-[10.5px] font-semibold text-[rgb(var(--fp-app-action))] shadow-sm\"\n >\n <mt-icon icon=\"general.plus\" class=\"[&_svg]:size-3\" />\n Drop to add\n </span>\n </div>\n </div>\n</ng-template>\n" }]
20755
21369
  }], ctorParameters: () => [], propDecorators: { onDocumentPointerDown: [{
20756
21370
  type: HostListener,
20757
21371
  args: ['document:pointerdown', ['$event']]
@@ -20763,6 +21377,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
20763
21377
  type: Output
20764
21378
  }], addTrigger: [{
20765
21379
  type: Output
21380
+ }], addNote: [{
21381
+ type: Output
20766
21382
  }] } });
20767
21383
 
20768
21384
  class BuilderTopbarComponent {
@@ -20973,6 +21589,9 @@ const TRIGGER_COLUMN_X = 40;
20973
21589
  const TRIGGER_COLUMN_TOP = 60;
20974
21590
  const TRIGGER_ROW_GAP = 200;
20975
21591
  const STEP_COLUMN_OFFSET_X = 360;
21592
+ const CANVAS_NOTE_AUTOLAYOUT_PADDING = 48;
21593
+ const TRIGGER_NOTE_WIDTH = 180;
21594
+ const TRIGGER_NOTE_HEIGHT = 132;
20976
21595
  class WorkflowBuilderPageComponent {
20977
21596
  store = inject(FlowplusWorkflowFacade);
20978
21597
  /** The Foblex canvas adapter — used to drive real fit/center/select. */
@@ -21870,6 +22489,20 @@ class WorkflowBuilderPageComponent {
21870
22489
  this.clearTransientAddIntent();
21871
22490
  this.store.setPaletteOpen(false);
21872
22491
  }
22492
+ onAddCanvasNote() {
22493
+ this.clearTransientAddIntent();
22494
+ const note = this.store.addCanvasNote(this.canvas()?.flowCenter() ?? { x: 160, y: 160 });
22495
+ this.canvas()?.focusNote(note.id);
22496
+ }
22497
+ onCanvasNoteUpdate(event) {
22498
+ this.store.updateCanvasNote(event.noteId, event.patch);
22499
+ }
22500
+ onCanvasNoteDuplicate(event) {
22501
+ this.store.duplicateCanvasNote(event.noteId);
22502
+ }
22503
+ onCanvasNoteDelete(event) {
22504
+ this.store.deleteCanvasNote(event.noteId);
22505
+ }
21873
22506
  onCanvasBackgroundClick() {
21874
22507
  // Click on empty canvas background closes the editor modal + palette drawer
21875
22508
  // so the user has the full canvas back.
@@ -21933,6 +22566,7 @@ class WorkflowBuilderPageComponent {
21933
22566
  const s = this.store.builderState();
21934
22567
  if (!s.workflowId)
21935
22568
  return;
22569
+ const noteMembers = this.canvasNoteAutoLayoutMembers();
21936
22570
  // Triggers are virtual nodes the Dagre engine doesn't see. Place them in
21937
22571
  // a clean left column (entry points, left→right flow) so they never sit
21938
22572
  // on top of step nodes.
@@ -21955,19 +22589,109 @@ class WorkflowBuilderPageComponent {
21955
22589
  connections: s.connections,
21956
22590
  nodeSize: (step) => nodeSizeById.get(step.id) ?? null,
21957
22591
  });
22592
+ const nextNodePositions = layout.nodes.map((n) => ({
22593
+ stepId: n.stepId,
22594
+ x: n.x + stepOffsetX,
22595
+ y: n.y,
22596
+ }));
22597
+ const notePositions = this.canvasNoteAutoLayoutPositions(noteMembers, nextNodePositions, triggerPositions);
21958
22598
  this.canvas()?.suspendPositionSync();
21959
22599
  this.store.setLayoutPositions({
21960
22600
  triggers: triggerPositions,
21961
- nodes: layout.nodes.map((n) => ({
21962
- stepId: n.stepId,
21963
- x: n.x + stepOffsetX,
21964
- y: n.y,
21965
- })),
22601
+ nodes: nextNodePositions,
22602
+ notes: notePositions,
21966
22603
  });
21967
22604
  // Positions just changed — snap the viewport to the arranged graph once
21968
22605
  // it re-renders.
21969
22606
  this.canvas()?.armInitialFit();
21970
22607
  }
22608
+ canvasNoteAutoLayoutMembers() {
22609
+ const itemRects = this.currentCanvasItemRects();
22610
+ const members = new Map();
22611
+ for (const note of this.store.canvasNotes()) {
22612
+ const noteRect = {
22613
+ id: note.id,
22614
+ x: note.x,
22615
+ y: note.y,
22616
+ width: note.width,
22617
+ height: note.height,
22618
+ };
22619
+ const overlapped = itemRects
22620
+ .filter((rect) => rectsOverlap(noteRect, rect))
22621
+ .map((rect) => rect.id);
22622
+ if (overlapped.length)
22623
+ members.set(note.id, overlapped);
22624
+ }
22625
+ return members;
22626
+ }
22627
+ canvasNoteAutoLayoutPositions(members, nodes, triggers) {
22628
+ if (!members.size)
22629
+ return [];
22630
+ const nextRects = this.nextCanvasItemRects(nodes, triggers);
22631
+ const notes = this.store.canvasNotes();
22632
+ const updates = [];
22633
+ for (const note of notes) {
22634
+ const ids = members.get(note.id);
22635
+ if (!ids?.length)
22636
+ continue;
22637
+ const rects = ids
22638
+ .map((id) => nextRects.get(id))
22639
+ .filter((rect) => rect != null);
22640
+ const bounds = boundsForRects(rects);
22641
+ if (!bounds)
22642
+ continue;
22643
+ updates.push({
22644
+ noteId: note.id,
22645
+ x: bounds.x - CANVAS_NOTE_AUTOLAYOUT_PADDING,
22646
+ y: bounds.y - CANVAS_NOTE_AUTOLAYOUT_PADDING,
22647
+ width: bounds.width + CANVAS_NOTE_AUTOLAYOUT_PADDING * 2,
22648
+ height: bounds.height + CANVAS_NOTE_AUTOLAYOUT_PADDING * 2,
22649
+ });
22650
+ }
22651
+ return updates;
22652
+ }
22653
+ currentCanvasItemRects() {
22654
+ return [
22655
+ ...this.store.nodeVms().map((node) => ({
22656
+ id: `step:${node.stepId}`,
22657
+ x: node.x,
22658
+ y: node.y,
22659
+ width: node.width ?? 250,
22660
+ height: node.height ?? 116,
22661
+ })),
22662
+ ...this.store.triggerNodeVms().map((trigger) => ({
22663
+ id: `trigger:${trigger.triggerId}`,
22664
+ x: trigger.x,
22665
+ y: trigger.y,
22666
+ width: TRIGGER_NOTE_WIDTH,
22667
+ height: TRIGGER_NOTE_HEIGHT,
22668
+ })),
22669
+ ];
22670
+ }
22671
+ nextCanvasItemRects(nodes, triggers) {
22672
+ const currentNodes = new Map(this.store.nodeVms().map((node) => [node.stepId, node]));
22673
+ const rects = new Map();
22674
+ for (const node of nodes) {
22675
+ const vm = currentNodes.get(node.stepId);
22676
+ rects.set(`step:${node.stepId}`, {
22677
+ id: `step:${node.stepId}`,
22678
+ x: node.x,
22679
+ y: node.y,
22680
+ width: vm?.width ?? 250,
22681
+ height: vm?.height ?? 116,
22682
+ });
22683
+ }
22684
+ for (const trigger of triggers) {
22685
+ rects.set(`trigger:${trigger.triggerId}`, {
22686
+ id: `trigger:${trigger.triggerId}`,
22687
+ x: trigger.x,
22688
+ y: trigger.y,
22689
+ width: TRIGGER_NOTE_WIDTH,
22690
+ height: TRIGGER_NOTE_HEIGHT,
22691
+ });
22692
+ }
22693
+ return rects;
22694
+ }
21971
22695
  deleteSelection() {
21972
22696
  const sel = this.store.selection();
21973
22697
  for (const stepId of sel.stepIds) {
@@ -21978,6 +22702,8 @@ class WorkflowBuilderPageComponent {
21978
22702
  }
21979
22703
  for (const connId of sel.connectionIds)
21980
22704
  this.store.deleteConnection(connId);
22705
+ for (const noteId of sel.canvasNoteIds)
22706
+ this.store.deleteCanvasNote(noteId);
21981
22707
  }
21982
22708
  /* -------- node hover-bar actions (Phase 3) -------- */
21983
22709
  onNodeQuickAdd(e) {
@@ -22216,7 +22942,7 @@ class WorkflowBuilderPageComponent {
22216
22942
  this.store.setInspectorOpen(true);
22217
22943
  }
22218
22944
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowBuilderPageComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
22219
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkflowBuilderPageComponent, isStandalone: true, selector: "fp-workflow-builder-page", viewQueries: [{ propertyName: "canvas", first: true, predicate: FlowCanvasComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"relative flex h-full min-h-full w-full flex-col overflow-hidden bg-(--p-surface-50) text-(--p-text-color)\"\n id=\"fp-builder-root\"\n>\n <fp-builder-topbar\n (validate)=\"onValidate()\"\n (testRun)=\"onOpenTestRun()\"\n (publish)=\"onPublish()\"\n (unpublish)=\"onUnpublish()\"\n (openSettings)=\"onOpenSettings()\"\n (toggleNodeFinder)=\"onTogglePalette()\"\n />\n\n <div\n class=\"relative min-h-0 flex-1 overflow-hidden\"\n id=\"fp-workspace-root\"\n [class.is-bottom-open]=\"store.ui().bottomPanelOpen\"\n [class.is-palette-expanded]=\"store.ui().paletteOpen\"\n >\n @if (store.busy()) {\n <div\n class=\"pointer-events-none absolute inset-x-0 top-0 z-[7] h-0.5 overflow-hidden bg-(--p-primary-color)/15\"\n role=\"status\"\n aria-label=\"Working...\"\n >\n <div\n class=\"h-full w-[30%] bg-(--p-primary-color) animate-[fp-busy-bar-slide_1.1s_cubic-bezier(0.4,0,0.2,1)_infinite]\"\n ></div>\n </div>\n }\n\n <fp-palette\n (openRequested)=\"onOpenPaletteFromRail()\"\n (closeRequested)=\"onClosePalette()\"\n (add)=\"onPaletteAdd($event)\"\n (addTrigger)=\"onPaletteAddTrigger($event)\"\n />\n\n <main class=\"absolute inset-0 overflow-hidden bg-(--p-surface-50)\">\n <fp-flow-canvas\n (paletteDrop)=\"onPaletteDrop($event)\"\n (connectionCreate)=\"onConnectionCreate($event)\"\n (connectionReassign)=\"onConnectionReassign($event)\"\n (triggerStartConnect)=\"onTriggerStartConnect($event)\"\n (triggerStartReassign)=\"onTriggerStartReassign($event)\"\n (triggerStartDisconnect)=\"onTriggerStartDisconnect($event)\"\n (connectionQuickAdd)=\"onConnectionQuickAdd($event)\"\n (quickAddPick)=\"onQuickAddPick($event)\"\n (autoLayoutRequested)=\"autoLayout()\"\n (nodeQuickAdd)=\"onNodeQuickAdd($event)\"\n (nodePortPlusClick)=\"onNodePortPlusClick($event)\"\n (nodeDuplicate)=\"onNodeDuplicate($event)\"\n (nodeRemove)=\"onNodeRemove($event)\"\n (nodeOpenDetails)=\"onNodeOpenDetails($event)\"\n (edgeInsertStep)=\"onEdgeInsertStep($event)\"\n (edgeRemove)=\"onEdgeRemove($event)\"\n (edgeEditFormula)=\"onEdgeEditFormula($event)\"\n (edgeOpenDetails)=\"onEdgeOpenDetails($event)\"\n (assignNodeToConnection)=\"onAssignNodeToConnection($event)\"\n (openChildWorkflow)=\"onOpenChildWorkflow($event)\"\n (starterAddTrigger)=\"onStarterAddTrigger($event)\"\n (triggerOpenDetails)=\"onTriggerOpenDetails($event)\"\n (triggerExecute)=\"onTriggerExecute($event)\"\n (triggerToggleEnabled)=\"onTriggerToggleEnabled($event)\"\n (triggerDelete)=\"onTriggerDelete($event)\"\n (requestAddStep)=\"onRequestAddStep($event)\"\n (canvasBackgroundClick)=\"onCanvasBackgroundClick()\"\n />\n\n @if (store.loading()) {\n <div\n class=\"pointer-events-none absolute inset-0 z-[7] flex flex-col items-center justify-center gap-3.5 bg-(--p-surface-50)/70 text-[12.5px] text-(--p-text-muted-color) backdrop-blur-[2px] animate-[fp-fade-in_240ms_ease-out]\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n class=\"h-9 w-9 rounded-full border-[3px] border-(--p-primary-color)/20 border-t-(--p-primary-color) animate-[fp-loading-spin_720ms_linear_infinite]\"\n aria-hidden=\"true\"\n ></div>\n <span>{{ \"flowplus.common.loading\" | transloco }}</span>\n </div>\n }\n </main>\n\n <fp-bottom-panel\n [class.h-96]=\"store.ui().bottomPanelOpen\"\n [class.h-10]=\"!store.ui().bottomPanelOpen\"\n (focus)=\"onFocusIssue($event)\"\n />\n </div>\n\n <fp-command-palette\n [open]=\"commandPaletteOpen()\"\n [actions]=\"commandPaletteActions()\"\n (pick)=\"onCommandPalettePick($event)\"\n (dismiss)=\"closeCommandPalette()\"\n />\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: BuilderTopbarComponent, selector: "fp-builder-topbar", outputs: ["validate", "testRun", "publish", "unpublish", "openSettings", "toggleNodeFinder"] }, { kind: "component", type: PaletteComponent, selector: "fp-palette", outputs: ["closeRequested", "openRequested", "add", "addTrigger"] }, { kind: "component", type: FlowCanvasComponent, selector: "fp-flow-canvas", outputs: ["paletteDrop", "connectionCreate", "connectionReassign", "triggerStartConnect", "triggerStartReassign", "triggerStartDisconnect", "connectionQuickAdd", "quickAddPick", "autoLayoutRequested", "loaded", "nodeQuickAdd", "nodePortPlusClick", "nodeDuplicate", "nodeRemove", "nodeOpenDetails", "edgeInsertStep", "edgeRemove", "edgeEditFormula", "edgeOpenDetails", "assignNodeToConnection", "openChildWorkflow", "starterAddTrigger", "triggerOpenDetails", "triggerExecute", "triggerToggleEnabled", "triggerDelete", "canvasBackgroundClick", "requestAddStep"] }, { kind: "component", type: BottomPanelComponent, selector: "fp-bottom-panel", outputs: ["focus"] }, { kind: "component", type: CommandPaletteComponent, selector: "fp-command-palette", inputs: ["open", "actions"], outputs: ["pick", "dismiss"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
22945
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: WorkflowBuilderPageComponent, isStandalone: true, selector: "fp-workflow-builder-page", viewQueries: [{ propertyName: "canvas", first: true, predicate: FlowCanvasComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"relative flex h-full min-h-full w-full flex-col overflow-hidden bg-(--p-surface-50) text-(--p-text-color)\"\n id=\"fp-builder-root\"\n>\n <fp-builder-topbar\n (validate)=\"onValidate()\"\n (testRun)=\"onOpenTestRun()\"\n (publish)=\"onPublish()\"\n (unpublish)=\"onUnpublish()\"\n (openSettings)=\"onOpenSettings()\"\n (toggleNodeFinder)=\"onTogglePalette()\"\n />\n\n <div\n class=\"relative min-h-0 flex-1 overflow-hidden\"\n id=\"fp-workspace-root\"\n [class.is-bottom-open]=\"store.ui().bottomPanelOpen\"\n [class.is-palette-expanded]=\"store.ui().paletteOpen\"\n >\n @if (store.busy()) {\n <div\n class=\"pointer-events-none absolute inset-x-0 top-0 z-[7] h-0.5 overflow-hidden bg-(--p-primary-color)/15\"\n role=\"status\"\n aria-label=\"Working...\"\n >\n <div\n class=\"h-full w-[30%] bg-(--p-primary-color) animate-[fp-busy-bar-slide_1.1s_cubic-bezier(0.4,0,0.2,1)_infinite]\"\n ></div>\n </div>\n }\n\n <fp-palette\n (openRequested)=\"onOpenPaletteFromRail()\"\n (closeRequested)=\"onClosePalette()\"\n (add)=\"onPaletteAdd($event)\"\n (addTrigger)=\"onPaletteAddTrigger($event)\"\n (addNote)=\"onAddCanvasNote()\"\n />\n\n <main class=\"absolute inset-0 overflow-hidden bg-(--p-surface-50)\">\n <fp-flow-canvas\n (paletteDrop)=\"onPaletteDrop($event)\"\n (connectionCreate)=\"onConnectionCreate($event)\"\n (connectionReassign)=\"onConnectionReassign($event)\"\n (triggerStartConnect)=\"onTriggerStartConnect($event)\"\n (triggerStartReassign)=\"onTriggerStartReassign($event)\"\n (triggerStartDisconnect)=\"onTriggerStartDisconnect($event)\"\n (connectionQuickAdd)=\"onConnectionQuickAdd($event)\"\n (quickAddPick)=\"onQuickAddPick($event)\"\n (autoLayoutRequested)=\"autoLayout()\"\n (nodeQuickAdd)=\"onNodeQuickAdd($event)\"\n (nodePortPlusClick)=\"onNodePortPlusClick($event)\"\n (nodeDuplicate)=\"onNodeDuplicate($event)\"\n (nodeRemove)=\"onNodeRemove($event)\"\n (nodeOpenDetails)=\"onNodeOpenDetails($event)\"\n (edgeInsertStep)=\"onEdgeInsertStep($event)\"\n (edgeRemove)=\"onEdgeRemove($event)\"\n (edgeEditFormula)=\"onEdgeEditFormula($event)\"\n (edgeOpenDetails)=\"onEdgeOpenDetails($event)\"\n (assignNodeToConnection)=\"onAssignNodeToConnection($event)\"\n (openChildWorkflow)=\"onOpenChildWorkflow($event)\"\n (starterAddTrigger)=\"onStarterAddTrigger($event)\"\n (triggerOpenDetails)=\"onTriggerOpenDetails($event)\"\n (triggerExecute)=\"onTriggerExecute($event)\"\n (triggerToggleEnabled)=\"onTriggerToggleEnabled($event)\"\n (triggerDelete)=\"onTriggerDelete($event)\"\n (requestAddStep)=\"onRequestAddStep($event)\"\n (noteUpdate)=\"onCanvasNoteUpdate($event)\"\n (noteDuplicate)=\"onCanvasNoteDuplicate($event)\"\n (noteDelete)=\"onCanvasNoteDelete($event)\"\n (canvasBackgroundClick)=\"onCanvasBackgroundClick()\"\n />\n\n @if (store.loading()) {\n <div\n class=\"pointer-events-none absolute inset-0 z-[7] flex flex-col items-center justify-center gap-3.5 bg-(--p-surface-50)/70 text-[12.5px] text-(--p-text-muted-color) backdrop-blur-[2px] animate-[fp-fade-in_240ms_ease-out]\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n class=\"h-9 w-9 rounded-full border-[3px] border-(--p-primary-color)/20 border-t-(--p-primary-color) animate-[fp-loading-spin_720ms_linear_infinite]\"\n aria-hidden=\"true\"\n ></div>\n <span>{{ \"flowplus.common.loading\" | transloco }}</span>\n </div>\n }\n </main>\n\n <fp-bottom-panel\n [class.h-96]=\"store.ui().bottomPanelOpen\"\n [class.h-10]=\"!store.ui().bottomPanelOpen\"\n (focus)=\"onFocusIssue($event)\"\n />\n </div>\n\n <fp-command-palette\n [open]=\"commandPaletteOpen()\"\n [actions]=\"commandPaletteActions()\"\n (pick)=\"onCommandPalettePick($event)\"\n (dismiss)=\"closeCommandPalette()\"\n />\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: TranslocoModule }, { kind: "component", type: BuilderTopbarComponent, selector: "fp-builder-topbar", outputs: ["validate", "testRun", "publish", "unpublish", "openSettings", "toggleNodeFinder"] }, { kind: "component", type: PaletteComponent, selector: "fp-palette", outputs: ["closeRequested", "openRequested", "add", "addTrigger", "addNote"] }, { kind: "component", type: FlowCanvasComponent, selector: "fp-flow-canvas", outputs: ["paletteDrop", "connectionCreate", "connectionReassign", "triggerStartConnect", "triggerStartReassign", "triggerStartDisconnect", "connectionQuickAdd", "quickAddPick", "autoLayoutRequested", "loaded", "nodeQuickAdd", "nodePortPlusClick", "nodeDuplicate", "nodeRemove", "nodeOpenDetails", "edgeInsertStep", "edgeRemove", "edgeEditFormula", "edgeOpenDetails", "assignNodeToConnection", "openChildWorkflow", "starterAddTrigger", "triggerOpenDetails", "triggerExecute", "triggerToggleEnabled", "triggerDelete", "canvasBackgroundClick", "requestAddStep", "noteUpdate", "noteDuplicate", "noteDelete"] }, { kind: "component", type: BottomPanelComponent, selector: "fp-bottom-panel", outputs: ["focus"] }, { kind: "component", type: CommandPaletteComponent, selector: "fp-command-palette", inputs: ["open", "actions"], outputs: ["pick", "dismiss"] }, { kind: "pipe", type: i2.TranslocoPipe, name: "transloco" }] });
22220
22946
  }
22221
22947
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: WorkflowBuilderPageComponent, decorators: [{
22222
22948
  type: Component,
@@ -22228,7 +22954,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
22228
22954
  FlowCanvasComponent,
22229
22955
  BottomPanelComponent,
22230
22956
  CommandPaletteComponent,
22231
- ], template: "<div\n class=\"relative flex h-full min-h-full w-full flex-col overflow-hidden bg-(--p-surface-50) text-(--p-text-color)\"\n id=\"fp-builder-root\"\n>\n <fp-builder-topbar\n (validate)=\"onValidate()\"\n (testRun)=\"onOpenTestRun()\"\n (publish)=\"onPublish()\"\n (unpublish)=\"onUnpublish()\"\n (openSettings)=\"onOpenSettings()\"\n (toggleNodeFinder)=\"onTogglePalette()\"\n />\n\n <div\n class=\"relative min-h-0 flex-1 overflow-hidden\"\n id=\"fp-workspace-root\"\n [class.is-bottom-open]=\"store.ui().bottomPanelOpen\"\n [class.is-palette-expanded]=\"store.ui().paletteOpen\"\n >\n @if (store.busy()) {\n <div\n class=\"pointer-events-none absolute inset-x-0 top-0 z-[7] h-0.5 overflow-hidden bg-(--p-primary-color)/15\"\n role=\"status\"\n aria-label=\"Working...\"\n >\n <div\n class=\"h-full w-[30%] bg-(--p-primary-color) animate-[fp-busy-bar-slide_1.1s_cubic-bezier(0.4,0,0.2,1)_infinite]\"\n ></div>\n </div>\n }\n\n <fp-palette\n (openRequested)=\"onOpenPaletteFromRail()\"\n (closeRequested)=\"onClosePalette()\"\n (add)=\"onPaletteAdd($event)\"\n (addTrigger)=\"onPaletteAddTrigger($event)\"\n />\n\n <main class=\"absolute inset-0 overflow-hidden bg-(--p-surface-50)\">\n <fp-flow-canvas\n (paletteDrop)=\"onPaletteDrop($event)\"\n (connectionCreate)=\"onConnectionCreate($event)\"\n (connectionReassign)=\"onConnectionReassign($event)\"\n (triggerStartConnect)=\"onTriggerStartConnect($event)\"\n (triggerStartReassign)=\"onTriggerStartReassign($event)\"\n (triggerStartDisconnect)=\"onTriggerStartDisconnect($event)\"\n (connectionQuickAdd)=\"onConnectionQuickAdd($event)\"\n (quickAddPick)=\"onQuickAddPick($event)\"\n (autoLayoutRequested)=\"autoLayout()\"\n (nodeQuickAdd)=\"onNodeQuickAdd($event)\"\n (nodePortPlusClick)=\"onNodePortPlusClick($event)\"\n (nodeDuplicate)=\"onNodeDuplicate($event)\"\n (nodeRemove)=\"onNodeRemove($event)\"\n (nodeOpenDetails)=\"onNodeOpenDetails($event)\"\n (edgeInsertStep)=\"onEdgeInsertStep($event)\"\n (edgeRemove)=\"onEdgeRemove($event)\"\n (edgeEditFormula)=\"onEdgeEditFormula($event)\"\n (edgeOpenDetails)=\"onEdgeOpenDetails($event)\"\n (assignNodeToConnection)=\"onAssignNodeToConnection($event)\"\n (openChildWorkflow)=\"onOpenChildWorkflow($event)\"\n (starterAddTrigger)=\"onStarterAddTrigger($event)\"\n (triggerOpenDetails)=\"onTriggerOpenDetails($event)\"\n (triggerExecute)=\"onTriggerExecute($event)\"\n (triggerToggleEnabled)=\"onTriggerToggleEnabled($event)\"\n (triggerDelete)=\"onTriggerDelete($event)\"\n (requestAddStep)=\"onRequestAddStep($event)\"\n (canvasBackgroundClick)=\"onCanvasBackgroundClick()\"\n />\n\n @if (store.loading()) {\n <div\n class=\"pointer-events-none absolute inset-0 z-[7] flex flex-col items-center justify-center gap-3.5 bg-(--p-surface-50)/70 text-[12.5px] text-(--p-text-muted-color) backdrop-blur-[2px] animate-[fp-fade-in_240ms_ease-out]\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n class=\"h-9 w-9 rounded-full border-[3px] border-(--p-primary-color)/20 border-t-(--p-primary-color) animate-[fp-loading-spin_720ms_linear_infinite]\"\n aria-hidden=\"true\"\n ></div>\n <span>{{ \"flowplus.common.loading\" | transloco }}</span>\n </div>\n }\n </main>\n\n <fp-bottom-panel\n [class.h-96]=\"store.ui().bottomPanelOpen\"\n [class.h-10]=\"!store.ui().bottomPanelOpen\"\n (focus)=\"onFocusIssue($event)\"\n />\n </div>\n\n <fp-command-palette\n [open]=\"commandPaletteOpen()\"\n [actions]=\"commandPaletteActions()\"\n (pick)=\"onCommandPalettePick($event)\"\n (dismiss)=\"closeCommandPalette()\"\n />\n</div>\n" }]
22957
+ ], template: "<div\n class=\"relative flex h-full min-h-full w-full flex-col overflow-hidden bg-(--p-surface-50) text-(--p-text-color)\"\n id=\"fp-builder-root\"\n>\n <fp-builder-topbar\n (validate)=\"onValidate()\"\n (testRun)=\"onOpenTestRun()\"\n (publish)=\"onPublish()\"\n (unpublish)=\"onUnpublish()\"\n (openSettings)=\"onOpenSettings()\"\n (toggleNodeFinder)=\"onTogglePalette()\"\n />\n\n <div\n class=\"relative min-h-0 flex-1 overflow-hidden\"\n id=\"fp-workspace-root\"\n [class.is-bottom-open]=\"store.ui().bottomPanelOpen\"\n [class.is-palette-expanded]=\"store.ui().paletteOpen\"\n >\n @if (store.busy()) {\n <div\n class=\"pointer-events-none absolute inset-x-0 top-0 z-[7] h-0.5 overflow-hidden bg-(--p-primary-color)/15\"\n role=\"status\"\n aria-label=\"Working...\"\n >\n <div\n class=\"h-full w-[30%] bg-(--p-primary-color) animate-[fp-busy-bar-slide_1.1s_cubic-bezier(0.4,0,0.2,1)_infinite]\"\n ></div>\n </div>\n }\n\n <fp-palette\n (openRequested)=\"onOpenPaletteFromRail()\"\n (closeRequested)=\"onClosePalette()\"\n (add)=\"onPaletteAdd($event)\"\n (addTrigger)=\"onPaletteAddTrigger($event)\"\n (addNote)=\"onAddCanvasNote()\"\n />\n\n <main class=\"absolute inset-0 overflow-hidden bg-(--p-surface-50)\">\n <fp-flow-canvas\n (paletteDrop)=\"onPaletteDrop($event)\"\n (connectionCreate)=\"onConnectionCreate($event)\"\n (connectionReassign)=\"onConnectionReassign($event)\"\n (triggerStartConnect)=\"onTriggerStartConnect($event)\"\n (triggerStartReassign)=\"onTriggerStartReassign($event)\"\n (triggerStartDisconnect)=\"onTriggerStartDisconnect($event)\"\n (connectionQuickAdd)=\"onConnectionQuickAdd($event)\"\n (quickAddPick)=\"onQuickAddPick($event)\"\n (autoLayoutRequested)=\"autoLayout()\"\n (nodeQuickAdd)=\"onNodeQuickAdd($event)\"\n (nodePortPlusClick)=\"onNodePortPlusClick($event)\"\n (nodeDuplicate)=\"onNodeDuplicate($event)\"\n (nodeRemove)=\"onNodeRemove($event)\"\n (nodeOpenDetails)=\"onNodeOpenDetails($event)\"\n (edgeInsertStep)=\"onEdgeInsertStep($event)\"\n (edgeRemove)=\"onEdgeRemove($event)\"\n (edgeEditFormula)=\"onEdgeEditFormula($event)\"\n (edgeOpenDetails)=\"onEdgeOpenDetails($event)\"\n (assignNodeToConnection)=\"onAssignNodeToConnection($event)\"\n (openChildWorkflow)=\"onOpenChildWorkflow($event)\"\n (starterAddTrigger)=\"onStarterAddTrigger($event)\"\n (triggerOpenDetails)=\"onTriggerOpenDetails($event)\"\n (triggerExecute)=\"onTriggerExecute($event)\"\n (triggerToggleEnabled)=\"onTriggerToggleEnabled($event)\"\n (triggerDelete)=\"onTriggerDelete($event)\"\n (requestAddStep)=\"onRequestAddStep($event)\"\n (noteUpdate)=\"onCanvasNoteUpdate($event)\"\n (noteDuplicate)=\"onCanvasNoteDuplicate($event)\"\n (noteDelete)=\"onCanvasNoteDelete($event)\"\n (canvasBackgroundClick)=\"onCanvasBackgroundClick()\"\n />\n\n @if (store.loading()) {\n <div\n class=\"pointer-events-none absolute inset-0 z-[7] flex flex-col items-center justify-center gap-3.5 bg-(--p-surface-50)/70 text-[12.5px] text-(--p-text-muted-color) backdrop-blur-[2px] animate-[fp-fade-in_240ms_ease-out]\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <div\n class=\"h-9 w-9 rounded-full border-[3px] border-(--p-primary-color)/20 border-t-(--p-primary-color) animate-[fp-loading-spin_720ms_linear_infinite]\"\n aria-hidden=\"true\"\n ></div>\n <span>{{ \"flowplus.common.loading\" | transloco }}</span>\n </div>\n }\n </main>\n\n <fp-bottom-panel\n [class.h-96]=\"store.ui().bottomPanelOpen\"\n [class.h-10]=\"!store.ui().bottomPanelOpen\"\n (focus)=\"onFocusIssue($event)\"\n />\n </div>\n\n <fp-command-palette\n [open]=\"commandPaletteOpen()\"\n [actions]=\"commandPaletteActions()\"\n (pick)=\"onCommandPalettePick($event)\"\n (dismiss)=\"closeCommandPalette()\"\n />\n</div>\n" }]
22232
22958
  }], ctorParameters: () => [], propDecorators: { canvas: [{ type: i0.ViewChild, args: [i0.forwardRef(() => FlowCanvasComponent), { isSignal: true }] }] } });
22233
22959
  function readObject(value) {
22234
22960
  return value && typeof value === 'object' && !Array.isArray(value)
@@ -22333,6 +23059,27 @@ function uniqueStrings(values) {
22333
23059
  }
22334
23060
  return out;
22335
23061
  }
23062
+ function rectsOverlap(a, b) {
23063
+ return !(a.x + a.width < b.x ||
23064
+ b.x + b.width < a.x ||
23065
+ a.y + a.height < b.y ||
23066
+ b.y + b.height < a.y);
23067
+ }
23068
+ function boundsForRects(rects) {
23069
+ if (!rects.length)
23070
+ return null;
23071
+ const minX = Math.min(...rects.map((rect) => rect.x));
23072
+ const minY = Math.min(...rects.map((rect) => rect.y));
23073
+ const maxX = Math.max(...rects.map((rect) => rect.x + rect.width));
23074
+ const maxY = Math.max(...rects.map((rect) => rect.y + rect.height));
23075
+ return {
23076
+ id: 'bounds',
23077
+ x: minX,
23078
+ y: minY,
23079
+ width: maxX - minX,
23080
+ height: maxY - minY,
23081
+ };
23082
+ }
22336
23083
  function readString$1(record, key) {
22337
23084
  const value = record[key];
22338
23085
  return typeof value === 'string' && value.trim() ? value : null;
@@ -23166,5 +23913,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
23166
23913
  * Generated bundle index. Do not edit.
23167
23914
  */
23168
23915
 
23169
- export { APP_STATES, ApplyAutomationExecutionDetail, ApplyBuilderCommand, ApplyBuilderSnapshot, AutomationBuilderCatalogApiService, AutomationDesignApiService, AutomationExecutionApiService, AutomationExecutionsPageComponent, BottomPanelComponent, BuilderTopbarComponent, ClearAutomationRuntimeState, ClearCommandHistory, ClearConflict, ClearPendingOperation, ClearSelection, ClearWorkflowBuilder, CommitStepUpdates, CommitWorkflowMetadata, ConnectionCreated, ConnectionCreationFailed, ContextPickerComponent, ContextPillButtonComponent, ContextPillComponent, CreateConnection, CreateStep, CreateTrigger, CreateWorkflow, DagreFlowLayoutEngine, DeleteConnection, DeleteStep, DeleteTrigger, DeleteWorkflow, DuplicateWorkflow, EndpointBuilder, FLOWPLUS_FORM_DESIGNER_ADAPTER, FLOWPLUS_WORKFLOW_CONFIG, FLOWPLUS_WORKFLOW_DEFAULT_RUNTIME, FLOWPLUS_WORKFLOW_DEFAULT_STATE, FLOWPLUS_WORKFLOW_NAVIGATION, FLOWPLUS_WORKFLOW_ROUTES, FLOW_NODE_BASE_HEIGHT, FLOW_NODE_CARD_WIDTH, FLOW_NODE_MULTI_OUTPUT_GAP, FLOW_NODE_OUTPUT_SIZE, FlowCanvasComponent, FlowNodeComponent, FlowplusWorkflowActionKey, FlowplusWorkflowFacade, FlowplusWorkflowNavigationService, FlowplusWorkflowOverlayService, FlowplusWorkflowState, InspectorShellComponent, KeyboardShortcutsService, LOCAL_FALLBACK_CATALOG, LoadAutomationExecution, LoadContextCatalog, LoadContextCatalogForStep, LoadLatestAutomationExecution, LoadLayout, LoadTriggers, LoadWorkflowBuilder, LoadWorkflowCatalog, LoadWorkflowList, MarkLayoutDirty, MoveStep, NODE_COLOR_TOKEN, NODE_DEFAULT_ICON, NODE_MT_ICON, NoopFlowplusFormDesignerAdapter, PaletteComponent, PaletteDragSourceDirective, PollAutomationExecution, ProblemsPanelComponent, ProcessCreateDialogComponent, PublishWorkflow, RedoBuilderCommand, ReloadWorkflowBuilder, RunAutomationTrigger, RunWorkflowTest, SaveLayout, SelectConnection, SelectRuntimeNodeRun, SelectStep, SelectTrigger, SetActiveInspectorTab, SetBottomPanelOpen, SetBottomPanelTab, SetCanvasViewport, SetContextCatalog, SetCreateDialogOpen, SetInspectorOpen, SetLayout, SetLayoutAutosavePaused, SetMinimap, SetPaletteOpen, SetPaletteSearch, SetPendingOperation, SetReadonly, SetSelectedRuntimeTrigger, SetSelection, SetSelectionFromCanvas, SetStudioFilter, SetValidation, SetWorkflowCatalog, SetWorkflowDefinition, StepCreated, StepCreationFailed, TRIGGER_MT_ICON, TestRunPanelComponent, UnconfiguredFormDesignerAdapter, UndoBuilderCommand, UnpublishWorkflow, UpdateConnection, UpdateStep, UpdateTrigger, UpdateWorkflowMetadata, UpdateWorkflowResources, UpdateWorkflowVariables, ValidateWorkflow, WorkflowBuilderPageComponent, WorkflowCatalogApiService, WorkflowContextApiService, WorkflowDebugApiService, WorkflowDefinitionApiService, WorkflowFormApiService, WorkflowRunDebuggerPageComponent, WorkflowRuntimeApiService, WorkflowStudioPageComponent, WorkflowValidationApiService, applyBuilderSnapshot, avatarSeverityForStep, avatarSeverityForTrigger, canonicalTriggerTypeForStarterKind, coerceTranslatable, colorVarFor, computeBranchLanes, computeSaveStatus, connectionCanvasId, derivePortsForStep, extractMessage, flowNodeCardHeightForOutputs, flowNodeLayoutHeightForOutputs, flowNodeOutputCenterY, flowNodeOutputStackHeight, fromWorkflowConnectionDomain, fromWorkflowStepDomain, hasOtherPending, iconFor, indexValidationByConnectionId, indexValidationByStepId, indexValidationByTriggerId, inputPortId, isEntityDirty, mapHttpError, mtIconForStep, mtIconForTrigger, newClientMutationId, nextOperationId, nodeCanvasId, outputPortId, parseConnectionId, parseNodeId, parsePortId, parseTriggerOutputPortId, parseTriggerStartConnectionId, patchLayoutPosition, patchTriggerLayoutPosition, provideFlowplusFormDesignerAdapter, provideFlowplusWorkflow, provideFlowplusWorkflowNavigation, provideFlowplusWorkflowNavigationDefaults, readRecord, removeStepWithEdges, resolveTranslatable, resolveTriggerStartNodeKey, resolveTriggerStartStep, resolveTriggerStartStepId, resolveWorkflowTriggerKey, savingOrSaved, tempNodeCanvasId, toWorkflowConnectionDomain, toWorkflowDefinitionDomain, toWorkflowStepDomain, toWorkflowTriggerDomain, triggerCanvasId, triggerOutputPortId, triggerStartConnectionCanvasId, triggerStarterKindFor, unwrap, unwrapApiData, upsertConnection, upsertStep };
23916
+ export { APP_STATES, AddCanvasNote, ApplyAutomationExecutionDetail, ApplyBuilderCommand, ApplyBuilderSnapshot, AutomationBuilderCatalogApiService, AutomationDesignApiService, AutomationExecutionApiService, AutomationExecutionsPageComponent, BottomPanelComponent, BuilderTopbarComponent, ClearAutomationRuntimeState, ClearCommandHistory, ClearConflict, ClearPendingOperation, ClearSelection, ClearWorkflowBuilder, CommitStepUpdates, CommitWorkflowMetadata, ConnectionCreated, ConnectionCreationFailed, ContextPickerComponent, ContextPillButtonComponent, ContextPillComponent, CreateConnection, CreateStep, CreateTrigger, CreateWorkflow, DagreFlowLayoutEngine, DeleteCanvasNote, DeleteConnection, DeleteStep, DeleteTrigger, DeleteWorkflow, DuplicateCanvasNote, DuplicateWorkflow, EndpointBuilder, FLOWPLUS_FORM_DESIGNER_ADAPTER, FLOWPLUS_WORKFLOW_CONFIG, FLOWPLUS_WORKFLOW_DEFAULT_RUNTIME, FLOWPLUS_WORKFLOW_DEFAULT_STATE, FLOWPLUS_WORKFLOW_NAVIGATION, FLOWPLUS_WORKFLOW_ROUTES, FLOW_NODE_BASE_HEIGHT, FLOW_NODE_CARD_WIDTH, FLOW_NODE_MULTI_OUTPUT_GAP, FLOW_NODE_OUTPUT_SIZE, FlowCanvasComponent, FlowNodeComponent, FlowplusWorkflowActionKey, FlowplusWorkflowFacade, FlowplusWorkflowNavigationService, FlowplusWorkflowOverlayService, FlowplusWorkflowState, InspectorShellComponent, KeyboardShortcutsService, LOCAL_FALLBACK_CATALOG, LoadAutomationExecution, LoadContextCatalog, LoadContextCatalogForStep, LoadLatestAutomationExecution, LoadLayout, LoadTriggers, LoadWorkflowBuilder, LoadWorkflowCatalog, LoadWorkflowList, MarkLayoutDirty, MoveStep, NODE_COLOR_TOKEN, NODE_DEFAULT_ICON, NODE_MT_ICON, NoopFlowplusFormDesignerAdapter, PaletteComponent, PaletteDragSourceDirective, PollAutomationExecution, ProblemsPanelComponent, ProcessCreateDialogComponent, PublishWorkflow, RedoBuilderCommand, ReloadWorkflowBuilder, RunAutomationTrigger, RunWorkflowTest, SaveLayout, SelectCanvasNote, SelectConnection, SelectRuntimeNodeRun, SelectStep, SelectTrigger, SetActiveInspectorTab, SetBottomPanelOpen, SetBottomPanelTab, SetCanvasViewport, SetContextCatalog, SetCreateDialogOpen, SetInspectorOpen, SetLayout, SetLayoutAutosavePaused, SetMinimap, SetPaletteOpen, SetPaletteSearch, SetPendingOperation, SetReadonly, SetSelectedRuntimeTrigger, SetSelection, SetSelectionFromCanvas, SetStudioFilter, SetValidation, SetWorkflowCatalog, SetWorkflowDefinition, StepCreated, StepCreationFailed, TRIGGER_MT_ICON, TestRunPanelComponent, UnconfiguredFormDesignerAdapter, UndoBuilderCommand, UnpublishWorkflow, UpdateCanvasNote, UpdateConnection, UpdateStep, UpdateTrigger, UpdateWorkflowMetadata, UpdateWorkflowResources, UpdateWorkflowVariables, ValidateWorkflow, WorkflowBuilderPageComponent, WorkflowCatalogApiService, WorkflowContextApiService, WorkflowDebugApiService, WorkflowDefinitionApiService, WorkflowFormApiService, WorkflowRunDebuggerPageComponent, WorkflowRuntimeApiService, WorkflowStudioPageComponent, WorkflowValidationApiService, applyBuilderSnapshot, avatarSeverityForStep, avatarSeverityForTrigger, canonicalTriggerTypeForStarterKind, coerceTranslatable, colorVarFor, computeBranchLanes, computeSaveStatus, connectionCanvasId, derivePortsForStep, extractMessage, flowNodeCardHeightForOutputs, flowNodeLayoutHeightForOutputs, flowNodeOutputCenterY, flowNodeOutputStackHeight, fromWorkflowConnectionDomain, fromWorkflowStepDomain, hasOtherPending, iconFor, indexValidationByConnectionId, indexValidationByStepId, indexValidationByTriggerId, inputPortId, isEntityDirty, mapHttpError, mtIconForStep, mtIconForTrigger, newClientMutationId, nextOperationId, nodeCanvasId, outputPortId, parseConnectionId, parseNodeId, parsePortId, parseTriggerOutputPortId, parseTriggerStartConnectionId, patchLayoutPosition, patchTriggerLayoutPosition, provideFlowplusFormDesignerAdapter, provideFlowplusWorkflow, provideFlowplusWorkflowNavigation, provideFlowplusWorkflowNavigationDefaults, readRecord, removeStepWithEdges, resolveTranslatable, resolveTriggerStartNodeKey, resolveTriggerStartStep, resolveTriggerStartStepId, resolveWorkflowTriggerKey, savingOrSaved, tempNodeCanvasId, toWorkflowConnectionDomain, toWorkflowDefinitionDomain, toWorkflowStepDomain, toWorkflowTriggerDomain, triggerCanvasId, triggerOutputPortId, triggerStartConnectionCanvasId, triggerStarterKindFor, unwrap, unwrapApiData, upsertConnection, upsertStep };
23170
23917
  //# sourceMappingURL=masterteam-flowplus-workflow.mjs.map