@aglyn/aglyn 1.0.0-beta.182 → 1.0.0-beta.183

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 (28) hide show
  1. package/package.json +11 -11
  2. package/src/lib/app-utils/compose-reusable-components.d.ts +10 -0
  3. package/src/lib/app-utils/compose-reusable-components.js +6 -1
  4. package/src/lib/app-utils/compose-reusable-components.js.map +1 -1
  5. package/src/lib/app-utils/definition-canvas-tree.d.ts +24 -0
  6. package/src/lib/app-utils/definition-canvas-tree.js +38 -0
  7. package/src/lib/app-utils/definition-canvas-tree.js.map +1 -1
  8. package/src/lib/app-utils/docs-index.generated.js +9 -2
  9. package/src/lib/app-utils/docs-index.generated.js.map +1 -1
  10. package/src/lib/app-utils/load-referenced-components.d.ts +166 -0
  11. package/src/lib/app-utils/load-referenced-components.js +219 -0
  12. package/src/lib/app-utils/load-referenced-components.js.map +1 -0
  13. package/src/lib/app-utils/reusable-component-kind.d.ts +119 -0
  14. package/src/lib/app-utils/reusable-component-kind.js +119 -0
  15. package/src/lib/app-utils/reusable-component-kind.js.map +1 -0
  16. package/src/lib/app-utils/server.d.ts +1 -0
  17. package/src/lib/app-utils/server.js +2 -0
  18. package/src/lib/app-utils/server.js.map +1 -1
  19. package/src/lib/foundation/constants/components.d.ts +7 -0
  20. package/src/lib/foundation/constants/components.js +6 -0
  21. package/src/lib/foundation/constants/components.js.map +1 -1
  22. package/src/lib/foundation/definitions/platform.types.d.ts +29 -0
  23. package/src/lib/foundation/definitions/platform.types.js.map +1 -1
  24. package/src/lib/foundation/index.d.ts +1 -1
  25. package/src/lib/foundation/index.js.map +1 -1
  26. package/src/lib/plugin-manager/plugin-resource-drafts.d.ts +17 -3
  27. package/src/lib/plugin-manager/plugin-resource-drafts.js +2 -2
  28. package/src/lib/plugin-manager/plugin-resource-drafts.js.map +1 -1
@@ -58,4 +58,28 @@ export declare function canvasTreeToDefinition(nodes: Nodes | undefined): {
58
58
  nodes: Nodes;
59
59
  ambiguousRoot: boolean;
60
60
  };
61
+ /** One element of a nested tree, as an element preset's `data` holds it. */
62
+ interface NestedNode {
63
+ $id?: string | null;
64
+ nodes?: NestedNode[];
65
+ [key: string]: unknown;
66
+ }
67
+ /**
68
+ * A nested tree — an element preset's `data` — as a definition: a node map
69
+ * keyed by fresh ids, with `rootId` naming the outermost element (AGL-3287).
70
+ *
71
+ * What a new component is seeded with when it starts from a preset, such as
72
+ * a Header email block starting from the email plugin's Header. The canvas
73
+ * mints ids the same way when the preset is dropped into a document, so a
74
+ * component started from a preset and one promoted from a dropped copy of it
75
+ * hold the same tree.
76
+ *
77
+ * Values are copied, never shared: a registered preset is a live object the
78
+ * drawer keeps reading, and a definition that aliased its props would carry
79
+ * every later edit back into the preset.
80
+ */
81
+ export declare function nestedToDefinition(nested: NestedNode, createId: () => string): {
82
+ rootId: string;
83
+ nodes: Nodes;
84
+ };
61
85
  export {};
@@ -1,4 +1,5 @@
1
1
  import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ import { _ as _object_without_properties_loose } from "@swc/helpers/_/_object_without_properties_loose";
2
3
  /**
3
4
  * @license
4
5
  * Copyright 2026 Aglyn LLC
@@ -122,5 +123,42 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
122
123
  ambiguousRoot: false
123
124
  };
124
125
  }
126
+ /**
127
+ * A nested tree — an element preset's `data` — as a definition: a node map
128
+ * keyed by fresh ids, with `rootId` naming the outermost element (AGL-3287).
129
+ *
130
+ * What a new component is seeded with when it starts from a preset, such as
131
+ * a Header email block starting from the email plugin's Header. The canvas
132
+ * mints ids the same way when the preset is dropped into a document, so a
133
+ * component started from a preset and one promoted from a dropped copy of it
134
+ * hold the same tree.
135
+ *
136
+ * Values are copied, never shared: a registered preset is a live object the
137
+ * drawer keeps reading, and a definition that aliased its props would carry
138
+ * every later edit back into the preset.
139
+ */ export function nestedToDefinition(nested, createId) {
140
+ const nodes = {};
141
+ const visit = (node, parentId)=>{
142
+ const { $id: _presetId, nodes: children } = node, fields = _object_without_properties_loose(node, [
143
+ "$id",
144
+ "nodes"
145
+ ]);
146
+ const id = createId();
147
+ const entry = _extends({}, JSON.parse(JSON.stringify(fields)), {
148
+ $id: id,
149
+ parentId
150
+ });
151
+ nodes[id] = entry;
152
+ if (Array.isArray(children) && children.length) {
153
+ entry.nodes = children.map((child)=>visit(child, id));
154
+ }
155
+ return id;
156
+ };
157
+ const rootId = visit(nested != null ? nested : {}, null);
158
+ return {
159
+ rootId,
160
+ nodes
161
+ };
162
+ }
125
163
 
126
164
  //# sourceMappingURL=definition-canvas-tree.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/definition-canvas-tree.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 { CANVAS_ROOT_ELEMENT_ID } from '../foundation/constants/canvas'\n\ntype Nodes = Record<string, any>\n\n/**\n * Adapt a reusable-component definition for the canvas, and back (AGL-680).\n *\n * The canvas requires its root to be the canonical `_@_` node — `setNodes`\n * stores whatever it is handed and `rootNode` looks up that exact id, so a\n * tree without it has no root and renders nothing.\n *\n * A definition's root is NOT `_@_`: `promoteToComponent` walks a selected\n * subtree, so the root is whichever node was selected. Loading one straight\n * into the canvas therefore produced an empty editor with \"Error rendering\n * undefined\" in the console — found by the editor smoke test, not by\n * typechecking, because both shapes are `Record<string, node>`.\n *\n * So the definition is wrapped under a synthetic canvas root on the way in\n * and unwrapped on the way out. The wrapper is never published: the tenant\n * runtime grafts from `rootId`, and an extra always-empty container in\n * every instance would be both wrong and wasteful.\n *\n * A map with no findable root always yields the empty canvas root rather\n * than being passed through (AGL-753). Passing it through renders `'Invalid\n * node'` with Add Element disabled — an editor with no way out, so the\n * document can never be repaired from the UI. Components created before\n * AGL-693 are stored as exactly that (`nodes: {}`), which is why this is a\n * load-time repair and not a migration.\n */\nexport function definitionToCanvasTree(definition: {\n rootId?: string\n nodes?: Nodes\n}): Nodes {\n const nodes = definition?.nodes ?? {}\n // Already canvas-shaped (a tree saved by this editor) — leave it alone.\n if (nodes[CANVAS_ROOT_ELEMENT_ID]) return nodes\n\n const rootId =\n definition?.rootId && nodes[definition.rootId]\n ? definition.rootId\n : (Object.keys(nodes).find((id) => !nodes[id]?.parentId) ??\n // A definition promoted out of a screen can keep the `parentId` it\n // had there, so \"no parent\" is not always \"no parentId\" — a parent\n // that is not itself in the map is just as much a root.\n Object.keys(nodes).find((id) => !nodes[nodes[id]?.parentId]))\n if (!rootId) {\n return {\n [CANVAS_ROOT_ELEMENT_ID]: {\n $id: CANVAS_ROOT_ELEMENT_ID,\n componentId: 'div',\n parentId: null,\n nodes: [],\n },\n }\n }\n\n return {\n ...nodes,\n [rootId]: { ...nodes[rootId], parentId: CANVAS_ROOT_ELEMENT_ID },\n [CANVAS_ROOT_ELEMENT_ID]: {\n $id: CANVAS_ROOT_ELEMENT_ID,\n componentId: 'box',\n parentId: null,\n nodes: [rootId],\n },\n }\n}\n\n/**\n * Recover the definition shape from a canvas tree.\n *\n * Returns the single child of the synthetic root as `rootId` and drops the\n * wrapper. A canvas root with several children means the author added\n * siblings at the top level — there is no single definition root to name,\n * so the wrapper is kept and reported, letting the caller refuse rather\n * than silently publishing only the first child.\n */\nexport function canvasTreeToDefinition(nodes: Nodes | undefined): {\n rootId?: string\n nodes: Nodes\n ambiguousRoot: boolean\n} {\n const map = nodes ?? {}\n const canvasRoot = map[CANVAS_ROOT_ELEMENT_ID]\n if (!canvasRoot) {\n const rootId = Object.keys(map).find((id) => !map[id]?.parentId)\n return { rootId, nodes: map, ambiguousRoot: false }\n }\n\n const children: string[] = Array.isArray(canvasRoot.nodes)\n ? canvasRoot.nodes.filter((id: string) => map[id])\n : []\n if (children.length !== 1) {\n return {\n rootId: CANVAS_ROOT_ELEMENT_ID,\n nodes: map,\n ambiguousRoot: true,\n }\n }\n\n const rootId = children[0]\n const stripped: Nodes = {}\n for (const [id, node] of Object.entries(map)) {\n if (id === CANVAS_ROOT_ELEMENT_ID) continue\n stripped[id] = id === rootId ? { ...node, parentId: null } : node\n }\n return { rootId, nodes: stripped, ambiguousRoot: false }\n}\n"],"names":["CANVAS_ROOT_ELEMENT_ID","definitionToCanvasTree","definition","Object","nodes","rootId","keys","find","id","parentId","$id","componentId","canvasTreeToDefinition","map","canvasRoot","ambiguousRoot","children","Array","isArray","filter","length","stripped","node","entries"],"mappings":";AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,sBAAsB,QAAQ,oCAAgC;AAIvE;;;;;;;;;;;;;;;;;;;;;;;;CAwBC,GACD,OAAO,SAASC,uBAAuBC,UAGtC;cAQQC;IAPP,MAAMC,gBAAQF,8BAAAA,WAAYE,KAAK,mBAAI,CAAC;IACpC,wEAAwE;IACxE,IAAIA,KAAK,CAACJ,uBAAuB,EAAE,OAAOI;IAE1C,MAAMC,SACJH,CAAAA,8BAAAA,WAAYG,MAAM,KAAID,KAAK,CAACF,WAAWG,MAAM,CAAC,GAC1CH,WAAWG,MAAM,IAChBF,oBAAAA,OAAOG,IAAI,CAACF,OAAOG,IAAI,CAAC,CAACC;YAAQJ;eAAD,GAACA,YAAAA,KAAK,CAACI,GAAG,qBAATJ,UAAWK,QAAQ;kBAApDN,oBACD,mEAAmE;IACnE,mEAAmE;IACnE,wDAAwD;IACxDA,OAAOG,IAAI,CAACF,OAAOG,IAAI,CAAC,CAACC;YAAcJ;eAAP,CAACA,KAAK,EAACA,YAAAA,KAAK,CAACI,GAAG,qBAATJ,UAAWK,QAAQ,CAAC;;IACjE,IAAI,CAACJ,QAAQ;QACX,OAAO;YACL,CAACL,uBAAuB,EAAE;gBACxBU,KAAKV;gBACLW,aAAa;gBACbF,UAAU;gBACVL,OAAO,EAAE;YACX;QACF;IACF;IAEA,OAAO,aACFA;QACH,CAACC,OAAO,EAAE,aAAKD,KAAK,CAACC,OAAO;YAAEI,UAAUT;;QACxC,CAACA,uBAAuB,EAAE;YACxBU,KAAKV;YACLW,aAAa;YACbF,UAAU;YACVL,OAAO;gBAACC;aAAO;QACjB;;AAEJ;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASO,uBAAuBR,KAAwB;IAK7D,MAAMS,MAAMT,gBAAAA,QAAS,CAAC;IACtB,MAAMU,aAAaD,GAAG,CAACb,uBAAuB;IAC9C,IAAI,CAACc,YAAY;QACf,MAAMT,SAASF,OAAOG,IAAI,CAACO,KAAKN,IAAI,CAAC,CAACC;gBAAQK;mBAAD,GAACA,UAAAA,GAAG,CAACL,GAAG,qBAAPK,QAASJ,QAAQ;;QAC/D,OAAO;YAAEJ;YAAQD,OAAOS;YAAKE,eAAe;QAAM;IACpD;IAEA,MAAMC,WAAqBC,MAAMC,OAAO,CAACJ,WAAWV,KAAK,IACrDU,WAAWV,KAAK,CAACe,MAAM,CAAC,CAACX,KAAeK,GAAG,CAACL,GAAG,IAC/C,EAAE;IACN,IAAIQ,SAASI,MAAM,KAAK,GAAG;QACzB,OAAO;YACLf,QAAQL;YACRI,OAAOS;YACPE,eAAe;QACjB;IACF;IAEA,MAAMV,SAASW,QAAQ,CAAC,EAAE;IAC1B,MAAMK,WAAkB,CAAC;IACzB,KAAK,MAAM,CAACb,IAAIc,KAAK,IAAInB,OAAOoB,OAAO,CAACV,KAAM;QAC5C,IAAIL,OAAOR,wBAAwB;QACnCqB,QAAQ,CAACb,GAAG,GAAGA,OAAOH,SAAS,aAAKiB;YAAMb,UAAU;aAASa;IAC/D;IACA,OAAO;QAAEjB;QAAQD,OAAOiB;QAAUN,eAAe;IAAM;AACzD"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/definition-canvas-tree.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 { CANVAS_ROOT_ELEMENT_ID } from '../foundation/constants/canvas'\n\ntype Nodes = Record<string, any>\n\n/**\n * Adapt a reusable-component definition for the canvas, and back (AGL-680).\n *\n * The canvas requires its root to be the canonical `_@_` node — `setNodes`\n * stores whatever it is handed and `rootNode` looks up that exact id, so a\n * tree without it has no root and renders nothing.\n *\n * A definition's root is NOT `_@_`: `promoteToComponent` walks a selected\n * subtree, so the root is whichever node was selected. Loading one straight\n * into the canvas therefore produced an empty editor with \"Error rendering\n * undefined\" in the console — found by the editor smoke test, not by\n * typechecking, because both shapes are `Record<string, node>`.\n *\n * So the definition is wrapped under a synthetic canvas root on the way in\n * and unwrapped on the way out. The wrapper is never published: the tenant\n * runtime grafts from `rootId`, and an extra always-empty container in\n * every instance would be both wrong and wasteful.\n *\n * A map with no findable root always yields the empty canvas root rather\n * than being passed through (AGL-753). Passing it through renders `'Invalid\n * node'` with Add Element disabled — an editor with no way out, so the\n * document can never be repaired from the UI. Components created before\n * AGL-693 are stored as exactly that (`nodes: {}`), which is why this is a\n * load-time repair and not a migration.\n */\nexport function definitionToCanvasTree(definition: {\n rootId?: string\n nodes?: Nodes\n}): Nodes {\n const nodes = definition?.nodes ?? {}\n // Already canvas-shaped (a tree saved by this editor) — leave it alone.\n if (nodes[CANVAS_ROOT_ELEMENT_ID]) return nodes\n\n const rootId =\n definition?.rootId && nodes[definition.rootId]\n ? definition.rootId\n : (Object.keys(nodes).find((id) => !nodes[id]?.parentId) ??\n // A definition promoted out of a screen can keep the `parentId` it\n // had there, so \"no parent\" is not always \"no parentId\" — a parent\n // that is not itself in the map is just as much a root.\n Object.keys(nodes).find((id) => !nodes[nodes[id]?.parentId]))\n if (!rootId) {\n return {\n [CANVAS_ROOT_ELEMENT_ID]: {\n $id: CANVAS_ROOT_ELEMENT_ID,\n componentId: 'div',\n parentId: null,\n nodes: [],\n },\n }\n }\n\n return {\n ...nodes,\n [rootId]: { ...nodes[rootId], parentId: CANVAS_ROOT_ELEMENT_ID },\n [CANVAS_ROOT_ELEMENT_ID]: {\n $id: CANVAS_ROOT_ELEMENT_ID,\n componentId: 'box',\n parentId: null,\n nodes: [rootId],\n },\n }\n}\n\n/**\n * Recover the definition shape from a canvas tree.\n *\n * Returns the single child of the synthetic root as `rootId` and drops the\n * wrapper. A canvas root with several children means the author added\n * siblings at the top level — there is no single definition root to name,\n * so the wrapper is kept and reported, letting the caller refuse rather\n * than silently publishing only the first child.\n */\nexport function canvasTreeToDefinition(nodes: Nodes | undefined): {\n rootId?: string\n nodes: Nodes\n ambiguousRoot: boolean\n} {\n const map = nodes ?? {}\n const canvasRoot = map[CANVAS_ROOT_ELEMENT_ID]\n if (!canvasRoot) {\n const rootId = Object.keys(map).find((id) => !map[id]?.parentId)\n return { rootId, nodes: map, ambiguousRoot: false }\n }\n\n const children: string[] = Array.isArray(canvasRoot.nodes)\n ? canvasRoot.nodes.filter((id: string) => map[id])\n : []\n if (children.length !== 1) {\n return {\n rootId: CANVAS_ROOT_ELEMENT_ID,\n nodes: map,\n ambiguousRoot: true,\n }\n }\n\n const rootId = children[0]\n const stripped: Nodes = {}\n for (const [id, node] of Object.entries(map)) {\n if (id === CANVAS_ROOT_ELEMENT_ID) continue\n stripped[id] = id === rootId ? { ...node, parentId: null } : node\n }\n return { rootId, nodes: stripped, ambiguousRoot: false }\n}\n\n/** One element of a nested tree, as an element preset's `data` holds it. */\ninterface NestedNode {\n $id?: string | null\n nodes?: NestedNode[]\n [key: string]: unknown\n}\n\n/**\n * A nested tree — an element preset's `data` — as a definition: a node map\n * keyed by fresh ids, with `rootId` naming the outermost element (AGL-3287).\n *\n * What a new component is seeded with when it starts from a preset, such as\n * a Header email block starting from the email plugin's Header. The canvas\n * mints ids the same way when the preset is dropped into a document, so a\n * component started from a preset and one promoted from a dropped copy of it\n * hold the same tree.\n *\n * Values are copied, never shared: a registered preset is a live object the\n * drawer keeps reading, and a definition that aliased its props would carry\n * every later edit back into the preset.\n */\nexport function nestedToDefinition(\n nested: NestedNode,\n createId: () => string,\n): { rootId: string; nodes: Nodes } {\n const nodes: Nodes = {}\n const visit = (node: NestedNode, parentId: string | null): string => {\n const { $id: _presetId, nodes: children, ...fields } = node\n const id = createId()\n const entry: Nodes[string] = {\n ...(JSON.parse(JSON.stringify(fields)) as Record<string, unknown>),\n $id: id,\n parentId,\n }\n nodes[id] = entry\n if (Array.isArray(children) && children.length) {\n entry.nodes = children.map((child) => visit(child, id))\n }\n return id\n }\n const rootId = visit(nested ?? {}, null)\n return { rootId, nodes }\n}\n"],"names":["CANVAS_ROOT_ELEMENT_ID","definitionToCanvasTree","definition","Object","nodes","rootId","keys","find","id","parentId","$id","componentId","canvasTreeToDefinition","map","canvasRoot","ambiguousRoot","children","Array","isArray","filter","length","stripped","node","entries","nestedToDefinition","nested","createId","visit","_presetId","fields","entry","JSON","parse","stringify","child"],"mappings":"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"}
@@ -1458,7 +1458,7 @@ export const ASSIST_DOCS_INDEX = [
1458
1458
  title: 'Reusable components',
1459
1459
  heading: 'Insert instances',
1460
1460
  anchor: '#insert-instances',
1461
- text: 'Insert instances from Your components in the element drawer, on any screen, layout, template or other component.\nOn the canvas an instance renders its actual content, not a placeholder — what you see is what the page will render, with properties already resolved. The rendered elements are not canvas elements, though: clicking anywhere on an instance selects the instance, which is the only thing there you can select, move or delete. To change what is inside it, open the component.'
1461
+ text: 'Insert instances from Your components in the element drawer, on any screen, layout, template or other component. Emails have their own: see Reusable email blocks.\nOn the canvas an instance renders its actual content, not a placeholder — what you see is what the page will render, with properties already resolved. The rendered elements are not canvas elements, though: clicking anywhere on an instance selects the instance, which is the only thing there you can select, move or delete. To change what is inside it, open the component.'
1462
1462
  },
1463
1463
  {
1464
1464
  path: '/building-sites/besigner/reusable-components',
@@ -1493,7 +1493,7 @@ export const ASSIST_DOCS_INDEX = [
1493
1493
  title: 'Reusable components',
1494
1494
  heading: 'Used by',
1495
1495
  anchor: '#used-by',
1496
- text: 'A component\'s detail page has a Used by card listing everything that places an instance of it, so deleting one is not a guess.\nThree places are searched, which is everywhere the renderer expands an instance:\n- the published version of every screen,\n- the published version of every layout,\n- and other reusable components — a component can be placed inside another one, so one used nowhere else can still be very much in use.\nUnpublished drafts and templates in your library are not searched. If the check fails — a dropped connection, say — the card says so and shows a Try again button. It never reports "nothing uses this" when it could not actually look.\nIf a component is deleted while instances remain, those instances are left untouched rather than emptied: a missing definition never takes a published page down.'
1496
+ text: 'A component\'s detail page has a Used by card listing everything that places an instance of it, so deleting one is not a guess.\nEverywhere the renderer expands an instance is searched:\n- the published version of every screen, the emails you design for campaigns included,\n- the published version of every layout,\n- other reusable components — a component can be placed inside another one, so one used nowhere else can still be very much in use,\n- and the site\'s own emails — a header or footer placed in a booking confirmation or an order receipt goes out in every one sent.\nUnpublished drafts and templates in your library are not searched. If the check fails — a dropped connection, say — the card says so and shows a Try again button. It never reports "nothing uses this" when it could not actually look.\nIf a component is deleted while instances remain, those instances are left untouched rather than emptied: a missing definition never takes a published page down.'
1497
1497
  },
1498
1498
  {
1499
1499
  path: '/building-sites/besigner/reusable-components',
@@ -1509,6 +1509,13 @@ export const ASSIST_DOCS_INDEX = [
1509
1509
  anchor: '#duplicate',
1510
1510
  text: 'To start a new component from an existing one, choose Duplicate… in its row menu on the Components page, or More → Duplicate on its detail page. The copy carries the definition, its properties and the latest saved version, under the name you give it. It has no instances: every screen keeps pointing at the original, and you place the copy where you want it. Duplicating needs the same plan as creating a component.'
1511
1511
  },
1512
+ {
1513
+ path: '/building-sites/besigner/reusable-components',
1514
+ title: 'Reusable components',
1515
+ heading: 'Reusable email blocks (header and footer)',
1516
+ anchor: '#reusable-email-blocks',
1517
+ text: 'Most emails a site sends start and end the same way: your logo at the top, your address at the bottom. Build those once as email blocks and add them to any email. Change the block, and every email using it changes too.\nAn email block is a reusable component made for emails. It is built from the email elements — email text, email image, email button and the rest — because mail apps can\'t show the elements your pages use. So each kind stays in its own place:\n- In an email, the element drawer lists your email blocks under Your email blocks, and never your page components.\n- On a page, you only see Your components, never an email block.\nThe Components page marks each row Email or Page, so you can tell them apart.\nMake a header or footer\n1. Open Components and choose Create Component.\n2. Give it a name, and under Where will you use it? choose In emails.\n3. Under Start with, pick one:\n- Header — a spot for your logo, with your company name under it.\n- Footer — your company name and address, and a line telling readers why they get your emails.\n- Blank — an empty block.\n4. Choose Next, then open the block in the Besigner and replace the sample words with your own. Pick your logo from the media library: until you do, the header\'s picture shows nothing in a sent email.\n5. Save & publish. Emails use the published version of the block.\nYou don\'t need an unsubscribe link in your footer. Campaign emails add one for you.\nThe same Header and Footer are in every email\'s element drawer too, under Sections & Blocks. Drop one into an email to use it in that email only.\nYou can also turn part of an email into a block: select it and choose Save as reusable component in the Attributes panel. Saved from an email, it becomes an email block.\nAdd one to an email\nOpen any email — a campaign email,'
1518
+ },
1512
1519
  {
1513
1520
  path: '/building-sites/besigner/reusable-components',
1514
1521
  title: 'Reusable components',