@prenta/admin 0.92.0 → 0.94.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 (425) hide show
  1. package/CHANGELOG.md +663 -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__/lib/version-diff.test.d.ts +2 -0
  74. package/dist/__tests__/lib/version-diff.test.d.ts.map +1 -0
  75. package/dist/__tests__/lib/version-diff.test.js +230 -0
  76. package/dist/__tests__/lib/version-diff.test.js.map +1 -0
  77. package/dist/__tests__/lib/version-history.test.d.ts +2 -0
  78. package/dist/__tests__/lib/version-history.test.d.ts.map +1 -0
  79. package/dist/__tests__/lib/version-history.test.js +125 -0
  80. package/dist/__tests__/lib/version-history.test.js.map +1 -0
  81. package/dist/__tests__/styles/canvas-container-queries.test.d.ts +2 -0
  82. package/dist/__tests__/styles/canvas-container-queries.test.d.ts.map +1 -0
  83. package/dist/__tests__/styles/canvas-container-queries.test.js +87 -0
  84. package/dist/__tests__/styles/canvas-container-queries.test.js.map +1 -0
  85. package/dist/__tests__/styles/packaging.test.d.ts +2 -0
  86. package/dist/__tests__/styles/packaging.test.d.ts.map +1 -0
  87. package/dist/__tests__/styles/packaging.test.js +31 -0
  88. package/dist/__tests__/styles/packaging.test.js.map +1 -0
  89. package/dist/__tests__/styles/responsive-layer.test.d.ts +2 -0
  90. package/dist/__tests__/styles/responsive-layer.test.d.ts.map +1 -0
  91. package/dist/__tests__/styles/responsive-layer.test.js +127 -0
  92. package/dist/__tests__/styles/responsive-layer.test.js.map +1 -0
  93. package/dist/__tests__/styles/tokens-drift.test.d.ts +2 -0
  94. package/dist/__tests__/styles/tokens-drift.test.d.ts.map +1 -0
  95. package/dist/__tests__/styles/tokens-drift.test.js +92 -0
  96. package/dist/__tests__/styles/tokens-drift.test.js.map +1 -0
  97. package/dist/__tests__/views/admin-renderer-compat.test.d.ts +2 -0
  98. package/dist/__tests__/views/admin-renderer-compat.test.d.ts.map +1 -0
  99. package/dist/__tests__/views/admin-renderer-compat.test.js +43 -0
  100. package/dist/__tests__/views/admin-renderer-compat.test.js.map +1 -0
  101. package/dist/__tests__/views/admin-section-renderers.test.js +52 -4
  102. package/dist/__tests__/views/admin-section-renderers.test.js.map +1 -1
  103. package/dist/__tests__/views/canvas-review-mode.render.test.d.ts +2 -0
  104. package/dist/__tests__/views/canvas-review-mode.render.test.d.ts.map +1 -0
  105. package/dist/__tests__/views/canvas-review-mode.render.test.js +70 -0
  106. package/dist/__tests__/views/canvas-review-mode.render.test.js.map +1 -0
  107. package/dist/__tests__/views/editor-top-bar.render.test.js +30 -1
  108. package/dist/__tests__/views/editor-top-bar.render.test.js.map +1 -1
  109. package/dist/__tests__/views/page-tab.render.test.d.ts +2 -0
  110. package/dist/__tests__/views/page-tab.render.test.d.ts.map +1 -0
  111. package/dist/__tests__/views/page-tab.render.test.js +151 -0
  112. package/dist/__tests__/views/page-tab.render.test.js.map +1 -0
  113. package/dist/__tests__/views/section-class-parity.test.d.ts +2 -0
  114. package/dist/__tests__/views/section-class-parity.test.d.ts.map +1 -0
  115. package/dist/__tests__/views/section-class-parity.test.js +81 -0
  116. package/dist/__tests__/views/section-class-parity.test.js.map +1 -0
  117. package/dist/__tests__/views/sections-panel-design-owned.render.test.d.ts +2 -0
  118. package/dist/__tests__/views/sections-panel-design-owned.render.test.d.ts.map +1 -0
  119. package/dist/__tests__/views/sections-panel-design-owned.render.test.js +86 -0
  120. package/dist/__tests__/views/sections-panel-design-owned.render.test.js.map +1 -0
  121. package/dist/__tests__/views/version-history-panel.render.test.d.ts +2 -0
  122. package/dist/__tests__/views/version-history-panel.render.test.d.ts.map +1 -0
  123. package/dist/__tests__/views/version-history-panel.render.test.js +153 -0
  124. package/dist/__tests__/views/version-history-panel.render.test.js.map +1 -0
  125. package/dist/components/BreakpointPicker.d.ts +18 -0
  126. package/dist/components/BreakpointPicker.d.ts.map +1 -0
  127. package/dist/components/BreakpointPicker.js +31 -0
  128. package/dist/components/BreakpointPicker.js.map +1 -0
  129. package/dist/components/CanvasEditTip.d.ts +6 -0
  130. package/dist/components/CanvasEditTip.d.ts.map +1 -0
  131. package/dist/components/CanvasEditTip.js +22 -0
  132. package/dist/components/CanvasEditTip.js.map +1 -0
  133. package/dist/components/EditorRail.d.ts +36 -0
  134. package/dist/components/EditorRail.d.ts.map +1 -0
  135. package/dist/components/EditorRail.js +104 -0
  136. package/dist/components/EditorRail.js.map +1 -0
  137. package/dist/components/EditorRailTabs.d.ts +32 -0
  138. package/dist/components/EditorRailTabs.d.ts.map +1 -0
  139. package/dist/components/EditorRailTabs.js +45 -0
  140. package/dist/components/EditorRailTabs.js.map +1 -0
  141. package/dist/components/MediaBlock.d.ts +49 -0
  142. package/dist/components/MediaBlock.d.ts.map +1 -0
  143. package/dist/components/MediaBlock.js +50 -0
  144. package/dist/components/MediaBlock.js.map +1 -0
  145. package/dist/components/ReflowMeter.d.ts +28 -0
  146. package/dist/components/ReflowMeter.d.ts.map +1 -0
  147. package/dist/components/ReflowMeter.js +42 -0
  148. package/dist/components/ReflowMeter.js.map +1 -0
  149. package/dist/components/ZoomChip.d.ts +17 -0
  150. package/dist/components/ZoomChip.d.ts.map +1 -0
  151. package/dist/components/ZoomChip.js +17 -0
  152. package/dist/components/ZoomChip.js.map +1 -0
  153. package/dist/components/breakpoint-icons.d.ts +3 -0
  154. package/dist/components/breakpoint-icons.d.ts.map +1 -0
  155. package/dist/components/breakpoint-icons.js +16 -0
  156. package/dist/components/breakpoint-icons.js.map +1 -0
  157. package/dist/components/page-tab/AuthorPicker.d.ts +31 -0
  158. package/dist/components/page-tab/AuthorPicker.d.ts.map +1 -0
  159. package/dist/components/page-tab/AuthorPicker.js +95 -0
  160. package/dist/components/page-tab/AuthorPicker.js.map +1 -0
  161. package/dist/components/page-tab/DirectiveSwitch.d.ts +38 -0
  162. package/dist/components/page-tab/DirectiveSwitch.d.ts.map +1 -0
  163. package/dist/components/page-tab/DirectiveSwitch.js +33 -0
  164. package/dist/components/page-tab/DirectiveSwitch.js.map +1 -0
  165. package/dist/components/seo/EditorSeoScoreBadge.d.ts +8 -1
  166. package/dist/components/seo/EditorSeoScoreBadge.d.ts.map +1 -1
  167. package/dist/components/seo/EditorSeoScoreBadge.js +2 -2
  168. package/dist/components/seo/EditorSeoScoreBadge.js.map +1 -1
  169. package/dist/components/seo/PageAuditPanel.d.ts +14 -0
  170. package/dist/components/seo/PageAuditPanel.d.ts.map +1 -0
  171. package/dist/components/seo/PageAuditPanel.js +59 -0
  172. package/dist/components/seo/PageAuditPanel.js.map +1 -0
  173. package/dist/components/seo/SeoCharCount.d.ts +19 -7
  174. package/dist/components/seo/SeoCharCount.d.ts.map +1 -1
  175. package/dist/components/seo/SeoCharCount.js +30 -16
  176. package/dist/components/seo/SeoCharCount.js.map +1 -1
  177. package/dist/components/seo/SeoEditorPane.d.ts +15 -1
  178. package/dist/components/seo/SeoEditorPane.d.ts.map +1 -1
  179. package/dist/components/seo/SeoEditorPane.js +118 -21
  180. package/dist/components/seo/SeoEditorPane.js.map +1 -1
  181. package/dist/components/seo/SerpPreview.d.ts +20 -0
  182. package/dist/components/seo/SerpPreview.d.ts.map +1 -0
  183. package/dist/components/seo/SerpPreview.js +6 -0
  184. package/dist/components/seo/SerpPreview.js.map +1 -0
  185. package/dist/hooks/usePageAudit.d.ts +41 -0
  186. package/dist/hooks/usePageAudit.d.ts.map +1 -0
  187. package/dist/hooks/usePageAudit.js +68 -0
  188. package/dist/hooks/usePageAudit.js.map +1 -0
  189. package/dist/hooks/useReflowGuard.d.ts +47 -0
  190. package/dist/hooks/useReflowGuard.d.ts.map +1 -0
  191. package/dist/hooks/useReflowGuard.js +52 -0
  192. package/dist/hooks/useReflowGuard.js.map +1 -0
  193. package/dist/layout/primitives/AdminShell.d.ts.map +1 -1
  194. package/dist/layout/primitives/AdminShell.js +2 -2
  195. package/dist/layout/primitives/AdminShell.js.map +1 -1
  196. package/dist/lib/breakpoints.d.ts +51 -0
  197. package/dist/lib/breakpoints.d.ts.map +1 -0
  198. package/dist/lib/breakpoints.js +84 -0
  199. package/dist/lib/breakpoints.js.map +1 -0
  200. package/dist/lib/canvas-edit-chrome.d.ts +22 -0
  201. package/dist/lib/canvas-edit-chrome.d.ts.map +1 -0
  202. package/dist/lib/canvas-edit-chrome.js +81 -0
  203. package/dist/lib/canvas-edit-chrome.js.map +1 -0
  204. package/dist/lib/media-format.d.ts +55 -0
  205. package/dist/lib/media-format.d.ts.map +1 -0
  206. package/dist/lib/media-format.js +93 -0
  207. package/dist/lib/media-format.js.map +1 -0
  208. package/dist/lib/page-audit-input.d.ts +45 -0
  209. package/dist/lib/page-audit-input.d.ts.map +1 -0
  210. package/dist/lib/page-audit-input.js +53 -0
  211. package/dist/lib/page-audit-input.js.map +1 -0
  212. package/dist/lib/page-editor-service.d.ts +12 -0
  213. package/dist/lib/page-editor-service.d.ts.map +1 -1
  214. package/dist/lib/page-editor-service.js +6 -0
  215. package/dist/lib/page-editor-service.js.map +1 -1
  216. package/dist/lib/page-redirect.d.ts +39 -0
  217. package/dist/lib/page-redirect.d.ts.map +1 -0
  218. package/dist/lib/page-redirect.js +38 -0
  219. package/dist/lib/page-redirect.js.map +1 -0
  220. package/dist/lib/rail-width.d.ts +23 -0
  221. package/dist/lib/rail-width.d.ts.map +1 -0
  222. package/dist/lib/rail-width.js +59 -0
  223. package/dist/lib/rail-width.js.map +1 -0
  224. package/dist/lib/reflow-baseline.d.ts +45 -0
  225. package/dist/lib/reflow-baseline.d.ts.map +1 -0
  226. package/dist/lib/reflow-baseline.js +38 -0
  227. package/dist/lib/reflow-baseline.js.map +1 -0
  228. package/dist/lib/reflow-probe.d.ts +159 -0
  229. package/dist/lib/reflow-probe.d.ts.map +1 -0
  230. package/dist/lib/reflow-probe.js +228 -0
  231. package/dist/lib/reflow-probe.js.map +1 -0
  232. package/dist/lib/seo-service.d.ts +10 -0
  233. package/dist/lib/seo-service.d.ts.map +1 -1
  234. package/dist/lib/seo-service.js.map +1 -1
  235. package/dist/lib/version-diff.d.ts +50 -0
  236. package/dist/lib/version-diff.d.ts.map +1 -0
  237. package/dist/lib/version-diff.js +294 -0
  238. package/dist/lib/version-diff.js.map +1 -0
  239. package/dist/lib/version-history.d.ts +73 -0
  240. package/dist/lib/version-history.d.ts.map +1 -0
  241. package/dist/lib/version-history.js +126 -0
  242. package/dist/lib/version-history.js.map +1 -0
  243. package/dist/prenta-admin.css +1 -1
  244. package/dist/views/page-editor/CanvasMetaLine.d.ts +19 -0
  245. package/dist/views/page-editor/CanvasMetaLine.d.ts.map +1 -0
  246. package/dist/views/page-editor/CanvasMetaLine.js +23 -0
  247. package/dist/views/page-editor/CanvasMetaLine.js.map +1 -0
  248. package/dist/views/page-editor/EditorCanvas.d.ts +47 -8
  249. package/dist/views/page-editor/EditorCanvas.d.ts.map +1 -1
  250. package/dist/views/page-editor/EditorCanvas.js +68 -34
  251. package/dist/views/page-editor/EditorCanvas.js.map +1 -1
  252. package/dist/views/page-editor/EditorTopBar.d.ts +21 -2
  253. package/dist/views/page-editor/EditorTopBar.d.ts.map +1 -1
  254. package/dist/views/page-editor/EditorTopBar.js +13 -20
  255. package/dist/views/page-editor/EditorTopBar.js.map +1 -1
  256. package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
  257. package/dist/views/page-editor/PageSectionEditor.js +383 -78
  258. package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
  259. package/dist/views/page-editor/PageTab.d.ts +36 -0
  260. package/dist/views/page-editor/PageTab.d.ts.map +1 -0
  261. package/dist/views/page-editor/PageTab.js +65 -0
  262. package/dist/views/page-editor/PageTab.js.map +1 -0
  263. package/dist/views/page-editor/ReviewBar.d.ts +21 -0
  264. package/dist/views/page-editor/ReviewBar.d.ts.map +1 -0
  265. package/dist/views/page-editor/ReviewBar.js +22 -0
  266. package/dist/views/page-editor/ReviewBar.js.map +1 -0
  267. package/dist/views/page-editor/SectionInspector.d.ts.map +1 -1
  268. package/dist/views/page-editor/SectionInspector.js +64 -20
  269. package/dist/views/page-editor/SectionInspector.js.map +1 -1
  270. package/dist/views/page-editor/SectionsPanel.d.ts +30 -5
  271. package/dist/views/page-editor/SectionsPanel.d.ts.map +1 -1
  272. package/dist/views/page-editor/SectionsPanel.js +38 -7
  273. package/dist/views/page-editor/SectionsPanel.js.map +1 -1
  274. package/dist/views/page-editor/SitePreviewFrame.d.ts.map +1 -1
  275. package/dist/views/page-editor/SitePreviewFrame.js +3 -0
  276. package/dist/views/page-editor/SitePreviewFrame.js.map +1 -1
  277. package/dist/views/page-editor/VersionHistoryPanel.d.ts +29 -0
  278. package/dist/views/page-editor/VersionHistoryPanel.d.ts.map +1 -0
  279. package/dist/views/page-editor/VersionHistoryPanel.js +81 -0
  280. package/dist/views/page-editor/VersionHistoryPanel.js.map +1 -0
  281. package/dist/views/page-editor/reflow-context.d.ts +28 -0
  282. package/dist/views/page-editor/reflow-context.d.ts.map +1 -0
  283. package/dist/views/page-editor/reflow-context.js +14 -0
  284. package/dist/views/page-editor/reflow-context.js.map +1 -0
  285. package/dist/views/page-editor/sections/ByTheNumbersSection.d.ts.map +1 -1
  286. package/dist/views/page-editor/sections/ByTheNumbersSection.js +1 -1
  287. package/dist/views/page-editor/sections/ByTheNumbersSection.js.map +1 -1
  288. package/dist/views/page-editor/sections/CtaBannerSection.d.ts.map +1 -1
  289. package/dist/views/page-editor/sections/CtaBannerSection.js +1 -1
  290. package/dist/views/page-editor/sections/CtaBannerSection.js.map +1 -1
  291. package/dist/views/page-editor/sections/FeatureHighlightSection.d.ts.map +1 -1
  292. package/dist/views/page-editor/sections/FeatureHighlightSection.js +2 -2
  293. package/dist/views/page-editor/sections/FeatureHighlightSection.js.map +1 -1
  294. package/dist/views/page-editor/sections/GallerySection.d.ts.map +1 -1
  295. package/dist/views/page-editor/sections/GallerySection.js +7 -3
  296. package/dist/views/page-editor/sections/GallerySection.js.map +1 -1
  297. package/dist/views/page-editor/sections/HeroBannerSection.js +1 -1
  298. package/dist/views/page-editor/sections/HeroBannerSection.js.map +1 -1
  299. package/dist/views/page-editor/sections/MissionSection.d.ts.map +1 -1
  300. package/dist/views/page-editor/sections/MissionSection.js +1 -1
  301. package/dist/views/page-editor/sections/MissionSection.js.map +1 -1
  302. package/dist/views/page-editor/sections/PullQuoteSection.d.ts.map +1 -1
  303. package/dist/views/page-editor/sections/PullQuoteSection.js +1 -1
  304. package/dist/views/page-editor/sections/PullQuoteSection.js.map +1 -1
  305. package/dist/views/page-editor/sections/RelatedPostsSection.d.ts.map +1 -1
  306. package/dist/views/page-editor/sections/RelatedPostsSection.js +1 -1
  307. package/dist/views/page-editor/sections/RelatedPostsSection.js.map +1 -1
  308. package/dist/views/page-editor/sections/SectionRenderer.d.ts +13 -1
  309. package/dist/views/page-editor/sections/SectionRenderer.d.ts.map +1 -1
  310. package/dist/views/page-editor/sections/SectionRenderer.js +66 -26
  311. package/dist/views/page-editor/sections/SectionRenderer.js.map +1 -1
  312. package/dist/views/page-editor/sections/admin-renderers.d.ts +47 -15
  313. package/dist/views/page-editor/sections/admin-renderers.d.ts.map +1 -1
  314. package/dist/views/page-editor/sections/admin-renderers.js +27 -15
  315. package/dist/views/page-editor/sections/admin-renderers.js.map +1 -1
  316. package/dist/views/page-editor/sections/parts.d.ts +0 -1
  317. package/dist/views/page-editor/sections/parts.d.ts.map +1 -1
  318. package/dist/views/page-editor/sections/parts.js +41 -16
  319. package/dist/views/page-editor/sections/parts.js.map +1 -1
  320. package/dist/views/page-editor/viewports.d.ts +7 -14
  321. package/dist/views/page-editor/viewports.d.ts.map +1 -1
  322. package/dist/views/page-editor/viewports.js +7 -12
  323. package/dist/views/page-editor/viewports.js.map +1 -1
  324. package/dist/views/post-editor/PostEditorCanvas.d.ts +2 -1
  325. package/dist/views/post-editor/PostEditorCanvas.d.ts.map +1 -1
  326. package/dist/views/post-editor/PostEditorCanvas.js +5 -2
  327. package/dist/views/post-editor/PostEditorCanvas.js.map +1 -1
  328. package/dist/views/post-editor/PostSectionEditor.d.ts.map +1 -1
  329. package/dist/views/post-editor/PostSectionEditor.js +2 -20
  330. package/dist/views/post-editor/PostSectionEditor.js.map +1 -1
  331. package/dist/views/post-editor/PostTemplateEditor.d.ts.map +1 -1
  332. package/dist/views/post-editor/PostTemplateEditor.js +4 -1
  333. package/dist/views/post-editor/PostTemplateEditor.js.map +1 -1
  334. package/package.json +5 -3
  335. package/src/AdminRoot.tsx +27 -6
  336. package/src/__tests__/components/breakpoint-controls.render.test.tsx +57 -0
  337. package/src/__tests__/components/canvas-edit-tip.render.test.tsx +41 -0
  338. package/src/__tests__/components/editor-rail.render.test.tsx +192 -0
  339. package/src/__tests__/components/media-block.render.test.tsx +126 -0
  340. package/src/__tests__/components/page-audit-panel.render.test.tsx +64 -0
  341. package/src/__tests__/components/reflow-meter.render.test.tsx +144 -0
  342. package/src/__tests__/components/seo-char-count.test.tsx +26 -6
  343. package/src/__tests__/components/serp-preview.render.test.tsx +32 -0
  344. package/src/__tests__/lib/breakpoints.test.ts +84 -0
  345. package/src/__tests__/lib/canvas-edit-chrome.test.ts +56 -0
  346. package/src/__tests__/lib/media-format.test.ts +89 -0
  347. package/src/__tests__/lib/page-audit-input.test.ts +144 -0
  348. package/src/__tests__/lib/page-editor-service.test.ts +2 -0
  349. package/src/__tests__/lib/page-redirect.test.ts +29 -0
  350. package/src/__tests__/lib/rail-width.test.ts +86 -0
  351. package/src/__tests__/lib/reflow-baseline.test.ts +64 -0
  352. package/src/__tests__/lib/reflow-probe.test.ts +95 -0
  353. package/src/__tests__/lib/version-diff.test.ts +278 -0
  354. package/src/__tests__/lib/version-history.test.ts +166 -0
  355. package/src/__tests__/styles/canvas-container-queries.test.ts +103 -0
  356. package/src/__tests__/styles/packaging.test.ts +38 -0
  357. package/src/__tests__/styles/responsive-layer.test.ts +152 -0
  358. package/src/__tests__/styles/tokens-drift.test.ts +110 -0
  359. package/src/__tests__/views/admin-renderer-compat.test.ts +48 -0
  360. package/src/__tests__/views/admin-section-renderers.test.tsx +70 -4
  361. package/src/__tests__/views/canvas-review-mode.render.test.tsx +94 -0
  362. package/src/__tests__/views/editor-top-bar.render.test.tsx +43 -1
  363. package/src/__tests__/views/page-tab.render.test.tsx +226 -0
  364. package/src/__tests__/views/section-class-parity.test.ts +98 -0
  365. package/src/__tests__/views/sections-panel-design-owned.render.test.tsx +104 -0
  366. package/src/__tests__/views/version-history-panel.render.test.tsx +195 -0
  367. package/src/components/BreakpointPicker.tsx +114 -0
  368. package/src/components/CanvasEditTip.tsx +46 -0
  369. package/src/components/EditorRail.tsx +219 -0
  370. package/src/components/EditorRailTabs.tsx +117 -0
  371. package/src/components/MediaBlock.tsx +223 -0
  372. package/src/components/ReflowMeter.tsx +125 -0
  373. package/src/components/ZoomChip.tsx +39 -0
  374. package/src/components/breakpoint-icons.ts +17 -0
  375. package/src/components/page-tab/AuthorPicker.tsx +240 -0
  376. package/src/components/page-tab/DirectiveSwitch.tsx +89 -0
  377. package/src/components/seo/EditorSeoScoreBadge.tsx +9 -1
  378. package/src/components/seo/PageAuditPanel.tsx +192 -0
  379. package/src/components/seo/SeoCharCount.tsx +55 -24
  380. package/src/components/seo/SeoEditorPane.tsx +213 -31
  381. package/src/components/seo/SerpPreview.tsx +41 -0
  382. package/src/hooks/usePageAudit.ts +98 -0
  383. package/src/hooks/useReflowGuard.ts +96 -0
  384. package/src/layout/primitives/AdminShell.tsx +13 -1
  385. package/src/lib/breakpoints.ts +107 -0
  386. package/src/lib/canvas-edit-chrome.ts +82 -0
  387. package/src/lib/media-format.ts +99 -0
  388. package/src/lib/page-audit-input.ts +104 -0
  389. package/src/lib/page-editor-service.ts +18 -0
  390. package/src/lib/page-redirect.ts +60 -0
  391. package/src/lib/rail-width.ts +58 -0
  392. package/src/lib/reflow-baseline.ts +70 -0
  393. package/src/lib/reflow-probe.ts +299 -0
  394. package/src/lib/seo-service.ts +10 -0
  395. package/src/lib/version-diff.ts +376 -0
  396. package/src/lib/version-history.ts +192 -0
  397. package/src/styles/prenta-tokens.css +120 -42
  398. package/src/styles/responsive.css +281 -0
  399. package/src/styles/theme.css +138 -0
  400. package/src/views/page-editor/CanvasMetaLine.tsx +50 -0
  401. package/src/views/page-editor/EditorCanvas.tsx +140 -54
  402. package/src/views/page-editor/EditorTopBar.tsx +55 -51
  403. package/src/views/page-editor/PageSectionEditor.tsx +575 -185
  404. package/src/views/page-editor/PageTab.tsx +433 -0
  405. package/src/views/page-editor/ReviewBar.tsx +76 -0
  406. package/src/views/page-editor/SectionInspector.tsx +84 -42
  407. package/src/views/page-editor/SectionsPanel.tsx +282 -141
  408. package/src/views/page-editor/SitePreviewFrame.tsx +3 -0
  409. package/src/views/page-editor/VersionHistoryPanel.tsx +302 -0
  410. package/src/views/page-editor/reflow-context.ts +43 -0
  411. package/src/views/page-editor/sections/ByTheNumbersSection.tsx +3 -2
  412. package/src/views/page-editor/sections/CtaBannerSection.tsx +3 -1
  413. package/src/views/page-editor/sections/FeatureHighlightSection.tsx +3 -2
  414. package/src/views/page-editor/sections/GallerySection.tsx +7 -3
  415. package/src/views/page-editor/sections/HeroBannerSection.tsx +2 -2
  416. package/src/views/page-editor/sections/MissionSection.tsx +4 -2
  417. package/src/views/page-editor/sections/PullQuoteSection.tsx +3 -1
  418. package/src/views/page-editor/sections/RelatedPostsSection.tsx +2 -1
  419. package/src/views/page-editor/sections/SectionRenderer.tsx +115 -34
  420. package/src/views/page-editor/sections/admin-renderers.tsx +54 -15
  421. package/src/views/page-editor/sections/parts.tsx +46 -18
  422. package/src/views/page-editor/viewports.ts +13 -26
  423. package/src/views/post-editor/PostEditorCanvas.tsx +18 -11
  424. package/src/views/post-editor/PostSectionEditor.tsx +1 -56
  425. package/src/views/post-editor/PostTemplateEditor.tsx +3 -0
@@ -0,0 +1,69 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { RAIL_DEFAULT_WIDTH, RAIL_MAX_WIDTH, RAIL_MIN_WIDTH, clampRailWidth, readRailWidth, writeRailWidth, } from '../../lib/rail-width.js';
3
+ function fakeStorage(initial = {}) {
4
+ const map = new Map(Object.entries(initial));
5
+ return {
6
+ getItem: (k) => map.get(k) ?? null,
7
+ setItem: (k, v) => void map.set(k, v),
8
+ read: (k) => map.get(k) ?? null,
9
+ };
10
+ }
11
+ describe('clampRailWidth', () => {
12
+ it('holds the rail inside its usable range', () => {
13
+ expect(clampRailWidth(200)).toBe(RAIL_MIN_WIDTH);
14
+ expect(clampRailWidth(900)).toBe(RAIL_MAX_WIDTH);
15
+ expect(clampRailWidth(420)).toBe(420);
16
+ });
17
+ it('rounds to whole pixels', () => {
18
+ // Pointer deltas are fractional on scaled displays; a fractional width
19
+ // makes the rail's border land between device pixels and shimmer.
20
+ expect(clampRailWidth(420.6)).toBe(421);
21
+ });
22
+ it('falls back to the default on junk rather than producing NaN width', () => {
23
+ expect(clampRailWidth(Number.NaN)).toBe(RAIL_DEFAULT_WIDTH);
24
+ expect(clampRailWidth(Number.POSITIVE_INFINITY)).toBe(RAIL_DEFAULT_WIDTH);
25
+ });
26
+ });
27
+ describe('readRailWidth', () => {
28
+ it('returns the default when nothing is stored', () => {
29
+ expect(readRailWidth(fakeStorage())).toBe(RAIL_DEFAULT_WIDTH);
30
+ });
31
+ it('reads a stored width', () => {
32
+ expect(readRailWidth(fakeStorage({ 'pb.panelW': '480' }))).toBe(480);
33
+ });
34
+ it('clamps a stored width that is out of range', () => {
35
+ // A width persisted before the range changed, or hand-edited in devtools,
36
+ // must not be able to render an unusable rail.
37
+ expect(readRailWidth(fakeStorage({ 'pb.panelW': '5000' }))).toBe(RAIL_MAX_WIDTH);
38
+ expect(readRailWidth(fakeStorage({ 'pb.panelW': '10' }))).toBe(RAIL_MIN_WIDTH);
39
+ });
40
+ it('survives a non-numeric stored value', () => {
41
+ expect(readRailWidth(fakeStorage({ 'pb.panelW': 'wide' }))).toBe(RAIL_DEFAULT_WIDTH);
42
+ });
43
+ it('survives storage that throws', () => {
44
+ // Safari in private mode throws on getItem. A rail that cannot size itself
45
+ // must not take down the editor.
46
+ const hostile = {
47
+ getItem: () => {
48
+ throw new Error('SecurityError');
49
+ },
50
+ };
51
+ expect(readRailWidth(hostile)).toBe(RAIL_DEFAULT_WIDTH);
52
+ });
53
+ });
54
+ describe('writeRailWidth', () => {
55
+ it('persists the clamped value, not the raw one', () => {
56
+ const store = fakeStorage();
57
+ writeRailWidth(9000, store);
58
+ expect(store.read('pb.panelW')).toBe(String(RAIL_MAX_WIDTH));
59
+ });
60
+ it('swallows a storage failure', () => {
61
+ const hostile = {
62
+ setItem: () => {
63
+ throw new Error('QuotaExceededError');
64
+ },
65
+ };
66
+ expect(() => writeRailWidth(400, hostile)).not.toThrow();
67
+ });
68
+ });
69
+ //# sourceMappingURL=rail-width.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rail-width.test.js","sourceRoot":"","sources":["../../../src/__tests__/lib/rail-width.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=reflow-baseline.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reflow-baseline.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/lib/reflow-baseline.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,56 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { baselineFieldText, snapshotBaseline } from '../../lib/reflow-baseline.js';
3
+ const sections = [
4
+ {
5
+ id: 's1',
6
+ sectionType: 'hero',
7
+ name: 'Hero',
8
+ visible: true,
9
+ content: {
10
+ heading: 'Ship content without layout drift',
11
+ body: 'A design-owned authoring surface.',
12
+ stats: [{ label: 'Uptime', value: '99.9%' }],
13
+ count: 3,
14
+ },
15
+ settings: {},
16
+ },
17
+ ];
18
+ describe('reflow baseline', () => {
19
+ it('captures nothing for a page that is not live', () => {
20
+ // A draft has no published layout to break. An empty snapshot would be
21
+ // worse than none: it reads as "every field was blank", so every line of
22
+ // existing copy is reported as newly added.
23
+ expect(snapshotBaseline(sections, 'DRAFT')).toBeNull();
24
+ expect(snapshotBaseline(sections, 'SCHEDULED')).toBeNull();
25
+ expect(snapshotBaseline(sections, 'PUBLISHED')).not.toBeNull();
26
+ });
27
+ it('is a copy, so later edits to the draft do not move it', () => {
28
+ // The baseline is what is live. If it aliased the section's content object,
29
+ // editing the draft would silently drag the baseline along and the guard
30
+ // would report that nothing ever changed.
31
+ const baseline = snapshotBaseline(sections, 'PUBLISHED');
32
+ sections[0].content.heading = 'Edited in the draft';
33
+ expect(baselineFieldText(baseline, 's1', 'heading')).toBe('Ship content without layout drift');
34
+ sections[0].content.heading = 'Ship content without layout drift';
35
+ });
36
+ it('resolves repeater paths the renderers emit', () => {
37
+ const baseline = snapshotBaseline(sections, 'PUBLISHED');
38
+ expect(baselineFieldText(baseline, 's1', 'stats.0.label')).toBe('Uptime');
39
+ });
40
+ it('returns undefined for anything that is not baseline text', () => {
41
+ const baseline = snapshotBaseline(sections, 'PUBLISHED');
42
+ // No snapshot at all.
43
+ expect(baselineFieldText(null, 's1', 'heading')).toBeUndefined();
44
+ // Section added since publish.
45
+ expect(baselineFieldText(baseline, 'brand-new', 'heading')).toBeUndefined();
46
+ // Field that did not exist.
47
+ expect(baselineFieldText(baseline, 's1', 'subheading')).toBeUndefined();
48
+ // Path that runs off the end of a scalar.
49
+ expect(baselineFieldText(baseline, 's1', 'heading.nope')).toBeUndefined();
50
+ // Present but not a string — a count has no wrapping to compare.
51
+ expect(baselineFieldText(baseline, 's1', 'count')).toBeUndefined();
52
+ // Index past the end of a repeater.
53
+ expect(baselineFieldText(baseline, 's1', 'stats.7.label')).toBeUndefined();
54
+ });
55
+ });
56
+ //# sourceMappingURL=reflow-baseline.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reflow-baseline.test.js","sourceRoot":"","sources":["../../../src/__tests__/lib/reflow-baseline.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=reflow-probe.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reflow-probe.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/lib/reflow-probe.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,73 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { linesFromMetrics, reflowSeverity, worstBreakpoint, } from '../../lib/reflow-probe.js';
3
+ describe('linesFromMetrics', () => {
4
+ it('divides height by line height', () => {
5
+ expect(linesFromMetrics(72, 24, 16)).toBe(3);
6
+ });
7
+ it('rounds rather than floors', () => {
8
+ // Sub-pixel line heights accumulate; 2.97 lines is three lines on screen,
9
+ // and flooring would under-report exactly the overflow being guarded.
10
+ expect(linesFromMetrics(71.3, 24, 16)).toBe(3);
11
+ });
12
+ it('falls back to a ratio when line-height computes to normal', () => {
13
+ // `parseFloat('normal')` is NaN — every element that never set a
14
+ // line-height would otherwise measure as one line forever.
15
+ expect(linesFromMetrics(60, Number.NaN, 16)).toBe(3);
16
+ });
17
+ it('never reports zero lines', () => {
18
+ // An empty field still occupies a line's worth of intent, and zero would
19
+ // read as "this shrank" against any baseline.
20
+ expect(linesFromMetrics(0, 24, 16)).toBe(1);
21
+ expect(linesFromMetrics(0, 0, 0)).toBe(1);
22
+ });
23
+ });
24
+ describe('reflowSeverity', () => {
25
+ it('flags one extra line as growth and two as bad', () => {
26
+ expect(reflowSeverity(3, 2)).toBe('grew');
27
+ expect(reflowSeverity(4, 2)).toBe('grew-badly');
28
+ });
29
+ it('stays quiet when copy shrinks', () => {
30
+ // Shorter copy cannot break a layout that already held the longer version.
31
+ // Flagging it would put a warning on the edit the editor was asked to make.
32
+ expect(reflowSeverity(1, 3)).toBe('same');
33
+ expect(reflowSeverity(2, 2)).toBe('same');
34
+ });
35
+ });
36
+ describe('worstBreakpoint', () => {
37
+ const at = (pairs) => pairs.map(([id, lines]) => ({ id, lines }));
38
+ it('picks the biggest jump', () => {
39
+ const worst = worstBreakpoint(at([
40
+ ['wide', 3],
41
+ ['laptop', 5],
42
+ ['mobile', 4],
43
+ ]), at([
44
+ ['wide', 3],
45
+ ['laptop', 3],
46
+ ['mobile', 3],
47
+ ]));
48
+ expect(worst).toBe('laptop');
49
+ });
50
+ it('breaks a tie toward the narrowest width', () => {
51
+ // The same damage at several sizes is most likely to look broken where
52
+ // there is least room.
53
+ const worst = worstBreakpoint(at([
54
+ ['4k', 4],
55
+ ['laptop', 4],
56
+ ['mobile', 4],
57
+ ]), at([
58
+ ['4k', 3],
59
+ ['laptop', 3],
60
+ ['mobile', 3],
61
+ ]));
62
+ expect(worst).toBe('mobile');
63
+ });
64
+ it('returns nothing when no breakpoint grew', () => {
65
+ expect(worstBreakpoint(at([['wide', 2]]), at([['wide', 3]]))).toBeNull();
66
+ });
67
+ it('ignores breakpoints missing from the baseline', () => {
68
+ // A baseline captured before a preset existed must not make that preset
69
+ // look like a regression.
70
+ expect(worstBreakpoint(at([['4k', 9]]), at([['wide', 1]]))).toBeNull();
71
+ });
72
+ });
73
+ //# sourceMappingURL=reflow-probe.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reflow-probe.test.js","sourceRoot":"","sources":["../../../src/__tests__/lib/reflow-probe.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=version-diff.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-diff.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/lib/version-diff.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,230 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { changedSectionIds, diffVersions, formatValue, groupChangesBySection, } from '../../lib/version-diff.js';
3
+ function section(overrides) {
4
+ return {
5
+ sectionType: 'hero',
6
+ name: 'Hero Banner',
7
+ visible: true,
8
+ content: {},
9
+ settings: {},
10
+ ...overrides,
11
+ };
12
+ }
13
+ describe('formatValue', () => {
14
+ it('renders absent and blank values as an em dash so an empty diff cell is never blank', () => {
15
+ expect(formatValue(null)).toBe('—');
16
+ expect(formatValue(undefined)).toBe('—');
17
+ expect(formatValue('')).toBe('—');
18
+ expect(formatValue(' ')).toBe('—');
19
+ expect(formatValue([])).toBe('—');
20
+ expect(formatValue({})).toBe('—');
21
+ });
22
+ it('summarises structured values rather than dumping them', () => {
23
+ expect(formatValue([{ value: '1' }, { value: '2' }])).toBe('2 items');
24
+ expect(formatValue([{ value: '1' }])).toBe('1 item');
25
+ expect(formatValue({ name: 'hero.jpg', url: '/m/hero.jpg' })).toBe('hero.jpg');
26
+ // No name-ish key: fall back to a shape description, not "[object Object]".
27
+ expect(formatValue({ x: 1, y: 2 })).toBe('2 fields');
28
+ });
29
+ it('reads booleans and numbers as words the panel can show', () => {
30
+ expect(formatValue(true)).toBe('Yes');
31
+ expect(formatValue(false)).toBe('No');
32
+ expect(formatValue(0)).toBe('0');
33
+ });
34
+ });
35
+ describe('diffVersions', () => {
36
+ it('labels content fields through the section registry, not by raw key', () => {
37
+ const before = [section({ id: 's1', content: { heading: 'Old headline' } })];
38
+ const after = [section({ id: 's1', content: { heading: 'New headline' } })];
39
+ const changes = diffVersions(after, before);
40
+ expect(changes).toEqual([
41
+ {
42
+ sectionId: 's1',
43
+ sectionName: 'Hero Banner',
44
+ // 'heading' is `label: 'Heading'` on the hero type in the registry.
45
+ field: 'Heading',
46
+ from: 'Old headline',
47
+ to: 'New headline',
48
+ },
49
+ ]);
50
+ });
51
+ it('falls back to the raw key for a type the registry does not describe', () => {
52
+ const before = [section({ id: 's1', sectionType: 'opal-pricing', content: { tiers: 'a' } })];
53
+ const after = [section({ id: 's1', sectionType: 'opal-pricing', content: { tiers: 'b' } })];
54
+ expect(diffVersions(after, before)[0]?.field).toBe('tiers');
55
+ });
56
+ it('reports a field cleared to nothing — the key exists only in the older snapshot', () => {
57
+ const before = [section({ id: 's1', content: { heading: 'Something' } })];
58
+ const after = [section({ id: 's1', content: {} })];
59
+ expect(diffVersions(after, before)).toEqual([
60
+ { sectionId: 's1', sectionName: 'Hero Banner', field: 'Heading', from: 'Something', to: '—' },
61
+ ]);
62
+ });
63
+ it('reports visibility as a state, because hiding a section is the one structural edit there is', () => {
64
+ const before = [section({ id: 's1', visible: true })];
65
+ const after = [section({ id: 's1', visible: false })];
66
+ expect(diffVersions(after, before)).toEqual([
67
+ {
68
+ sectionId: 's1',
69
+ sectionName: 'Hero Banner',
70
+ field: 'Visibility',
71
+ from: 'Shown',
72
+ to: 'Hidden',
73
+ },
74
+ ]);
75
+ });
76
+ it('reports added and removed sections', () => {
77
+ const before = [section({ id: 's1' })];
78
+ const after = [section({ id: 's1' }), section({ id: 's2', name: 'CTA', sectionType: 'cta' })];
79
+ expect(diffVersions(after, before)).toEqual([
80
+ { sectionId: 's2', sectionName: 'CTA', field: 'Section', from: '—', to: 'Added' },
81
+ ]);
82
+ expect(diffVersions(before, after)).toEqual([
83
+ { sectionId: 's2', sectionName: 'CTA', field: 'Section', from: 'Present', to: 'Removed' },
84
+ ]);
85
+ });
86
+ it('reports nothing when the snapshots match', () => {
87
+ const same = [section({ id: 's1', content: { heading: 'Same' } })];
88
+ expect(diffVersions(same, [section({ id: 's1', content: { heading: 'Same' } })])).toEqual([]);
89
+ });
90
+ it('reports nothing against a missing predecessor', () => {
91
+ // The oldest version we hold is not the moment the page was created, so
92
+ // reporting every field as "added" would state something false.
93
+ expect(diffVersions([section({ id: 's1', content: { heading: 'x' } })], undefined)).toEqual([]);
94
+ expect(diffVersions([section({ id: 's1' })], null)).toEqual([]);
95
+ });
96
+ it('does not report a settings key that was merely absent versus explicitly default', () => {
97
+ // `{}` and `{ backgroundType: undefined }` format identically, so neither
98
+ // produces a row. Without this the first save after a schema addition
99
+ // would light up every section on the canvas.
100
+ const before = [section({ id: 's1', settings: {} })];
101
+ const after = [section({ id: 's1', settings: { backgroundType: undefined } })];
102
+ expect(diffVersions(after, before)).toEqual([]);
103
+ });
104
+ it('labels known settings and reports a real background change', () => {
105
+ const before = [section({ id: 's1', settings: { backgroundType: 'none' } })];
106
+ const after = [section({ id: 's1', settings: { backgroundType: 'solid' } })];
107
+ expect(diffVersions(after, before)).toEqual([
108
+ {
109
+ sectionId: 's1',
110
+ sectionName: 'Hero Banner',
111
+ field: 'Background type',
112
+ from: 'none',
113
+ to: 'solid',
114
+ },
115
+ ]);
116
+ });
117
+ });
118
+ describe('diffVersions — structured fields', () => {
119
+ const stats = (rows) => section({ id: 's1', sectionType: 'stats', name: 'By The Numbers', content: { stats: rows } });
120
+ it('catches an edit that keeps the row count', () => {
121
+ // The bug this exists for: deciding "did it change" from the DISPLAY
122
+ // summary collapses an array to "2 items", so retyping a row while keeping
123
+ // the count formats identically on both sides and the field reports no
124
+ // change — no panel row AND no outline on the canvas, which is exactly the
125
+ // edit a reviewer opened history to find.
126
+ const before = [
127
+ stats([
128
+ { value: '10k', label: 'Users' },
129
+ { value: '99%', label: 'Uptime' },
130
+ ]),
131
+ ];
132
+ const after = [
133
+ stats([
134
+ { value: '20k', label: 'Users' },
135
+ { value: '99%', label: 'Uptime' },
136
+ ]),
137
+ ];
138
+ const changes = diffVersions(after, before);
139
+ expect(changes).toHaveLength(1);
140
+ expect(changes[0]?.from).toBe('10k');
141
+ expect(changes[0]?.to).toBe('20k');
142
+ // Named down to the row and the sub-field, so "which one" has an answer.
143
+ expect(changes[0]?.field).toBe('Stats · Stat 1 · Value');
144
+ expect(changedSectionIds(changes).has('s1')).toBe(true);
145
+ });
146
+ it('never emits a row whose before and after both read as a bare count', () => {
147
+ const before = [
148
+ stats([
149
+ { value: 'a', label: 'x' },
150
+ { value: 'b', label: 'y' },
151
+ ]),
152
+ ];
153
+ const after = [
154
+ stats([
155
+ { value: 'c', label: 'x' },
156
+ { value: 'd', label: 'y' },
157
+ ]),
158
+ ];
159
+ for (const change of diffVersions(after, before)) {
160
+ expect(change.from).not.toBe(change.to);
161
+ expect(change.from).not.toMatch(/^\d+ items?$/);
162
+ }
163
+ });
164
+ it('reports added and removed rows', () => {
165
+ const before = [stats([{ value: '10k', label: 'Users' }])];
166
+ const after = [
167
+ stats([
168
+ { value: '10k', label: 'Users' },
169
+ { value: '99%', label: 'Uptime' },
170
+ ]),
171
+ ];
172
+ expect(diffVersions(after, before)).toEqual([
173
+ expect.objectContaining({ field: 'Stats · Stat 2', from: '—', to: 'Added' }),
174
+ ]);
175
+ expect(diffVersions(before, after)).toEqual([
176
+ expect.objectContaining({ field: 'Stats · Stat 2', from: 'Present', to: 'Removed' }),
177
+ ]);
178
+ });
179
+ it('catches a nested change inside a media object', () => {
180
+ // Same failure one level down: two media objects with the same `name` used
181
+ // to format identically while pointing at different files.
182
+ const media = (url) => ({ name: 'hero.jpg', url });
183
+ const before = [section({ id: 's1', content: { imageUrl: media('/a.jpg') } })];
184
+ const after = [section({ id: 's1', content: { imageUrl: media('/b.jpg') } })];
185
+ expect(diffVersions(after, before)).toHaveLength(1);
186
+ });
187
+ it('treats every spelling of blank as unchanged', () => {
188
+ // Tightening the comparison must not start reporting "— → —".
189
+ for (const [before, after] of [
190
+ ['', null],
191
+ [null, undefined],
192
+ [undefined, ' '],
193
+ [[], undefined],
194
+ [{}, null],
195
+ ]) {
196
+ const changes = diffVersions([section({ id: 's1', content: { heading: after } })], [section({ id: 's1', content: { heading: before } })]);
197
+ expect(changes, `${JSON.stringify(before)} → ${JSON.stringify(after)}`).toEqual([]);
198
+ }
199
+ });
200
+ it('reports an unchanged repeater as no change at all', () => {
201
+ const rows = [{ value: '10k', label: 'Users' }];
202
+ expect(diffVersions([stats(rows)], [stats([...rows.map((r) => ({ ...r }))])])).toEqual([]);
203
+ });
204
+ });
205
+ describe('groupChangesBySection', () => {
206
+ it('groups by section in first-seen order', () => {
207
+ const before = [
208
+ section({ id: 's1', content: { heading: 'a', body: 'a' } }),
209
+ section({ id: 's2', name: 'CTA', sectionType: 'cta', content: { heading: 'a' } }),
210
+ ];
211
+ const after = [
212
+ section({ id: 's1', content: { heading: 'b', body: 'b' } }),
213
+ section({ id: 's2', name: 'CTA', sectionType: 'cta', content: { heading: 'b' } }),
214
+ ];
215
+ const groups = groupChangesBySection(diffVersions(after, before));
216
+ expect(groups.map((g) => g.sectionId)).toEqual(['s1', 's2']);
217
+ expect(groups[0]?.changes).toHaveLength(2);
218
+ expect(groups[1]?.changes).toHaveLength(1);
219
+ });
220
+ });
221
+ describe('changedSectionIds', () => {
222
+ it('collapses several changes on one section to a single outline', () => {
223
+ const before = [section({ id: 's1', content: { heading: 'a', body: 'a' } })];
224
+ const after = [section({ id: 's1', content: { heading: 'b', body: 'b' } })];
225
+ const ids = changedSectionIds(diffVersions(after, before));
226
+ expect(ids.size).toBe(1);
227
+ expect(ids.has('s1')).toBe(true);
228
+ });
229
+ });
230
+ //# sourceMappingURL=version-diff.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-diff.test.js","sourceRoot":"","sources":["../../../src/__tests__/lib/version-diff.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=version-history.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version-history.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/lib/version-history.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,125 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { dayLabel, groupVersionsByDay, initials, toPageVersion, versionKind, } from '../../lib/version-history.js';
3
+ const NOW = new Date('2026-08-02T17:00:00Z');
4
+ function version(id, createdAt) {
5
+ return {
6
+ id,
7
+ changeType: 'UPDATE',
8
+ createdAt,
9
+ author: { id: 'u1', name: 'Sarah Mills' },
10
+ sections: [],
11
+ };
12
+ }
13
+ describe('toPageVersion', () => {
14
+ it('lifts the section snapshot out of the version payload', () => {
15
+ const mapped = toPageVersion({
16
+ id: 'v1',
17
+ changeType: 'PUBLISH',
18
+ createdAt: '2026-08-02T12:00:00Z',
19
+ data: {
20
+ sections: [
21
+ { id: 's1', sectionType: 'hero', name: 'Hero', visible: true, content: {}, settings: {} },
22
+ ],
23
+ },
24
+ changedBy: { id: 'u1', name: 'Sarah Mills', email: 's@example.com' },
25
+ });
26
+ expect(mapped.sections).toHaveLength(1);
27
+ expect(mapped.sections[0]?.id).toBe('s1');
28
+ expect(mapped.changeType).toBe('PUBLISH');
29
+ expect(mapped.author.name).toBe('Sarah Mills');
30
+ });
31
+ it('preserves an unmanaged section rather than dropping it from the snapshot', () => {
32
+ // Coercing a snapshot more strictly than the live document reads would
33
+ // make an externally-managed section look deleted in every version.
34
+ const mapped = toPageVersion({
35
+ id: 'v1',
36
+ changeType: 'UPDATE',
37
+ createdAt: '2026-08-02T12:00:00Z',
38
+ data: {
39
+ sections: [
40
+ { id: 's1', sectionType: 'opal-pricing', name: 'Pricing', visible: true, content: {} },
41
+ ],
42
+ },
43
+ });
44
+ expect(mapped.sections).toHaveLength(1);
45
+ expect(mapped.sections[0]?.sectionType).toBe('opal-pricing');
46
+ });
47
+ it('falls back through name → email → a real word for the avatar', () => {
48
+ expect(toPageVersion({ id: 'v', changeType: 'UPDATE', createdAt: '', data: null }).author.name).toBe('Unknown');
49
+ expect(toPageVersion({
50
+ id: 'v',
51
+ changeType: 'UPDATE',
52
+ createdAt: '',
53
+ data: null,
54
+ changedBy: { id: 'u', name: ' ', email: 'a@b.com' },
55
+ }).author.name).toBe('a@b.com');
56
+ });
57
+ it('yields no sections for a version whose payload has none', () => {
58
+ expect(toPageVersion({ id: 'v', changeType: 'DELETE', createdAt: '', data: {} }).sections).toEqual([]);
59
+ });
60
+ });
61
+ describe('versionKind', () => {
62
+ it('marks a published snapshot as the one a visitor could have seen', () => {
63
+ expect(versionKind('PUBLISH')).toEqual({ label: 'Published', tone: 'live' });
64
+ });
65
+ it('calls UPDATE "Saved", never "Draft"', () => {
66
+ // Every row written before cms-core started recording PUBLISH is an UPDATE
67
+ // whether or not it went live, so "Draft" would be false for those.
68
+ expect(versionKind('UPDATE').label).toBe('Saved');
69
+ });
70
+ it('distinguishes a rollback from authored content', () => {
71
+ expect(versionKind('RESTORE').label).toBe('Restored');
72
+ });
73
+ it('degrades to a neutral label for an unrecognised type', () => {
74
+ expect(versionKind('SOMETHING_NEW').label).toBe('Saved');
75
+ });
76
+ });
77
+ describe('dayLabel', () => {
78
+ it('names today and yesterday', () => {
79
+ expect(dayLabel('2026-08-02T09:00:00', NOW)).toBe('Today');
80
+ expect(dayLabel('2026-08-01T23:59:00', NOW)).toBe('Yesterday');
81
+ });
82
+ it('drops the year within the current one and keeps it outside', () => {
83
+ expect(dayLabel('2026-07-27T09:00:00', NOW)).not.toMatch(/2026/);
84
+ expect(dayLabel('2025-07-27T09:00:00', NOW)).toMatch(/2025/);
85
+ });
86
+ it('does not throw on an unparseable timestamp', () => {
87
+ expect(dayLabel('not-a-date', NOW)).toBe('Unknown date');
88
+ });
89
+ });
90
+ describe('groupVersionsByDay', () => {
91
+ it('groups consecutive runs and keeps newest-first order', () => {
92
+ const groups = groupVersionsByDay([
93
+ version('v5', '2026-08-02T16:12:00'),
94
+ version('v4', '2026-08-02T11:03:00'),
95
+ version('v3', '2026-08-01T17:47:00'),
96
+ version('v1', '2026-07-27T09:30:00'),
97
+ ], NOW);
98
+ expect(groups.map((g) => g.label)).toEqual(['Today', 'Yesterday', expect.stringMatching(/Jul/)]);
99
+ expect(groups[0]?.versions.map((v) => v.id)).toEqual(['v5', 'v4']);
100
+ });
101
+ it('does not merge two non-adjacent runs that share a label', () => {
102
+ // A Map keyed on the label would silently reorder an unsorted page into
103
+ // something that reads as chronological but is not.
104
+ const groups = groupVersionsByDay([
105
+ version('a', '2026-08-02T16:00:00'),
106
+ version('b', '2026-08-01T16:00:00'),
107
+ version('c', '2026-08-02T09:00:00'),
108
+ ], NOW);
109
+ expect(groups.map((g) => g.label)).toEqual(['Today', 'Yesterday', 'Today']);
110
+ });
111
+ it('returns nothing for an empty list', () => {
112
+ expect(groupVersionsByDay([], NOW)).toEqual([]);
113
+ });
114
+ });
115
+ describe('initials', () => {
116
+ it('takes first and last for a full name, two letters for one word', () => {
117
+ expect(initials('Sarah Mills')).toBe('SM');
118
+ expect(initials('Sarah Jane Mills')).toBe('SM');
119
+ expect(initials('sarah')).toBe('SA');
120
+ });
121
+ it('never renders an empty monogram', () => {
122
+ expect(initials(' ')).toBe('?');
123
+ });
124
+ });
125
+ //# sourceMappingURL=version-history.test.js.map