@aglyn/aglyn 1.0.0-beta.172 → 1.0.0-beta.174

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.
Files changed (39) hide show
  1. package/package.json +10 -10
  2. package/src/lib/app-utils/campaign-membership.d.ts +10 -8
  3. package/src/lib/app-utils/campaign-membership.js +11 -9
  4. package/src/lib/app-utils/campaign-membership.js.map +1 -1
  5. package/src/lib/app-utils/compose-layout-props.d.ts +6 -2
  6. package/src/lib/app-utils/compose-layout-props.js +8 -3
  7. package/src/lib/app-utils/compose-layout-props.js.map +1 -1
  8. package/src/lib/app-utils/compose-reusable-components.d.ts +85 -1
  9. package/src/lib/app-utils/compose-reusable-components.js +137 -11
  10. package/src/lib/app-utils/compose-reusable-components.js.map +1 -1
  11. package/src/lib/app-utils/crm.d.ts +3 -3
  12. package/src/lib/app-utils/crm.js.map +1 -1
  13. package/src/lib/app-utils/docs-help.generated.d.ts +1 -1
  14. package/src/lib/app-utils/docs-help.generated.js +1 -0
  15. package/src/lib/app-utils/docs-help.generated.js.map +1 -1
  16. package/src/lib/app-utils/docs-index.generated.js +11 -4
  17. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  18. package/src/lib/app-utils/element-color-scheme.d.ts +43 -0
  19. package/src/lib/app-utils/element-color-scheme.js +41 -0
  20. package/src/lib/app-utils/element-color-scheme.js.map +1 -0
  21. package/src/lib/app-utils/layout-style-overrides.d.ts +106 -0
  22. package/src/lib/app-utils/layout-style-overrides.js +158 -0
  23. package/src/lib/app-utils/layout-style-overrides.js.map +1 -0
  24. package/src/lib/app-utils/server.d.ts +2 -0
  25. package/src/lib/app-utils/server.js +4 -0
  26. package/src/lib/app-utils/server.js.map +1 -1
  27. package/src/lib/canvas-manager/canvas-manager.d.ts +5 -0
  28. package/src/lib/canvas-manager/canvas-manager.js +7 -0
  29. package/src/lib/canvas-manager/canvas-manager.js.map +1 -1
  30. package/src/lib/foundation/definitions/platform.types.d.ts +9 -0
  31. package/src/lib/foundation/definitions/platform.types.js.map +1 -1
  32. package/src/lib/plugin-manager/first-party-plugins.generated.d.ts +6 -1
  33. package/src/lib/plugin-manager/first-party-plugins.generated.js +17 -11
  34. package/src/lib/plugin-manager/first-party-plugins.generated.js.map +1 -1
  35. package/src/lib/plugin-manager/plugin-host-collections.d.ts +52 -1
  36. package/src/lib/plugin-manager/plugin-host-collections.js +23 -6
  37. package/src/lib/plugin-manager/plugin-host-collections.js.map +1 -1
  38. package/src/lib/types/nodes.d.ts +12 -0
  39. package/src/lib/types/nodes.js.map +1 -1
@@ -16,7 +16,7 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
16
16
  * limitations under the License.
17
17
  */ import { getRegisteringPluginId } from "../app-utils/registering-plugin.js";
18
18
  import { hostContentCollectionLabel } from "../foundation/definitions/host-content-collections.js";
19
- import { PLUGIN_HOST_COLLECTIONS_DECLARED } from "./first-party-plugins.generated.js";
19
+ import { PLUGIN_HOST_COLLECTIONS_DECLARED, PLUGIN_ORG_COLLECTIONS_DECLARED } from "./first-party-plugins.generated.js";
20
20
  import { definePluginServiceContract, registerPluginService, resolvePluginServices } from "./plugin-services.js";
21
21
  export const PLUGIN_HOST_COLLECTIONS = definePluginServiceContract('core.host-collections', {
22
22
  multiple: true
@@ -80,10 +80,12 @@ export const PLUGIN_HOST_COLLECTIONS = definePluginServiceContract('core.host-co
80
80
  }
81
81
  /**
82
82
  * The console hub slug a reference row for this collection links to, or
83
- * `undefined` when the owner routes none.
83
+ * `undefined` when the owner routes none. A row found by the org pass names an
84
+ * org collection, so an org declaration answers when no host one does.
84
85
  */ export function pluginHostCollectionRouteSlug(name) {
85
- var _pluginHostCollection;
86
- return (_pluginHostCollection = pluginHostCollection(name)) == null ? void 0 : _pluginHostCollection.routeSlug;
86
+ var _ref;
87
+ var _pluginHostCollection, _pluginOrgCollection;
88
+ return (_ref = (_pluginHostCollection = pluginHostCollection(name)) == null ? void 0 : _pluginHostCollection.routeSlug) != null ? _ref : (_pluginOrgCollection = pluginOrgCollection(name)) == null ? void 0 : _pluginOrgCollection.routeSlug;
87
89
  }
88
90
  /**
89
91
  * The declared collections the media scan reads generically — the list the
@@ -117,9 +119,24 @@ export const PLUGIN_HOST_COLLECTIONS = definePluginServiceContract('core.host-co
117
119
  * by two paths is the kind of difference nobody notices until a screenshot
118
120
  * disagrees with a test.
119
121
  */ export function pluginHostCollectionLabel(name) {
120
- var _pluginHostCollection_label, _pluginHostCollection;
121
- const declared = (_pluginHostCollection = pluginHostCollection(name)) == null ? void 0 : (_pluginHostCollection_label = _pluginHostCollection.label) == null ? void 0 : _pluginHostCollection_label.trim();
122
+ var _ref;
123
+ var _this, _pluginHostCollection, _pluginOrgCollection;
124
+ const declared = (_this = (_ref = (_pluginHostCollection = pluginHostCollection(name)) == null ? void 0 : _pluginHostCollection.label) != null ? _ref : (_pluginOrgCollection = pluginOrgCollection(name)) == null ? void 0 : _pluginOrgCollection.label) == null ? void 0 : _this.trim();
122
125
  return declared || hostContentCollectionLabel(name);
123
126
  }
127
+ /** Every declared org collection, with its owner. */ export function listPluginOrgCollections() {
128
+ return PLUGIN_ORG_COLLECTIONS_DECLARED.map((row)=>_extends({}, row));
129
+ }
130
+ /** One declared org collection by name, with its owner, or `null`. */ export function pluginOrgCollection(name) {
131
+ var _listPluginOrgCollections_find;
132
+ const key = name.trim();
133
+ return (_listPluginOrgCollections_find = listPluginOrgCollections().find((one)=>one.name === key)) != null ? _listPluginOrgCollections_find : null;
134
+ }
135
+ /** The declared org collections the media scan reads generically. */ export function pluginOrgCollectionsScannedGenerically() {
136
+ return listPluginOrgCollections().filter((one)=>{
137
+ var _one_mediaScan;
138
+ return ((_one_mediaScan = one.mediaScan) != null ? _one_mediaScan : 'generic') === 'generic';
139
+ });
140
+ }
124
141
 
125
142
  //# sourceMappingURL=plugin-host-collections.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-host-collections.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getRegisteringPluginId } from '../app-utils/registering-plugin'\nimport { hostContentCollectionLabel } from '../foundation/definitions/host-content-collections'\nimport { PLUGIN_HOST_COLLECTIONS_DECLARED } from './first-party-plugins.generated'\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * The host subcollections a plugin owns, declared by that plugin (AGL-3124).\n *\n * A first-party plugin writes ordinary documents under `hosts/{hostId}` with\n * the ordinary SDKs, and several core surfaces have to know which plugin owns\n * which of them: the media-usage scan decides what to read before an author\n * deletes an asset, a reference row deep-links to the console page that shows\n * one, and the artifact counters total what a site holds.\n *\n * Core answered all three by listing the collections and switching over plugin\n * ids — `services` and `resources` are bookings', `campaigns` and `experiments`\n * are marketing's, `functions`, `variables` and `actions` are logic's — which\n * is the platform holding a map of its plugins' storage. Every entry is also a\n * thing that can silently fall out of step: a plugin that ships a new\n * collection is invisible to all three until somebody remembers the list.\n *\n * So the plugin declares what it owns, once, and the three readers ask.\n *\n * ## What a declaration says, and what it deliberately does not\n *\n * A declaration names the collection, how the media scan should treat it, the\n * console slug a reference row links to, and whether a document in it is an\n * ARTIFACT the site's counters total beside screens and layouts. It does NOT\n * name fields: the scan flattens a document generically on purpose, and a\n * field list here would be the same staleness trap one level down.\n *\n * The media default is inherited from the scan's own rule and is the whole\n * reason that rule survives this move: **a collection is scanned unless its\n * declaration says why it is not.** A plugin that ships a media-bearing\n * collection is covered the day it lands rather than the day somebody\n * remembers it, and `mediaScan: 'none'` without a reason is refused — \"it\n * probably has no images in it\" is the guess the scan's design exists to\n * avoid making.\n *\n * ## One collection, one owner\n *\n * Two plugins writing one collection under one site would be two schemas in\n * one place, and the scan, the deep link and the counters would each pick a\n * winner by registration order. So a collection another plugin declared is\n * refused naming both, and the incumbent keeps it; the same plugin declaring\n * again replaces its own.\n *\n * ## Declared in the config, not registered at boot (AGL-3080)\n *\n * A first-party plugin declares its collections in `plugins.config.json` and\n * the generator compiles them, so every reader has the whole list the moment\n * it imports this module. That is not a preference: the media scan answers\n * \"what uses this asset\" in the moment before an author deletes it, every way\n * it can be wrong points at \"unused\", and it runs inside a console request\n * that loads no plugin code. A registry that request had not filled would\n * report every plugin-owned document as holding nothing, with nothing red —\n * the shape of AGL-3025, on the one answer here that somebody acts on.\n *\n * {@link registerPluginHostCollections} is still the door for a plugin the\n * compiler never sees, and the two compose: the compiled rows are a floor a\n * registration cannot lower, and a registration naming a compiled collection\n * is refused the same way a second plugin's would be.\n *\n * Reached by its own subpath rather than through `plugin-manager/index.ts`:\n * the readers are the media scan, the console counters and the reference\n * rows, none of which a published page renders.\n */\n\n/** How the media-usage scan reads a collection. */\nexport type PluginHostCollectionMediaScan =\n /** Flattened and searched generically, with no per-collection decoder. */\n | 'generic'\n /** Read by a pass that knows its shape, the way screens and layouts are. */\n | 'own'\n /** Not read; {@link PluginHostCollectionDeclaration.mediaScanReason} says why. */\n | 'none'\n\nexport interface PluginHostCollectionDeclaration {\n /** The subcollection under `hosts/{hostId}`, spelled as Firestore holds it. */\n name: string\n /**\n * What one of its documents is called in a reference row. Derived from the\n * collection name when absent, so a new collection reads correctly without\n * a second list to keep in step.\n */\n label?: string\n /** Default `generic` — see this module's note on why the default is to scan. */\n mediaScan?: PluginHostCollectionMediaScan\n /**\n * Why the scan does not read it. Required with `mediaScan: 'none'`, and it\n * has to answer what scanning would COST or what it would get WRONG.\n */\n mediaScanReason?: string\n /**\n * The `[pluginSlug]` segment of the plugin's console hub, for a reference\n * row's deep link. Optional on purpose: a row with no destination still\n * renders as text, which is better than the row not existing.\n */\n routeSlug?: string\n /**\n * Whether a document here is a live ARTIFACT — something the site's own\n * counters total beside its screens, layouts, components and templates.\n */\n artifact?: boolean\n}\n\n/** A declaration with the plugin that made it. */\nexport type ResolvedPluginHostCollection = PluginHostCollectionDeclaration & {\n pluginId: string\n}\n\nexport const PLUGIN_HOST_COLLECTIONS =\n definePluginServiceContract<PluginHostCollectionDeclaration>(\n 'core.host-collections',\n { multiple: true },\n )\n\n/**\n * Declares the host subcollections the registering plugin owns. The owner is\n * the loader's marker when a register fn is running, else `options.pluginId`;\n * with neither the registration throws. A collection another plugin declared\n * throws naming both, and nothing in the same call is registered — a\n * declaration is one plugin's statement about its own storage, so half of it\n * landing would be worse than none.\n */\nexport function registerPluginHostCollections(\n collections: readonly PluginHostCollectionDeclaration[],\n options?: { pluginId?: string },\n): void {\n const pluginId = (getRegisteringPluginId() ?? options?.pluginId ?? '').trim()\n const prepared = collections.map((declaration) => {\n const name = declaration.name?.trim() ?? ''\n if (!name) throw new Error('a host collection needs a name')\n if (declaration.mediaScan === 'none' && !declaration.mediaScanReason?.trim()) {\n throw new Error(\n `host collection \"${name}\" is not scanned for media and says no ` +\n 'reason: mediaScanReason has to name what scanning would cost, or ' +\n 'what it would get wrong',\n )\n }\n const incumbent = pluginHostCollection(name)\n if (incumbent && pluginId && incumbent.pluginId !== pluginId) {\n throw new Error(\n `host collection \"${name}\" is already declared by ` +\n `\"${incumbent.pluginId}\"; refused \"${pluginId}\"`,\n )\n }\n return { ...declaration, name }\n })\n for (const declaration of prepared) {\n registerPluginService(PLUGIN_HOST_COLLECTIONS, declaration, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n key: declaration.name,\n })\n }\n}\n\n/**\n * Every declared collection, with its owner: the compiled rows first, then\n * anything registered at runtime that they do not already name.\n */\nexport function listPluginHostCollections(): ResolvedPluginHostCollection[] {\n const compiled = PLUGIN_HOST_COLLECTIONS_DECLARED.map((row) => ({ ...row }))\n const named = new Set(compiled.map((row) => row.name))\n const registered = resolvePluginServices(PLUGIN_HOST_COLLECTIONS)\n .map((entry) => ({ ...entry.impl, pluginId: entry.pluginId }))\n .filter((row) => !named.has(row.name))\n return [...compiled, ...registered]\n}\n\n/** One declared collection by name, with its owner, or `null`. */\nexport function pluginHostCollection(\n name: string,\n): ResolvedPluginHostCollection | null {\n const key = name.trim()\n return listPluginHostCollections().find((one) => one.name === key) ?? null\n}\n\n/** The plugin that owns a collection, or `undefined` when none declares it. */\nexport function pluginIdForHostCollection(name: string): string | undefined {\n return pluginHostCollection(name)?.pluginId\n}\n\n/**\n * The console hub slug a reference row for this collection links to, or\n * `undefined` when the owner routes none.\n */\nexport function pluginHostCollectionRouteSlug(\n name: string,\n): string | undefined {\n return pluginHostCollection(name)?.routeSlug\n}\n\n/**\n * The declared collections the media scan reads generically — the list the\n * scan walks, in place of one core keeps by hand.\n */\nexport function pluginHostCollectionsScannedGenerically(): string[] {\n return listPluginHostCollections()\n .filter((one) => (one.mediaScan ?? 'generic') === 'generic')\n .map((one) => one.name)\n}\n\n/** The declared collections the scan does not read, with the reason given. */\nexport function pluginHostCollectionsExcludedFromMediaScan(): Array<{\n name: string\n reason: string\n pluginId: string\n}> {\n return listPluginHostCollections()\n .filter((one) => one.mediaScan === 'none')\n .map((one) => ({\n name: one.name,\n reason: one.mediaScanReason ?? '',\n pluginId: one.pluginId,\n }))\n}\n\n/** The declared collections whose documents the site's artifact counters total. */\nexport function pluginHostArtifactCollections(): string[] {\n return listPluginHostCollections()\n .filter((one) => one.artifact === true)\n .map((one) => one.name)\n}\n\n/**\n * What to call one of a collection's documents in a reference row: the\n * declared label, else core's own derivation from the name.\n *\n * The derivation is `hostContentCollectionLabel` rather than a second copy of\n * it, because a reference row does not know or care whether the collection it\n * names belongs to a plugin — two spellings of \"Product category\" reachable\n * by two paths is the kind of difference nobody notices until a screenshot\n * disagrees with a test.\n */\nexport function pluginHostCollectionLabel(name: string): string {\n const declared = pluginHostCollection(name)?.label?.trim()\n return declared || hostContentCollectionLabel(name)\n}\n"],"names":["getRegisteringPluginId","hostContentCollectionLabel","PLUGIN_HOST_COLLECTIONS_DECLARED","definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_HOST_COLLECTIONS","multiple","registerPluginHostCollections","collections","options","pluginId","trim","prepared","map","declaration","name","Error","mediaScan","mediaScanReason","incumbent","pluginHostCollection","key","listPluginHostCollections","compiled","row","named","Set","registered","entry","impl","filter","has","find","one","pluginIdForHostCollection","pluginHostCollectionRouteSlug","routeSlug","pluginHostCollectionsScannedGenerically","pluginHostCollectionsExcludedFromMediaScan","reason","pluginHostArtifactCollections","artifact","pluginHostCollectionLabel","declared","label"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/plugin-manager/plugin-host-collections.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getRegisteringPluginId } from '../app-utils/registering-plugin'\nimport { hostContentCollectionLabel } from '../foundation/definitions/host-content-collections'\nimport {\n PLUGIN_HOST_COLLECTIONS_DECLARED,\n PLUGIN_ORG_COLLECTIONS_DECLARED,\n} from './first-party-plugins.generated'\nimport {\n definePluginServiceContract,\n registerPluginService,\n resolvePluginServices,\n} from './plugin-services'\n\n/**\n * The host subcollections a plugin owns, declared by that plugin (AGL-3124).\n *\n * A first-party plugin writes ordinary documents under `hosts/{hostId}` with\n * the ordinary SDKs, and several core surfaces have to know which plugin owns\n * which of them: the media-usage scan decides what to read before an author\n * deletes an asset, a reference row deep-links to the console page that shows\n * one, and the artifact counters total what a site holds.\n *\n * Core answered all three by listing the collections and switching over plugin\n * ids — `services` and `resources` are bookings', `campaigns` and `experiments`\n * are marketing's, `functions`, `variables` and `actions` are logic's — which\n * is the platform holding a map of its plugins' storage. Every entry is also a\n * thing that can silently fall out of step: a plugin that ships a new\n * collection is invisible to all three until somebody remembers the list.\n *\n * So the plugin declares what it owns, once, and the three readers ask.\n *\n * ## What a declaration says, and what it deliberately does not\n *\n * A declaration names the collection, how the media scan should treat it, the\n * console slug a reference row links to, and whether a document in it is an\n * ARTIFACT the site's counters total beside screens and layouts. It does NOT\n * name fields: the scan flattens a document generically on purpose, and a\n * field list here would be the same staleness trap one level down.\n *\n * The media default is inherited from the scan's own rule and is the whole\n * reason that rule survives this move: **a collection is scanned unless its\n * declaration says why it is not.** A plugin that ships a media-bearing\n * collection is covered the day it lands rather than the day somebody\n * remembers it, and `mediaScan: 'none'` without a reason is refused — \"it\n * probably has no images in it\" is the guess the scan's design exists to\n * avoid making.\n *\n * ## One collection, one owner\n *\n * Two plugins writing one collection under one site would be two schemas in\n * one place, and the scan, the deep link and the counters would each pick a\n * winner by registration order. So a collection another plugin declared is\n * refused naming both, and the incumbent keeps it; the same plugin declaring\n * again replaces its own.\n *\n * ## Declared in the config, not registered at boot (AGL-3080)\n *\n * A first-party plugin declares its collections in `plugins.config.json` and\n * the generator compiles them, so every reader has the whole list the moment\n * it imports this module. That is not a preference: the media scan answers\n * \"what uses this asset\" in the moment before an author deletes it, every way\n * it can be wrong points at \"unused\", and it runs inside a console request\n * that loads no plugin code. A registry that request had not filled would\n * report every plugin-owned document as holding nothing, with nothing red —\n * the shape of AGL-3025, on the one answer here that somebody acts on.\n *\n * {@link registerPluginHostCollections} is still the door for a plugin the\n * compiler never sees, and the two compose: the compiled rows are a floor a\n * registration cannot lower, and a registration naming a compiled collection\n * is refused the same way a second plugin's would be.\n *\n * Reached by its own subpath rather than through `plugin-manager/index.ts`:\n * the readers are the media scan, the console counters and the reference\n * rows, none of which a published page renders.\n */\n\n/** How the media-usage scan reads a collection. */\nexport type PluginHostCollectionMediaScan =\n /** Flattened and searched generically, with no per-collection decoder. */\n | 'generic'\n /** Read by a pass that knows its shape, the way screens and layouts are. */\n | 'own'\n /** Not read; {@link PluginHostCollectionDeclaration.mediaScanReason} says why. */\n | 'none'\n\nexport interface PluginHostCollectionDeclaration {\n /** The subcollection under `hosts/{hostId}`, spelled as Firestore holds it. */\n name: string\n /**\n * What one of its documents is called in a reference row. Derived from the\n * collection name when absent, so a new collection reads correctly without\n * a second list to keep in step.\n */\n label?: string\n /** Default `generic` — see this module's note on why the default is to scan. */\n mediaScan?: PluginHostCollectionMediaScan\n /**\n * Why the scan does not read it. Required with `mediaScan: 'none'`, and it\n * has to answer what scanning would COST or what it would get WRONG.\n */\n mediaScanReason?: string\n /**\n * The `[pluginSlug]` segment of the plugin's console hub, for a reference\n * row's deep link. Optional on purpose: a row with no destination still\n * renders as text, which is better than the row not existing.\n */\n routeSlug?: string\n /**\n * Whether a document here is a live ARTIFACT — something the site's own\n * counters total beside its screens, layouts, components and templates.\n */\n artifact?: boolean\n}\n\n/** A declaration with the plugin that made it. */\nexport type ResolvedPluginHostCollection = PluginHostCollectionDeclaration & {\n pluginId: string\n}\n\nexport const PLUGIN_HOST_COLLECTIONS =\n definePluginServiceContract<PluginHostCollectionDeclaration>(\n 'core.host-collections',\n { multiple: true },\n )\n\n/**\n * Declares the host subcollections the registering plugin owns. The owner is\n * the loader's marker when a register fn is running, else `options.pluginId`;\n * with neither the registration throws. A collection another plugin declared\n * throws naming both, and nothing in the same call is registered — a\n * declaration is one plugin's statement about its own storage, so half of it\n * landing would be worse than none.\n */\nexport function registerPluginHostCollections(\n collections: readonly PluginHostCollectionDeclaration[],\n options?: { pluginId?: string },\n): void {\n const pluginId = (getRegisteringPluginId() ?? options?.pluginId ?? '').trim()\n const prepared = collections.map((declaration) => {\n const name = declaration.name?.trim() ?? ''\n if (!name) throw new Error('a host collection needs a name')\n if (declaration.mediaScan === 'none' && !declaration.mediaScanReason?.trim()) {\n throw new Error(\n `host collection \"${name}\" is not scanned for media and says no ` +\n 'reason: mediaScanReason has to name what scanning would cost, or ' +\n 'what it would get wrong',\n )\n }\n const incumbent = pluginHostCollection(name)\n if (incumbent && pluginId && incumbent.pluginId !== pluginId) {\n throw new Error(\n `host collection \"${name}\" is already declared by ` +\n `\"${incumbent.pluginId}\"; refused \"${pluginId}\"`,\n )\n }\n return { ...declaration, name }\n })\n for (const declaration of prepared) {\n registerPluginService(PLUGIN_HOST_COLLECTIONS, declaration, {\n ...(options?.pluginId ? { pluginId: options.pluginId } : {}),\n key: declaration.name,\n })\n }\n}\n\n/**\n * Every declared collection, with its owner: the compiled rows first, then\n * anything registered at runtime that they do not already name.\n */\nexport function listPluginHostCollections(): ResolvedPluginHostCollection[] {\n const compiled = PLUGIN_HOST_COLLECTIONS_DECLARED.map((row) => ({ ...row }))\n const named = new Set(compiled.map((row) => row.name))\n const registered = resolvePluginServices(PLUGIN_HOST_COLLECTIONS)\n .map((entry) => ({ ...entry.impl, pluginId: entry.pluginId }))\n .filter((row) => !named.has(row.name))\n return [...compiled, ...registered]\n}\n\n/** One declared collection by name, with its owner, or `null`. */\nexport function pluginHostCollection(\n name: string,\n): ResolvedPluginHostCollection | null {\n const key = name.trim()\n return listPluginHostCollections().find((one) => one.name === key) ?? null\n}\n\n/** The plugin that owns a collection, or `undefined` when none declares it. */\nexport function pluginIdForHostCollection(name: string): string | undefined {\n return pluginHostCollection(name)?.pluginId\n}\n\n/**\n * The console hub slug a reference row for this collection links to, or\n * `undefined` when the owner routes none. A row found by the org pass names an\n * org collection, so an org declaration answers when no host one does.\n */\nexport function pluginHostCollectionRouteSlug(\n name: string,\n): string | undefined {\n return pluginHostCollection(name)?.routeSlug ?? pluginOrgCollection(name)?.routeSlug\n}\n\n/**\n * The declared collections the media scan reads generically — the list the\n * scan walks, in place of one core keeps by hand.\n */\nexport function pluginHostCollectionsScannedGenerically(): string[] {\n return listPluginHostCollections()\n .filter((one) => (one.mediaScan ?? 'generic') === 'generic')\n .map((one) => one.name)\n}\n\n/** The declared collections the scan does not read, with the reason given. */\nexport function pluginHostCollectionsExcludedFromMediaScan(): Array<{\n name: string\n reason: string\n pluginId: string\n}> {\n return listPluginHostCollections()\n .filter((one) => one.mediaScan === 'none')\n .map((one) => ({\n name: one.name,\n reason: one.mediaScanReason ?? '',\n pluginId: one.pluginId,\n }))\n}\n\n/** The declared collections whose documents the site's artifact counters total. */\nexport function pluginHostArtifactCollections(): string[] {\n return listPluginHostCollections()\n .filter((one) => one.artifact === true)\n .map((one) => one.name)\n}\n\n/**\n * What to call one of a collection's documents in a reference row: the\n * declared label, else core's own derivation from the name.\n *\n * The derivation is `hostContentCollectionLabel` rather than a second copy of\n * it, because a reference row does not know or care whether the collection it\n * names belongs to a plugin — two spellings of \"Product category\" reachable\n * by two paths is the kind of difference nobody notices until a screenshot\n * disagrees with a test.\n */\nexport function pluginHostCollectionLabel(name: string): string {\n const declared = (\n pluginHostCollection(name)?.label ?? pluginOrgCollection(name)?.label\n )?.trim()\n return declared || hostContentCollectionLabel(name)\n}\n\n/**\n * The ORG collections a plugin owns, for the media-usage scan (AGL-3273).\n *\n * Everything above is about `hosts/{hostId}/…`, and the scan's completeness\n * guard sweeps the source for exactly that shape. Some plugin storage lives on\n * the organization instead — Marketing's email sends are\n * `orgs/{orgId}/campaigns`, each naming the site it was sent as — and a\n * collection the scan does not know about is read as holding nothing, which\n * is the answer \"Find where this is used\" must never give by omission.\n *\n * So a plugin declares those too, in the `orgCollections` block of\n * `plugins.config.json`, compiled like the host rows. The same inverted\n * default holds — scanned unless the declaration says why not — and\n * `siteField` names the field that says which site a document belongs to, so\n * a reference row can name that site and link into its hub.\n *\n * Compiled only. There is no runtime door: nothing outside the first-party\n * config writes org storage the scan must read, and a door nobody uses is a\n * registry the scan's request would have to be trusted to have filled.\n */\nexport interface PluginOrgCollectionDeclaration {\n /** The subcollection under `orgs/{orgId}`, spelled as Firestore holds it. */\n name: string\n /** What one of its documents is called in a reference row. */\n label?: string\n /** Default `generic`. An org collection has no dedicated pass to be `own`. */\n mediaScan?: Exclude<PluginHostCollectionMediaScan, 'own'>\n /** Why the scan does not read it. Required with `mediaScan: 'none'`. */\n mediaScanReason?: string\n /** The `[pluginSlug]` of the site hub a reference row links to. */\n routeSlug?: string\n /**\n * The field on each document naming the site it BELONGS to. Two readers\n * act on it: a reference row names that site and links into its hub, and\n * deleting the site deletes the documents that name it (`eraseHost`). A\n * collection whose documents are placed on sites rather than owned by one\n * declares none.\n */\n siteField?: string\n}\n\n/** An org declaration with the plugin that made it. */\nexport type ResolvedPluginOrgCollection = PluginOrgCollectionDeclaration & {\n pluginId: string\n}\n\n/** Every declared org collection, with its owner. */\nexport function listPluginOrgCollections(): ResolvedPluginOrgCollection[] {\n return PLUGIN_ORG_COLLECTIONS_DECLARED.map((row) => ({ ...row }))\n}\n\n/** One declared org collection by name, with its owner, or `null`. */\nexport function pluginOrgCollection(\n name: string,\n): ResolvedPluginOrgCollection | null {\n const key = name.trim()\n return listPluginOrgCollections().find((one) => one.name === key) ?? null\n}\n\n/** The declared org collections the media scan reads generically. */\nexport function pluginOrgCollectionsScannedGenerically(): ResolvedPluginOrgCollection[] {\n return listPluginOrgCollections().filter(\n (one) => (one.mediaScan ?? 'generic') === 'generic',\n )\n}\n"],"names":["getRegisteringPluginId","hostContentCollectionLabel","PLUGIN_HOST_COLLECTIONS_DECLARED","PLUGIN_ORG_COLLECTIONS_DECLARED","definePluginServiceContract","registerPluginService","resolvePluginServices","PLUGIN_HOST_COLLECTIONS","multiple","registerPluginHostCollections","collections","options","pluginId","trim","prepared","map","declaration","name","Error","mediaScan","mediaScanReason","incumbent","pluginHostCollection","key","listPluginHostCollections","compiled","row","named","Set","registered","entry","impl","filter","has","find","one","pluginIdForHostCollection","pluginHostCollectionRouteSlug","pluginOrgCollection","routeSlug","pluginHostCollectionsScannedGenerically","pluginHostCollectionsExcludedFromMediaScan","reason","pluginHostArtifactCollections","artifact","pluginHostCollectionLabel","declared","label","listPluginOrgCollections","pluginOrgCollectionsScannedGenerically"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,QAAQ,qCAAiC;AACxE,SAASC,0BAA0B,QAAQ,wDAAoD;AAC/F,SACEC,gCAAgC,EAChCC,+BAA+B,QAC1B,qCAAiC;AACxC,SACEC,2BAA2B,EAC3BC,qBAAqB,EACrBC,qBAAqB,QAChB,uBAAmB;AA4G1B,OAAO,MAAMC,0BACXH,4BACE,yBACA;IAAEI,UAAU;AAAK,GAClB;AAEH;;;;;;;CAOC,GACD,OAAO,SAASC,8BACdC,WAAuD,EACvDC,OAA+B;QAEbX,MAAAA;IAAlB,MAAMY,WAAW,EAACZ,QAAAA,0BAAAA,oCAAAA,0BAA4BW,2BAAAA,QAASC,QAAQ,YAA7CZ,OAAiD,IAAIa,IAAI;IAC3E,MAAMC,WAAWJ,YAAYK,GAAG,CAAC,CAACC;;YACnBA,mBAE4BA;QAFzC,MAAMC,gBAAOD,oBAAAA,YAAYC,IAAI,qBAAhBD,kBAAkBH,IAAI,qBAAM;QACzC,IAAI,CAACI,MAAM,MAAM,IAAIC,MAAM;QAC3B,IAAIF,YAAYG,SAAS,KAAK,UAAU,GAACH,+BAAAA,YAAYI,eAAe,qBAA3BJ,6BAA6BH,IAAI,KAAI;YAC5E,MAAM,IAAIK,MACR,CAAC,iBAAiB,EAAED,KAAK,uCAAuC,CAAC,GAC/D,sEACA;QAEN;QACA,MAAMI,YAAYC,qBAAqBL;QACvC,IAAII,aAAaT,YAAYS,UAAUT,QAAQ,KAAKA,UAAU;YAC5D,MAAM,IAAIM,MACR,CAAC,iBAAiB,EAAED,KAAK,yBAAyB,CAAC,GACjD,CAAC,CAAC,EAAEI,UAAUT,QAAQ,CAAC,YAAY,EAAEA,SAAS,CAAC,CAAC;QAEtD;QACA,OAAO,aAAKI;YAAaC;;IAC3B;IACA,KAAK,MAAMD,eAAeF,SAAU;QAClCT,sBAAsBE,yBAAyBS,aAAa,aACtDL,CAAAA,2BAAAA,QAASC,QAAQ,IAAG;YAAEA,UAAUD,QAAQC,QAAQ;QAAC,IAAI,CAAC;YAC1DW,KAAKP,YAAYC,IAAI;;IAEzB;AACF;AAEA;;;CAGC,GACD,OAAO,SAASO;IACd,MAAMC,WAAWvB,iCAAiCa,GAAG,CAAC,CAACW,MAAS,aAAKA;IACrE,MAAMC,QAAQ,IAAIC,IAAIH,SAASV,GAAG,CAAC,CAACW,MAAQA,IAAIT,IAAI;IACpD,MAAMY,aAAavB,sBAAsBC,yBACtCQ,GAAG,CAAC,CAACe,QAAW,aAAKA,MAAMC,IAAI;YAAEnB,UAAUkB,MAAMlB,QAAQ;YACzDoB,MAAM,CAAC,CAACN,MAAQ,CAACC,MAAMM,GAAG,CAACP,IAAIT,IAAI;IACtC,OAAO;WAAIQ;WAAaI;KAAW;AACrC;AAEA,gEAAgE,GAChE,OAAO,SAASP,qBACdL,IAAY;QAGLO;IADP,MAAMD,MAAMN,KAAKJ,IAAI;IACrB,QAAOW,kCAAAA,4BAA4BU,IAAI,CAAC,CAACC,MAAQA,IAAIlB,IAAI,KAAKM,gBAAvDC,kCAA+D;AACxE;AAEA,6EAA6E,GAC7E,OAAO,SAASY,0BAA0BnB,IAAY;QAC7CK;IAAP,QAAOA,wBAAAA,qBAAqBL,0BAArBK,sBAA4BV,QAAQ;AAC7C;AAEA;;;;CAIC,GACD,OAAO,SAASyB,8BACdpB,IAAY;;QAELK,uBAAyCgB;IAAhD,gBAAOhB,wBAAAA,qBAAqBL,0BAArBK,sBAA4BiB,SAAS,oBAAID,uBAAAA,oBAAoBrB,0BAApBqB,qBAA2BC,SAAS;AACtF;AAEA;;;CAGC,GACD,OAAO,SAASC;IACd,OAAOhB,4BACJQ,MAAM,CAAC,CAACG;YAASA;eAAD,EAACA,iBAAAA,IAAIhB,SAAS,YAAbgB,iBAAiB,eAAe;OACjDpB,GAAG,CAAC,CAACoB,MAAQA,IAAIlB,IAAI;AAC1B;AAEA,4EAA4E,GAC5E,OAAO,SAASwB;IAKd,OAAOjB,4BACJQ,MAAM,CAAC,CAACG,MAAQA,IAAIhB,SAAS,KAAK,QAClCJ,GAAG,CAAC,CAACoB;YAEIA;eAFK;YACblB,MAAMkB,IAAIlB,IAAI;YACdyB,MAAM,GAAEP,uBAAAA,IAAIf,eAAe,YAAnBe,uBAAuB;YAC/BvB,UAAUuB,IAAIvB,QAAQ;QACxB;;AACJ;AAEA,iFAAiF,GACjF,OAAO,SAAS+B;IACd,OAAOnB,4BACJQ,MAAM,CAAC,CAACG,MAAQA,IAAIS,QAAQ,KAAK,MACjC7B,GAAG,CAAC,CAACoB,MAAQA,IAAIlB,IAAI;AAC1B;AAEA;;;;;;;;;CASC,GACD,OAAO,SAAS4B,0BAA0B5B,IAAY;;QAElDK,OAAAA,uBAAqCgB;IADvC,MAAMQ,YACJxB,iBAAAA,wBAAAA,qBAAqBL,0BAArBK,sBAA4ByB,KAAK,oBAAIT,uBAAAA,oBAAoBrB,0BAApBqB,qBAA2BS,KAAK,qBADtD,AACfzB,MACCT,IAAI;IACP,OAAOiC,YAAY7C,2BAA2BgB;AAChD;AAgDA,mDAAmD,GACnD,OAAO,SAAS+B;IACd,OAAO7C,gCAAgCY,GAAG,CAAC,CAACW,MAAS,aAAKA;AAC5D;AAEA,oEAAoE,GACpE,OAAO,SAASY,oBACdrB,IAAY;QAGL+B;IADP,MAAMzB,MAAMN,KAAKJ,IAAI;IACrB,QAAOmC,iCAAAA,2BAA2Bd,IAAI,CAAC,CAACC,MAAQA,IAAIlB,IAAI,KAAKM,gBAAtDyB,iCAA8D;AACvE;AAEA,mEAAmE,GACnE,OAAO,SAASC;IACd,OAAOD,2BAA2BhB,MAAM,CACtC,CAACG;YAASA;eAAD,EAACA,iBAAAA,IAAIhB,SAAS,YAAbgB,iBAAiB,eAAe;;AAE9C"}
@@ -324,6 +324,18 @@ export interface NodeSchema<P = JSX.AnyProps> extends NodeI<P> {
324
324
  * beats the component's own default.
325
325
  */
326
326
  attrOverrides?: Record<string, Record<string, unknown>>;
327
+ /**
328
+ * EDITOR-ONLY: a screen's per-page restyling of its layout's elements
329
+ * (AGL-3286), parked on the screen canvas ROOT while the besigner is open.
330
+ *
331
+ * Stored on the screen VERSION as `layoutStyleOverrides`, never in `nodes`:
332
+ * the editor injects it here on load and lifts it back out on save
333
+ * (`injectLayoutStyleOverrides` / `extractLayoutStyleOverrides`). Carried on
334
+ * the root so an edit is an undo step, marks the screen dirty and rides the
335
+ * working draft like any other change. Composition ignores it — the screen
336
+ * root is dropped when it is grafted into the layout's slot.
337
+ */
338
+ layoutStyleOverrides?: Record<string, Record<string, Record<string, unknown>>>;
327
339
  /**
328
340
  * Interactions this element carries — the click, hover and
329
341
  * scroll-into-view choreography authored on it in the besigner.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/types/nodes.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n ConditionDefinition,\n DataType,\n Validator,\n} from '@data-driven-forms/react-form-renderer'\nimport type {\n FieldActions,\n ResolvePropsFunction,\n} from '@data-driven-forms/react-form-renderer/common-types'\nimport type { MuiStyledOptions } from '@mui/system/createStyled'\nimport type { SvgIconProps } from '@mui/material/SvgIcon'\n/** Minimal icon-props type used in component/preset schemas. Structurally compatible with @aglyn/shared-ui-jsx MdiIconProps. */\nexport type MdiIconProps<D extends React.ElementType = 'svg', P = object> = SvgIconProps<D, P> & {\n path?: string\n}\nimport type {\n ComponentId,\n ComponentsLinealOrder,\n NodeId,\n PresetId,\n} from '../foundation'\nimport { ComponentCategory } from '../foundation/constants/components'\nimport type { ITimestamp } from '@aglyn/shared-util-timestamp'\nimport type React from 'react'\nimport type { ComponentClass, ComponentProps } from 'react'\nimport type { NODE_ROOT_ID } from '../canvas-manager'\nimport type { NodeInteraction } from '../app-utils/node-interactions'\n\nimport type {\n FEATURE_FLAG,\n FieldComponentType,\n RICH_TEXT_COMMANDS,\n} from '../foundation'\nimport type { PluginId } from '../plugin-manager'\nimport type { ElementContentMap, Node, Props, Taxonomic } from './ast'\nimport type { AglynDocument } from '../foundation'\nimport type { HostUid } from '../foundation'\n\ndeclare module './ast' {\n /**\n * This map registers all node types that may be used as top-level content in\n * the document.\n *\n * These types are accepted inside `root` nodes.\n *\n * This interface can be augmented to register custom node types.\n *\n * @example\n * declare module 'hast' {\n * interface RootContentMap {\n * // Allow using raw nodes defined by `rehype-raw`.\n * raw: Raw;\n * }\n * }\n */\n export interface RootContentMap {\n canvasNode: CanvasNode\n canvasElement: CanvasElement\n }\n\n /**\n * This map registers all node types that may be used as content in an\n * element.\n *\n * These types are accepted inside `element` nodes.\n *\n * This interface can be augmented to register custom node types.\n *\n * @example\n * declare module 'hast' {\n * interface RootContentMap {\n * custom: Custom;\n * }\n * }\n */\n export interface ElementContentMap {\n canvasNode: CanvasNode\n canvasElement: CanvasElement\n }\n}\n\n/**\n * This map registers all node types that may be used as content in an canvas\n * node.\n *\n * These types are accepted inside `element` nodes.\n *\n * This interface can be augmented to register custom node types.\n *\n * @example\n * declare module 'hast' {\n * interface RootContentMap {\n * custom: Custom;\n * }\n * }\n */\nexport interface CanvasNodeContentMap extends ElementContentMap {\n canvasNode: CanvasNode\n canvasElement: CanvasElement\n}\n\nexport type CanvasNodeContent = CanvasNodeContentMap[keyof CanvasNodeContentMap]\n\n/**\n * CanvasNode represents a the schema for building a JSX Element.\n */\nexport interface CanvasNode extends Node {\n /**\n * Represents this variant of a Node.\n */\n type: 'node'\n\n /**\n * List representing the children of a node.\n */\n children?: CanvasNodeContent[] | undefined\n\n /**\n * Represents the element's internal name defined by the user\n */\n name?: string | undefined\n\n /**\n * Represents the element's parent unique identifier\n */\n parentId?: string | undefined\n\n /**\n * Represents the elements' component unique identifier\n */\n componentId?: string | undefined\n\n /**\n * Represents the elements' components' plugin unique identifier\n */\n pluginId?: string | undefined\n\n /**\n * Represents information associated with the element.\n */\n props?: Props | undefined\n}\n\n/**\n * CanvasElement represents a living JSX Element\n */\nexport interface CanvasElement extends CanvasNode {\n /**\n * Represents the element's unique identifier\n */\n $id: string\n}\n\nexport type AbstractId = string\n\n// @TODO ⚠️ Refactor for better adoption of hast\nexport interface AbstractNodeSchema<P = JSX.AnyProps> extends CanvasNode {\n /**\n * The unique identifier for a node\n */\n $id: AbstractId\n title?: string\n displayName?: string\n description?: string\n\n children?: any\n nodes?: any\n\n /**\n * The node style properties for emotion\n */\n sx?: JSX.SxProps\n /**\n * The node props/attributes passed to the component\n */\n props?: P\n\n category?: any\n icon?: any\n\n createdAt?: ITimestamp\n updatedAt?: ITimestamp\n deletedAt?: ITimestamp\n}\n\n/**\n * CanvasPreset represents an encapsulated 'node' type as the root node for\n * crafting a living JSX Element\n */\nexport interface CanvasPreset extends Node, Taxonomic {\n /**\n * Represents this variant of a Node.\n */\n type: 'preset'\n\n /**\n * Represents the element's unique identifier\n */\n $id: string\n\n /**\n * Represents the elements' component unique identifier\n */\n componentId?: string | undefined\n\n /**\n * Represents the elements' components' plugin unique identifier\n */\n pluginId?: string | undefined\n\n /**\n * Represents information associated with the element.\n */\n props?: Props | undefined\n\n /**\n * Represents the element's internal name defined by the user\n */\n name?: string | undefined\n}\n\nexport type { NodeId }\n\n// @TODO ⚠️ Refactor for better adoption of hast\ntype PresetI = AglynDocument &\n Omit<AbstractNodeSchema<any>, 'type' | 'data'> &\n Omit<CanvasPreset, 'type' | 'data'>\n\ninterface PresetII extends PresetI {\n type: NodeType.PRESET | string\n}\n\nexport interface PresetSchema<P = JSX.AnyProps> extends PresetII {\n $id: PresetId\n pluginId?: PluginId\n displayName?: string\n description?: string\n category?: string | ComponentCategory\n /**\n * Extra words this element should be findable by in the element picker —\n * synonyms and the name people reach for when it is not the name we chose\n * (\"picture\" for Image, \"dropdown\" for Select).\n *\n * Search terms only; never rendered. A hit here ranks BELOW every hit on a\n * name, so a tag widens what the picker finds without letting a synonym\n * outrank the element actually called that (AGL-2486).\n */\n tags?: string[]\n /** Alias of {@link tags}; both are searched. */\n keywords?: string[]\n icon?: MdiIconProps\n data: NodeSchemaNested<P>\n /**\n * Interaction templates wired when the preset is inserted (AGL-589):\n * the host app resolves each `presetRef` marker to a freshly minted node\n * and persists the result. Presets that need hover choreography (a\n * primitive dropdown panel) ship working out of the box instead of asking\n * the user to author the interactions by hand.\n *\n * TEMPLATES, and named so. These are not interactions: they are\n * unresolved, they reference nodes that do not exist yet, and they are\n * consumed at insert time and never stored. What they resolve INTO lives\n * on the node as {@link NodeSchema.interactions}, and a `PresetSchema` is\n * assignable to a `NodeSchema` — so while the two shared a name, one of\n * them had to be wrong wherever a preset was passed as a node.\n */\n interactionTemplates?: PresetInteractionSchema[]\n}\n\n/**\n * A visibility step inside a {@link PresetSchema} interaction template.\n * Only the basic client visibility steps are expressible — presets\n * scaffold in-page choreography, never server automations.\n */\nexport interface PresetInteractionStepSchema {\n type: 'showElement' | 'hideElement' | 'toggleElement'\n /** `presetRef` of the preset node this step targets. */\n targetRef: string\n /** Optional grace delay, validated to 0–5000ms like the builder. */\n delayMs?: number\n /** Self-dismissal for steps that can reveal the target. */\n dismissOn?: Array<'escape' | 'outsideClick'>\n}\n\n/** An interaction template a preset wires on insert (AGL-589). */\nexport interface PresetInteractionSchema {\n name: string\n event: 'elementClick' | 'elementHoverEnter' | 'elementHoverLeave'\n /** `presetRef` of the preset node whose selector fires the trigger. */\n triggerRef: string\n steps: PresetInteractionStepSchema[]\n}\n\n// @TODO ⚠️ Refactor for better adoption of hast\ntype NodeI<P> = AglynDocument & Omit<AbstractNodeSchema<P>, 'type'>\n\nexport interface NodeSchema<P = JSX.AnyProps> extends NodeI<P> {\n type: NodeType | string\n /**\n * List of the children unique identifiers for the node\n */\n nodes?: NodeId[]\n /**\n * Class name to pass the DOM node, can also be defined in props\n */\n className?: string\n /**\n * Preset-authoring marker (AGL-589): names a node inside a preset's\n * `data` tree so the preset's interaction templates can reference it\n * (`triggerRef` / `targetRef`). Cloned onto the inserted node; inert\n * everywhere else.\n */\n presetRef?: string\n /**\n * Per-instance style overrides on a reusable-component instance node\n * (AGL-1306), keyed by target: `root` (see `STYLE_OVERRIDES_ROOT_KEY`)\n * addresses the component ROOT, and any other key is a DEFINITION-internal\n * node id addressing one leaf inside the component (AGL-1332). Each slice\n * is an sx-shaped record merged over that node's own sx at graft time\n * (`composeReusableComponentNodes` → `mergeNodeSx`), so one page's CTA can\n * switch to a white background AND repaint its headline without widening\n * the component's declared props or detaching.\n *\n * Keyed on the definition's id, never the grafted `cmp__{instance}__{def}`\n * id: the graft id is derived per placement, so it would not survive a\n * duplicate or a re-key, and the same component could not offer two\n * instances the same authoring vocabulary. A key naming a node the\n * definition no longer has is ignored — a component edited later degrades\n * to its own styling instead of taking a page down.\n *\n * A first-class node field, NOT a prop: `AglynNode`'s constructor drops\n * unknown top-level keys, and anything in `props` is spread onto the\n * instance's wrapper DOM element. Lives on the instance node in the\n * document that places it — it survives component republish and never\n * pins the instance to an old component version, because the graft\n * always merges it over the definition's CURRENT nodes.\n */\n styleOverrides?: Record<string, JSX.SxProps>\n /**\n * Per-instance ATTRIBUTE overrides on a reusable-component instance node\n * (AGL-1899) — the same addressing as {@link styleOverrides}, applied to\n * a node's props instead of its sx, so one placement can carry a\n * different button `variant`, `size` or `href` without the component\n * declaring a prop for it and without detaching.\n *\n * Merged **per named prop**: a slice replaces only the props it names and\n * leaves the definition's others alone, so a component that later adds a\n * prop still gives every existing instance the new default. That is\n * deliberately shallower than the sx merge next door — an sx slice\n * cascades because `@scheme dark` and breakpoint objects are themselves\n * selectors, whereas a prop value is one value, and half-merging two\n * objects that merely look alike (say a `slotProps`) would produce a\n * third thing neither side asked for.\n *\n * `sx` is refused here. Styling an instance already has a mechanism one\n * field up, and honouring both would leave two writers racing for one\n * rendered value with no rule to say which wins.\n *\n * Applied BEFORE `{{prop.*}}` substitution, so an override may itself be\n * written in tokens and still resolves, and an override on a prop the\n * definition binds to a declared prop wins — the more specific placement\n * beats the component's own default.\n */\n attrOverrides?: Record<string, Record<string, unknown>>\n /**\n * Interactions this element carries — the click, hover and\n * scroll-into-view choreography authored on it in the besigner.\n *\n * ## Why they live on the NODE\n *\n * They used to be rows in `hosts/{hostId}/actions`, bound to an element by\n * a `[data-aglyn=\"leaf:<id>\"]` selector — the same collection the Workflows\n * page lists. An interaction is not a site-wide automation, and storing it\n * as one had four consequences, every one of them a thing an author would\n * expect to work:\n *\n * - it was not versioned with the document, so it neither published nor\n * rolled back with the screen it belongs to;\n * - it did not travel with a component copied to another site, or packaged\n * into a template or a plugin — the element arrived inert;\n * - deleting the element left the action behind, pointing at a selector\n * nothing resolves;\n * - and a nav's hover choreography sat in the site's automation list\n * beside \"when an order is placed\", which is a different kind of thing.\n *\n * Host actions remain, and remain the right home for a genuine site event.\n * The split is the concept: an ACTION is something the site does, an\n * INTERACTION is something an element does.\n *\n * ## Shape\n *\n * Deliberately the same `HostAction` the automations engine already takes,\n * so nothing about the runtime changes: the selector is stamped from this\n * node's own id when the page is composed, and the compiled result is the\n * shape the client engine has always consumed. `trigger.selector` is\n * therefore ignored here — the node IS the selector.\n */\n interactions?: NodeInteraction[]\n /**\n * Hidden by the author — the eye on the element's hierarchy row.\n *\n * Renders `display: none` on the canvas AND on the published site. It is\n * the plain \"I do not want this on the page\" switch: no runtime, no\n * interaction, nothing reveals it. Showing it again is the same eye.\n *\n * ## Why a FIELD and not a style\n *\n * Because it has to override the element's own display rather than replace\n * it. An author who toggles the eye on a Stack laid out with\n * `display: flex` must get that flex back when they toggle it off, and\n * writing `display: none` into `node.sx` destroys the value it overwrote —\n * there is nothing left to restore. Composed LAST in `Leaf` instead, so it\n * wins on the cascade and the stored style is never touched.\n *\n * ## Not {@link ELEMENT_HIDDEN_CLASS}\n *\n * `aglyn-hidden` is a different thing wearing a similar name: it means\n * \"starts hidden, and an interaction shows it\" — a mega-menu panel, a\n * drawer. That class is part of a runtime contract (the show/hide steps\n * toggle it, and the canvas reveals it for designing), and it is bespoke to\n * that job. A visibility toggle that wrote it would enrol every hidden\n * element in a choreography it is not part of.\n */\n hidden?: boolean\n /**\n * The computed node parent (only for type completion)\n */\n readonly parent?: NodeSchema<any> | null\n /**\n * The computed node parent (only for type completion)\n */\n readonly children?: NodeSchema<any>[]\n /**\n * The computed index in parent nodes (only for type completion)\n */\n readonly index?: number | null\n /**\n * The computed label (only for type completion)\n */\n readonly labelShort?: string | undefined\n /**\n * The computed breadcrumb path (only for type completion)\n */\n readonly breadcrumbPath?: NodeBreadcrumbPath\n /**\n * The computed component schema (only for type completion)\n */\n readonly componentSchema?: ComponentSchema | undefined\n /**\n * The computed guard for of child nodes (only for type completion)\n */\n readonly hasNodes?: boolean\n /**\n * The computed property for the resolved props from component schema\n */\n readonly resolvedProps?: P\n}\n\nexport enum NodeType {\n NODE = 'node',\n TEXT = 'text',\n NUMBER = 'number',\n SCREEN = 'screen',\n REF = 'ref',\n PRESET = 'preset',\n}\n\nexport type NodeSchemaJSON<P = JSX.AnyProps> = Omit<\n NodeSchema<P>,\n | 'parent'\n | 'index'\n | 'labelShort'\n | 'breadcrumbPath'\n | 'componentSchema'\n | 'hasNodes'\n>\n\nexport type NodeSchemaNested<P = JSX.AnyProps> = Omit<\n NodeSchemaJSON<P>,\n 'nodes'\n> & { nodes?: NodeSchemaNested<any>[] }\n\nexport type NodesMap = Record<NodeId, NodeSchema<any>>\n\nexport type NodeBreadcrumbPath = [\n root: string & typeof NODE_ROOT_ID,\n ...nodes: [...ancestors: NodeId[], node: NodeId],\n]\n\nexport type ProcessableNodes =\n | NodeSchemaNested<any>[]\n | NodeSchemaNested<any>\n | Record<NodeId, NodeSchema>\n\nexport { ComponentCategory }\n\nexport type { ComponentId }\nexport type { PresetId }\n\nexport type ComponentFactory<\n P extends ComponentProps<C> | any = any,\n C extends keyof JSX.IntrinsicElements | JSX.ElementConstructor<any> = any,\n> = ComponentClass<P> | JSX.ElementConstructor<P> | keyof JSX.IntrinsicElements\n// | keyof JSX.IntrinsicElements[keyof JSX.IntrinsicElements]\n\nexport type { ComponentsLinealOrder }\n\n/**\n * What the Attributes panel knows about the element a field edits, handed to\n * an attribute's `resolveProps` as its fourth argument.\n *\n * data-driven-forms gives a field its own value and the form's values, which\n * are the element's props and nothing else. A rule about the element as a\n * whole needs more than that: whether a Link Container holds anything that\n * names its link depends on the elements inside it. So the panel adds the\n * node, and a lookup for the ids listed in its `nodes`.\n *\n * Absent wherever a field is drawn outside that panel, including a unit test\n * calling the function directly, so a `resolveProps` that reads it has to\n * treat a missing context as having nothing to say.\n */\nexport interface AttributeFieldContext {\n /** The element the Attributes panel is editing. */\n node: NodeSchema<any>\n /** An element of the same document by id, such as a child in `nodes`. */\n getNode: (id: NodeId) => NodeSchema<any> | undefined\n}\n\n/**\n * An attribute's `resolveProps`: the three arguments data-driven-forms\n * passes, then the {@link AttributeFieldContext} the Attributes panel adds.\n */\nexport type AttributeResolveProps = (\n ...args: [\n ...Parameters<ResolvePropsFunction>,\n context?: AttributeFieldContext,\n ]\n) => ReturnType<ResolvePropsFunction>\n\n// @TODO ⚠️ Refactor for better adoption of hast\nexport interface AttributeSchema extends Dictionary<any> {\n name: string\n dataType?: DataType\n component: string | FieldComponentType\n validate?: Validator[]\n condition?: ConditionDefinition | ConditionDefinition[]\n initializeOnMount?: boolean\n initialValue?: any\n clearedValue?: any\n clearOnUnmount?: boolean\n actions?: FieldActions\n resolveProps?: AttributeResolveProps\n description?: string\n}\n\n// @TODO ⚠️ Refactor for better adoption of hast\nexport interface ComponentSchema<P = any> {\n $id?: ComponentId\n pluginId?: PluginId\n kind?: 'element' | 'plaintext' | 'markdown'\n\n displayName: string\n title?: string\n subtitle?: string\n description?: string\n category?: string | ComponentCategory\n\n /**\n * Extra words this element should be findable by in the element picker —\n * synonyms and the name people reach for when it is not the name we chose\n * (\"picture\" for Image, \"dropdown\" for Select).\n *\n * Search terms only; never rendered. A hit here ranks BELOW every hit on a\n * name, so a tag widens what the picker finds without letting a synonym\n * outrank the element actually called that (AGL-2486).\n */\n tags?: string[]\n /** Alias of {@link tags}; both are searched. */\n keywords?: string[]\n\n /**\n * Icon props for display around besigner\n */\n icon?: MdiIconProps\n /**\n * Options to be passed to styled(Component, \\{...styledOptions\\})\n */\n styledOptions?: MuiStyledOptions\n\n /**\n * Define a limitation for nodes allowed as direct descendents\n */\n restrictChildren?: ComponentsLinealOrder\n /**\n * Define a limitation for nodes allowed to be direct ancestors\n */\n restrictParent?: ComponentsLinealOrder\n\n /**\n * Filter props\n */\n resolveProps?: JSX.ResolveProps<NodeSchema<P>>\n\n /**\n * Attribute fields to modify the contextual properties\n * New version\n */\n attributes?: AttributeSchema[]\n\n /**\n * Which groups of formatting the inline rich-text toolbar offers for this\n * component (AGL-2557). Read only where `flags.richTextEditable` is on.\n *\n * Omitted means every group, which is what leaves Typography exactly as it\n * was. Naming a subset narrows the toolbar AND the commit: a surface that\n * offers neither lists nor links is phrasing-only, and the sanitizer holds\n * it to that whatever an author pastes in.\n */\n richTextCommands?: RICH_TEXT_COMMANDS[]\n\n /**\n * Feature flags\n */\n flags?: {\n /**\n * Disable the use of emotion styled\n */\n emotion?: FEATURE_FLAG\n /**\n * Can the nodes of this component type be cloned?\n */\n cloning?: FEATURE_FLAG\n /**\n * Allow dragging nodes of this component type\n */\n dragging?: FEATURE_FLAG\n /**\n * Allow dropping nodes inside nodes of this component type\n */\n dropping?: FEATURE_FLAG\n /**\n * Allow editing element attributes of this component type\n */\n editing?: FEATURE_FLAG\n /**\n * Allow removing nodes of this component type\n */\n removing?: FEATURE_FLAG\n /**\n * Describe nodes of this component type to be self-closing\n */\n selfClosing?: FEATURE_FLAG\n /**\n * Component renders its `children` prop as text content the editor may\n * edit directly (Attributes \"Text\" field, inline canvas editing).\n */\n textEditable?: FEATURE_FLAG\n /**\n * Component also accepts basic rich text (AGL-54): the inline editor\n * upgrades to a formatting surface and commits sanitized HTML into the\n * `html` prop (with `children` kept as the plain-text fallback).\n */\n richTextEditable?: FEATURE_FLAG\n /**\n * Component reads its children POSITIONALLY (AGL-1237), so the renderer\n * hands it one React child per node child instead of the single `<Branch>`\n * element it normally passes.\n *\n * MUI's Accordion does `const [summary, ...rest] = Children.toArray(children)`,\n * and `toArray` does not traverse into an element — so with the default\n * wrapping the summary swallowed the whole subtree and the Collapse got\n * nothing. Every accordion on every published site expanded to reveal an\n * empty panel while its content rendered unconditionally inside the\n * summary's heading.\n *\n * Opt in only for components with this contract; the wrapper is what keeps\n * the Branch/Stem/Leaf seam swappable for everything else.\n */\n positionalChildren?: FEATURE_FLAG\n }\n}\n\nexport type SchemasByCategory = Record<\n ComponentCategory | string,\n (ComponentSchema<any> | PresetSchema<any>)[]\n>\n\nexport interface ScreenSchema {\n nodes: NodeSchemaNested\n createdAt?: ITimestamp\n updatedAt?: ITimestamp\n hostId?: HostUid\n}\n"],"names":["ComponentCategory","NodeType"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAuBD,SAASA,iBAAiB,QAAQ,wCAAoC;AAqbtE,OAAO,IAAA,AAAKC,kCAAAA;;;;;;;WAAAA;MAOX;AA6BD,SAASD,iBAAiB,GAAE"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/types/nodes.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n ConditionDefinition,\n DataType,\n Validator,\n} from '@data-driven-forms/react-form-renderer'\nimport type {\n FieldActions,\n ResolvePropsFunction,\n} from '@data-driven-forms/react-form-renderer/common-types'\nimport type { MuiStyledOptions } from '@mui/system/createStyled'\nimport type { SvgIconProps } from '@mui/material/SvgIcon'\n/** Minimal icon-props type used in component/preset schemas. Structurally compatible with @aglyn/shared-ui-jsx MdiIconProps. */\nexport type MdiIconProps<D extends React.ElementType = 'svg', P = object> = SvgIconProps<D, P> & {\n path?: string\n}\nimport type {\n ComponentId,\n ComponentsLinealOrder,\n NodeId,\n PresetId,\n} from '../foundation'\nimport { ComponentCategory } from '../foundation/constants/components'\nimport type { ITimestamp } from '@aglyn/shared-util-timestamp'\nimport type React from 'react'\nimport type { ComponentClass, ComponentProps } from 'react'\nimport type { NODE_ROOT_ID } from '../canvas-manager'\nimport type { NodeInteraction } from '../app-utils/node-interactions'\n\nimport type {\n FEATURE_FLAG,\n FieldComponentType,\n RICH_TEXT_COMMANDS,\n} from '../foundation'\nimport type { PluginId } from '../plugin-manager'\nimport type { ElementContentMap, Node, Props, Taxonomic } from './ast'\nimport type { AglynDocument } from '../foundation'\nimport type { HostUid } from '../foundation'\n\ndeclare module './ast' {\n /**\n * This map registers all node types that may be used as top-level content in\n * the document.\n *\n * These types are accepted inside `root` nodes.\n *\n * This interface can be augmented to register custom node types.\n *\n * @example\n * declare module 'hast' {\n * interface RootContentMap {\n * // Allow using raw nodes defined by `rehype-raw`.\n * raw: Raw;\n * }\n * }\n */\n export interface RootContentMap {\n canvasNode: CanvasNode\n canvasElement: CanvasElement\n }\n\n /**\n * This map registers all node types that may be used as content in an\n * element.\n *\n * These types are accepted inside `element` nodes.\n *\n * This interface can be augmented to register custom node types.\n *\n * @example\n * declare module 'hast' {\n * interface RootContentMap {\n * custom: Custom;\n * }\n * }\n */\n export interface ElementContentMap {\n canvasNode: CanvasNode\n canvasElement: CanvasElement\n }\n}\n\n/**\n * This map registers all node types that may be used as content in an canvas\n * node.\n *\n * These types are accepted inside `element` nodes.\n *\n * This interface can be augmented to register custom node types.\n *\n * @example\n * declare module 'hast' {\n * interface RootContentMap {\n * custom: Custom;\n * }\n * }\n */\nexport interface CanvasNodeContentMap extends ElementContentMap {\n canvasNode: CanvasNode\n canvasElement: CanvasElement\n}\n\nexport type CanvasNodeContent = CanvasNodeContentMap[keyof CanvasNodeContentMap]\n\n/**\n * CanvasNode represents a the schema for building a JSX Element.\n */\nexport interface CanvasNode extends Node {\n /**\n * Represents this variant of a Node.\n */\n type: 'node'\n\n /**\n * List representing the children of a node.\n */\n children?: CanvasNodeContent[] | undefined\n\n /**\n * Represents the element's internal name defined by the user\n */\n name?: string | undefined\n\n /**\n * Represents the element's parent unique identifier\n */\n parentId?: string | undefined\n\n /**\n * Represents the elements' component unique identifier\n */\n componentId?: string | undefined\n\n /**\n * Represents the elements' components' plugin unique identifier\n */\n pluginId?: string | undefined\n\n /**\n * Represents information associated with the element.\n */\n props?: Props | undefined\n}\n\n/**\n * CanvasElement represents a living JSX Element\n */\nexport interface CanvasElement extends CanvasNode {\n /**\n * Represents the element's unique identifier\n */\n $id: string\n}\n\nexport type AbstractId = string\n\n// @TODO ⚠️ Refactor for better adoption of hast\nexport interface AbstractNodeSchema<P = JSX.AnyProps> extends CanvasNode {\n /**\n * The unique identifier for a node\n */\n $id: AbstractId\n title?: string\n displayName?: string\n description?: string\n\n children?: any\n nodes?: any\n\n /**\n * The node style properties for emotion\n */\n sx?: JSX.SxProps\n /**\n * The node props/attributes passed to the component\n */\n props?: P\n\n category?: any\n icon?: any\n\n createdAt?: ITimestamp\n updatedAt?: ITimestamp\n deletedAt?: ITimestamp\n}\n\n/**\n * CanvasPreset represents an encapsulated 'node' type as the root node for\n * crafting a living JSX Element\n */\nexport interface CanvasPreset extends Node, Taxonomic {\n /**\n * Represents this variant of a Node.\n */\n type: 'preset'\n\n /**\n * Represents the element's unique identifier\n */\n $id: string\n\n /**\n * Represents the elements' component unique identifier\n */\n componentId?: string | undefined\n\n /**\n * Represents the elements' components' plugin unique identifier\n */\n pluginId?: string | undefined\n\n /**\n * Represents information associated with the element.\n */\n props?: Props | undefined\n\n /**\n * Represents the element's internal name defined by the user\n */\n name?: string | undefined\n}\n\nexport type { NodeId }\n\n// @TODO ⚠️ Refactor for better adoption of hast\ntype PresetI = AglynDocument &\n Omit<AbstractNodeSchema<any>, 'type' | 'data'> &\n Omit<CanvasPreset, 'type' | 'data'>\n\ninterface PresetII extends PresetI {\n type: NodeType.PRESET | string\n}\n\nexport interface PresetSchema<P = JSX.AnyProps> extends PresetII {\n $id: PresetId\n pluginId?: PluginId\n displayName?: string\n description?: string\n category?: string | ComponentCategory\n /**\n * Extra words this element should be findable by in the element picker —\n * synonyms and the name people reach for when it is not the name we chose\n * (\"picture\" for Image, \"dropdown\" for Select).\n *\n * Search terms only; never rendered. A hit here ranks BELOW every hit on a\n * name, so a tag widens what the picker finds without letting a synonym\n * outrank the element actually called that (AGL-2486).\n */\n tags?: string[]\n /** Alias of {@link tags}; both are searched. */\n keywords?: string[]\n icon?: MdiIconProps\n data: NodeSchemaNested<P>\n /**\n * Interaction templates wired when the preset is inserted (AGL-589):\n * the host app resolves each `presetRef` marker to a freshly minted node\n * and persists the result. Presets that need hover choreography (a\n * primitive dropdown panel) ship working out of the box instead of asking\n * the user to author the interactions by hand.\n *\n * TEMPLATES, and named so. These are not interactions: they are\n * unresolved, they reference nodes that do not exist yet, and they are\n * consumed at insert time and never stored. What they resolve INTO lives\n * on the node as {@link NodeSchema.interactions}, and a `PresetSchema` is\n * assignable to a `NodeSchema` — so while the two shared a name, one of\n * them had to be wrong wherever a preset was passed as a node.\n */\n interactionTemplates?: PresetInteractionSchema[]\n}\n\n/**\n * A visibility step inside a {@link PresetSchema} interaction template.\n * Only the basic client visibility steps are expressible — presets\n * scaffold in-page choreography, never server automations.\n */\nexport interface PresetInteractionStepSchema {\n type: 'showElement' | 'hideElement' | 'toggleElement'\n /** `presetRef` of the preset node this step targets. */\n targetRef: string\n /** Optional grace delay, validated to 0–5000ms like the builder. */\n delayMs?: number\n /** Self-dismissal for steps that can reveal the target. */\n dismissOn?: Array<'escape' | 'outsideClick'>\n}\n\n/** An interaction template a preset wires on insert (AGL-589). */\nexport interface PresetInteractionSchema {\n name: string\n event: 'elementClick' | 'elementHoverEnter' | 'elementHoverLeave'\n /** `presetRef` of the preset node whose selector fires the trigger. */\n triggerRef: string\n steps: PresetInteractionStepSchema[]\n}\n\n// @TODO ⚠️ Refactor for better adoption of hast\ntype NodeI<P> = AglynDocument & Omit<AbstractNodeSchema<P>, 'type'>\n\nexport interface NodeSchema<P = JSX.AnyProps> extends NodeI<P> {\n type: NodeType | string\n /**\n * List of the children unique identifiers for the node\n */\n nodes?: NodeId[]\n /**\n * Class name to pass the DOM node, can also be defined in props\n */\n className?: string\n /**\n * Preset-authoring marker (AGL-589): names a node inside a preset's\n * `data` tree so the preset's interaction templates can reference it\n * (`triggerRef` / `targetRef`). Cloned onto the inserted node; inert\n * everywhere else.\n */\n presetRef?: string\n /**\n * Per-instance style overrides on a reusable-component instance node\n * (AGL-1306), keyed by target: `root` (see `STYLE_OVERRIDES_ROOT_KEY`)\n * addresses the component ROOT, and any other key is a DEFINITION-internal\n * node id addressing one leaf inside the component (AGL-1332). Each slice\n * is an sx-shaped record merged over that node's own sx at graft time\n * (`composeReusableComponentNodes` → `mergeNodeSx`), so one page's CTA can\n * switch to a white background AND repaint its headline without widening\n * the component's declared props or detaching.\n *\n * Keyed on the definition's id, never the grafted `cmp__{instance}__{def}`\n * id: the graft id is derived per placement, so it would not survive a\n * duplicate or a re-key, and the same component could not offer two\n * instances the same authoring vocabulary. A key naming a node the\n * definition no longer has is ignored — a component edited later degrades\n * to its own styling instead of taking a page down.\n *\n * A first-class node field, NOT a prop: `AglynNode`'s constructor drops\n * unknown top-level keys, and anything in `props` is spread onto the\n * instance's wrapper DOM element. Lives on the instance node in the\n * document that places it — it survives component republish and never\n * pins the instance to an old component version, because the graft\n * always merges it over the definition's CURRENT nodes.\n */\n styleOverrides?: Record<string, JSX.SxProps>\n /**\n * Per-instance ATTRIBUTE overrides on a reusable-component instance node\n * (AGL-1899) — the same addressing as {@link styleOverrides}, applied to\n * a node's props instead of its sx, so one placement can carry a\n * different button `variant`, `size` or `href` without the component\n * declaring a prop for it and without detaching.\n *\n * Merged **per named prop**: a slice replaces only the props it names and\n * leaves the definition's others alone, so a component that later adds a\n * prop still gives every existing instance the new default. That is\n * deliberately shallower than the sx merge next door — an sx slice\n * cascades because `@scheme dark` and breakpoint objects are themselves\n * selectors, whereas a prop value is one value, and half-merging two\n * objects that merely look alike (say a `slotProps`) would produce a\n * third thing neither side asked for.\n *\n * `sx` is refused here. Styling an instance already has a mechanism one\n * field up, and honouring both would leave two writers racing for one\n * rendered value with no rule to say which wins.\n *\n * Applied BEFORE `{{prop.*}}` substitution, so an override may itself be\n * written in tokens and still resolves, and an override on a prop the\n * definition binds to a declared prop wins — the more specific placement\n * beats the component's own default.\n */\n attrOverrides?: Record<string, Record<string, unknown>>\n /**\n * EDITOR-ONLY: a screen's per-page restyling of its layout's elements\n * (AGL-3286), parked on the screen canvas ROOT while the besigner is open.\n *\n * Stored on the screen VERSION as `layoutStyleOverrides`, never in `nodes`:\n * the editor injects it here on load and lifts it back out on save\n * (`injectLayoutStyleOverrides` / `extractLayoutStyleOverrides`). Carried on\n * the root so an edit is an undo step, marks the screen dirty and rides the\n * working draft like any other change. Composition ignores it — the screen\n * root is dropped when it is grafted into the layout's slot.\n */\n layoutStyleOverrides?: Record<string, Record<string, Record<string, unknown>>>\n /**\n * Interactions this element carries — the click, hover and\n * scroll-into-view choreography authored on it in the besigner.\n *\n * ## Why they live on the NODE\n *\n * They used to be rows in `hosts/{hostId}/actions`, bound to an element by\n * a `[data-aglyn=\"leaf:<id>\"]` selector — the same collection the Workflows\n * page lists. An interaction is not a site-wide automation, and storing it\n * as one had four consequences, every one of them a thing an author would\n * expect to work:\n *\n * - it was not versioned with the document, so it neither published nor\n * rolled back with the screen it belongs to;\n * - it did not travel with a component copied to another site, or packaged\n * into a template or a plugin — the element arrived inert;\n * - deleting the element left the action behind, pointing at a selector\n * nothing resolves;\n * - and a nav's hover choreography sat in the site's automation list\n * beside \"when an order is placed\", which is a different kind of thing.\n *\n * Host actions remain, and remain the right home for a genuine site event.\n * The split is the concept: an ACTION is something the site does, an\n * INTERACTION is something an element does.\n *\n * ## Shape\n *\n * Deliberately the same `HostAction` the automations engine already takes,\n * so nothing about the runtime changes: the selector is stamped from this\n * node's own id when the page is composed, and the compiled result is the\n * shape the client engine has always consumed. `trigger.selector` is\n * therefore ignored here — the node IS the selector.\n */\n interactions?: NodeInteraction[]\n /**\n * Hidden by the author — the eye on the element's hierarchy row.\n *\n * Renders `display: none` on the canvas AND on the published site. It is\n * the plain \"I do not want this on the page\" switch: no runtime, no\n * interaction, nothing reveals it. Showing it again is the same eye.\n *\n * ## Why a FIELD and not a style\n *\n * Because it has to override the element's own display rather than replace\n * it. An author who toggles the eye on a Stack laid out with\n * `display: flex` must get that flex back when they toggle it off, and\n * writing `display: none` into `node.sx` destroys the value it overwrote —\n * there is nothing left to restore. Composed LAST in `Leaf` instead, so it\n * wins on the cascade and the stored style is never touched.\n *\n * ## Not {@link ELEMENT_HIDDEN_CLASS}\n *\n * `aglyn-hidden` is a different thing wearing a similar name: it means\n * \"starts hidden, and an interaction shows it\" — a mega-menu panel, a\n * drawer. That class is part of a runtime contract (the show/hide steps\n * toggle it, and the canvas reveals it for designing), and it is bespoke to\n * that job. A visibility toggle that wrote it would enrol every hidden\n * element in a choreography it is not part of.\n */\n hidden?: boolean\n /**\n * The computed node parent (only for type completion)\n */\n readonly parent?: NodeSchema<any> | null\n /**\n * The computed node parent (only for type completion)\n */\n readonly children?: NodeSchema<any>[]\n /**\n * The computed index in parent nodes (only for type completion)\n */\n readonly index?: number | null\n /**\n * The computed label (only for type completion)\n */\n readonly labelShort?: string | undefined\n /**\n * The computed breadcrumb path (only for type completion)\n */\n readonly breadcrumbPath?: NodeBreadcrumbPath\n /**\n * The computed component schema (only for type completion)\n */\n readonly componentSchema?: ComponentSchema | undefined\n /**\n * The computed guard for of child nodes (only for type completion)\n */\n readonly hasNodes?: boolean\n /**\n * The computed property for the resolved props from component schema\n */\n readonly resolvedProps?: P\n}\n\nexport enum NodeType {\n NODE = 'node',\n TEXT = 'text',\n NUMBER = 'number',\n SCREEN = 'screen',\n REF = 'ref',\n PRESET = 'preset',\n}\n\nexport type NodeSchemaJSON<P = JSX.AnyProps> = Omit<\n NodeSchema<P>,\n | 'parent'\n | 'index'\n | 'labelShort'\n | 'breadcrumbPath'\n | 'componentSchema'\n | 'hasNodes'\n>\n\nexport type NodeSchemaNested<P = JSX.AnyProps> = Omit<\n NodeSchemaJSON<P>,\n 'nodes'\n> & { nodes?: NodeSchemaNested<any>[] }\n\nexport type NodesMap = Record<NodeId, NodeSchema<any>>\n\nexport type NodeBreadcrumbPath = [\n root: string & typeof NODE_ROOT_ID,\n ...nodes: [...ancestors: NodeId[], node: NodeId],\n]\n\nexport type ProcessableNodes =\n | NodeSchemaNested<any>[]\n | NodeSchemaNested<any>\n | Record<NodeId, NodeSchema>\n\nexport { ComponentCategory }\n\nexport type { ComponentId }\nexport type { PresetId }\n\nexport type ComponentFactory<\n P extends ComponentProps<C> | any = any,\n C extends keyof JSX.IntrinsicElements | JSX.ElementConstructor<any> = any,\n> = ComponentClass<P> | JSX.ElementConstructor<P> | keyof JSX.IntrinsicElements\n// | keyof JSX.IntrinsicElements[keyof JSX.IntrinsicElements]\n\nexport type { ComponentsLinealOrder }\n\n/**\n * What the Attributes panel knows about the element a field edits, handed to\n * an attribute's `resolveProps` as its fourth argument.\n *\n * data-driven-forms gives a field its own value and the form's values, which\n * are the element's props and nothing else. A rule about the element as a\n * whole needs more than that: whether a Link Container holds anything that\n * names its link depends on the elements inside it. So the panel adds the\n * node, and a lookup for the ids listed in its `nodes`.\n *\n * Absent wherever a field is drawn outside that panel, including a unit test\n * calling the function directly, so a `resolveProps` that reads it has to\n * treat a missing context as having nothing to say.\n */\nexport interface AttributeFieldContext {\n /** The element the Attributes panel is editing. */\n node: NodeSchema<any>\n /** An element of the same document by id, such as a child in `nodes`. */\n getNode: (id: NodeId) => NodeSchema<any> | undefined\n}\n\n/**\n * An attribute's `resolveProps`: the three arguments data-driven-forms\n * passes, then the {@link AttributeFieldContext} the Attributes panel adds.\n */\nexport type AttributeResolveProps = (\n ...args: [\n ...Parameters<ResolvePropsFunction>,\n context?: AttributeFieldContext,\n ]\n) => ReturnType<ResolvePropsFunction>\n\n// @TODO ⚠️ Refactor for better adoption of hast\nexport interface AttributeSchema extends Dictionary<any> {\n name: string\n dataType?: DataType\n component: string | FieldComponentType\n validate?: Validator[]\n condition?: ConditionDefinition | ConditionDefinition[]\n initializeOnMount?: boolean\n initialValue?: any\n clearedValue?: any\n clearOnUnmount?: boolean\n actions?: FieldActions\n resolveProps?: AttributeResolveProps\n description?: string\n}\n\n// @TODO ⚠️ Refactor for better adoption of hast\nexport interface ComponentSchema<P = any> {\n $id?: ComponentId\n pluginId?: PluginId\n kind?: 'element' | 'plaintext' | 'markdown'\n\n displayName: string\n title?: string\n subtitle?: string\n description?: string\n category?: string | ComponentCategory\n\n /**\n * Extra words this element should be findable by in the element picker —\n * synonyms and the name people reach for when it is not the name we chose\n * (\"picture\" for Image, \"dropdown\" for Select).\n *\n * Search terms only; never rendered. A hit here ranks BELOW every hit on a\n * name, so a tag widens what the picker finds without letting a synonym\n * outrank the element actually called that (AGL-2486).\n */\n tags?: string[]\n /** Alias of {@link tags}; both are searched. */\n keywords?: string[]\n\n /**\n * Icon props for display around besigner\n */\n icon?: MdiIconProps\n /**\n * Options to be passed to styled(Component, \\{...styledOptions\\})\n */\n styledOptions?: MuiStyledOptions\n\n /**\n * Define a limitation for nodes allowed as direct descendents\n */\n restrictChildren?: ComponentsLinealOrder\n /**\n * Define a limitation for nodes allowed to be direct ancestors\n */\n restrictParent?: ComponentsLinealOrder\n\n /**\n * Filter props\n */\n resolveProps?: JSX.ResolveProps<NodeSchema<P>>\n\n /**\n * Attribute fields to modify the contextual properties\n * New version\n */\n attributes?: AttributeSchema[]\n\n /**\n * Which groups of formatting the inline rich-text toolbar offers for this\n * component (AGL-2557). Read only where `flags.richTextEditable` is on.\n *\n * Omitted means every group, which is what leaves Typography exactly as it\n * was. Naming a subset narrows the toolbar AND the commit: a surface that\n * offers neither lists nor links is phrasing-only, and the sanitizer holds\n * it to that whatever an author pastes in.\n */\n richTextCommands?: RICH_TEXT_COMMANDS[]\n\n /**\n * Feature flags\n */\n flags?: {\n /**\n * Disable the use of emotion styled\n */\n emotion?: FEATURE_FLAG\n /**\n * Can the nodes of this component type be cloned?\n */\n cloning?: FEATURE_FLAG\n /**\n * Allow dragging nodes of this component type\n */\n dragging?: FEATURE_FLAG\n /**\n * Allow dropping nodes inside nodes of this component type\n */\n dropping?: FEATURE_FLAG\n /**\n * Allow editing element attributes of this component type\n */\n editing?: FEATURE_FLAG\n /**\n * Allow removing nodes of this component type\n */\n removing?: FEATURE_FLAG\n /**\n * Describe nodes of this component type to be self-closing\n */\n selfClosing?: FEATURE_FLAG\n /**\n * Component renders its `children` prop as text content the editor may\n * edit directly (Attributes \"Text\" field, inline canvas editing).\n */\n textEditable?: FEATURE_FLAG\n /**\n * Component also accepts basic rich text (AGL-54): the inline editor\n * upgrades to a formatting surface and commits sanitized HTML into the\n * `html` prop (with `children` kept as the plain-text fallback).\n */\n richTextEditable?: FEATURE_FLAG\n /**\n * Component reads its children POSITIONALLY (AGL-1237), so the renderer\n * hands it one React child per node child instead of the single `<Branch>`\n * element it normally passes.\n *\n * MUI's Accordion does `const [summary, ...rest] = Children.toArray(children)`,\n * and `toArray` does not traverse into an element — so with the default\n * wrapping the summary swallowed the whole subtree and the Collapse got\n * nothing. Every accordion on every published site expanded to reveal an\n * empty panel while its content rendered unconditionally inside the\n * summary's heading.\n *\n * Opt in only for components with this contract; the wrapper is what keeps\n * the Branch/Stem/Leaf seam swappable for everything else.\n */\n positionalChildren?: FEATURE_FLAG\n }\n}\n\nexport type SchemasByCategory = Record<\n ComponentCategory | string,\n (ComponentSchema<any> | PresetSchema<any>)[]\n>\n\nexport interface ScreenSchema {\n nodes: NodeSchemaNested\n createdAt?: ITimestamp\n updatedAt?: ITimestamp\n hostId?: HostUid\n}\n"],"names":["ComponentCategory","NodeType"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAuBD,SAASA,iBAAiB,QAAQ,wCAAoC;AAictE,OAAO,IAAA,AAAKC,kCAAAA;;;;;;;WAAAA;MAOX;AA6BD,SAASD,iBAAiB,GAAE"}