@kywi-software/core 0.10.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 (248) hide show
  1. package/AGENT-PATTERNS.md +111 -23
  2. package/README.md +67 -0
  3. package/dist/admin/content-form.d.ts.map +1 -1
  4. package/dist/admin/content-form.js +97 -10
  5. package/dist/admin/content-form.js.map +1 -1
  6. package/dist/admin/layout-editor/component-actions.d.ts +219 -22
  7. package/dist/admin/layout-editor/component-actions.d.ts.map +1 -1
  8. package/dist/admin/layout-editor/component-actions.js +356 -36
  9. package/dist/admin/layout-editor/component-actions.js.map +1 -1
  10. package/dist/admin/layout-editor/component-fork.d.ts +224 -0
  11. package/dist/admin/layout-editor/component-fork.d.ts.map +1 -0
  12. package/dist/admin/layout-editor/component-fork.js +342 -0
  13. package/dist/admin/layout-editor/component-fork.js.map +1 -0
  14. package/dist/admin/layout-editor/editor-canvas.d.ts +28 -7
  15. package/dist/admin/layout-editor/editor-canvas.d.ts.map +1 -1
  16. package/dist/admin/layout-editor/editor-canvas.js +145 -32
  17. package/dist/admin/layout-editor/editor-canvas.js.map +1 -1
  18. package/dist/admin/layout-editor/editor-dialog.d.ts +164 -0
  19. package/dist/admin/layout-editor/editor-dialog.d.ts.map +1 -0
  20. package/dist/admin/layout-editor/editor-dialog.js +195 -0
  21. package/dist/admin/layout-editor/editor-dialog.js.map +1 -0
  22. package/dist/admin/layout-editor/editor-state.d.ts +76 -0
  23. package/dist/admin/layout-editor/editor-state.d.ts.map +1 -1
  24. package/dist/admin/layout-editor/editor-state.js +47 -1
  25. package/dist/admin/layout-editor/editor-state.js.map +1 -1
  26. package/dist/admin/layout-editor/editor.css +366 -4
  27. package/dist/admin/layout-editor/index.d.ts +10 -1
  28. package/dist/admin/layout-editor/index.d.ts.map +1 -1
  29. package/dist/admin/layout-editor/index.js +11 -1
  30. package/dist/admin/layout-editor/index.js.map +1 -1
  31. package/dist/admin/layout-editor/inline-text.d.ts +56 -4
  32. package/dist/admin/layout-editor/inline-text.d.ts.map +1 -1
  33. package/dist/admin/layout-editor/inline-text.js +134 -11
  34. package/dist/admin/layout-editor/inline-text.js.map +1 -1
  35. package/dist/admin/layout-editor/layout-editor.d.ts +58 -1
  36. package/dist/admin/layout-editor/layout-editor.d.ts.map +1 -1
  37. package/dist/admin/layout-editor/layout-editor.js +160 -190
  38. package/dist/admin/layout-editor/layout-editor.js.map +1 -1
  39. package/dist/admin/layout-editor/linked-component.d.ts +39 -0
  40. package/dist/admin/layout-editor/linked-component.d.ts.map +1 -0
  41. package/dist/admin/layout-editor/linked-component.js +42 -0
  42. package/dist/admin/layout-editor/linked-component.js.map +1 -0
  43. package/dist/admin/layout-editor/module-overlay.d.ts +36 -14
  44. package/dist/admin/layout-editor/module-overlay.d.ts.map +1 -1
  45. package/dist/admin/layout-editor/module-overlay.js +233 -25
  46. package/dist/admin/layout-editor/module-overlay.js.map +1 -1
  47. package/dist/admin/layout-editor/node-lookup.d.ts +35 -0
  48. package/dist/admin/layout-editor/node-lookup.d.ts.map +1 -1
  49. package/dist/admin/layout-editor/node-lookup.js +91 -0
  50. package/dist/admin/layout-editor/node-lookup.js.map +1 -1
  51. package/dist/admin/layout-editor/overlay-shell.d.ts.map +1 -1
  52. package/dist/admin/layout-editor/overlay-shell.js +150 -32
  53. package/dist/admin/layout-editor/overlay-shell.js.map +1 -1
  54. package/dist/admin/layout-editor/props-panel.d.ts +54 -1
  55. package/dist/admin/layout-editor/props-panel.d.ts.map +1 -1
  56. package/dist/admin/layout-editor/props-panel.js +119 -10
  57. package/dist/admin/layout-editor/props-panel.js.map +1 -1
  58. package/dist/admin/layout-editor/section-overlay.d.ts +16 -1
  59. package/dist/admin/layout-editor/section-overlay.d.ts.map +1 -1
  60. package/dist/admin/layout-editor/section-overlay.js +21 -11
  61. package/dist/admin/layout-editor/section-overlay.js.map +1 -1
  62. package/dist/admin/layout-editor/section-template-picker.d.ts +27 -1
  63. package/dist/admin/layout-editor/section-template-picker.d.ts.map +1 -1
  64. package/dist/admin/layout-editor/section-template-picker.js +71 -17
  65. package/dist/admin/layout-editor/section-template-picker.js.map +1 -1
  66. package/dist/admin/layout-editor/section-variant-container-config.js +5 -5
  67. package/dist/admin/layout-editor/section-variant-container-config.js.map +1 -1
  68. package/dist/admin/layout-editor/use-chrome-flip.d.ts +111 -11
  69. package/dist/admin/layout-editor/use-chrome-flip.d.ts.map +1 -1
  70. package/dist/admin/layout-editor/use-chrome-flip.js +233 -33
  71. package/dist/admin/layout-editor/use-chrome-flip.js.map +1 -1
  72. package/dist/admin/layout-editor/use-component-connect.d.ts +61 -0
  73. package/dist/admin/layout-editor/use-component-connect.d.ts.map +1 -0
  74. package/dist/admin/layout-editor/use-component-connect.js +292 -0
  75. package/dist/admin/layout-editor/use-component-connect.js.map +1 -0
  76. package/dist/admin/layout-editor/use-component-fork.d.ts +123 -0
  77. package/dist/admin/layout-editor/use-component-fork.d.ts.map +1 -0
  78. package/dist/admin/layout-editor/use-component-fork.js +346 -0
  79. package/dist/admin/layout-editor/use-component-fork.js.map +1 -0
  80. package/dist/admin/layout-editor/use-component-library.d.ts +94 -0
  81. package/dist/admin/layout-editor/use-component-library.d.ts.map +1 -0
  82. package/dist/admin/layout-editor/use-component-library.js +55 -0
  83. package/dist/admin/layout-editor/use-component-library.js.map +1 -0
  84. package/dist/admin/registry.d.ts +11 -2
  85. package/dist/admin/registry.d.ts.map +1 -1
  86. package/dist/admin/registry.js +40 -4
  87. package/dist/admin/registry.js.map +1 -1
  88. package/dist/admin/server.d.ts +1 -0
  89. package/dist/admin/server.d.ts.map +1 -1
  90. package/dist/admin/server.js +5 -0
  91. package/dist/admin/server.js.map +1 -1
  92. package/dist/admin/styles/admin.css +57 -1
  93. package/dist/admin/surfaces/components/component-edit.d.ts +3 -0
  94. package/dist/admin/surfaces/components/component-edit.d.ts.map +1 -0
  95. package/dist/admin/surfaces/components/component-edit.js +235 -0
  96. package/dist/admin/surfaces/components/component-edit.js.map +1 -0
  97. package/dist/admin/surfaces/components/components-data.d.ts +153 -0
  98. package/dist/admin/surfaces/components/components-data.d.ts.map +1 -0
  99. package/dist/admin/surfaces/components/components-data.js +154 -0
  100. package/dist/admin/surfaces/components/components-data.js.map +1 -0
  101. package/dist/admin/surfaces/components/components-list.d.ts +3 -0
  102. package/dist/admin/surfaces/components/components-list.d.ts.map +1 -0
  103. package/dist/admin/surfaces/components/components-list.js +196 -0
  104. package/dist/admin/surfaces/components/components-list.js.map +1 -0
  105. package/dist/admin/surfaces/components/definition-canvas.d.ts +73 -0
  106. package/dist/admin/surfaces/components/definition-canvas.d.ts.map +1 -0
  107. package/dist/admin/surfaces/components/definition-canvas.js +207 -0
  108. package/dist/admin/surfaces/components/definition-canvas.js.map +1 -0
  109. package/dist/admin/surfaces/layouts/layout-editor-client.d.ts.map +1 -1
  110. package/dist/admin/surfaces/layouts/layout-editor-client.js +14 -2
  111. package/dist/admin/surfaces/layouts/layout-editor-client.js.map +1 -1
  112. package/dist/admin/surfaces/layouts/layouts-data.d.ts +3 -14
  113. package/dist/admin/surfaces/layouts/layouts-data.d.ts.map +1 -1
  114. package/dist/admin/surfaces/layouts/layouts-data.js +4 -7
  115. package/dist/admin/surfaces/layouts/layouts-data.js.map +1 -1
  116. package/dist/admin/surfaces/layouts/layouts-list.d.ts.map +1 -1
  117. package/dist/admin/surfaces/layouts/layouts-list.js +16 -22
  118. package/dist/admin/surfaces/layouts/layouts-list.js.map +1 -1
  119. package/dist/admin/surfaces/settings/admin-features.d.ts.map +1 -1
  120. package/dist/admin/surfaces/settings/admin-features.js +1 -0
  121. package/dist/admin/surfaces/settings/admin-features.js.map +1 -1
  122. package/dist/api/bulk-routes.d.ts.map +1 -1
  123. package/dist/api/bulk-routes.js +25 -0
  124. package/dist/api/bulk-routes.js.map +1 -1
  125. package/dist/api/changeset-routes.d.ts.map +1 -1
  126. package/dist/api/changeset-routes.js +17 -3
  127. package/dist/api/changeset-routes.js.map +1 -1
  128. package/dist/api/component-routes.d.ts.map +1 -1
  129. package/dist/api/component-routes.js +228 -8
  130. package/dist/api/component-routes.js.map +1 -1
  131. package/dist/api/content-routes.d.ts.map +1 -1
  132. package/dist/api/content-routes.js +64 -0
  133. package/dist/api/content-routes.js.map +1 -1
  134. package/dist/api/feed-routes.d.ts +6 -6
  135. package/dist/api/graphql/resolvers.d.ts +4 -4
  136. package/dist/api/layout-routes.d.ts.map +1 -1
  137. package/dist/api/layout-routes.js +4 -17
  138. package/dist/api/layout-routes.js.map +1 -1
  139. package/dist/api/layout-write-guard.d.ts +80 -0
  140. package/dist/api/layout-write-guard.d.ts.map +1 -0
  141. package/dist/api/layout-write-guard.js +88 -0
  142. package/dist/api/layout-write-guard.js.map +1 -0
  143. package/dist/api/section-template-routes.d.ts.map +1 -1
  144. package/dist/api/section-template-routes.js +11 -0
  145. package/dist/api/section-template-routes.js.map +1 -1
  146. package/dist/api/translation-routes.d.ts.map +1 -1
  147. package/dist/api/translation-routes.js +8 -0
  148. package/dist/api/translation-routes.js.map +1 -1
  149. package/dist/api/version-routes.d.ts.map +1 -1
  150. package/dist/api/version-routes.js +20 -8
  151. package/dist/api/version-routes.js.map +1 -1
  152. package/dist/api/workflow-routes.d.ts.map +1 -1
  153. package/dist/api/workflow-routes.js +53 -8
  154. package/dist/api/workflow-routes.js.map +1 -1
  155. package/dist/db/analytics-queries.d.ts +3 -3
  156. package/dist/db/apply-snapshot.d.ts +75 -8
  157. package/dist/db/apply-snapshot.d.ts.map +1 -1
  158. package/dist/db/apply-snapshot.js +104 -8
  159. package/dist/db/apply-snapshot.js.map +1 -1
  160. package/dist/db/audit-queries.d.ts +12 -12
  161. package/dist/db/category-queries.d.ts +3 -3
  162. package/dist/db/changeset-audit-queries.d.ts +4 -4
  163. package/dist/db/changeset-queries.d.ts +7 -7
  164. package/dist/db/comment-queries.d.ts +8 -8
  165. package/dist/db/comment-subscription-queries.d.ts +2 -2
  166. package/dist/db/component-instance-queries.d.ts +197 -0
  167. package/dist/db/component-instance-queries.d.ts.map +1 -0
  168. package/dist/db/component-instance-queries.js +405 -0
  169. package/dist/db/component-instance-queries.js.map +1 -0
  170. package/dist/db/content-queries.d.ts.map +1 -1
  171. package/dist/db/content-queries.js +39 -2
  172. package/dist/db/content-queries.js.map +1 -1
  173. package/dist/db/extension-queries.d.ts +7 -7
  174. package/dist/db/feed-queries.d.ts +3 -3
  175. package/dist/db/index.d.ts +1 -0
  176. package/dist/db/index.d.ts.map +1 -1
  177. package/dist/db/index.js +1 -0
  178. package/dist/db/index.js.map +1 -1
  179. package/dist/db/layout-queries.d.ts +13 -1
  180. package/dist/db/layout-queries.d.ts.map +1 -1
  181. package/dist/db/layout-queries.js +52 -7
  182. package/dist/db/layout-queries.js.map +1 -1
  183. package/dist/db/locale-queries.d.ts.map +1 -1
  184. package/dist/db/locale-queries.js +0 -0
  185. package/dist/db/locale-queries.js.map +1 -1
  186. package/dist/db/media-queries.d.ts +12 -12
  187. package/dist/db/revision-queries.d.ts +3 -3
  188. package/dist/db/site-admin-queries.d.ts +2 -2
  189. package/dist/db/site-membership-queries.d.ts +3 -3
  190. package/dist/db/site-plugin-queries.d.ts +2 -2
  191. package/dist/db/site-queries.d.ts +6 -6
  192. package/dist/db/version-queries.d.ts +12 -12
  193. package/dist/db/webhook-queries.d.ts +11 -11
  194. package/dist/db/workflow-queries.d.ts +6 -6
  195. package/dist/db/workflow-queries.d.ts.map +1 -1
  196. package/dist/db/workflow-queries.js +11 -2
  197. package/dist/db/workflow-queries.js.map +1 -1
  198. package/dist/layout/comments-module.d.ts.map +1 -1
  199. package/dist/layout/comments-module.js +6 -2
  200. package/dist/layout/comments-module.js.map +1 -1
  201. package/dist/layout/component-refs.d.ts +465 -0
  202. package/dist/layout/component-refs.d.ts.map +1 -0
  203. package/dist/layout/component-refs.js +848 -0
  204. package/dist/layout/component-refs.js.map +1 -0
  205. package/dist/layout/index.d.ts +2 -1
  206. package/dist/layout/index.d.ts.map +1 -1
  207. package/dist/layout/index.js +1 -0
  208. package/dist/layout/index.js.map +1 -1
  209. package/dist/layout/module-permissions.d.ts +49 -1
  210. package/dist/layout/module-permissions.d.ts.map +1 -1
  211. package/dist/layout/module-permissions.js +117 -6
  212. package/dist/layout/module-permissions.js.map +1 -1
  213. package/dist/layout/module-render-context.d.ts +32 -0
  214. package/dist/layout/module-render-context.d.ts.map +1 -1
  215. package/dist/layout/module-render-context.js +32 -1
  216. package/dist/layout/module-render-context.js.map +1 -1
  217. package/dist/layout/modules-extended.d.ts.map +1 -1
  218. package/dist/layout/modules-extended.js +21 -11
  219. package/dist/layout/modules-extended.js.map +1 -1
  220. package/dist/layout/modules.d.ts.map +1 -1
  221. package/dist/layout/modules.js +15 -10
  222. package/dist/layout/modules.js.map +1 -1
  223. package/dist/layout/renderer.d.ts +51 -4
  224. package/dist/layout/renderer.d.ts.map +1 -1
  225. package/dist/layout/renderer.js +36 -38
  226. package/dist/layout/renderer.js.map +1 -1
  227. package/dist/layout/types.d.ts +42 -0
  228. package/dist/layout/types.d.ts.map +1 -1
  229. package/dist/layout/types.js +54 -11
  230. package/dist/layout/types.js.map +1 -1
  231. package/dist/schema/base.d.ts +1 -1
  232. package/dist/schema/components.d.ts +150 -0
  233. package/dist/schema/components.d.ts.map +1 -1
  234. package/dist/schema/components.js +50 -4
  235. package/dist/schema/components.js.map +1 -1
  236. package/dist/scope/inline-editing.d.ts.map +1 -1
  237. package/dist/scope/inline-editing.js +10 -0
  238. package/dist/scope/inline-editing.js.map +1 -1
  239. package/dist/scope/sticky-offset.d.ts +185 -0
  240. package/dist/scope/sticky-offset.d.ts.map +1 -0
  241. package/dist/scope/sticky-offset.js +432 -0
  242. package/dist/scope/sticky-offset.js.map +1 -0
  243. package/dist/site/styles.css +41 -3
  244. package/dist/versioning/scheduler.d.ts +8 -0
  245. package/dist/versioning/scheduler.d.ts.map +1 -1
  246. package/dist/versioning/scheduler.js +21 -7
  247. package/dist/versioning/scheduler.js.map +1 -1
  248. package/package.json +1 -1
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * inline-text.ts
3
- * Which modules can be text-edited directly on the page, and how to read the
4
- * edited value back out of the DOM (kywi-cms#94).
3
+ * Which modules can be text-edited directly on the page, WHICH ELEMENT each
4
+ * edit binds to, and how to read the edited value back out of the DOM
5
+ * (kywi-cms#94, kywi-cms#145).
5
6
  *
6
7
  * The front-of-site editor makes a text-bearing module's own rendered element
7
8
  * `contenteditable` — the real `<h2>`, `<p>` or rich-text `<div>` the visitor
@@ -9,16 +10,43 @@
9
10
  * Committing writes the SAME prop the props panel writes, through the SAME
10
11
  * `UPDATE_PROPS` action, so there is one save path and undo/redo just work.
11
12
  */
13
+ import { INLINE_PROP_ATTR } from '../../layout/module-render-context.js';
12
14
  import { sanitizeHtml } from '../../util/sanitize.js';
13
15
  /** Prop types that hold editable copy. */
14
16
  const TEXT_PROP_TYPES = new Set(['text', 'textarea', 'richText']);
15
17
  /**
16
- * Modules whose first prop happens to be text-typed but must never be edited by
17
- * typing on the page: `html`'s `code` prop is a raw-HTML escape hatch (admin
18
- * only, see ADMIN_ONLY_MODULE_TYPES) where contenteditable would mangle the
19
- * source the author is deliberately hand-writing.
18
+ * Modules whose text-typed props must never be edited by typing on the page,
19
+ * because what the module RENDERS is not what the prop HOLDS.
20
+ *
21
+ * `html`/`embed` hold raw source an author is deliberately hand-writing;
22
+ * `tabs`/`carousel`/`tagCloud`/`breadcrumbs` hold JSON; `spacer` holds a CSS
23
+ * length; `feedDisplay`/`categoryList` hold a slug/id. Each of those is a
24
+ * single-text-prop module, so without this list rule 3 below would keep
25
+ * offering them the legacy wrapper binding — and a commit would serialize the
26
+ * module's rendered text over the JSON/length/slug the prop actually holds
27
+ * (kywi-cms#145: the same silent corruption, one class out). Double-click on
28
+ * these now routes to the props-rail fallback (kywi-cms#118) instead.
29
+ *
30
+ * `container` and `columns` are listed for INTENT, not because they would
31
+ * otherwise slip through: `container` declares two text props (`maxWidth`,
32
+ * `padding`) so rule 3 already rejects it, and `columns` leads with a `select`
33
+ * so prop resolution already rejects it. They are here so that a later prop
34
+ * edit to either — dropping one of the container's two, reordering the columns
35
+ * — cannot quietly re-open a hole.
20
36
  */
21
- const NOT_INLINE_EDITABLE = new Set(['html']);
37
+ const NOT_INLINE_EDITABLE = new Set([
38
+ 'html',
39
+ 'embed',
40
+ 'tabs',
41
+ 'carousel',
42
+ 'tagCloud',
43
+ 'breadcrumbs',
44
+ 'container',
45
+ 'columns',
46
+ 'spacer',
47
+ 'feedDisplay',
48
+ 'categoryList',
49
+ ]);
22
50
  /**
23
51
  * The module's *primary* text prop, or null when it has none.
24
52
  *
@@ -42,11 +70,105 @@ export function resolveInlineTextProp(config) {
42
70
  return null;
43
71
  return { name, html: definition.type === 'richText' };
44
72
  }
73
+ /**
74
+ * EVERY text-typed prop a module declares, in declaration order.
75
+ *
76
+ * This is the config-side half of the #145 safety guard: a module with more than
77
+ * one of these can never be bound at the wrapper, because the wrapper renders
78
+ * all of them and a commit would concatenate them into whichever one was bound.
79
+ */
80
+ export function listInlineTextProps(config) {
81
+ if (!config)
82
+ return [];
83
+ if (NOT_INLINE_EDITABLE.has(config.name))
84
+ return [];
85
+ const entries = Object.entries(config.props ?? {});
86
+ return entries
87
+ .filter(([, definition]) => TEXT_PROP_TYPES.has(definition.type))
88
+ .map(([name, definition]) => ({ name, html: definition.type === 'richText' }));
89
+ }
90
+ /** The prop anchors inside `wrapper` that name a text prop this module declares. */
91
+ function anchorsWithin(wrapper, byName) {
92
+ const found = [];
93
+ // `Array.from`, not `for…of`: the package's `lib` is ["ES2022", "DOM"]
94
+ // without "DOM.Iterable".
95
+ for (const el of Array.from(wrapper.querySelectorAll(`[${INLINE_PROP_ATTR}]`))) {
96
+ const prop = byName.get(el.getAttribute(INLINE_PROP_ATTR) ?? '');
97
+ if (prop)
98
+ found.push({ element: el, prop });
99
+ }
100
+ return found;
101
+ }
102
+ /**
103
+ * Which element + prop a double-click inside `wrapper` binds — or null when
104
+ * there is nothing safe to bind, in which case the caller routes the gesture to
105
+ * the props-rail fallback (kywi-cms#118) rather than to the wrapper.
106
+ *
107
+ * The rules, in order (kywi-cms#145):
108
+ *
109
+ * 1. The anchor the gesture actually landed in. Double-clicking a card's body
110
+ * edits the body, its footer edits the footer — the prop the owner pointed
111
+ * at, not "the module's first prop".
112
+ * 2. Otherwise, an unambiguous single anchor in the module: a gesture anywhere
113
+ * on a module with only one editable element can only have meant that one.
114
+ * Two or more anchors and a gesture that landed on neither is genuinely
115
+ * ambiguous → fallback.
116
+ * 3. Otherwise (a custom module with no anchors at all): the legacy whole-
117
+ * wrapper binding, and ONLY when the module declares exactly one text prop —
118
+ * the case where "everything the wrapper renders" and "the bound prop" cannot
119
+ * disagree. This is what keeps heading/text/richText/button working for
120
+ * module packs that never adopt the anchors.
121
+ *
122
+ * Anything else fails closed. A wrapper binding on a multi-text-prop module is
123
+ * the #145 corruption itself, so it is not reachable from here by any path.
124
+ */
125
+ export function resolveInlineBinding(config, wrapper, target) {
126
+ const props = listInlineTextProps(config);
127
+ if (props.length === 0)
128
+ return null;
129
+ const byName = new Map(props.map(p => [p.name, p]));
130
+ // 1 — the anchor under the pointer.
131
+ if (target && (target === wrapper || wrapper.contains(target))) {
132
+ const anchor = target.closest(`[${INLINE_PROP_ATTR}]`);
133
+ if (anchor && anchor !== wrapper && wrapper.contains(anchor)) {
134
+ const prop = byName.get(anchor.getAttribute(INLINE_PROP_ATTR) ?? '');
135
+ if (prop)
136
+ return { prop, element: anchor, kind: 'anchor' };
137
+ }
138
+ }
139
+ // 2 — exactly one anchored prop in the whole module.
140
+ const anchors = anchorsWithin(wrapper, byName);
141
+ if (anchors.length === 1) {
142
+ const only = anchors[0];
143
+ return { prop: only.prop, element: only.element, kind: 'anchor' };
144
+ }
145
+ if (anchors.length > 1)
146
+ return null;
147
+ // 3 — legacy wrapper binding, single-text-prop modules only.
148
+ //
149
+ // COUPLING (kywi-cms#145 review): this rule's safety is not `props.length === 1`
150
+ // alone — it also rests on `resolveInlineTextProp`'s "first declared prop must
151
+ // be text" rule. That is what keeps `divider` (color, then a `thickness` CSS
152
+ // length), `countdown` (targetDate, then `label`), `navMenu` (menuSlug, …) and
153
+ // `component` (slug, then `componentId`) out of wrapper binding even though
154
+ // each declares exactly one text-typed prop: the wrapper renders a computed
155
+ // value, not that prop. Reordering any module's props would silently reopen
156
+ // wrapper binding for it, so the exact wrapper-bindable set is pinned by an
157
+ // invariant test over the whole registry in `__tests__/inline-text.test.tsx`.
158
+ const primary = resolveInlineTextProp(config);
159
+ if (primary && props.length === 1)
160
+ return { prop: primary, element: wrapper, kind: 'wrapper' };
161
+ return null;
162
+ }
45
163
  /**
46
164
  * Read the edited value back out of an editing host.
47
165
  *
48
- * The host is the module's wrapper element, which also carries the editor's own
49
- * floating chrome (type chip, action cluster). Chrome is marked with
166
+ * For an anchored host (`data-kywi-prop`) the host IS the prop's element — it
167
+ * renders that prop's copy and nothing else, editor chrome included so its
168
+ * content is the value, directly.
169
+ *
170
+ * For the legacy wrapper host the host also carries the editor's own floating
171
+ * chrome (type chip, action cluster). Chrome is marked with
50
172
  * `data-kywi-editor-chrome` and skipped, so the value comes from the module's
51
173
  * own rendered element — `innerHTML` for a rich-text prop, `textContent` for a
52
174
  * plain one.
@@ -56,8 +178,9 @@ export function resolveInlineTextProp(config) {
56
178
  * shape the public page would strip anyway.
57
179
  */
58
180
  export function readInlineTextValue(host, html) {
59
- const content = Array.from(host.children).find((el) => !el.hasAttribute('data-kywi-editor-chrome'));
60
- const target = content ?? host;
181
+ const target = host.hasAttribute(INLINE_PROP_ATTR)
182
+ ? host
183
+ : (Array.from(host.children).find((el) => !el.hasAttribute('data-kywi-editor-chrome')) ?? host);
61
184
  if (html)
62
185
  return sanitizeHtml(target.innerHTML);
63
186
  // contenteditable leaves non-breaking spaces behind where the browser padded
@@ -1 +1 @@
1
- {"version":3,"file":"inline-text.js","sourceRoot":"","sources":["../../../src/admin/layout-editor/inline-text.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAUrD,0CAA0C;AAC1C,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAA;AAEjE;;;;;GAKG;AACH,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;AAE7C;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAuC;IAC3E,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IACxB,IAAI,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IACrD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAA0C,CAAA;IAC3F,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IACxB,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IACvB,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,GAAG,KAAK,CAAA;IAChC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IACtD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,KAAK,UAAU,EAAE,CAAA;AACvD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAiB,EAAE,IAAa;IAClE,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAC5C,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,yBAAyB,CAAC,CACzB,CAAA;IAC5B,MAAM,MAAM,GAAG,OAAO,IAAI,IAAI,CAAA;IAC9B,IAAI,IAAI;QAAE,OAAO,YAAY,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;IAC/C,6EAA6E;IAC7E,4EAA4E;IAC5E,OAAO,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;AAClE,CAAC"}
1
+ {"version":3,"file":"inline-text.js","sourceRoot":"","sources":["../../../src/admin/layout-editor/inline-text.ts"],"names":[],"mappings":"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"}
@@ -35,7 +35,64 @@ interface LayoutEditorProps {
35
35
  onChange?: (layout: LayoutDocument) => void;
36
36
  /** Current user's role — gates admin-only modules in the palette. */
37
37
  userRole?: string;
38
+ /**
39
+ * WHAT THE DOCUMENT ON THIS CANVAS ACTUALLY IS. Defaults to `'page'` — a real
40
+ * piece of content, where every component affordance means what it says.
41
+ *
42
+ * The other two values are canvases where the document is NOT a page, and the
43
+ * component affordances are withheld for the same underlying reason: everything
44
+ * component-shaped this editor offers is about a PLACEMENT — "save this bit of
45
+ * the page as a component", "point this placeholder at a component", "drop a
46
+ * linked copy in here" — and neither of these canvases has placements.
47
+ *
48
+ * - `'definition'` — the document IS a component definition (kywi-cms#147 T5
49
+ * review, I1). Set by the admin `components.edit` surface, which seeds a
50
+ * one-node document out of a stored definition and PUTs it back.
51
+ * - `'blueprint'` — the document is a saved layout / section blueprint
52
+ * (kywi-cms#147 final review, M2). Set by the standalone Layouts editor,
53
+ * which writes `kywi_section_templates` rows.
54
+ *
55
+ * WHY THE DEFINITION CANVAS HAD TO KNOW. Left unsaid, the seeded definition node
56
+ * looked like an ordinary unlinked section to the rail and to the insert picker,
57
+ * which offered all three affordances; and the save fork, which exists to ask
58
+ * "does this edit belong to the component or to this page?", would ask a question
59
+ * with no second answer. The worst of it was reachable in three clicks: link a
60
+ * module inside the definition to some OTHER component Z, edit it, press Save.
61
+ * The fork saw an edited instance of Z, PUT Z's definition site-wide — published
62
+ * pages included — and only then did the definition extract refuse the nested
63
+ * link, so the save the owner actually asked for never happened. A write nobody
64
+ * requested landed; the requested one did not.
65
+ *
66
+ * WHY THE BLUEPRINT CANVAS DOES TOO. A blueprint is deliberately not indexed
67
+ * (the reasoning is at the decision site in `api/section-template-routes.ts`):
68
+ * `kywi_component_instances` tracks placements in CONTENT, and a blueprint is a
69
+ * stencil, not a page. So a component minted here, and linked from the blueprint
70
+ * it was minted out of, is a link nothing can see — its usage count reads "0
71
+ * instances across 0 pages", and the delete confirmation says so to the owner's
72
+ * face while a blueprint still renders it. Every claim the feature makes about
73
+ * blast radius is a claim about content. Saving a component out of a blueprint is
74
+ * a coherent thing to want, and it needs the index to reach blueprints first; it
75
+ * is not something to leave half-working with honest-looking copy on top.
76
+ *
77
+ * In both non-page modes:
78
+ * - "Save as component" (module, section and container), the props rail's
79
+ * component picker and the insert picker's component rows are all WITHHELD —
80
+ * the same shape as the linked-ancestor withdrawal (T3 review I1), for the
81
+ * same reason: an action that cannot cohere is not offered;
82
+ * - the save fork is INERT ({@link useComponentFork}'s `inert`) — a definition
83
+ * save IS the propagation, and a blueprint save writes a stencil, so in
84
+ * neither case is there a "this page" to fork against;
85
+ * - everything else — palette, DnD, props and style editing, undo/redo — works
86
+ * exactly as it does on a page. The one palette entry that goes is the
87
+ * `component` placeholder itself, which here could only ever be a node
88
+ * pointing at nothing (see the filter below).
89
+ *
90
+ * Detach is deliberately KEPT in both: a definition stored before the nested-link
91
+ * refusal, or a blueprint saved from a linked section, can still hold a link, and
92
+ * detaching it is how the owner resolves it.
93
+ */
94
+ canvasKind?: 'page' | 'definition' | 'blueprint';
38
95
  }
39
- export declare function LayoutEditor({ initialLayout, moduleRegistry, moduleComponents, onSave, onCancel, audiences, onChange, userRole, }: LayoutEditorProps): import("react/jsx-runtime").JSX.Element;
96
+ export declare function LayoutEditor({ initialLayout, moduleRegistry, moduleComponents, onSave, onCancel, audiences, onChange, userRole, canvasKind, }: LayoutEditorProps): import("react/jsx-runtime").JSX.Element;
40
97
  export {};
41
98
  //# sourceMappingURL=layout-editor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"layout-editor.d.ts","sourceRoot":"","sources":["../../../src/admin/layout-editor/layout-editor.tsx"],"names":[],"mappings":"AACA,OAAO,KAA+D,MAAM,OAAO,CAAA;AAEnF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAmB9D,OAAO,cAAc,CAAA;AAarB,UAAU,iBAAiB;IACzB,aAAa,EAAE,cAAc,CAAA;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,cAAc,CAAA;IAC9B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC,CAAC,CAAA;IACzH;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IAC7E,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB;0EACsE;IACtE,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC/C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAA;IAC3C,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAYD,wBAAgB,YAAY,CAAC,EAC3B,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,MAAM,EACN,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,QAAQ,GACT,EAAE,iBAAiB,2CA8ZnB"}
1
+ {"version":3,"file":"layout-editor.d.ts","sourceRoot":"","sources":["../../../src/admin/layout-editor/layout-editor.tsx"],"names":[],"mappings":"AACA,OAAO,KAAwE,MAAM,OAAO,CAAA;AAE5F,OAAO,KAAK,EAAE,cAAc,EAAmC,MAAM,uBAAuB,CAAA;AAC5F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAmB9D,OAAO,cAAc,CAAA;AAIrB,UAAU,iBAAiB;IACzB,aAAa,EAAE,cAAc,CAAA;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,cAAc,EAAE,cAAc,CAAA;IAC9B,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC,CAAC,CAAA;IACzH;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAA;IAC7E,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB;0EACsE;IACtE,SAAS,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC/C;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAA;IAC3C,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuDG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,WAAW,CAAA;CACjD;AAYD,wBAAgB,YAAY,CAAC,EAC3B,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,MAAM,EACN,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,UAAmB,GACpB,EAAE,iBAAiB,2CAwZnB"}