@kywi-software/core 0.11.0 → 0.12.0

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 (216) hide show
  1. package/AGENT-PATTERNS.md +96 -10
  2. package/dist/admin/content-form.d.ts.map +1 -1
  3. package/dist/admin/content-form.js +97 -10
  4. package/dist/admin/content-form.js.map +1 -1
  5. package/dist/admin/layout-editor/component-actions.d.ts +219 -22
  6. package/dist/admin/layout-editor/component-actions.d.ts.map +1 -1
  7. package/dist/admin/layout-editor/component-actions.js +356 -36
  8. package/dist/admin/layout-editor/component-actions.js.map +1 -1
  9. package/dist/admin/layout-editor/component-fork.d.ts +224 -0
  10. package/dist/admin/layout-editor/component-fork.d.ts.map +1 -0
  11. package/dist/admin/layout-editor/component-fork.js +342 -0
  12. package/dist/admin/layout-editor/component-fork.js.map +1 -0
  13. package/dist/admin/layout-editor/editor-canvas.d.ts +8 -0
  14. package/dist/admin/layout-editor/editor-canvas.d.ts.map +1 -1
  15. package/dist/admin/layout-editor/editor-canvas.js +120 -12
  16. package/dist/admin/layout-editor/editor-canvas.js.map +1 -1
  17. package/dist/admin/layout-editor/editor-dialog.d.ts +164 -0
  18. package/dist/admin/layout-editor/editor-dialog.d.ts.map +1 -0
  19. package/dist/admin/layout-editor/editor-dialog.js +195 -0
  20. package/dist/admin/layout-editor/editor-dialog.js.map +1 -0
  21. package/dist/admin/layout-editor/editor-state.d.ts +76 -0
  22. package/dist/admin/layout-editor/editor-state.d.ts.map +1 -1
  23. package/dist/admin/layout-editor/editor-state.js +47 -1
  24. package/dist/admin/layout-editor/editor-state.js.map +1 -1
  25. package/dist/admin/layout-editor/editor.css +279 -2
  26. package/dist/admin/layout-editor/index.d.ts +10 -1
  27. package/dist/admin/layout-editor/index.d.ts.map +1 -1
  28. package/dist/admin/layout-editor/index.js +11 -1
  29. package/dist/admin/layout-editor/index.js.map +1 -1
  30. package/dist/admin/layout-editor/layout-editor.d.ts +58 -1
  31. package/dist/admin/layout-editor/layout-editor.d.ts.map +1 -1
  32. package/dist/admin/layout-editor/layout-editor.js +160 -190
  33. package/dist/admin/layout-editor/layout-editor.js.map +1 -1
  34. package/dist/admin/layout-editor/linked-component.d.ts +39 -0
  35. package/dist/admin/layout-editor/linked-component.d.ts.map +1 -0
  36. package/dist/admin/layout-editor/linked-component.js +42 -0
  37. package/dist/admin/layout-editor/linked-component.js.map +1 -0
  38. package/dist/admin/layout-editor/module-overlay.d.ts +10 -1
  39. package/dist/admin/layout-editor/module-overlay.d.ts.map +1 -1
  40. package/dist/admin/layout-editor/module-overlay.js +33 -4
  41. package/dist/admin/layout-editor/module-overlay.js.map +1 -1
  42. package/dist/admin/layout-editor/node-lookup.d.ts +35 -0
  43. package/dist/admin/layout-editor/node-lookup.d.ts.map +1 -1
  44. package/dist/admin/layout-editor/node-lookup.js +91 -0
  45. package/dist/admin/layout-editor/node-lookup.js.map +1 -1
  46. package/dist/admin/layout-editor/overlay-shell.d.ts.map +1 -1
  47. package/dist/admin/layout-editor/overlay-shell.js +114 -23
  48. package/dist/admin/layout-editor/overlay-shell.js.map +1 -1
  49. package/dist/admin/layout-editor/props-panel.d.ts +54 -1
  50. package/dist/admin/layout-editor/props-panel.d.ts.map +1 -1
  51. package/dist/admin/layout-editor/props-panel.js +118 -9
  52. package/dist/admin/layout-editor/props-panel.js.map +1 -1
  53. package/dist/admin/layout-editor/section-overlay.d.ts +9 -1
  54. package/dist/admin/layout-editor/section-overlay.d.ts.map +1 -1
  55. package/dist/admin/layout-editor/section-overlay.js +14 -4
  56. package/dist/admin/layout-editor/section-overlay.js.map +1 -1
  57. package/dist/admin/layout-editor/section-template-picker.d.ts +27 -1
  58. package/dist/admin/layout-editor/section-template-picker.d.ts.map +1 -1
  59. package/dist/admin/layout-editor/section-template-picker.js +71 -17
  60. package/dist/admin/layout-editor/section-template-picker.js.map +1 -1
  61. package/dist/admin/layout-editor/use-component-connect.d.ts +61 -0
  62. package/dist/admin/layout-editor/use-component-connect.d.ts.map +1 -0
  63. package/dist/admin/layout-editor/use-component-connect.js +292 -0
  64. package/dist/admin/layout-editor/use-component-connect.js.map +1 -0
  65. package/dist/admin/layout-editor/use-component-fork.d.ts +123 -0
  66. package/dist/admin/layout-editor/use-component-fork.d.ts.map +1 -0
  67. package/dist/admin/layout-editor/use-component-fork.js +346 -0
  68. package/dist/admin/layout-editor/use-component-fork.js.map +1 -0
  69. package/dist/admin/layout-editor/use-component-library.d.ts +94 -0
  70. package/dist/admin/layout-editor/use-component-library.d.ts.map +1 -0
  71. package/dist/admin/layout-editor/use-component-library.js +55 -0
  72. package/dist/admin/layout-editor/use-component-library.js.map +1 -0
  73. package/dist/admin/registry.d.ts +11 -2
  74. package/dist/admin/registry.d.ts.map +1 -1
  75. package/dist/admin/registry.js +40 -4
  76. package/dist/admin/registry.js.map +1 -1
  77. package/dist/admin/server.d.ts +1 -0
  78. package/dist/admin/server.d.ts.map +1 -1
  79. package/dist/admin/server.js +5 -0
  80. package/dist/admin/server.js.map +1 -1
  81. package/dist/admin/styles/admin.css +43 -0
  82. package/dist/admin/surfaces/components/component-edit.d.ts +3 -0
  83. package/dist/admin/surfaces/components/component-edit.d.ts.map +1 -0
  84. package/dist/admin/surfaces/components/component-edit.js +235 -0
  85. package/dist/admin/surfaces/components/component-edit.js.map +1 -0
  86. package/dist/admin/surfaces/components/components-data.d.ts +153 -0
  87. package/dist/admin/surfaces/components/components-data.d.ts.map +1 -0
  88. package/dist/admin/surfaces/components/components-data.js +154 -0
  89. package/dist/admin/surfaces/components/components-data.js.map +1 -0
  90. package/dist/admin/surfaces/components/components-list.d.ts +3 -0
  91. package/dist/admin/surfaces/components/components-list.d.ts.map +1 -0
  92. package/dist/admin/surfaces/components/components-list.js +196 -0
  93. package/dist/admin/surfaces/components/components-list.js.map +1 -0
  94. package/dist/admin/surfaces/components/definition-canvas.d.ts +73 -0
  95. package/dist/admin/surfaces/components/definition-canvas.d.ts.map +1 -0
  96. package/dist/admin/surfaces/components/definition-canvas.js +207 -0
  97. package/dist/admin/surfaces/components/definition-canvas.js.map +1 -0
  98. package/dist/admin/surfaces/layouts/layout-editor-client.d.ts.map +1 -1
  99. package/dist/admin/surfaces/layouts/layout-editor-client.js +14 -2
  100. package/dist/admin/surfaces/layouts/layout-editor-client.js.map +1 -1
  101. package/dist/admin/surfaces/layouts/layouts-data.d.ts +3 -14
  102. package/dist/admin/surfaces/layouts/layouts-data.d.ts.map +1 -1
  103. package/dist/admin/surfaces/layouts/layouts-data.js +4 -7
  104. package/dist/admin/surfaces/layouts/layouts-data.js.map +1 -1
  105. package/dist/admin/surfaces/layouts/layouts-list.d.ts.map +1 -1
  106. package/dist/admin/surfaces/layouts/layouts-list.js +16 -22
  107. package/dist/admin/surfaces/layouts/layouts-list.js.map +1 -1
  108. package/dist/admin/surfaces/settings/admin-features.d.ts.map +1 -1
  109. package/dist/admin/surfaces/settings/admin-features.js +1 -0
  110. package/dist/admin/surfaces/settings/admin-features.js.map +1 -1
  111. package/dist/api/bulk-routes.d.ts.map +1 -1
  112. package/dist/api/bulk-routes.js +25 -0
  113. package/dist/api/bulk-routes.js.map +1 -1
  114. package/dist/api/changeset-routes.d.ts.map +1 -1
  115. package/dist/api/changeset-routes.js +17 -3
  116. package/dist/api/changeset-routes.js.map +1 -1
  117. package/dist/api/component-routes.d.ts.map +1 -1
  118. package/dist/api/component-routes.js +228 -8
  119. package/dist/api/component-routes.js.map +1 -1
  120. package/dist/api/content-routes.d.ts.map +1 -1
  121. package/dist/api/content-routes.js +64 -0
  122. package/dist/api/content-routes.js.map +1 -1
  123. package/dist/api/feed-routes.d.ts +6 -6
  124. package/dist/api/graphql/resolvers.d.ts +4 -4
  125. package/dist/api/layout-routes.d.ts.map +1 -1
  126. package/dist/api/layout-routes.js +4 -17
  127. package/dist/api/layout-routes.js.map +1 -1
  128. package/dist/api/layout-write-guard.d.ts +80 -0
  129. package/dist/api/layout-write-guard.d.ts.map +1 -0
  130. package/dist/api/layout-write-guard.js +88 -0
  131. package/dist/api/layout-write-guard.js.map +1 -0
  132. package/dist/api/section-template-routes.d.ts.map +1 -1
  133. package/dist/api/section-template-routes.js +11 -0
  134. package/dist/api/section-template-routes.js.map +1 -1
  135. package/dist/api/translation-routes.d.ts.map +1 -1
  136. package/dist/api/translation-routes.js +8 -0
  137. package/dist/api/translation-routes.js.map +1 -1
  138. package/dist/api/version-routes.d.ts.map +1 -1
  139. package/dist/api/version-routes.js +20 -8
  140. package/dist/api/version-routes.js.map +1 -1
  141. package/dist/api/workflow-routes.d.ts.map +1 -1
  142. package/dist/api/workflow-routes.js +53 -8
  143. package/dist/api/workflow-routes.js.map +1 -1
  144. package/dist/db/analytics-queries.d.ts +3 -3
  145. package/dist/db/apply-snapshot.d.ts +75 -8
  146. package/dist/db/apply-snapshot.d.ts.map +1 -1
  147. package/dist/db/apply-snapshot.js +104 -8
  148. package/dist/db/apply-snapshot.js.map +1 -1
  149. package/dist/db/audit-queries.d.ts +12 -12
  150. package/dist/db/category-queries.d.ts +3 -3
  151. package/dist/db/changeset-audit-queries.d.ts +4 -4
  152. package/dist/db/changeset-queries.d.ts +7 -7
  153. package/dist/db/comment-queries.d.ts +8 -8
  154. package/dist/db/comment-subscription-queries.d.ts +2 -2
  155. package/dist/db/component-instance-queries.d.ts +197 -0
  156. package/dist/db/component-instance-queries.d.ts.map +1 -0
  157. package/dist/db/component-instance-queries.js +405 -0
  158. package/dist/db/component-instance-queries.js.map +1 -0
  159. package/dist/db/content-queries.d.ts.map +1 -1
  160. package/dist/db/content-queries.js +39 -2
  161. package/dist/db/content-queries.js.map +1 -1
  162. package/dist/db/extension-queries.d.ts +7 -7
  163. package/dist/db/feed-queries.d.ts +3 -3
  164. package/dist/db/index.d.ts +1 -0
  165. package/dist/db/index.d.ts.map +1 -1
  166. package/dist/db/index.js +1 -0
  167. package/dist/db/index.js.map +1 -1
  168. package/dist/db/layout-queries.d.ts +13 -1
  169. package/dist/db/layout-queries.d.ts.map +1 -1
  170. package/dist/db/layout-queries.js +52 -7
  171. package/dist/db/layout-queries.js.map +1 -1
  172. package/dist/db/locale-queries.d.ts.map +1 -1
  173. package/dist/db/locale-queries.js +0 -0
  174. package/dist/db/locale-queries.js.map +1 -1
  175. package/dist/db/media-queries.d.ts +12 -12
  176. package/dist/db/revision-queries.d.ts +3 -3
  177. package/dist/db/site-admin-queries.d.ts +2 -2
  178. package/dist/db/site-membership-queries.d.ts +3 -3
  179. package/dist/db/site-plugin-queries.d.ts +2 -2
  180. package/dist/db/site-queries.d.ts +6 -6
  181. package/dist/db/version-queries.d.ts +12 -12
  182. package/dist/db/webhook-queries.d.ts +11 -11
  183. package/dist/db/workflow-queries.d.ts +6 -6
  184. package/dist/db/workflow-queries.d.ts.map +1 -1
  185. package/dist/db/workflow-queries.js +11 -2
  186. package/dist/db/workflow-queries.js.map +1 -1
  187. package/dist/layout/component-refs.d.ts +465 -0
  188. package/dist/layout/component-refs.d.ts.map +1 -0
  189. package/dist/layout/component-refs.js +848 -0
  190. package/dist/layout/component-refs.js.map +1 -0
  191. package/dist/layout/index.d.ts +2 -1
  192. package/dist/layout/index.d.ts.map +1 -1
  193. package/dist/layout/index.js +1 -0
  194. package/dist/layout/index.js.map +1 -1
  195. package/dist/layout/module-permissions.d.ts +49 -1
  196. package/dist/layout/module-permissions.d.ts.map +1 -1
  197. package/dist/layout/module-permissions.js +117 -6
  198. package/dist/layout/module-permissions.js.map +1 -1
  199. package/dist/layout/renderer.d.ts +51 -4
  200. package/dist/layout/renderer.d.ts.map +1 -1
  201. package/dist/layout/renderer.js +36 -38
  202. package/dist/layout/renderer.js.map +1 -1
  203. package/dist/layout/types.d.ts +42 -0
  204. package/dist/layout/types.d.ts.map +1 -1
  205. package/dist/layout/types.js +54 -11
  206. package/dist/layout/types.js.map +1 -1
  207. package/dist/schema/base.d.ts +1 -1
  208. package/dist/schema/components.d.ts +150 -0
  209. package/dist/schema/components.d.ts.map +1 -1
  210. package/dist/schema/components.js +50 -4
  211. package/dist/schema/components.js.map +1 -1
  212. package/dist/versioning/scheduler.d.ts +8 -0
  213. package/dist/versioning/scheduler.d.ts.map +1 -1
  214. package/dist/versioning/scheduler.js +21 -7
  215. package/dist/versioning/scheduler.js.map +1 -1
  216. package/package.json +1 -1
@@ -8,14 +8,27 @@
8
8
  * - Clone / Duplicate
9
9
  * - Copy / Paste (clipboard serialization)
10
10
  */
11
- import type { LayoutSection, ModuleNode } from '../../layout/types.js';
12
- import type { KywiComponentRow } from '../../schema/components.js';
13
- /** Data shape for saving a new component (passed to createComponent). */
11
+ import type { LayoutSection, ModuleNode, VariantContainer } from '../../layout/types.js';
12
+ import { type ComponentPlacementLookup } from '../../layout/component-refs.js';
13
+ /**
14
+ * Data shape for saving a new component — literally the `POST /components` body.
15
+ *
16
+ * `description` and `moduleType` are OPTIONAL, not nullable, because the route's
17
+ * create schema declares both `z.string().optional()`: an explicit JSON `null` is
18
+ * "Expected string, received null" and the request 400s. This shape used to send
19
+ * `description: null` always and `moduleType: null` for every section component,
20
+ * so **save-as-component could not have worked against the real API at all** —
21
+ * nothing caught it because the editor's unit tests stub `fetch` and echo the body
22
+ * back, so no test ever put a body past the actual validator (verified against
23
+ * the route's schema, kywi-cms#147 T3). Omitting a key is the only way to say
24
+ * "there isn't one" that the API accepts.
25
+ */
14
26
  export interface ComponentDefinitionInput {
15
27
  name: string;
16
- description: string | null;
17
- type: 'module' | 'section';
18
- moduleType: string | null;
28
+ description?: string;
29
+ type: 'module' | 'section' | 'variantContainer';
30
+ /** Present only for a `module` component; the other types have no module type. */
31
+ moduleType?: string;
19
32
  definition: Record<string, unknown>;
20
33
  }
21
34
  /** Data shape for saving a new section template. */
@@ -26,7 +39,7 @@ export interface SectionTemplateInput {
26
39
  }
27
40
  /** Serialized clipboard payload for copy/paste. */
28
41
  export interface ClipboardPayload {
29
- kind: 'module' | 'section';
42
+ kind: 'module' | 'section' | 'variantContainer';
30
43
  data: string;
31
44
  }
32
45
  /**
@@ -66,6 +79,21 @@ export declare function resolveSectionComponentDefinition(component: {
66
79
  type: string;
67
80
  definition?: unknown;
68
81
  } | null | undefined): LayoutSection | null;
82
+ /**
83
+ * Project a saved variant-container component to its stored
84
+ * {@link VariantContainer} definition — the shape the renderer's
85
+ * `VariantContainerComponentResolver` expects (kywi-cms#147). Returns null for a
86
+ * non-container component or a malformed definition, so an unresolvable
87
+ * reference falls through to the container's own inline copy.
88
+ *
89
+ * The public site's `variantContainerComponentResolver` is just
90
+ * `id => resolveVariantContainerComponentDefinition(await getComponentById(db, id, siteId))`
91
+ * — the third member of the family, wired exactly like the other two.
92
+ */
93
+ export declare function resolveVariantContainerComponentDefinition(component: {
94
+ type: string;
95
+ definition?: unknown;
96
+ } | null | undefined): VariantContainer | null;
69
97
  /**
70
98
  * Build a `LayoutSection` that references a saved section component by id
71
99
  * (kywi-cms#69). The resolved definition's columns/style are carried as the
@@ -79,29 +107,181 @@ export declare function buildConnectedSectionNode(component: {
79
107
  type: string;
80
108
  definition?: unknown;
81
109
  } | null | undefined): LayoutSection | null;
110
+ /**
111
+ * Build a `VariantContainer` that references a saved variant-container component
112
+ * by id (kywi-cms#147) — the third member of the `buildConnected*` family.
113
+ *
114
+ * As with a connected section, the resolved definition rides along as the inline
115
+ * fallback copy and `componentId` is stamped so the renderer resolves it live;
116
+ * returns null when the component isn't a resolvable container.
117
+ *
118
+ * The one thing NOT copied is the definition's experiment binding.
119
+ * `applyVariantContainerPlacement` gives the PLACEMENT's `experimentId` /
120
+ * `winnerId` priority precisely because they identify *this page's* experiment
121
+ * run — which arms report where, and which arm has been locked in. Carrying the
122
+ * saved component's binding into a new placement would silently enrol a second
123
+ * page's container in the first page's experiment (`cloneVariantContainer`'s own
124
+ * docstring flags the same hazard for duplication and tells callers to re-bind).
125
+ * So an `ab_test` placement is born with a FRESH `experimentId` and no winner,
126
+ * which is exactly what `WRAP_SECTION_IN_VARIANT_CONTAINER` mints for a
127
+ * hand-built container. A `personalization` container has no binding to mint.
128
+ */
129
+ export declare function buildConnectedVariantContainerNode(component: {
130
+ id: string;
131
+ type: string;
132
+ definition?: unknown;
133
+ } | null | undefined): VariantContainer | null;
134
+ /**
135
+ * How many materialisation passes {@link flattenComponentLinks} will make.
136
+ *
137
+ * WHY IT IS UNREACHABLE, and why it is here anyway (kywi-cms#147 T3 review, M3).
138
+ * Two independent bounds already guarantee convergence:
139
+ *
140
+ * - materialising a RESOLVABLE link strips every link inside what it bakes in
141
+ * (`stripLinksInSection`, called from the placement helpers — deliberately, so
142
+ * neither detach nor pre-resolution can promote a dormant nested link), so a
143
+ * resolvable link can never produce another one. Not even a cyclic definition
144
+ * — `A`'s definition naming `A` — survives one pass;
145
+ * - materialising an UNRESOLVABLE link only reveals what the document already
146
+ * held one level down, and that nesting is bounded by the node kinds
147
+ * themselves: container → arm section → module, with module definitions being
148
+ * leaves. Three passes, worst case, for a linked container.
149
+ *
150
+ * So this is a guard against a future change to those walkers, not an expected
151
+ * limit — and it fails SAFE rather than silently: flattening stops with links
152
+ * still inside, the API refuses the definition, and the owner sees the validator's
153
+ * own words in the dialog. Exported so a test can pin that reasoning instead of
154
+ * trusting it.
155
+ */
156
+ export declare const MAX_FLATTEN_PASSES = 8;
157
+ /**
158
+ * Materialise every component link INSIDE a selection, so what is left is a
159
+ * self-contained tree — the shape "save as component" is allowed to store.
160
+ *
161
+ * WHY THIS IS MANDATORY. `validateComponentDefinition` REFUSES a definition that
162
+ * links to another component (kywi-cms#147, T2 round 1), so saving a section that
163
+ * happens to contain a connected module would just 400. That refusal is not a
164
+ * technicality: a nested link is invisible to `collectComponentRefs` (which stops
165
+ * at a connected node), so the instance index, the usage counts and the delete
166
+ * gate would never see it, and the component could be deleted while a page still
167
+ * rendered it one level down. The flows that want nesting therefore MATERIALISE
168
+ * rather than link — which is this function.
169
+ *
170
+ * HOW. Each pass asks the canonical walker where the links are
171
+ * ({@link collectComponentRefs}) and rewrites each one with the canonical
172
+ * whole-document detach ({@link detachComponentFromLayout}) — the same code the
173
+ * server's detach-all runs — so a materialised link bakes in exactly what the
174
+ * page was rendering through it. A link the caller cannot resolve is materialised
175
+ * from the `unresolvable` source, which drops the link and keeps the node's own
176
+ * inline content: again exactly what the visitor sees today, since an
177
+ * unresolvable link renders its fallback copy.
178
+ *
179
+ * It LOOPS because `collectComponentRefs` deliberately does not descend into a
180
+ * connected section/container: an unresolvable connected section keeps its inline
181
+ * fallback copy, and that copy may itself hold a link which only becomes visible
182
+ * once the outer link is gone. Each pass removes at least one link, so the loop
183
+ * converges; the cap is a belt-and-braces bound, not an expected limit.
184
+ *
185
+ * The result is JSON-round-tripped: `detachComponentFromLayout` is a projection
186
+ * whose nested `props`/`style` still point into the component definitions it read
187
+ * (cached API rows in the editor), and a definition about to be POSTed must not
188
+ * alias them — see `detachedCopy`.
189
+ */
190
+ export declare function flattenComponentLinks<T extends ModuleNode | LayoutSection | VariantContainer>(source: T, resolveLink?: ComponentPlacementLookup): T;
191
+ /**
192
+ * How many component links {@link flattenComponentLinks} would materialise in
193
+ * this selection — the number the save dialog discloses.
194
+ *
195
+ * NOT `collectComponentRefs(...).length` (kywi-cms#147 T3 review, M2): that
196
+ * walker stops at a connected node, so a link hidden in a connected node's
197
+ * inline fallback copy is invisible to it while the flattening finds it on the
198
+ * next pass and rewrites it anyway. Counting one and doing the other means the
199
+ * dialog understates its own blast radius — "1 instance will stop following its
200
+ * source" for a save that actually unhooks two. So the count comes from running
201
+ * the real fixpoint and tallying what it rewrote.
202
+ *
203
+ * The root's OWN link is included when it carries one, so a caller talking about
204
+ * links *inside* the selection subtracts it (the flows that offer this never
205
+ * pass a linked root — saving an instance as a new component is not offered).
206
+ */
207
+ export declare function countComponentLinksToFlatten(source: ModuleNode | LayoutSection | VariantContainer, resolveLink?: ComponentPlacementLookup): number;
82
208
  /**
83
209
  * Convert a connected module node (with componentId) into a disconnected
84
- * inline node. Snapshots the component's current definition into the node.
210
+ * inline node, materialising the component's CURRENT definition.
211
+ *
212
+ * Thin wrapper over the canonical `layout/component-refs.ts` implementation —
213
+ * the same function the server-side detach-all runs — so detaching one instance
214
+ * in the editor and detaching every instance from the API produce byte-identical
215
+ * nodes for the same placement. Two implementations of "what does detaching
216
+ * produce" is how a page silently changes content depending on which button was
217
+ * pressed.
85
218
  *
86
- * If the node has no componentId or component is null, returns the node as-is.
219
+ * A node with no componentId is returned as-is (there is nothing to detach). A
220
+ * missing or unresolvable component drops the link and keeps the node exactly as
221
+ * it renders today, rather than inventing content the page never showed.
87
222
  */
88
- export declare function detachModuleFromComponent(node: ModuleNode, component: KywiComponentRow | null): ModuleNode;
223
+ export declare function detachModuleFromComponent(node: ModuleNode, component: {
224
+ type: string;
225
+ moduleType?: string | null;
226
+ definition?: unknown;
227
+ } | null): ModuleNode;
89
228
  /**
90
229
  * Convert a connected section (with componentId) into a disconnected inline
91
- * section (kywi-cms#69). Drops the link and keeps a deep local copy with fresh
92
- * ids (reuse `cloneSection`) so the detached section is fully editable. v1
93
- * sections are all-or-nothing references the section already carries the
94
- * resolved definition as its inline copy so detach simply strips the link;
95
- * no per-instance prop merge (unlike `detachModuleFromComponent`).
230
+ * section (kywi-cms#69), materialising the component's CURRENT definition.
231
+ *
232
+ * The section's inline `columns` are only the stale fallback copy the renderer
233
+ * shows when the link cannot be resolved; strict-link semantics say the live
234
+ * definition is what the page is actually rendering, so that is what detaching
235
+ * has to bake in — re-keyed off this section's own id exactly as the renderer
236
+ * re-keys it. Hence the `component` argument, and hence the delegation to the
237
+ * canonical `layout/component-refs.ts` implementation shared with the server.
238
+ *
239
+ * A section with no componentId is returned as-is; an unresolvable component
240
+ * degrades to dropping the link and keeping the inline copy, which is once again
241
+ * exactly what the page was rendering.
242
+ */
243
+ export declare function detachSectionFromComponent(section: LayoutSection, component: {
244
+ type: string;
245
+ definition?: unknown;
246
+ } | null): LayoutSection;
247
+ /**
248
+ * Convert a connected variant container into a disconnected inline one
249
+ * (kywi-cms#147), materialising the component's CURRENT definition — the
250
+ * container counterpart of {@link detachSectionFromComponent}, delegating to the
251
+ * same canonical implementation the server-side detach-all runs.
96
252
  *
97
- * If the section has no componentId, returns it as-is.
253
+ * The placement's own `experimentId`/`winnerId` survive the materialisation:
254
+ * they bind this page's container to this page's experiment run, which detaching
255
+ * must not silently re-point at whatever the component was saved with.
98
256
  */
99
- export declare function detachSectionFromComponent(section: LayoutSection): LayoutSection;
257
+ export declare function detachVariantContainerFromComponent(container: VariantContainer, component: {
258
+ type: string;
259
+ definition?: unknown;
260
+ } | null): VariantContainer;
261
+ /** Options for {@link saveAsComponent}. */
262
+ export interface SaveAsComponentOptions {
263
+ description?: string;
264
+ /**
265
+ * Resolve a component id to its stored definition, for materialising links
266
+ * INSIDE the selection ({@link flattenComponentLinks}). Every editor caller
267
+ * passes the loaded component library; a caller that omits it still cannot
268
+ * produce a definition the API refuses — an unresolved link is dropped and the
269
+ * node keeps its own inline content, which is what an unresolvable link renders
270
+ * anyway — but it loses the chance to bake in the linked content.
271
+ */
272
+ resolveLink?: ComponentPlacementLookup;
273
+ }
100
274
  /**
101
- * Build a component definition input from a module node or section.
102
- * This can then be passed to `createComponent()` from the DB layer.
275
+ * Build a component definition input from a module node, a section, or a
276
+ * section-level variant container (kywi-cms#147 adds the third).
277
+ * This can then be POSTed to `/components` (or passed to `createComponent()`).
278
+ *
279
+ * Any component link inside the selection is materialised first
280
+ * ({@link flattenComponentLinks}) — the API refuses a definition that links to
281
+ * another component, so this is not an optimisation but the difference between a
282
+ * save that works and a 400.
103
283
  */
104
- export declare function saveAsComponent(source: ModuleNode | LayoutSection, name: string, description?: string): ComponentDefinitionInput;
284
+ export declare function saveAsComponent(source: ModuleNode | LayoutSection | VariantContainer, name: string, options?: SaveAsComponentOptions): ComponentDefinitionInput;
105
285
  /**
106
286
  * Build a section template input from a layout section.
107
287
  * Templates are stored in `kywi_section_templates` and reused across pages.
@@ -115,13 +295,30 @@ export declare function cloneModule(node: ModuleNode): ModuleNode;
115
295
  * inserting or pasting a linked section keeps its live reference (kywi-cms#69).
116
296
  */
117
297
  export declare function cloneSection(section: LayoutSection): LayoutSection;
298
+ /**
299
+ * Deep-clone a variant container with fresh ids for the container, its variant
300
+ * arms and every section beneath them.
301
+ *
302
+ * A connected container's `componentId` is preserved, mirroring `cloneSection` /
303
+ * `cloneModule`: duplicating or pasting a linked container gives a second
304
+ * PLACEMENT of the same component, not a copy that has quietly stopped
305
+ * following it. The inline arms come along as the fallback copy, which is what
306
+ * keeps the duplicate rendering when the link can't be resolved.
307
+ *
308
+ * `mode` and the experiment binding are copied verbatim, so a duplicated
309
+ * `ab_test` container reports to the SAME experiment as its original. That is
310
+ * the honest copy semantic — two containers genuinely feeding one experiment —
311
+ * but it is rarely what a user wants, so callers offering a duplicate action on
312
+ * an A/B container should re-bind `experimentId` afterwards.
313
+ */
314
+ export declare function cloneVariantContainer(container: VariantContainer): VariantContainer;
118
315
  /**
119
316
  * Serialize a module or section for clipboard storage (e.g. sessionStorage).
120
317
  */
121
- export declare function copyToClipboard(kind: 'module' | 'section', source: ModuleNode | LayoutSection): ClipboardPayload;
318
+ export declare function copyToClipboard(kind: ClipboardPayload['kind'], source: ModuleNode | LayoutSection | VariantContainer): ClipboardPayload;
122
319
  /**
123
320
  * Deserialize a clipboard payload and return a fresh clone with new IDs.
124
321
  * Returns null if the data is corrupted.
125
322
  */
126
- export declare function pasteFromClipboard(payload: ClipboardPayload): ModuleNode | LayoutSection | null;
323
+ export declare function pasteFromClipboard(payload: ClipboardPayload): ModuleNode | LayoutSection | VariantContainer | null;
127
324
  //# sourceMappingURL=component-actions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"component-actions.d.ts","sourceRoot":"","sources":["../../../src/admin/layout-editor/component-actions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"component-actions.d.ts","sourceRoot":"","sources":["../../../src/admin/layout-editor/component-actions.ts"],"names":[],"mappings":"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"}