@prenta/admin 0.92.0 → 0.93.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 (374) hide show
  1. package/CHANGELOG.md +552 -0
  2. package/design-system/tokens.css +173 -71
  3. package/design-system/tokens.json +244 -63
  4. package/design-system/tokens.scss +203 -98
  5. package/design-system/tokens.tailwind.cjs +93 -38
  6. package/dist/AdminRoot.d.ts +27 -6
  7. package/dist/AdminRoot.d.ts.map +1 -1
  8. package/dist/AdminRoot.js.map +1 -1
  9. package/dist/__tests__/components/breakpoint-controls.render.test.d.ts +2 -0
  10. package/dist/__tests__/components/breakpoint-controls.render.test.d.ts.map +1 -0
  11. package/dist/__tests__/components/breakpoint-controls.render.test.js +51 -0
  12. package/dist/__tests__/components/breakpoint-controls.render.test.js.map +1 -0
  13. package/dist/__tests__/components/canvas-edit-tip.render.test.d.ts +2 -0
  14. package/dist/__tests__/components/canvas-edit-tip.render.test.d.ts.map +1 -0
  15. package/dist/__tests__/components/canvas-edit-tip.render.test.js +37 -0
  16. package/dist/__tests__/components/canvas-edit-tip.render.test.js.map +1 -0
  17. package/dist/__tests__/components/editor-rail.render.test.d.ts +2 -0
  18. package/dist/__tests__/components/editor-rail.render.test.d.ts.map +1 -0
  19. package/dist/__tests__/components/editor-rail.render.test.js +138 -0
  20. package/dist/__tests__/components/editor-rail.render.test.js.map +1 -0
  21. package/dist/__tests__/components/media-block.render.test.d.ts +2 -0
  22. package/dist/__tests__/components/media-block.render.test.d.ts.map +1 -0
  23. package/dist/__tests__/components/media-block.render.test.js +108 -0
  24. package/dist/__tests__/components/media-block.render.test.js.map +1 -0
  25. package/dist/__tests__/components/page-audit-panel.render.test.d.ts +2 -0
  26. package/dist/__tests__/components/page-audit-panel.render.test.d.ts.map +1 -0
  27. package/dist/__tests__/components/page-audit-panel.render.test.js +59 -0
  28. package/dist/__tests__/components/page-audit-panel.render.test.js.map +1 -0
  29. package/dist/__tests__/components/reflow-meter.render.test.d.ts +2 -0
  30. package/dist/__tests__/components/reflow-meter.render.test.d.ts.map +1 -0
  31. package/dist/__tests__/components/reflow-meter.render.test.js +94 -0
  32. package/dist/__tests__/components/reflow-meter.render.test.js.map +1 -0
  33. package/dist/__tests__/components/seo-char-count.test.js +24 -6
  34. package/dist/__tests__/components/seo-char-count.test.js.map +1 -1
  35. package/dist/__tests__/components/serp-preview.render.test.d.ts +2 -0
  36. package/dist/__tests__/components/serp-preview.render.test.d.ts.map +1 -0
  37. package/dist/__tests__/components/serp-preview.render.test.js +25 -0
  38. package/dist/__tests__/components/serp-preview.render.test.js.map +1 -0
  39. package/dist/__tests__/lib/breakpoints.test.d.ts +2 -0
  40. package/dist/__tests__/lib/breakpoints.test.d.ts.map +1 -0
  41. package/dist/__tests__/lib/breakpoints.test.js +67 -0
  42. package/dist/__tests__/lib/breakpoints.test.js.map +1 -0
  43. package/dist/__tests__/lib/canvas-edit-chrome.test.d.ts +2 -0
  44. package/dist/__tests__/lib/canvas-edit-chrome.test.d.ts.map +1 -0
  45. package/dist/__tests__/lib/canvas-edit-chrome.test.js +44 -0
  46. package/dist/__tests__/lib/canvas-edit-chrome.test.js.map +1 -0
  47. package/dist/__tests__/lib/media-format.test.d.ts +2 -0
  48. package/dist/__tests__/lib/media-format.test.d.ts.map +1 -0
  49. package/dist/__tests__/lib/media-format.test.js +70 -0
  50. package/dist/__tests__/lib/media-format.test.js.map +1 -0
  51. package/dist/__tests__/lib/page-audit-input.test.d.ts +2 -0
  52. package/dist/__tests__/lib/page-audit-input.test.d.ts.map +1 -0
  53. package/dist/__tests__/lib/page-audit-input.test.js +122 -0
  54. package/dist/__tests__/lib/page-audit-input.test.js.map +1 -0
  55. package/dist/__tests__/lib/page-editor-service.test.js +2 -0
  56. package/dist/__tests__/lib/page-editor-service.test.js.map +1 -1
  57. package/dist/__tests__/lib/page-redirect.test.d.ts +2 -0
  58. package/dist/__tests__/lib/page-redirect.test.d.ts.map +1 -0
  59. package/dist/__tests__/lib/page-redirect.test.js +26 -0
  60. package/dist/__tests__/lib/page-redirect.test.js.map +1 -0
  61. package/dist/__tests__/lib/rail-width.test.d.ts +2 -0
  62. package/dist/__tests__/lib/rail-width.test.d.ts.map +1 -0
  63. package/dist/__tests__/lib/rail-width.test.js +69 -0
  64. package/dist/__tests__/lib/rail-width.test.js.map +1 -0
  65. package/dist/__tests__/lib/reflow-baseline.test.d.ts +2 -0
  66. package/dist/__tests__/lib/reflow-baseline.test.d.ts.map +1 -0
  67. package/dist/__tests__/lib/reflow-baseline.test.js +56 -0
  68. package/dist/__tests__/lib/reflow-baseline.test.js.map +1 -0
  69. package/dist/__tests__/lib/reflow-probe.test.d.ts +2 -0
  70. package/dist/__tests__/lib/reflow-probe.test.d.ts.map +1 -0
  71. package/dist/__tests__/lib/reflow-probe.test.js +73 -0
  72. package/dist/__tests__/lib/reflow-probe.test.js.map +1 -0
  73. package/dist/__tests__/styles/canvas-container-queries.test.d.ts +2 -0
  74. package/dist/__tests__/styles/canvas-container-queries.test.d.ts.map +1 -0
  75. package/dist/__tests__/styles/canvas-container-queries.test.js +87 -0
  76. package/dist/__tests__/styles/canvas-container-queries.test.js.map +1 -0
  77. package/dist/__tests__/styles/packaging.test.d.ts +2 -0
  78. package/dist/__tests__/styles/packaging.test.d.ts.map +1 -0
  79. package/dist/__tests__/styles/packaging.test.js +31 -0
  80. package/dist/__tests__/styles/packaging.test.js.map +1 -0
  81. package/dist/__tests__/styles/tokens-drift.test.d.ts +2 -0
  82. package/dist/__tests__/styles/tokens-drift.test.d.ts.map +1 -0
  83. package/dist/__tests__/styles/tokens-drift.test.js +92 -0
  84. package/dist/__tests__/styles/tokens-drift.test.js.map +1 -0
  85. package/dist/__tests__/views/admin-renderer-compat.test.d.ts +2 -0
  86. package/dist/__tests__/views/admin-renderer-compat.test.d.ts.map +1 -0
  87. package/dist/__tests__/views/admin-renderer-compat.test.js +43 -0
  88. package/dist/__tests__/views/admin-renderer-compat.test.js.map +1 -0
  89. package/dist/__tests__/views/admin-section-renderers.test.js +52 -4
  90. package/dist/__tests__/views/admin-section-renderers.test.js.map +1 -1
  91. package/dist/__tests__/views/editor-top-bar.render.test.js +1 -1
  92. package/dist/__tests__/views/editor-top-bar.render.test.js.map +1 -1
  93. package/dist/__tests__/views/page-tab.render.test.d.ts +2 -0
  94. package/dist/__tests__/views/page-tab.render.test.d.ts.map +1 -0
  95. package/dist/__tests__/views/page-tab.render.test.js +151 -0
  96. package/dist/__tests__/views/page-tab.render.test.js.map +1 -0
  97. package/dist/__tests__/views/section-class-parity.test.d.ts +2 -0
  98. package/dist/__tests__/views/section-class-parity.test.d.ts.map +1 -0
  99. package/dist/__tests__/views/section-class-parity.test.js +81 -0
  100. package/dist/__tests__/views/section-class-parity.test.js.map +1 -0
  101. package/dist/__tests__/views/sections-panel-design-owned.render.test.d.ts +2 -0
  102. package/dist/__tests__/views/sections-panel-design-owned.render.test.d.ts.map +1 -0
  103. package/dist/__tests__/views/sections-panel-design-owned.render.test.js +86 -0
  104. package/dist/__tests__/views/sections-panel-design-owned.render.test.js.map +1 -0
  105. package/dist/components/BreakpointPicker.d.ts +18 -0
  106. package/dist/components/BreakpointPicker.d.ts.map +1 -0
  107. package/dist/components/BreakpointPicker.js +31 -0
  108. package/dist/components/BreakpointPicker.js.map +1 -0
  109. package/dist/components/CanvasEditTip.d.ts +6 -0
  110. package/dist/components/CanvasEditTip.d.ts.map +1 -0
  111. package/dist/components/CanvasEditTip.js +22 -0
  112. package/dist/components/CanvasEditTip.js.map +1 -0
  113. package/dist/components/EditorRail.d.ts +36 -0
  114. package/dist/components/EditorRail.d.ts.map +1 -0
  115. package/dist/components/EditorRail.js +104 -0
  116. package/dist/components/EditorRail.js.map +1 -0
  117. package/dist/components/EditorRailTabs.d.ts +32 -0
  118. package/dist/components/EditorRailTabs.d.ts.map +1 -0
  119. package/dist/components/EditorRailTabs.js +45 -0
  120. package/dist/components/EditorRailTabs.js.map +1 -0
  121. package/dist/components/MediaBlock.d.ts +49 -0
  122. package/dist/components/MediaBlock.d.ts.map +1 -0
  123. package/dist/components/MediaBlock.js +50 -0
  124. package/dist/components/MediaBlock.js.map +1 -0
  125. package/dist/components/ReflowMeter.d.ts +28 -0
  126. package/dist/components/ReflowMeter.d.ts.map +1 -0
  127. package/dist/components/ReflowMeter.js +42 -0
  128. package/dist/components/ReflowMeter.js.map +1 -0
  129. package/dist/components/ZoomChip.d.ts +17 -0
  130. package/dist/components/ZoomChip.d.ts.map +1 -0
  131. package/dist/components/ZoomChip.js +17 -0
  132. package/dist/components/ZoomChip.js.map +1 -0
  133. package/dist/components/breakpoint-icons.d.ts +3 -0
  134. package/dist/components/breakpoint-icons.d.ts.map +1 -0
  135. package/dist/components/breakpoint-icons.js +16 -0
  136. package/dist/components/breakpoint-icons.js.map +1 -0
  137. package/dist/components/page-tab/AuthorPicker.d.ts +31 -0
  138. package/dist/components/page-tab/AuthorPicker.d.ts.map +1 -0
  139. package/dist/components/page-tab/AuthorPicker.js +95 -0
  140. package/dist/components/page-tab/AuthorPicker.js.map +1 -0
  141. package/dist/components/page-tab/DirectiveSwitch.d.ts +38 -0
  142. package/dist/components/page-tab/DirectiveSwitch.d.ts.map +1 -0
  143. package/dist/components/page-tab/DirectiveSwitch.js +29 -0
  144. package/dist/components/page-tab/DirectiveSwitch.js.map +1 -0
  145. package/dist/components/seo/EditorSeoScoreBadge.d.ts +8 -1
  146. package/dist/components/seo/EditorSeoScoreBadge.d.ts.map +1 -1
  147. package/dist/components/seo/EditorSeoScoreBadge.js +2 -2
  148. package/dist/components/seo/EditorSeoScoreBadge.js.map +1 -1
  149. package/dist/components/seo/PageAuditPanel.d.ts +14 -0
  150. package/dist/components/seo/PageAuditPanel.d.ts.map +1 -0
  151. package/dist/components/seo/PageAuditPanel.js +59 -0
  152. package/dist/components/seo/PageAuditPanel.js.map +1 -0
  153. package/dist/components/seo/SeoCharCount.d.ts +19 -7
  154. package/dist/components/seo/SeoCharCount.d.ts.map +1 -1
  155. package/dist/components/seo/SeoCharCount.js +30 -16
  156. package/dist/components/seo/SeoCharCount.js.map +1 -1
  157. package/dist/components/seo/SeoEditorPane.d.ts +15 -1
  158. package/dist/components/seo/SeoEditorPane.d.ts.map +1 -1
  159. package/dist/components/seo/SeoEditorPane.js +118 -21
  160. package/dist/components/seo/SeoEditorPane.js.map +1 -1
  161. package/dist/components/seo/SerpPreview.d.ts +20 -0
  162. package/dist/components/seo/SerpPreview.d.ts.map +1 -0
  163. package/dist/components/seo/SerpPreview.js +6 -0
  164. package/dist/components/seo/SerpPreview.js.map +1 -0
  165. package/dist/hooks/usePageAudit.d.ts +41 -0
  166. package/dist/hooks/usePageAudit.d.ts.map +1 -0
  167. package/dist/hooks/usePageAudit.js +68 -0
  168. package/dist/hooks/usePageAudit.js.map +1 -0
  169. package/dist/hooks/useReflowGuard.d.ts +47 -0
  170. package/dist/hooks/useReflowGuard.d.ts.map +1 -0
  171. package/dist/hooks/useReflowGuard.js +52 -0
  172. package/dist/hooks/useReflowGuard.js.map +1 -0
  173. package/dist/lib/breakpoints.d.ts +51 -0
  174. package/dist/lib/breakpoints.d.ts.map +1 -0
  175. package/dist/lib/breakpoints.js +84 -0
  176. package/dist/lib/breakpoints.js.map +1 -0
  177. package/dist/lib/canvas-edit-chrome.d.ts +22 -0
  178. package/dist/lib/canvas-edit-chrome.d.ts.map +1 -0
  179. package/dist/lib/canvas-edit-chrome.js +81 -0
  180. package/dist/lib/canvas-edit-chrome.js.map +1 -0
  181. package/dist/lib/media-format.d.ts +55 -0
  182. package/dist/lib/media-format.d.ts.map +1 -0
  183. package/dist/lib/media-format.js +93 -0
  184. package/dist/lib/media-format.js.map +1 -0
  185. package/dist/lib/page-audit-input.d.ts +45 -0
  186. package/dist/lib/page-audit-input.d.ts.map +1 -0
  187. package/dist/lib/page-audit-input.js +53 -0
  188. package/dist/lib/page-audit-input.js.map +1 -0
  189. package/dist/lib/page-editor-service.d.ts +12 -0
  190. package/dist/lib/page-editor-service.d.ts.map +1 -1
  191. package/dist/lib/page-editor-service.js +6 -0
  192. package/dist/lib/page-editor-service.js.map +1 -1
  193. package/dist/lib/page-redirect.d.ts +39 -0
  194. package/dist/lib/page-redirect.d.ts.map +1 -0
  195. package/dist/lib/page-redirect.js +38 -0
  196. package/dist/lib/page-redirect.js.map +1 -0
  197. package/dist/lib/rail-width.d.ts +23 -0
  198. package/dist/lib/rail-width.d.ts.map +1 -0
  199. package/dist/lib/rail-width.js +59 -0
  200. package/dist/lib/rail-width.js.map +1 -0
  201. package/dist/lib/reflow-baseline.d.ts +45 -0
  202. package/dist/lib/reflow-baseline.d.ts.map +1 -0
  203. package/dist/lib/reflow-baseline.js +38 -0
  204. package/dist/lib/reflow-baseline.js.map +1 -0
  205. package/dist/lib/reflow-probe.d.ts +159 -0
  206. package/dist/lib/reflow-probe.d.ts.map +1 -0
  207. package/dist/lib/reflow-probe.js +228 -0
  208. package/dist/lib/reflow-probe.js.map +1 -0
  209. package/dist/lib/seo-service.d.ts +10 -0
  210. package/dist/lib/seo-service.d.ts.map +1 -1
  211. package/dist/lib/seo-service.js.map +1 -1
  212. package/dist/prenta-admin.css +1 -1
  213. package/dist/views/page-editor/CanvasMetaLine.d.ts +19 -0
  214. package/dist/views/page-editor/CanvasMetaLine.d.ts.map +1 -0
  215. package/dist/views/page-editor/CanvasMetaLine.js +23 -0
  216. package/dist/views/page-editor/CanvasMetaLine.js.map +1 -0
  217. package/dist/views/page-editor/EditorCanvas.d.ts +34 -8
  218. package/dist/views/page-editor/EditorCanvas.d.ts.map +1 -1
  219. package/dist/views/page-editor/EditorCanvas.js +68 -34
  220. package/dist/views/page-editor/EditorCanvas.js.map +1 -1
  221. package/dist/views/page-editor/EditorTopBar.d.ts +14 -1
  222. package/dist/views/page-editor/EditorTopBar.d.ts.map +1 -1
  223. package/dist/views/page-editor/EditorTopBar.js +8 -17
  224. package/dist/views/page-editor/EditorTopBar.js.map +1 -1
  225. package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
  226. package/dist/views/page-editor/PageSectionEditor.js +275 -53
  227. package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
  228. package/dist/views/page-editor/PageTab.d.ts +36 -0
  229. package/dist/views/page-editor/PageTab.d.ts.map +1 -0
  230. package/dist/views/page-editor/PageTab.js +65 -0
  231. package/dist/views/page-editor/PageTab.js.map +1 -0
  232. package/dist/views/page-editor/SectionInspector.d.ts.map +1 -1
  233. package/dist/views/page-editor/SectionInspector.js +63 -19
  234. package/dist/views/page-editor/SectionInspector.js.map +1 -1
  235. package/dist/views/page-editor/SectionsPanel.d.ts +30 -5
  236. package/dist/views/page-editor/SectionsPanel.d.ts.map +1 -1
  237. package/dist/views/page-editor/SectionsPanel.js +38 -7
  238. package/dist/views/page-editor/SectionsPanel.js.map +1 -1
  239. package/dist/views/page-editor/SitePreviewFrame.d.ts.map +1 -1
  240. package/dist/views/page-editor/SitePreviewFrame.js +3 -0
  241. package/dist/views/page-editor/SitePreviewFrame.js.map +1 -1
  242. package/dist/views/page-editor/reflow-context.d.ts +28 -0
  243. package/dist/views/page-editor/reflow-context.d.ts.map +1 -0
  244. package/dist/views/page-editor/reflow-context.js +14 -0
  245. package/dist/views/page-editor/reflow-context.js.map +1 -0
  246. package/dist/views/page-editor/sections/ByTheNumbersSection.d.ts.map +1 -1
  247. package/dist/views/page-editor/sections/ByTheNumbersSection.js +1 -1
  248. package/dist/views/page-editor/sections/ByTheNumbersSection.js.map +1 -1
  249. package/dist/views/page-editor/sections/CtaBannerSection.d.ts.map +1 -1
  250. package/dist/views/page-editor/sections/CtaBannerSection.js +1 -1
  251. package/dist/views/page-editor/sections/CtaBannerSection.js.map +1 -1
  252. package/dist/views/page-editor/sections/FeatureHighlightSection.d.ts.map +1 -1
  253. package/dist/views/page-editor/sections/FeatureHighlightSection.js +2 -2
  254. package/dist/views/page-editor/sections/FeatureHighlightSection.js.map +1 -1
  255. package/dist/views/page-editor/sections/GallerySection.d.ts.map +1 -1
  256. package/dist/views/page-editor/sections/GallerySection.js +7 -3
  257. package/dist/views/page-editor/sections/GallerySection.js.map +1 -1
  258. package/dist/views/page-editor/sections/HeroBannerSection.js +1 -1
  259. package/dist/views/page-editor/sections/HeroBannerSection.js.map +1 -1
  260. package/dist/views/page-editor/sections/MissionSection.d.ts.map +1 -1
  261. package/dist/views/page-editor/sections/MissionSection.js +1 -1
  262. package/dist/views/page-editor/sections/MissionSection.js.map +1 -1
  263. package/dist/views/page-editor/sections/PullQuoteSection.d.ts.map +1 -1
  264. package/dist/views/page-editor/sections/PullQuoteSection.js +1 -1
  265. package/dist/views/page-editor/sections/PullQuoteSection.js.map +1 -1
  266. package/dist/views/page-editor/sections/RelatedPostsSection.d.ts.map +1 -1
  267. package/dist/views/page-editor/sections/RelatedPostsSection.js +1 -1
  268. package/dist/views/page-editor/sections/RelatedPostsSection.js.map +1 -1
  269. package/dist/views/page-editor/sections/SectionRenderer.d.ts.map +1 -1
  270. package/dist/views/page-editor/sections/SectionRenderer.js +49 -24
  271. package/dist/views/page-editor/sections/SectionRenderer.js.map +1 -1
  272. package/dist/views/page-editor/sections/admin-renderers.d.ts +47 -15
  273. package/dist/views/page-editor/sections/admin-renderers.d.ts.map +1 -1
  274. package/dist/views/page-editor/sections/admin-renderers.js +27 -15
  275. package/dist/views/page-editor/sections/admin-renderers.js.map +1 -1
  276. package/dist/views/page-editor/sections/parts.d.ts +0 -1
  277. package/dist/views/page-editor/sections/parts.d.ts.map +1 -1
  278. package/dist/views/page-editor/sections/parts.js +41 -16
  279. package/dist/views/page-editor/sections/parts.js.map +1 -1
  280. package/dist/views/page-editor/viewports.d.ts +7 -14
  281. package/dist/views/page-editor/viewports.d.ts.map +1 -1
  282. package/dist/views/page-editor/viewports.js +7 -12
  283. package/dist/views/page-editor/viewports.js.map +1 -1
  284. package/dist/views/post-editor/PostEditorCanvas.d.ts +2 -1
  285. package/dist/views/post-editor/PostEditorCanvas.d.ts.map +1 -1
  286. package/dist/views/post-editor/PostEditorCanvas.js +5 -2
  287. package/dist/views/post-editor/PostEditorCanvas.js.map +1 -1
  288. package/dist/views/post-editor/PostSectionEditor.d.ts.map +1 -1
  289. package/dist/views/post-editor/PostSectionEditor.js +2 -20
  290. package/dist/views/post-editor/PostSectionEditor.js.map +1 -1
  291. package/dist/views/post-editor/PostTemplateEditor.d.ts.map +1 -1
  292. package/dist/views/post-editor/PostTemplateEditor.js +4 -1
  293. package/dist/views/post-editor/PostTemplateEditor.js.map +1 -1
  294. package/package.json +5 -3
  295. package/src/AdminRoot.tsx +27 -6
  296. package/src/__tests__/components/breakpoint-controls.render.test.tsx +57 -0
  297. package/src/__tests__/components/canvas-edit-tip.render.test.tsx +41 -0
  298. package/src/__tests__/components/editor-rail.render.test.tsx +192 -0
  299. package/src/__tests__/components/media-block.render.test.tsx +126 -0
  300. package/src/__tests__/components/page-audit-panel.render.test.tsx +64 -0
  301. package/src/__tests__/components/reflow-meter.render.test.tsx +144 -0
  302. package/src/__tests__/components/seo-char-count.test.tsx +26 -6
  303. package/src/__tests__/components/serp-preview.render.test.tsx +32 -0
  304. package/src/__tests__/lib/breakpoints.test.ts +84 -0
  305. package/src/__tests__/lib/canvas-edit-chrome.test.ts +56 -0
  306. package/src/__tests__/lib/media-format.test.ts +89 -0
  307. package/src/__tests__/lib/page-audit-input.test.ts +144 -0
  308. package/src/__tests__/lib/page-editor-service.test.ts +2 -0
  309. package/src/__tests__/lib/page-redirect.test.ts +29 -0
  310. package/src/__tests__/lib/rail-width.test.ts +86 -0
  311. package/src/__tests__/lib/reflow-baseline.test.ts +64 -0
  312. package/src/__tests__/lib/reflow-probe.test.ts +95 -0
  313. package/src/__tests__/styles/canvas-container-queries.test.ts +103 -0
  314. package/src/__tests__/styles/packaging.test.ts +38 -0
  315. package/src/__tests__/styles/tokens-drift.test.ts +110 -0
  316. package/src/__tests__/views/admin-renderer-compat.test.ts +48 -0
  317. package/src/__tests__/views/admin-section-renderers.test.tsx +70 -4
  318. package/src/__tests__/views/editor-top-bar.render.test.tsx +1 -1
  319. package/src/__tests__/views/page-tab.render.test.tsx +226 -0
  320. package/src/__tests__/views/section-class-parity.test.ts +98 -0
  321. package/src/__tests__/views/sections-panel-design-owned.render.test.tsx +104 -0
  322. package/src/components/BreakpointPicker.tsx +114 -0
  323. package/src/components/CanvasEditTip.tsx +46 -0
  324. package/src/components/EditorRail.tsx +219 -0
  325. package/src/components/EditorRailTabs.tsx +117 -0
  326. package/src/components/MediaBlock.tsx +223 -0
  327. package/src/components/ReflowMeter.tsx +125 -0
  328. package/src/components/ZoomChip.tsx +39 -0
  329. package/src/components/breakpoint-icons.ts +17 -0
  330. package/src/components/page-tab/AuthorPicker.tsx +240 -0
  331. package/src/components/page-tab/DirectiveSwitch.tsx +86 -0
  332. package/src/components/seo/EditorSeoScoreBadge.tsx +9 -1
  333. package/src/components/seo/PageAuditPanel.tsx +192 -0
  334. package/src/components/seo/SeoCharCount.tsx +55 -24
  335. package/src/components/seo/SeoEditorPane.tsx +213 -31
  336. package/src/components/seo/SerpPreview.tsx +41 -0
  337. package/src/hooks/usePageAudit.ts +98 -0
  338. package/src/hooks/useReflowGuard.ts +96 -0
  339. package/src/lib/breakpoints.ts +107 -0
  340. package/src/lib/canvas-edit-chrome.ts +82 -0
  341. package/src/lib/media-format.ts +99 -0
  342. package/src/lib/page-audit-input.ts +104 -0
  343. package/src/lib/page-editor-service.ts +18 -0
  344. package/src/lib/page-redirect.ts +60 -0
  345. package/src/lib/rail-width.ts +58 -0
  346. package/src/lib/reflow-baseline.ts +70 -0
  347. package/src/lib/reflow-probe.ts +299 -0
  348. package/src/lib/seo-service.ts +10 -0
  349. package/src/styles/prenta-tokens.css +120 -42
  350. package/src/styles/theme.css +130 -0
  351. package/src/views/page-editor/CanvasMetaLine.tsx +50 -0
  352. package/src/views/page-editor/EditorCanvas.tsx +109 -50
  353. package/src/views/page-editor/EditorTopBar.tsx +35 -43
  354. package/src/views/page-editor/PageSectionEditor.tsx +419 -148
  355. package/src/views/page-editor/PageTab.tsx +433 -0
  356. package/src/views/page-editor/SectionInspector.tsx +82 -41
  357. package/src/views/page-editor/SectionsPanel.tsx +281 -140
  358. package/src/views/page-editor/SitePreviewFrame.tsx +3 -0
  359. package/src/views/page-editor/reflow-context.ts +43 -0
  360. package/src/views/page-editor/sections/ByTheNumbersSection.tsx +3 -2
  361. package/src/views/page-editor/sections/CtaBannerSection.tsx +3 -1
  362. package/src/views/page-editor/sections/FeatureHighlightSection.tsx +3 -2
  363. package/src/views/page-editor/sections/GallerySection.tsx +7 -3
  364. package/src/views/page-editor/sections/HeroBannerSection.tsx +2 -2
  365. package/src/views/page-editor/sections/MissionSection.tsx +4 -2
  366. package/src/views/page-editor/sections/PullQuoteSection.tsx +3 -1
  367. package/src/views/page-editor/sections/RelatedPostsSection.tsx +2 -1
  368. package/src/views/page-editor/sections/SectionRenderer.tsx +69 -29
  369. package/src/views/page-editor/sections/admin-renderers.tsx +54 -15
  370. package/src/views/page-editor/sections/parts.tsx +46 -18
  371. package/src/views/page-editor/viewports.ts +13 -26
  372. package/src/views/post-editor/PostEditorCanvas.tsx +18 -11
  373. package/src/views/post-editor/PostSectionEditor.tsx +1 -56
  374. package/src/views/post-editor/PostTemplateEditor.tsx +3 -0
@@ -0,0 +1,52 @@
1
+ 'use client';
2
+ import { useEffect, useState } from 'react';
3
+ import { probeFieldReflow, reflowSeverity, worstBreakpoint, } from '../lib/reflow-probe.js';
4
+ const EMPTY = { rows: [], grown: [], worst: null, delta: 0 };
5
+ /**
6
+ * Measures how one field wraps at every breakpoint, and how that compares to
7
+ * what is published.
8
+ *
9
+ * ── Why it runs on a delay ────────────────────────────────────────────────
10
+ *
11
+ * A pass clones the canvas and lays it out twelve times (six widths, current
12
+ * and published). That is far too much to do per keystroke, and it is also
13
+ * pointless — mid-word text tells an editor nothing. So it settles first.
14
+ *
15
+ * `null` from the probe means the field is not measurable: the section may be
16
+ * rendered by a consumer's own component that carries no `data-cms-field`
17
+ * markers. The guard then reports nothing rather than a number read off the
18
+ * wrong element — an editor treats a line count as a fact, and a confident
19
+ * wrong one is worse than a blank.
20
+ */
21
+ export function useReflowGuard(options) {
22
+ const { canvas, sectionId, fieldPath, value, baseline, enabled = true, delayMs = 200 } = options;
23
+ const [result, setResult] = useState(EMPTY);
24
+ useEffect(() => {
25
+ if (!enabled || !canvas) {
26
+ setResult(EMPTY);
27
+ return;
28
+ }
29
+ const timer = setTimeout(() => {
30
+ const probed = probeFieldReflow({ canvas, sectionId, fieldPath, baselineText: baseline });
31
+ if (!probed) {
32
+ setResult(EMPTY);
33
+ return;
34
+ }
35
+ const rows = probed.current.map((row) => {
36
+ const was = probed.baseline.find((b) => b.id === row.id)?.lines ?? row.lines;
37
+ return { ...row, was, diff: row.lines - was, state: reflowSeverity(row.lines, was) };
38
+ });
39
+ setResult({
40
+ rows,
41
+ grown: rows.filter((r) => r.diff > 0),
42
+ worst: worstBreakpoint(probed.current, probed.baseline),
43
+ delta: baseline === undefined ? 0 : value.length - baseline.length,
44
+ });
45
+ }, delayMs);
46
+ return () => clearTimeout(timer);
47
+ // `value` is a dependency because the canvas re-renders as it changes —
48
+ // the probe reads the resulting DOM rather than the string itself.
49
+ }, [canvas, sectionId, fieldPath, value, baseline, enabled, delayMs]);
50
+ return result;
51
+ }
52
+ //# sourceMappingURL=useReflowGuard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useReflowGuard.js","sourceRoot":"","sources":["../../src/hooks/useReflowGuard.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAE3C,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,eAAe,GAGhB,MAAM,wBAAwB,CAAA;AAoB/B,MAAM,KAAK,GAAsB,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAA;AAE/E;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,cAAc,CAAC,OAW9B;IACC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,EAAE,OAAO,GAAG,GAAG,EAAE,GAAG,OAAO,CAAA;IAChG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAoB,KAAK,CAAC,CAAA;IAE9D,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;YACxB,SAAS,CAAC,KAAK,CAAC,CAAA;YAChB,OAAM;QACR,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,MAAM,MAAM,GAAG,gBAAgB,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC,CAAA;YACzF,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,SAAS,CAAC,KAAK,CAAC,CAAA;gBAChB,OAAM;YACR,CAAC;YAED,MAAM,IAAI,GAAgB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;gBACnD,MAAM,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,EAAE,KAAK,IAAI,GAAG,CAAC,KAAK,CAAA;gBAC5E,OAAO,EAAE,GAAG,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,CAAC,KAAK,GAAG,GAAG,EAAE,KAAK,EAAE,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAA;YACtF,CAAC,CAAC,CAAA;YAEF,SAAS,CAAC;gBACR,IAAI;gBACJ,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;gBACrC,KAAK,EAAE,eAAe,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC;gBACvD,KAAK,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM;aACnE,CAAC,CAAA;QACJ,CAAC,EAAE,OAAO,CAAC,CAAA;QAEX,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAA;QAChC,wEAAwE;QACxE,mEAAmE;IACrE,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;IAErE,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Breakpoint presets for the editor canvas — shared by the page editor, the
3
+ * post editor, and the site-preview frame.
4
+ *
5
+ * Each entry's `width` is the **real CSS width** the canvas renders at. The
6
+ * canvas is then scaled down with a transform to fit the available column, so
7
+ * the page reflows against its own width via container queries rather than
8
+ * being squeezed by a max-width clamp. A clamp does not reflow: it produces a
9
+ * narrow desktop layout, not the mobile layout.
10
+ *
11
+ * `4k` is 2560, not 3840. 2560 is the CSS width a 4K/Retina display reports
12
+ * once device-pixel-ratio is applied; 3840 is the physical pixel count and no
13
+ * stylesheet ever sees it. It is also the only preset above the canvas's
14
+ * `min-width: 2000px` container step, so it is the only one that exercises
15
+ * that branch.
16
+ */
17
+ export type BreakpointId = '4k' | 'wide' | 'laptop' | 'tabletL' | 'tabletP' | 'mobile';
18
+ export type BreakpointGroup = 'DESKTOP' | 'TABLET' | 'MOBILE';
19
+ export interface Breakpoint {
20
+ id: BreakpointId;
21
+ /** Full name, shown in the picker menu. */
22
+ name: string;
23
+ /** Two-letter form for the collapsed trigger. */
24
+ abbr: string;
25
+ /** lucide-react icon name */
26
+ icon: string;
27
+ /** Real CSS width of the preview surface, in px. */
28
+ width: number;
29
+ group: BreakpointGroup;
30
+ }
31
+ export declare const BREAKPOINTS: readonly Breakpoint[];
32
+ /** Menu order. Groups render as labelled sections in the breakpoint picker. */
33
+ export declare const BREAKPOINT_GROUPS: readonly BreakpointGroup[];
34
+ export declare const DEFAULT_BREAKPOINT: BreakpointId;
35
+ export declare function getBreakpoint(id: BreakpointId): Breakpoint;
36
+ export declare function breakpointsInGroup(group: BreakpointGroup): Breakpoint[];
37
+ export declare function coerceBreakpointId(value: unknown): BreakpointId;
38
+ export type Zoom = 'fit' | 1;
39
+ /**
40
+ * Scale factor for the canvas transform.
41
+ *
42
+ * `available` must come from a zero-height sentinel measured inside the canvas
43
+ * panel — NOT from `panel.clientWidth - padding`. The two disagree whenever the
44
+ * host scales the preview or a scrollbar appears, and the padding arithmetic is
45
+ * the kind that silently rots when the gutter changes.
46
+ *
47
+ * Never scales above 1: a 390px mobile canvas blown up to fill a 1200px column
48
+ * would show text at 3× the size the visitor sees.
49
+ */
50
+ export declare function fitScale(available: number, width: number, zoom?: Zoom): number;
51
+ //# sourceMappingURL=breakpoints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breakpoints.d.ts","sourceRoot":"","sources":["../../src/lib/breakpoints.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAA;AAEtF,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAE7D,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,YAAY,CAAA;IAChB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAA;IACZ,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAA;IACZ,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,eAAe,CAAA;CACvB;AAED,eAAO,MAAM,WAAW,EAAE,SAAS,UAAU,EAqB5C,CAAA;AAED,+EAA+E;AAC/E,eAAO,MAAM,iBAAiB,EAAE,SAAS,eAAe,EAAoC,CAAA;AAE5F,eAAO,MAAM,kBAAkB,EAAE,YAAqB,CAAA;AAItD,wBAAgB,aAAa,CAAC,EAAE,EAAE,YAAY,GAAG,UAAU,CAE1D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,UAAU,EAAE,CAEvE;AAaD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAI/D;AAED,MAAM,MAAM,IAAI,GAAG,KAAK,GAAG,CAAC,CAAA;AAE5B;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAE,IAAY,GAAG,MAAM,CAIrF"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Breakpoint presets for the editor canvas — shared by the page editor, the
3
+ * post editor, and the site-preview frame.
4
+ *
5
+ * Each entry's `width` is the **real CSS width** the canvas renders at. The
6
+ * canvas is then scaled down with a transform to fit the available column, so
7
+ * the page reflows against its own width via container queries rather than
8
+ * being squeezed by a max-width clamp. A clamp does not reflow: it produces a
9
+ * narrow desktop layout, not the mobile layout.
10
+ *
11
+ * `4k` is 2560, not 3840. 2560 is the CSS width a 4K/Retina display reports
12
+ * once device-pixel-ratio is applied; 3840 is the physical pixel count and no
13
+ * stylesheet ever sees it. It is also the only preset above the canvas's
14
+ * `min-width: 2000px` container step, so it is the only one that exercises
15
+ * that branch.
16
+ */
17
+ export const BREAKPOINTS = [
18
+ { id: '4k', name: '4K / Retina', abbr: '4K', icon: 'monitor', width: 2560, group: 'DESKTOP' },
19
+ { id: 'wide', name: 'Wide Screen', abbr: 'WS', icon: 'monitor', width: 1920, group: 'DESKTOP' },
20
+ { id: 'laptop', name: 'Laptop', abbr: 'LT', icon: 'laptop', width: 1440, group: 'DESKTOP' },
21
+ {
22
+ id: 'tabletL',
23
+ name: 'Tablet Landscape',
24
+ abbr: 'TL',
25
+ icon: 'tablet',
26
+ width: 1024,
27
+ group: 'TABLET',
28
+ },
29
+ {
30
+ id: 'tabletP',
31
+ name: 'Tablet Portrait',
32
+ abbr: 'TP',
33
+ icon: 'tablet',
34
+ width: 768,
35
+ group: 'TABLET',
36
+ },
37
+ { id: 'mobile', name: 'Mobile', abbr: 'M', icon: 'smartphone', width: 390, group: 'MOBILE' },
38
+ ];
39
+ /** Menu order. Groups render as labelled sections in the breakpoint picker. */
40
+ export const BREAKPOINT_GROUPS = ['DESKTOP', 'TABLET', 'MOBILE'];
41
+ export const DEFAULT_BREAKPOINT = 'wide';
42
+ const BY_ID = new Map(BREAKPOINTS.map((b) => [b.id, b]));
43
+ export function getBreakpoint(id) {
44
+ return BY_ID.get(id) ?? BY_ID.get(DEFAULT_BREAKPOINT);
45
+ }
46
+ export function breakpointsInGroup(group) {
47
+ return BREAKPOINTS.filter((b) => b.group === group);
48
+ }
49
+ /**
50
+ * Narrows arbitrary input (persisted state, a URL param, a stale prop) to a
51
+ * known id. The v0.92 preset ids — `1080p` and `tablet` — map onto their
52
+ * closest current equivalents so an editor open across the upgrade doesn't
53
+ * land on the default.
54
+ */
55
+ const LEGACY_IDS = {
56
+ '1080p': 'laptop', // was 1440 — same width, renamed
57
+ tablet: 'tabletP', // was 768 — same width, split into landscape/portrait
58
+ };
59
+ export function coerceBreakpointId(value) {
60
+ if (typeof value !== 'string')
61
+ return DEFAULT_BREAKPOINT;
62
+ if (BY_ID.has(value))
63
+ return value;
64
+ return LEGACY_IDS[value] ?? DEFAULT_BREAKPOINT;
65
+ }
66
+ /**
67
+ * Scale factor for the canvas transform.
68
+ *
69
+ * `available` must come from a zero-height sentinel measured inside the canvas
70
+ * panel — NOT from `panel.clientWidth - padding`. The two disagree whenever the
71
+ * host scales the preview or a scrollbar appears, and the padding arithmetic is
72
+ * the kind that silently rots when the gutter changes.
73
+ *
74
+ * Never scales above 1: a 390px mobile canvas blown up to fill a 1200px column
75
+ * would show text at 3× the size the visitor sees.
76
+ */
77
+ export function fitScale(available, width, zoom = 'fit') {
78
+ if (zoom !== 'fit')
79
+ return 1;
80
+ if (!Number.isFinite(available) || available <= 0 || width <= 0)
81
+ return 1;
82
+ return Math.min(1, available / width);
83
+ }
84
+ //# sourceMappingURL=breakpoints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breakpoints.js","sourceRoot":"","sources":["../../src/lib/breakpoints.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Shared edit affordances for the page/post canvas and Site preview iframe.
3
+ *
4
+ * Structure canvas: section hover is styled in `theme.css` under `.prenta-canvas`.
5
+ * Site preview: the same field chrome is injected into the iframe document when
6
+ * same-origin (CMS-host preview) so consumers get hover borders without shipping CSS.
7
+ */
8
+ /** localStorage key for the dismissible canvas tip. */
9
+ export declare const CANVAS_EDIT_TIP_STORAGE_KEY = "prenta.canvasEditTip.dismissed";
10
+ /**
11
+ * CSS injected into the Site preview iframe while editing is enabled.
12
+ * Uses system colors so it works without admin theme tokens in the iframe.
13
+ */
14
+ export declare const PREVIEW_EDITABLE_FIELD_CHROME_CSS: string;
15
+ /**
16
+ * Inject (or refresh) editable-field hover chrome into a same-origin preview
17
+ * iframe. No-ops on cross-origin frames (browser blocks contentDocument).
18
+ */
19
+ export declare function injectPreviewEditChrome(iframe: HTMLIFrameElement | null): boolean;
20
+ export declare function isCanvasEditTipDismissed(): boolean;
21
+ export declare function dismissCanvasEditTip(): void;
22
+ //# sourceMappingURL=canvas-edit-chrome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-edit-chrome.d.ts","sourceRoot":"","sources":["../../src/lib/canvas-edit-chrome.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,uDAAuD;AACvD,eAAO,MAAM,2BAA2B,mCAAmC,CAAA;AAE3E;;;GAGG;AACH,eAAO,MAAM,iCAAiC,QA0BtC,CAAA;AAIR;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,GAAG,OAAO,CAgBjF;AAED,wBAAgB,wBAAwB,IAAI,OAAO,CAMlD;AAED,wBAAgB,oBAAoB,IAAI,IAAI,CAM3C"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Shared edit affordances for the page/post canvas and Site preview iframe.
3
+ *
4
+ * Structure canvas: section hover is styled in `theme.css` under `.prenta-canvas`.
5
+ * Site preview: the same field chrome is injected into the iframe document when
6
+ * same-origin (CMS-host preview) so consumers get hover borders without shipping CSS.
7
+ */
8
+ /** localStorage key for the dismissible canvas tip. */
9
+ export const CANVAS_EDIT_TIP_STORAGE_KEY = 'prenta.canvasEditTip.dismissed';
10
+ /**
11
+ * CSS injected into the Site preview iframe while editing is enabled.
12
+ * Uses system colors so it works without admin theme tokens in the iframe.
13
+ */
14
+ export const PREVIEW_EDITABLE_FIELD_CHROME_CSS = `
15
+ /* Prenta Site preview — editable field hover chrome (injected by @prenta/admin) */
16
+ [data-cms-field] {
17
+ outline: 1px solid transparent;
18
+ outline-offset: 2px;
19
+ border-radius: 2px;
20
+ transition: outline-color 120ms ease;
21
+ cursor: text;
22
+ }
23
+ [data-cms-field]:hover,
24
+ [data-cms-field]:focus,
25
+ [data-cms-field]:focus-within {
26
+ outline-color: color-mix(in oklab, CanvasText 24%, transparent);
27
+ }
28
+ [data-cms-field][data-cms-field-kind="media"] {
29
+ cursor: pointer;
30
+ }
31
+ [data-cms-field][data-cms-field-kind="media"]:hover,
32
+ [data-cms-field][data-cms-field-kind="media"]:focus {
33
+ outline-color: color-mix(in oklab, CanvasText 32%, transparent);
34
+ }
35
+ @media (prefers-reduced-motion: reduce) {
36
+ [data-cms-field] {
37
+ transition: none;
38
+ }
39
+ }
40
+ `.trim();
41
+ const STYLE_ATTR = 'data-prenta-edit-chrome';
42
+ /**
43
+ * Inject (or refresh) editable-field hover chrome into a same-origin preview
44
+ * iframe. No-ops on cross-origin frames (browser blocks contentDocument).
45
+ */
46
+ export function injectPreviewEditChrome(iframe) {
47
+ try {
48
+ const doc = iframe?.contentDocument;
49
+ if (!doc?.head)
50
+ return false;
51
+ let style = doc.head.querySelector(`style[${STYLE_ATTR}]`);
52
+ if (!style) {
53
+ style = doc.createElement('style');
54
+ style.setAttribute(STYLE_ATTR, '1');
55
+ doc.head.appendChild(style);
56
+ }
57
+ style.textContent = PREVIEW_EDITABLE_FIELD_CHROME_CSS;
58
+ doc.documentElement.setAttribute('data-prenta-preview-editing', 'true');
59
+ return true;
60
+ }
61
+ catch {
62
+ return false;
63
+ }
64
+ }
65
+ export function isCanvasEditTipDismissed() {
66
+ try {
67
+ return localStorage.getItem(CANVAS_EDIT_TIP_STORAGE_KEY) === '1';
68
+ }
69
+ catch {
70
+ return false;
71
+ }
72
+ }
73
+ export function dismissCanvasEditTip() {
74
+ try {
75
+ localStorage.setItem(CANVAS_EDIT_TIP_STORAGE_KEY, '1');
76
+ }
77
+ catch {
78
+ /* private mode — tip will reappear next session */
79
+ }
80
+ }
81
+ //# sourceMappingURL=canvas-edit-chrome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-edit-chrome.js","sourceRoot":"","sources":["../../src/lib/canvas-edit-chrome.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,uDAAuD;AACvD,MAAM,CAAC,MAAM,2BAA2B,GAAG,gCAAgC,CAAA;AAE3E;;;GAGG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;CA0BhD,CAAC,IAAI,EAAE,CAAA;AAER,MAAM,UAAU,GAAG,yBAAyB,CAAA;AAE5C;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAgC;IACtE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,EAAE,eAAe,CAAA;QACnC,IAAI,CAAC,GAAG,EAAE,IAAI;YAAE,OAAO,KAAK,CAAA;QAC5B,IAAI,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,aAAa,CAAmB,SAAS,UAAU,GAAG,CAAC,CAAA;QAC5E,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;YAClC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,GAAG,CAAC,CAAA;YACnC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;QAC7B,CAAC;QACD,KAAK,CAAC,WAAW,GAAG,iCAAiC,CAAA;QACrD,GAAG,CAAC,eAAe,CAAC,YAAY,CAAC,6BAA6B,EAAE,MAAM,CAAC,CAAA;QACvE,OAAO,IAAI,CAAA;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,wBAAwB;IACtC,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,OAAO,CAAC,2BAA2B,CAAC,KAAK,GAAG,CAAA;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,IAAI,CAAC;QACH,YAAY,CAAC,OAAO,CAAC,2BAA2B,EAAE,GAAG,CAAC,CAAA;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,mDAAmD;IACrD,CAAC;AACH,CAAC"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Presentation helpers for the media block.
3
+ *
4
+ * Pure, because these are the parts with real logic — the rest of the block is
5
+ * layout. Ported from the design prototype so the labels an editor reads match
6
+ * the ones the design specifies.
7
+ */
8
+ /** The crop the template renders an image at. Drives the frame's aspect ratio. */
9
+ export type MediaCrop = 'section' | 'social';
10
+ /**
11
+ * Aspect ratio per crop, as a CSS `aspect-ratio` value.
12
+ *
13
+ * The frame shows the crop the TEMPLATE renders, not the asset's own ratio.
14
+ * Showing the asset's ratio would tell an editor their portrait photo is fine
15
+ * and then crop its subject out on the page.
16
+ */
17
+ export declare const CROP_RATIO: Record<MediaCrop, string>;
18
+ /**
19
+ * Human ratio for an asset's intrinsic dimensions.
20
+ *
21
+ * Snaps to a named ratio when within 0.05, else reports the number. The
22
+ * tolerance matters: a 2400×1600 photo is 1.5 exactly, but a 2401×1600 scan is
23
+ * 1.5006 and an editor should still read "3:2" rather than "1.50:1".
24
+ *
25
+ * Returns null when dimensions are unknown — the library does not always have
26
+ * them, and inventing "1.00:1" would be worse than saying nothing.
27
+ */
28
+ export declare function ratioLabel(width?: number, height?: number): string | null;
29
+ /**
30
+ * Short format badge — the file extension, uppercased.
31
+ *
32
+ * Falls back to the MIME subtype so a URL with no extension (a signed CDN link,
33
+ * an upload route) still shows something meaningful rather than a blank chip.
34
+ */
35
+ export declare function formatBadge(filenameOrUrl: string, mimeType?: string): string;
36
+ /**
37
+ * Filename for display, from a filename or a URL.
38
+ *
39
+ * Query strings are dropped — a signed URL's token is noise in a 200px-wide
40
+ * toolbar, and it is never what an editor is trying to read.
41
+ */
42
+ export declare function displayName(filenameOrUrl: string): string;
43
+ /**
44
+ * A stable background tint for an asset.
45
+ *
46
+ * The design fills the frame with a wash derived from the asset's dominant
47
+ * colour. The media library stores no such value, so it is derived from the
48
+ * name instead: the same asset is always the same colour, which is the property
49
+ * that actually matters for recognising a thumbnail, and it costs no storage
50
+ * and no extra request.
51
+ *
52
+ * Returned as a 135° gradient to the design's `color-mix(tint 45%, #fff)`.
53
+ */
54
+ export declare function assetWash(seed: string): string;
55
+ //# sourceMappingURL=media-format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-format.d.ts","sourceRoot":"","sources":["../../src/lib/media-format.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,kFAAkF;AAClF,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE5C;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAGhD,CAAA;AAaD;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKzE;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAY5E;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAGzD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK9C"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Presentation helpers for the media block.
3
+ *
4
+ * Pure, because these are the parts with real logic — the rest of the block is
5
+ * layout. Ported from the design prototype so the labels an editor reads match
6
+ * the ones the design specifies.
7
+ */
8
+ /**
9
+ * Aspect ratio per crop, as a CSS `aspect-ratio` value.
10
+ *
11
+ * The frame shows the crop the TEMPLATE renders, not the asset's own ratio.
12
+ * Showing the asset's ratio would tell an editor their portrait photo is fine
13
+ * and then crop its subject out on the page.
14
+ */
15
+ export const CROP_RATIO = {
16
+ section: '16 / 10',
17
+ social: '1.91 / 1',
18
+ };
19
+ /** Named ratios an editor recognises, nearest-match within a tolerance. */
20
+ const KNOWN_RATIOS = [
21
+ [1.91, '1.91:1'],
22
+ [16 / 9, '16:9'],
23
+ [1.5, '3:2'],
24
+ [4 / 3, '4:3'],
25
+ [1, '1:1'],
26
+ [0.8, '4:5'],
27
+ [9 / 16, '9:16'],
28
+ ];
29
+ /**
30
+ * Human ratio for an asset's intrinsic dimensions.
31
+ *
32
+ * Snaps to a named ratio when within 0.05, else reports the number. The
33
+ * tolerance matters: a 2400×1600 photo is 1.5 exactly, but a 2401×1600 scan is
34
+ * 1.5006 and an editor should still read "3:2" rather than "1.50:1".
35
+ *
36
+ * Returns null when dimensions are unknown — the library does not always have
37
+ * them, and inventing "1.00:1" would be worse than saying nothing.
38
+ */
39
+ export function ratioLabel(width, height) {
40
+ if (!width || !height || width <= 0 || height <= 0)
41
+ return null;
42
+ const r = width / height;
43
+ const best = KNOWN_RATIOS.reduce((a, b) => (Math.abs(b[0] - r) < Math.abs(a[0] - r) ? b : a));
44
+ return Math.abs(best[0] - r) < 0.05 ? best[1] : `${r.toFixed(2)}:1`;
45
+ }
46
+ /**
47
+ * Short format badge — the file extension, uppercased.
48
+ *
49
+ * Falls back to the MIME subtype so a URL with no extension (a signed CDN link,
50
+ * an upload route) still shows something meaningful rather than a blank chip.
51
+ */
52
+ export function formatBadge(filenameOrUrl, mimeType) {
53
+ const path = filenameOrUrl.split(/[?#]/)[0] ?? '';
54
+ const segment = path.split('/').pop() ?? '';
55
+ // Only a segment that actually contains a dot has an extension. Comparing
56
+ // against the whole path instead of the segment let a dotless filename —
57
+ // which is what signed CDN links look like — read as its own extension.
58
+ const ext = segment.includes('.') ? (segment.split('.').pop() ?? '') : '';
59
+ if (ext && ext.length <= 5 && /^[a-z0-9]+$/i.test(ext)) {
60
+ return ext.toUpperCase();
61
+ }
62
+ const sub = mimeType?.split('/')[1];
63
+ return sub ? sub.split('+')[0].toUpperCase() : 'FILE';
64
+ }
65
+ /**
66
+ * Filename for display, from a filename or a URL.
67
+ *
68
+ * Query strings are dropped — a signed URL's token is noise in a 200px-wide
69
+ * toolbar, and it is never what an editor is trying to read.
70
+ */
71
+ export function displayName(filenameOrUrl) {
72
+ const path = filenameOrUrl.split(/[?#]/)[0] ?? '';
73
+ return decodeURIComponent(path.split('/').pop() ?? '') || filenameOrUrl;
74
+ }
75
+ /**
76
+ * A stable background tint for an asset.
77
+ *
78
+ * The design fills the frame with a wash derived from the asset's dominant
79
+ * colour. The media library stores no such value, so it is derived from the
80
+ * name instead: the same asset is always the same colour, which is the property
81
+ * that actually matters for recognising a thumbnail, and it costs no storage
82
+ * and no extra request.
83
+ *
84
+ * Returned as a 135° gradient to the design's `color-mix(tint 45%, #fff)`.
85
+ */
86
+ export function assetWash(seed) {
87
+ let hash = 0;
88
+ for (let i = 0; i < seed.length; i++)
89
+ hash = (hash * 31 + seed.charCodeAt(i)) % 360;
90
+ const tint = `oklch(0.88 0.06 ${hash})`;
91
+ return `linear-gradient(135deg, ${tint}, color-mix(in srgb, ${tint} 45%, #fff))`;
92
+ }
93
+ //# sourceMappingURL=media-format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-format.js","sourceRoot":"","sources":["../../src/lib/media-format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import type { PageSection } from '@prenta/core/page-sections';
2
+ import type { PageAuditInput } from '@prenta/core/seo/page-audit';
3
+ import type { SeoContentRecord } from './seo-service.js';
4
+ /**
5
+ * One place that turns editor state into `PageAuditInput`.
6
+ *
7
+ * Two callers build the audit and their numbers must agree:
8
+ *
9
+ * - the page editor, from the SAVED record, so the header chip and rail dot
10
+ * have a score from first paint rather than after someone opens a tab;
11
+ * - the SEO pane, from LIVE form state, so the score moves as you type.
12
+ *
13
+ * They differ only in where the metadata comes from — saved versus unsaved —
14
+ * which is exactly the difference an editor expects to see. Everything else
15
+ * (which fields feed which check, how the title is rendered, what counts as
16
+ * unknown) is decided here once. Two hand-written copies of this mapping is how
17
+ * the editor ended up showing three different scores in the first place.
18
+ */
19
+ export interface AuditMetaSource {
20
+ /** Page-level meta title, before the site title template is applied. */
21
+ metaTitle: string;
22
+ metaDescription: string;
23
+ focusKeyword: string;
24
+ structuredDataType: string;
25
+ noindex: boolean;
26
+ /** `nosnippet` — blocks AI Overview and chat citations outright. */
27
+ nosnippet: boolean;
28
+ /** `NEVER` excludes the page from llms.txt. */
29
+ llmsInclude: string;
30
+ /** True when an OG image is set — dimensions are not stored, see below. */
31
+ hasOgImage: boolean;
32
+ }
33
+ export interface BuildAuditInputArgs {
34
+ sections: PageSection[];
35
+ meta: AuditMetaSource;
36
+ slug: string;
37
+ /** Document title and site template, for the rendered `<title>`. */
38
+ record: Pick<SeoContentRecord, 'title' | 'siteName' | 'titleTemplate' | 'url'> | null;
39
+ /** Fallback title when there is no saved record yet. */
40
+ fallbackTitle?: string;
41
+ }
42
+ export declare function buildPageAuditInput({ sections, meta, slug, record, fallbackTitle, }: BuildAuditInputArgs): PageAuditInput;
43
+ /** Meta source from a saved SEO record. */
44
+ export declare function metaFromRecord(record: SeoContentRecord): AuditMetaSource;
45
+ //# sourceMappingURL=page-audit-input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-audit-input.d.ts","sourceRoot":"","sources":["../../src/lib/page-audit-input.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAExD;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,SAAS,EAAE,MAAM,CAAA;IACjB,eAAe,EAAE,MAAM,CAAA;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,OAAO,EAAE,OAAO,CAAA;IAChB,oEAAoE;IACpE,SAAS,EAAE,OAAO,CAAA;IAClB,+CAA+C;IAC/C,WAAW,EAAE,MAAM,CAAA;IACnB,2EAA2E;IAC3E,UAAU,EAAE,OAAO,CAAA;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,IAAI,EAAE,eAAe,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,oEAAoE;IACpE,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,OAAO,GAAG,UAAU,GAAG,eAAe,GAAG,KAAK,CAAC,GAAG,IAAI,CAAA;IACrF,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,aAAa,GACd,EAAE,mBAAmB,GAAG,cAAc,CAsCtC;AAED,2CAA2C;AAC3C,wBAAgB,cAAc,CAAC,MAAM,EAAE,gBAAgB,GAAG,eAAe,CAWxE"}
@@ -0,0 +1,53 @@
1
+ import { resolveRenderedMetaTitle } from '@prenta/core/client';
2
+ export function buildPageAuditInput({ sections, meta, slug, record, fallbackTitle, }) {
3
+ // Measure the title a searcher actually sees — the page meta title with the
4
+ // site's template applied — not the raw field. Checking the raw field would
5
+ // give length advice that contradicts the rendered result.
6
+ const renderedTitle = record
7
+ ? resolveRenderedMetaTitle({
8
+ metaTitlePage: meta.metaTitle,
9
+ documentTitle: record.title,
10
+ site: {
11
+ siteName: record.siteName ?? undefined,
12
+ titleTemplate: record.titleTemplate ?? undefined,
13
+ },
14
+ })
15
+ : meta.metaTitle.trim() || fallbackTitle || '';
16
+ return {
17
+ sections,
18
+ seo: {
19
+ title: renderedTitle,
20
+ description: meta.metaDescription,
21
+ slug: slug || record?.url?.split('/').filter(Boolean).pop() || '',
22
+ keyword: meta.focusKeyword,
23
+ schema: meta.structuredDataType,
24
+ // `undefined` (unknown) when an image is set: the record stores a URL
25
+ // with no dimensions, and the check needs width and ratio. `null` when
26
+ // none is set, which is a genuine absence and correctly fails.
27
+ ogImage: meta.hasOgImage ? undefined : null,
28
+ robots: {
29
+ index: !meta.noindex,
30
+ snippet: !meta.nosnippet,
31
+ },
32
+ },
33
+ llmsTxt: meta.llmsInclude !== 'NEVER',
34
+ // Not resolved in the editor yet; both report not-applicable rather than
35
+ // failing an editor for a gap in the plumbing.
36
+ brand: '',
37
+ author: undefined,
38
+ };
39
+ }
40
+ /** Meta source from a saved SEO record. */
41
+ export function metaFromRecord(record) {
42
+ return {
43
+ metaTitle: record.metaTitlePage ?? '',
44
+ metaDescription: record.metaDescription ?? '',
45
+ focusKeyword: record.focusKeyword ?? '',
46
+ structuredDataType: record.structuredDataType ?? 'WebPage',
47
+ noindex: record.noindex,
48
+ nosnippet: record.nosnippet ?? false,
49
+ llmsInclude: record.llmsInclude ?? 'AUTO',
50
+ hasOgImage: Boolean(record.ogImage),
51
+ };
52
+ }
53
+ //# sourceMappingURL=page-audit-input.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-audit-input.js","sourceRoot":"","sources":["../../src/lib/page-audit-input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAA;AA6C9D,MAAM,UAAU,mBAAmB,CAAC,EAClC,QAAQ,EACR,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,aAAa,GACO;IACpB,4EAA4E;IAC5E,4EAA4E;IAC5E,2DAA2D;IAC3D,MAAM,aAAa,GAAG,MAAM;QAC1B,CAAC,CAAC,wBAAwB,CAAC;YACvB,aAAa,EAAE,IAAI,CAAC,SAAS;YAC7B,aAAa,EAAE,MAAM,CAAC,KAAK;YAC3B,IAAI,EAAE;gBACJ,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,SAAS;gBACtC,aAAa,EAAE,MAAM,CAAC,aAAa,IAAI,SAAS;aACjD;SACF,CAAC;QACJ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,aAAa,IAAI,EAAE,CAAA;IAEhD,OAAO;QACL,QAAQ;QACR,GAAG,EAAE;YACH,KAAK,EAAE,aAAa;YACpB,WAAW,EAAE,IAAI,CAAC,eAAe;YACjC,IAAI,EAAE,IAAI,IAAI,MAAM,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE;YACjE,OAAO,EAAE,IAAI,CAAC,YAAY;YAC1B,MAAM,EAAE,IAAI,CAAC,kBAAkB;YAC/B,sEAAsE;YACtE,uEAAuE;YACvE,+DAA+D;YAC/D,OAAO,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;YAC3C,MAAM,EAAE;gBACN,KAAK,EAAE,CAAC,IAAI,CAAC,OAAO;gBACpB,OAAO,EAAE,CAAC,IAAI,CAAC,SAAS;aACzB;SACF;QACD,OAAO,EAAE,IAAI,CAAC,WAAW,KAAK,OAAO;QACrC,yEAAyE;QACzE,+CAA+C;QAC/C,KAAK,EAAE,EAAE;QACT,MAAM,EAAE,SAAS;KAClB,CAAA;AACH,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,cAAc,CAAC,MAAwB;IACrD,OAAO;QACL,SAAS,EAAE,MAAM,CAAC,aAAa,IAAI,EAAE;QACrC,eAAe,EAAE,MAAM,CAAC,eAAe,IAAI,EAAE;QAC7C,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,EAAE;QACvC,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,IAAI,SAAS;QAC1D,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,KAAK;QACpC,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,MAAM;QACzC,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC;KACpC,CAAA;AACH,CAAC"}
@@ -22,6 +22,18 @@ export interface EditorPage {
22
22
  updatedAt: string | null;
23
23
  scheduledAt: string | null;
24
24
  scheduledUnpublishAt: string | null;
25
+ /**
26
+ * Displayed byline. Read by `composePageMeta` into the page's structured
27
+ * data, so this is a real output and not decoration.
28
+ *
29
+ * Deliberately separate from the document's `createdById`, which the API
30
+ * never reassigns. That field records who created the record — provenance —
31
+ * and overwriting it to change a displayed name would trade an audit trail
32
+ * for a label.
33
+ */
34
+ authorName: string;
35
+ /** CMS user behind the byline, when it is one. Empty for generic/guest. */
36
+ authorId: string;
25
37
  /**
26
38
  * True when the document was authored in the visual Page Builder (it carries
27
39
  * a `_layout` tree). Such a page's `sections` are *compiled* from the layout
@@ -1 +1 @@
1
- {"version":3,"file":"page-editor-service.d.ts","sourceRoot":"","sources":["../../src/lib/page-editor-service.ts"],"names":[],"mappings":"AAmBA,OAAO,EAEL,aAAa,EACb,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,uBAAuB,EACvB,WAAW,EACX,eAAe,EACf,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,iBAAiB,EAGjB,KAAK,WAAW,EACjB,MAAM,4BAA4B,CAAA;AAKnC,YAAY,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAI7D,OAAO,EACL,aAAa,EACb,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,uBAAuB,EACvB,WAAW,EACX,eAAe,EACf,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,iBAAiB,GAClB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,CAAA;AAEzE,eAAO,MAAM,gBAAgB,UAAU,CAAA;AAEvC,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,UAAU,CAAA;IAClB;;;;OAIG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC;;;;;OAKG;IACH,YAAY,EAAE,OAAO,CAAA;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAQD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,UAAQ,GAAG,gBAAgB,CAoCnF;AAoBD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAE3D;AAmDD,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAI5E;AAkCD,wDAAwD;AACxD,wBAAsB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAOtE;AAED,iDAAiD;AACjD,wBAAsB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAQzE;AAED,wEAAwE;AACxE,wBAAsB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAUvE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,YAAY,GAAE,MAAM,GAAG,IAAW,GAAG,UAAU,CAiBxE"}
1
+ {"version":3,"file":"page-editor-service.d.ts","sourceRoot":"","sources":["../../src/lib/page-editor-service.ts"],"names":[],"mappings":"AAmBA,OAAO,EAEL,aAAa,EACb,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,uBAAuB,EACvB,WAAW,EACX,eAAe,EACf,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,iBAAiB,EAGjB,KAAK,WAAW,EACjB,MAAM,4BAA4B,CAAA;AAKnC,YAAY,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAI7D,OAAO,EACL,aAAa,EACb,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,uBAAuB,EACvB,WAAW,EACX,eAAe,EACf,oBAAoB,EACpB,0BAA0B,EAC1B,qBAAqB,EACrB,iBAAiB,GAClB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,CAAA;AAEzE,eAAO,MAAM,gBAAgB,UAAU,CAAA;AAEvC,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,UAAU,CAAA;IAClB;;;;OAIG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC;;;;;;;;OAQG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,YAAY,EAAE,OAAO,CAAA;CACtB;AAED,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAQD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,UAAU,UAAQ,GAAG,gBAAgB,CAoCnF;AAoBD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,CAE3D;AAqDD,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAI5E;AAoCD,wDAAwD;AACxD,wBAAsB,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAOtE;AAED,iDAAiD;AACjD,wBAAsB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAQzE;AAED,wEAAwE;AACxE,wBAAsB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAUvE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,YAAY,GAAE,MAAM,GAAG,IAAW,GAAG,UAAU,CAmBxE"}
@@ -118,6 +118,8 @@ function rowToEditorPage(doc) {
118
118
  updatedAt: doc.updatedAt ?? null,
119
119
  scheduledAt: doc.scheduledAt ?? null,
120
120
  scheduledUnpublishAt: doc.scheduledUnpublishAt ?? null,
121
+ authorName: typeof data.authorName === 'string' ? data.authorName : '',
122
+ authorId: typeof data.authorId === 'string' ? data.authorId : '',
121
123
  builderOwned,
122
124
  };
123
125
  }
@@ -145,6 +147,8 @@ function toWriteBody(page, extra) {
145
147
  sections: page.sections.map(({ unmanaged: _unmanaged, ...rest }) => rest),
146
148
  metaTitle: page.seoTitle,
147
149
  metaDescription: page.seoDescription,
150
+ authorName: page.authorName,
151
+ authorId: page.authorId,
148
152
  ...extra,
149
153
  };
150
154
  return JSON.stringify(payload);
@@ -203,6 +207,8 @@ export function emptyPage(parentPageId = null) {
203
207
  updatedAt: null,
204
208
  scheduledAt: null,
205
209
  scheduledUnpublishAt: null,
210
+ authorName: '',
211
+ authorId: '',
206
212
  builderOwned: false,
207
213
  };
208
214
  }