@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,104 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useRef, useState } from 'react';
4
+ import { PanelLeftClose, PanelLeftOpen } from 'lucide-react';
5
+ import { RAIL_COLLAPSED_WIDTH, RAIL_DEFAULT_WIDTH, RAIL_MAX_WIDTH, RAIL_MIN_WIDTH, clampRailWidth, readRailWidth, writeRailWidth, } from '../lib/rail-width.js';
6
+ /**
7
+ * The editor's left rail: resizable, collapsible, and width-persistent.
8
+ *
9
+ * Collapsing UNMOUNTS the rail and swaps in a narrow strip rather than
10
+ * animating its width to zero. A zero-width panel leaves a visible sliver of
11
+ * border and keeps its contents in the accessibility tree, so a screen-reader
12
+ * user lands inside a panel sighted users cannot see.
13
+ *
14
+ * Children are a render prop rather than plain nodes because the panel header
15
+ * needs the live width and drag state — the numbers the rail owns — and
16
+ * threading them through context for one consumer is more machinery than the
17
+ * problem deserves.
18
+ */
19
+ export function EditorRail({ collapsedLabel, children }) {
20
+ const [width, setWidth] = useState(RAIL_DEFAULT_WIDTH);
21
+ const [open, setOpen] = useState(true);
22
+ const [dragging, setDragging] = useState(false);
23
+ const railRef = useRef(null);
24
+ // Read persisted width after mount, never during render: there is no
25
+ // localStorage on the server, and seeding initial state from it would
26
+ // hydrate-mismatch.
27
+ useEffect(() => {
28
+ setWidth(readRailWidth());
29
+ }, []);
30
+ const applyWidth = useCallback((next) => {
31
+ const clamped = clampRailWidth(next);
32
+ setWidth(clamped);
33
+ writeRailWidth(clamped);
34
+ }, []);
35
+ const collapse = useCallback(() => setOpen(false), []);
36
+ // Pointer listeners go on `window`, not the handle: the pointer routinely
37
+ // outruns a 7px target mid-drag, and a handle-local listener would drop the
38
+ // drag the moment it did.
39
+ useEffect(() => {
40
+ if (!dragging)
41
+ return;
42
+ const onMove = (event) => {
43
+ const left = railRef.current?.getBoundingClientRect().left ?? 0;
44
+ applyWidth(event.clientX - left);
45
+ };
46
+ const stop = () => setDragging(false);
47
+ window.addEventListener('pointermove', onMove);
48
+ window.addEventListener('pointerup', stop);
49
+ window.addEventListener('pointercancel', stop);
50
+ // Lock cursor and selection for the duration. Without this the cursor
51
+ // flickers between col-resize and text as it crosses elements, and the
52
+ // drag selects the panel's copy.
53
+ const { body } = document;
54
+ const prevCursor = body.style.cursor;
55
+ const prevSelect = body.style.userSelect;
56
+ body.style.cursor = 'col-resize';
57
+ body.style.userSelect = 'none';
58
+ return () => {
59
+ window.removeEventListener('pointermove', onMove);
60
+ window.removeEventListener('pointerup', stop);
61
+ window.removeEventListener('pointercancel', stop);
62
+ body.style.cursor = prevCursor;
63
+ body.style.userSelect = prevSelect;
64
+ };
65
+ }, [dragging, applyWidth]);
66
+ if (!open) {
67
+ return (_jsxs("div", { className: "bg-card border-border flex shrink-0 flex-col items-center gap-3 border-r py-3", style: { width: RAIL_COLLAPSED_WIDTH }, children: [_jsx("button", { type: "button", onClick: () => setOpen(true), "aria-label": `Expand ${collapsedLabel} panel`, title: `Expand ${collapsedLabel} panel`, className: "text-muted-foreground hover:bg-accent hover:text-accent-foreground flex h-6.5 w-6.5 items-center justify-center rounded-md transition-colors", children: _jsx(PanelLeftOpen, { className: "h-4 w-4", "aria-hidden": true }) }), _jsx("span", { className: "text-muted-foreground text-[10px] font-bold tracking-[0.8px] whitespace-nowrap uppercase [writing-mode:vertical-rl]", "aria-hidden": true, children: collapsedLabel })] }));
68
+ }
69
+ return (_jsxs("div", { ref: railRef, className: `bg-card border-border relative flex shrink-0 flex-col border-r ${
70
+ // A width transition during a drag fights the pointer: the panel
71
+ // chases the cursor a frame behind and the resize feels broken.
72
+ dragging ? '' : 'motion-safe:transition-[width] motion-safe:duration-(--mo-slow)'}`, style: { width }, children: [_jsx("div", { className: "flex min-h-0 flex-1 flex-col", children: children({ width, dragging, collapse }) }), _jsx("div", {
73
+ // `separator` with an orientation and a value range is what makes this
74
+ // operable without a pointer. A bare div with a pointerdown handler is
75
+ // invisible to keyboard and assistive-tech users.
76
+ role: "separator", "aria-orientation": "vertical", "aria-label": "Resize panel", "aria-valuenow": width, "aria-valuemin": RAIL_MIN_WIDTH, "aria-valuemax": RAIL_MAX_WIDTH, tabIndex: 0, onPointerDown: (event) => {
77
+ event.preventDefault();
78
+ setDragging(true);
79
+ }, onDoubleClick: () => applyWidth(RAIL_DEFAULT_WIDTH), onKeyDown: (event) => {
80
+ const step = event.shiftKey ? 48 : 16;
81
+ if (event.key === 'ArrowLeft') {
82
+ event.preventDefault();
83
+ applyWidth(width - step);
84
+ }
85
+ else if (event.key === 'ArrowRight') {
86
+ event.preventDefault();
87
+ applyWidth(width + step);
88
+ }
89
+ else if (event.key === 'Home') {
90
+ event.preventDefault();
91
+ applyWidth(RAIL_DEFAULT_WIDTH);
92
+ }
93
+ }, className: "group absolute inset-y-0 -right-[3px] z-30 w-[7px] cursor-col-resize focus-visible:outline-none", children: _jsx("span", { "aria-hidden": true, className: `pointer-events-none absolute inset-y-0 left-[2.5px] w-0.5 transition-colors ${dragging
94
+ ? 'bg-brand'
95
+ : 'group-hover:bg-brand group-focus-visible:bg-brand bg-transparent'}` }) })] }));
96
+ }
97
+ /**
98
+ * Header strip under the rail's tabs: panel title, the live width while
99
+ * dragging, and the collapse control.
100
+ */
101
+ export function RailHeader({ title, width, dragging, onCollapse, }) {
102
+ return (_jsxs("div", { className: "border-border flex items-center justify-between gap-2 border-b px-3.5 py-2.5", children: [_jsx("h2", { className: "text-foreground truncate text-[12.5px] font-semibold", children: title }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [dragging && (_jsxs("span", { className: "text-muted-foreground text-[11px] tabular-nums", "aria-hidden": true, children: [width, "px"] })), _jsx("button", { type: "button", onClick: onCollapse, "aria-label": `Collapse ${title} panel`, title: `Collapse ${title} panel`, className: "text-muted-foreground hover:bg-accent hover:text-accent-foreground flex h-6.5 w-6.5 items-center justify-center rounded-md transition-colors", children: _jsx(PanelLeftClose, { className: "h-4 w-4", "aria-hidden": true }) })] })] }));
103
+ }
104
+ //# sourceMappingURL=EditorRail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditorRail.js","sourceRoot":"","sources":["../../src/components/EditorRail.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ export type RailTabId = 'sections' | 'seo' | 'page';
2
+ export interface RailTab {
3
+ id: RailTabId;
4
+ label: string;
5
+ /** Panel title shown in the header below the strip. */
6
+ title: string;
7
+ /** Count pill (Sections). */
8
+ count?: number;
9
+ /**
10
+ * Score for the dot, banded by the page audit's own scale.
11
+ *
12
+ * `undefined` — not known yet; no dot is shown rather than a misleading one.
13
+ * `null` — deliberately not rated (a gate is off, e.g. the page is
14
+ * noindexed). Renders neutral, never as a low score.
15
+ */
16
+ score?: number | null;
17
+ }
18
+ export interface EditorRailTabsProps {
19
+ tabs: RailTab[];
20
+ active: RailTabId;
21
+ onChange: (id: RailTabId) => void;
22
+ }
23
+ /**
24
+ * Three equal tabs across the top of the rail.
25
+ *
26
+ * Each tab carries a status glyph so the rail reads at a glance without being
27
+ * opened: a count for Sections, a band-coloured dot for SEO. The dot is
28
+ * accompanied by text in the accessible name — colour alone is not a signal
29
+ * for anyone who cannot distinguish these hues.
30
+ */
31
+ export declare function EditorRailTabs({ tabs, active, onChange }: EditorRailTabsProps): import("react/jsx-runtime").JSX.Element;
32
+ //# sourceMappingURL=EditorRailTabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditorRailTabs.d.ts","sourceRoot":"","sources":["../../src/components/EditorRailTabs.tsx"],"names":[],"mappings":"AAIA,MAAM,MAAM,SAAS,GAAG,UAAU,GAAG,KAAK,GAAG,MAAM,CAAA;AAEnD,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,SAAS,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAA;IACb,6BAA6B;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,EAAE,CAAA;IACf,MAAM,EAAE,SAAS,CAAA;IACjB,QAAQ,EAAE,CAAC,EAAE,EAAE,SAAS,KAAK,IAAI,CAAA;CAClC;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,mBAAmB,2CA+E7E"}
@@ -0,0 +1,45 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { scoreBand } from '@prenta/core/seo/page-audit';
4
+ /**
5
+ * Three equal tabs across the top of the rail.
6
+ *
7
+ * Each tab carries a status glyph so the rail reads at a glance without being
8
+ * opened: a count for Sections, a band-coloured dot for SEO. The dot is
9
+ * accompanied by text in the accessible name — colour alone is not a signal
10
+ * for anyone who cannot distinguish these hues.
11
+ */
12
+ export function EditorRailTabs({ tabs, active, onChange }) {
13
+ return (_jsx("div", { role: "tablist", "aria-label": "Editor panels", className: "border-border flex border-b", children: tabs.map((tab) => {
14
+ const selected = tab.id === active;
15
+ // Bands with `scoreBand` from the audit itself, NOT `gradeFromScore`.
16
+ // The two disagree between 55 and 59 — a page scoring 57 read "fair"
17
+ // in the rings and "poor" on the tab summarising them, which is the
18
+ // same class of disagreement this dot was rewired to eliminate.
19
+ const band = typeof tab.score === 'number' ? scoreBand(tab.score) : null;
20
+ const unrated = tab.score === null;
21
+ return (_jsxs("button", { type: "button", role: "tab", id: `rail-tab-${tab.id}`, "aria-selected": selected, "aria-controls": `rail-panel-${tab.id}`, tabIndex: selected ? 0 : -1, onClick: () => onChange(tab.id), onKeyDown: (event) => {
22
+ // Arrow keys move between tabs — expected of a tablist, and the
23
+ // only way to reach a tab whose tabIndex is -1.
24
+ const index = tabs.findIndex((t) => t.id === active);
25
+ if (event.key === 'ArrowRight' || event.key === 'ArrowLeft') {
26
+ event.preventDefault();
27
+ const delta = event.key === 'ArrowRight' ? 1 : -1;
28
+ const next = tabs[(index + delta + tabs.length) % tabs.length];
29
+ if (next) {
30
+ onChange(next.id);
31
+ document.getElementById(`rail-tab-${next.id}`)?.focus();
32
+ }
33
+ }
34
+ }, className: `flex flex-1 items-center justify-center gap-1.5 border-b-2 pt-2.5 pb-2 text-[12.5px] transition-colors ${selected
35
+ ? 'border-brand text-brand font-semibold'
36
+ : 'text-muted-foreground hover:text-foreground border-transparent font-medium'}`, children: [tab.label, tab.count !== undefined && (_jsx("span", { className: `rounded-full px-1.5 text-[10.5px] font-semibold tabular-nums ${selected ? 'bg-accent text-accent-foreground' : 'bg-muted text-muted-foreground'}`, children: tab.count })), (band || unrated) && (_jsxs(_Fragment, { children: [_jsx("span", { className: `h-[7px] w-[7px] shrink-0 rounded-full ${band === 'good'
37
+ ? 'bg-success'
38
+ : band === 'fair'
39
+ ? 'bg-warning'
40
+ : band === 'poor'
41
+ ? 'bg-destructive'
42
+ : 'bg-muted-foreground/40'}`, "aria-hidden": true }), _jsx("span", { className: "sr-only", children: unrated ? 'not rated' : `score ${tab.score} of 100` })] }))] }, tab.id));
43
+ }) }));
44
+ }
45
+ //# sourceMappingURL=EditorRailTabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditorRailTabs.js","sourceRoot":"","sources":["../../src/components/EditorRailTabs.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type MediaCrop } from '../lib/media-format.js';
3
+ interface MediaItem {
4
+ id: string;
5
+ filename: string;
6
+ storageKey: string;
7
+ url?: string;
8
+ altTag?: string;
9
+ mimeType: string;
10
+ width?: number;
11
+ height?: number;
12
+ }
13
+ export interface MediaBlockProps {
14
+ /** Current asset URL, or empty for the empty state. */
15
+ value: string;
16
+ onChange: (url: string, item?: MediaItem) => void;
17
+ /** The crop the template renders this image at. */
18
+ crop?: MediaCrop;
19
+ /** Rows rendered between the frame and the toolbar (the social preview). */
20
+ children?: ReactNode;
21
+ disabled?: boolean;
22
+ /** Accessible label, e.g. "Hero image". */
23
+ label?: string;
24
+ }
25
+ /**
26
+ * One component for every image — section slots and the social/OG card alike.
27
+ *
28
+ * ── The frame shows the TEMPLATE's crop, not the asset's ─────────────────
29
+ *
30
+ * A section image renders at 16:10 and a social card at 1.91:1 regardless of
31
+ * what was uploaded. Framing the asset's own ratio would tell an editor their
32
+ * portrait photo looks fine and then crop its subject out on the page. What
33
+ * the asset actually is gets reported in the toolbar instead, as a ratio label
34
+ * beside its filename, so both facts are visible and neither is implied.
35
+ *
36
+ * ── The toolbar is always visible ────────────────────────────────────────
37
+ *
38
+ * Never hover-only. Replace and Remove are the two things an editor comes here
39
+ * to do, and hiding them behind a hover makes them undiscoverable on touch and
40
+ * invisible to anyone scanning the panel.
41
+ *
42
+ * ── The picker opens inside the shell ────────────────────────────────────
43
+ *
44
+ * Not a modal. Choosing an image is a comparison against the slot it is going
45
+ * into, and a dialog covering the slot removes the only context that matters.
46
+ */
47
+ export declare function MediaBlock({ value, onChange, crop, children, disabled, label, }: MediaBlockProps): import("react/jsx-runtime").JSX.Element;
48
+ export {};
49
+ //# sourceMappingURL=MediaBlock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MediaBlock.d.ts","sourceRoot":"","sources":["../../src/components/MediaBlock.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAGhD,OAAO,EAML,KAAK,SAAS,EACf,MAAM,wBAAwB,CAAA;AAE/B,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,uDAAuD;IACvD,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,SAAS,KAAK,IAAI,CAAA;IACjD,mDAAmD;IACnD,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,SAAS,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,IAAgB,EAChB,QAAQ,EACR,QAAgB,EAChB,KAAe,GAChB,EAAE,eAAe,2CA2JjB"}
@@ -0,0 +1,50 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import { Check, ImageIcon, Trash2 } from 'lucide-react';
5
+ import { useApiData } from '../lib/useApiData.js';
6
+ import { CROP_RATIO, assetWash, displayName, formatBadge, ratioLabel, } from '../lib/media-format.js';
7
+ /**
8
+ * One component for every image — section slots and the social/OG card alike.
9
+ *
10
+ * ── The frame shows the TEMPLATE's crop, not the asset's ─────────────────
11
+ *
12
+ * A section image renders at 16:10 and a social card at 1.91:1 regardless of
13
+ * what was uploaded. Framing the asset's own ratio would tell an editor their
14
+ * portrait photo looks fine and then crop its subject out on the page. What
15
+ * the asset actually is gets reported in the toolbar instead, as a ratio label
16
+ * beside its filename, so both facts are visible and neither is implied.
17
+ *
18
+ * ── The toolbar is always visible ────────────────────────────────────────
19
+ *
20
+ * Never hover-only. Replace and Remove are the two things an editor comes here
21
+ * to do, and hiding them behind a hover makes them undiscoverable on touch and
22
+ * invisible to anyone scanning the panel.
23
+ *
24
+ * ── The picker opens inside the shell ────────────────────────────────────
25
+ *
26
+ * Not a modal. Choosing an image is a comparison against the slot it is going
27
+ * into, and a dialog covering the slot removes the only context that matters.
28
+ */
29
+ export function MediaBlock({ value, onChange, crop = 'section', children, disabled = false, label = 'Image', }) {
30
+ const [picking, setPicking] = useState(false);
31
+ const { data } = useApiData(picking ? '/media?pageSize=50' : null);
32
+ const items = (Array.isArray(data) ? data : (data?.items ?? [])).filter((i) => i.mimeType?.startsWith('image/'));
33
+ const ratio = CROP_RATIO[crop];
34
+ const name = value ? displayName(value) : '';
35
+ return (_jsxs("div", { className: "border-border bg-card overflow-hidden rounded-[10px] border", children: [value ? (_jsx("div", { className: "w-full bg-cover bg-center", style: { aspectRatio: ratio, backgroundImage: assetWash(name) }, children: _jsx("img", { src: value, alt: "", className: "h-full w-full object-cover", style: { aspectRatio: ratio } }) })) : (_jsx("button", { type: "button", onClick: () => setPicking(true), disabled: disabled, "aria-label": `Choose ${label.toLowerCase()}`, className: "bg-muted hover:bg-accent group flex w-full items-center justify-center p-3 transition-colors disabled:opacity-50", style: { aspectRatio: ratio }, children: _jsxs("span", { className: "border-border group-hover:border-primary group-hover:text-primary text-muted-foreground flex h-full w-full flex-col items-center justify-center gap-1.5 rounded-lg border-[1.5px] border-dashed transition-colors", children: [_jsx(ImageIcon, { size: 22, "aria-hidden": true }), _jsx("span", { className: "text-foreground text-[12.5px] font-semibold", children: "No image selected" }), _jsx("span", { className: "text-[10.5px]", children: "Choose one from the library" })] }) })), children, _jsx("div", { className: "border-border flex items-center gap-2 border-t px-2.5 py-2", children: value ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-foreground min-w-0 flex-1 truncate text-[11.5px] font-semibold", title: name, children: name }), _jsx("span", { className: "text-muted-foreground shrink-0 text-[10.5px] tabular-nums", children: formatBadge(value) }), _jsx("button", { type: "button", onClick: () => setPicking((v) => !v), disabled: disabled, className: "border-border hover:bg-accent shrink-0 rounded-md border px-2 py-1 text-[11px] font-medium disabled:opacity-50", children: "Replace" }), _jsx("button", { type: "button", onClick: () => onChange(''), disabled: disabled, "aria-label": `Remove ${label.toLowerCase()}`, className: "text-muted-foreground hover:text-destructive shrink-0 rounded p-1 disabled:opacity-50", children: _jsx(Trash2, { size: 13, "aria-hidden": true }) })] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "text-muted-foreground min-w-0 flex-1 text-[11.5px]", children: "No image selected" }), _jsx("button", { type: "button", onClick: () => setPicking((v) => !v), disabled: disabled, className: "border-border hover:bg-accent shrink-0 rounded-md border px-2 py-1 text-[11px] font-medium disabled:opacity-50", children: "Choose" })] })) }), picking && (_jsx("div", { className: "border-border bg-muted max-h-[262px] overflow-y-auto border-t p-2", children: items.length === 0 ? (_jsx("p", { className: "text-muted-foreground px-1 py-3 text-center text-[11px]", children: "No images in the library yet." })) : (_jsx("ul", { className: "grid grid-cols-2 gap-2", children: items.map((item) => {
36
+ const url = item.url || item.storageKey;
37
+ const selected = url === value;
38
+ const itemRatio = ratioLabel(item.width, item.height);
39
+ return (_jsx("li", { children: _jsxs("button", { type: "button", onClick: () => {
40
+ onChange(url, item);
41
+ setPicking(false);
42
+ }, "aria-current": selected || undefined, className: `border-border bg-card w-full overflow-hidden rounded-lg border text-left transition-colors ${selected ? 'border-primary' : 'hover:border-ring'}`, children: [_jsxs("span", { className: "relative block w-full", style: {
43
+ aspectRatio: '16 / 10',
44
+ backgroundImage: assetWash(item.filename),
45
+ }, children: [url && _jsx("img", { src: url, alt: "", className: "h-full w-full object-cover" }), _jsx("span", { className: "absolute top-1 left-1 rounded-[5px] bg-white/80 px-1.5 py-px text-[9.5px] font-extrabold tracking-wide text-zinc-800", children: formatBadge(item.filename, item.mimeType) }), selected && (_jsx("span", { className: "bg-primary text-primary-foreground absolute top-1 right-1 flex h-4 w-4 items-center justify-center rounded-full", children: _jsx(Check, { size: 10, "aria-hidden": true }) }))] }), _jsxs("span", { className: "block px-1.5 py-1", children: [_jsx("span", { className: "text-foreground block truncate text-[10px] font-semibold", children: item.filename }), _jsx("span", { className: "text-muted-foreground block text-[9px] tabular-nums", children: item.width && item.height
46
+ ? `${item.width} × ${item.height}${itemRatio ? ` · ${itemRatio}` : ''}`
47
+ : 'Dimensions unknown' })] })] }) }, item.id));
48
+ }) })) }))] }));
49
+ }
50
+ //# sourceMappingURL=MediaBlock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MediaBlock.js","sourceRoot":"","sources":["../../src/components/MediaBlock.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { type BreakpointId } from '../lib/breakpoints.js';
2
+ import type { ReflowGuardResult } from '../hooks/useReflowGuard.js';
3
+ export interface ReflowMeterProps {
4
+ guard: ReflowGuardResult;
5
+ /** Character count of the current value. */
6
+ length: number;
7
+ /** Breakpoint the canvas is currently showing — marked in the row of dots. */
8
+ device: BreakpointId;
9
+ /** Switch the canvas to a breakpoint so the editor can look at the damage. */
10
+ onProof: (id: BreakpointId) => void;
11
+ }
12
+ /**
13
+ * Per-field reflow readout: character count, line count where you're looking,
14
+ * one dot per breakpoint, and a proof prompt when wrapping changed.
15
+ *
16
+ * ── Why the dots are buttons ──────────────────────────────────────────────
17
+ *
18
+ * The guard's whole value is that it reports damage at a width you are NOT
19
+ * looking at. Telling an editor "this now runs three lines on mobile" while
20
+ * making them hunt for the breakpoint switcher wastes the finding. Each dot
21
+ * takes the canvas there.
22
+ *
23
+ * Colour is never the only signal: every dot carries its breakpoint's initials
24
+ * and an accessible name stating the line count and what it was published at,
25
+ * and the alert below repeats the finding in words.
26
+ */
27
+ export declare function ReflowMeter({ guard, length, device, onProof }: ReflowMeterProps): import("react/jsx-runtime").JSX.Element;
28
+ //# sourceMappingURL=ReflowMeter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReflowMeter.d.ts","sourceRoot":"","sources":["../../src/components/ReflowMeter.tsx"],"names":[],"mappings":"AAGA,OAAO,EAA8B,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACrF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAEnE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,iBAAiB,CAAA;IACxB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAA;IACd,8EAA8E;IAC9E,MAAM,EAAE,YAAY,CAAA;IACpB,8EAA8E;IAC9E,OAAO,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,IAAI,CAAA;CACpC;AAQD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,gBAAgB,2CAuF/E"}
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { AlertTriangle } from 'lucide-react';
4
+ import { BREAKPOINTS, getBreakpoint } from '../lib/breakpoints.js';
5
+ const DOT_TONE = {
6
+ same: 'bg-success/12 text-success',
7
+ grew: 'bg-warning/15 text-warning',
8
+ 'grew-badly': 'bg-destructive/12 text-destructive',
9
+ };
10
+ /**
11
+ * Per-field reflow readout: character count, line count where you're looking,
12
+ * one dot per breakpoint, and a proof prompt when wrapping changed.
13
+ *
14
+ * ── Why the dots are buttons ──────────────────────────────────────────────
15
+ *
16
+ * The guard's whole value is that it reports damage at a width you are NOT
17
+ * looking at. Telling an editor "this now runs three lines on mobile" while
18
+ * making them hunt for the breakpoint switcher wastes the finding. Each dot
19
+ * takes the canvas there.
20
+ *
21
+ * Colour is never the only signal: every dot carries its breakpoint's initials
22
+ * and an accessible name stating the line count and what it was published at,
23
+ * and the alert below repeats the finding in words.
24
+ */
25
+ export function ReflowMeter({ guard, length, device, onProof }) {
26
+ const here = guard.rows.find((r) => r.id === device);
27
+ const worst = guard.worst ? guard.rows.find((r) => r.id === guard.worst) : undefined;
28
+ // Named breakpoints while the list is short enough to read; initials once it
29
+ // isn't; "every breakpoint" when it's all of them, which is shorter and is
30
+ // also the more useful statement.
31
+ const grown = guard.grown;
32
+ const scope = grown.length === BREAKPOINTS.length
33
+ ? 'every breakpoint'
34
+ : grown.length > 3
35
+ ? grown.map((r) => getBreakpoint(r.id).abbr).join(', ')
36
+ : grown.map((r) => getBreakpoint(r.id).name).join(', ');
37
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mt-1.5 flex items-center gap-2", children: [_jsxs("span", { className: "text-muted-foreground text-[11px] whitespace-nowrap tabular-nums", children: [_jsx("b", { className: "text-foreground/80 font-semibold", children: length }), " chars", guard.delta !== 0 && (_jsxs("span", { className: guard.delta > 0 ? 'text-warning font-bold' : '', children: [' ', "(", guard.delta > 0 ? '+' : '', guard.delta, ")"] })), here && (_jsxs(_Fragment, { children: [' · ', _jsx("b", { className: "text-foreground/80 font-semibold", children: here.lines }), " line", here.lines === 1 ? '' : 's', " here"] }))] }), guard.rows.length > 0 && (_jsx("div", { className: "ml-auto flex gap-0.75", children: guard.rows.map((row) => {
38
+ const bp = getBreakpoint(row.id);
39
+ return (_jsxs("button", { type: "button", onClick: () => onProof(row.id), className: `flex h-4.25 w-4.25 items-center justify-center rounded-[5px] text-[8px] font-bold transition-transform hover:-translate-y-px ${DOT_TONE[row.state] ?? 'bg-muted text-muted-foreground'} ${device === row.id ? 'outline-primary outline-[1.5px] outline-offset-1' : ''}`, title: `${bp.name} — ${row.lines} line${row.lines === 1 ? '' : 's'}${row.diff > 0 ? ` (published: ${row.was}) — wrapping changed` : ''}. Click to proof.`, children: [_jsx("span", { "aria-hidden": true, children: bp.abbr }), _jsxs("span", { className: "sr-only", children: [bp.name, ": ", row.lines, " line", row.lines === 1 ? '' : 's', row.diff > 0 ? `, was ${row.was} when published` : '', " \u2014 proof here"] })] }, row.id));
40
+ }) }))] }), worst && (_jsxs("div", { className: "bg-warning/10 border-warning/30 mt-1.5 grid grid-cols-[auto_1fr] gap-x-2.5 gap-y-1 rounded-lg border p-2.5", children: [_jsx(AlertTriangle, { size: 14, className: "text-warning mt-px shrink-0", "aria-hidden": true }), _jsxs("p", { className: "text-[11.5px] leading-normal", children: ["Wrapping changed on ", _jsx("b", { className: "text-warning font-bold", children: scope }), " \u2014", ' ', getBreakpoint(worst.id).name, " now runs ", worst.lines, " lines instead of ", worst.was, ". Proof it before publishing."] }), _jsxs("button", { type: "button", onClick: () => onProof(worst.id), className: "border-warning text-warning hover:bg-warning/15 col-start-2 mt-0.5 justify-self-start rounded-md border px-2.5 py-1 text-[11px] font-semibold whitespace-nowrap transition-colors", children: ["Proof on ", getBreakpoint(worst.id).name] })] }))] }));
41
+ }
42
+ //# sourceMappingURL=ReflowMeter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReflowMeter.js","sourceRoot":"","sources":["../../src/components/ReflowMeter.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { Zoom } from '../lib/breakpoints.js';
2
+ export interface ZoomChipProps {
3
+ /** Current zoom mode. */
4
+ zoom: Zoom;
5
+ /** Actual rendered scale, 0–1 — what the canvas measured, not the mode. */
6
+ scale: number;
7
+ onChange: (zoom: Zoom) => void;
8
+ }
9
+ /**
10
+ * Reads out the canvas scale and toggles fit ↔ 100%.
11
+ *
12
+ * It shows the *measured* scale rather than the mode, because "fit" is not a
13
+ * number an editor can reason about — at 1920px in a narrow rail it might be
14
+ * 38%, and that is the fact that explains why the preview looks small.
15
+ */
16
+ export declare function ZoomChip({ zoom, scale, onChange }: ZoomChipProps): import("react/jsx-runtime").JSX.Element;
17
+ //# sourceMappingURL=ZoomChip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ZoomChip.d.ts","sourceRoot":"","sources":["../../src/components/ZoomChip.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAA;AAEjD,MAAM,WAAW,aAAa;IAC5B,yBAAyB;IACzB,IAAI,EAAE,IAAI,CAAA;IACV,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;CAC/B;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,aAAa,2CAmBhE"}
@@ -0,0 +1,17 @@
1
+ 'use client';
2
+ import { jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * Reads out the canvas scale and toggles fit ↔ 100%.
5
+ *
6
+ * It shows the *measured* scale rather than the mode, because "fit" is not a
7
+ * number an editor can reason about — at 1920px in a narrow rail it might be
8
+ * 38%, and that is the fact that explains why the preview looks small.
9
+ */
10
+ export function ZoomChip({ zoom, scale, onChange }) {
11
+ const percent = Math.round(scale * 100);
12
+ const next = zoom === 'fit' ? 1 : 'fit';
13
+ return (_jsxs("button", { type: "button", onClick: () => onChange(next), "aria-label": zoom === 'fit'
14
+ ? `Canvas scaled to ${percent} percent to fit. Switch to actual size.`
15
+ : 'Canvas at actual size. Switch to fit.', title: zoom === 'fit' ? 'Fit to window — click for 100%' : 'Actual size — click to fit', className: "border-border bg-background text-muted-foreground hover:bg-accent hover:text-accent-foreground h-[34px] min-w-[54px] rounded-lg border px-2 text-[11.5px] font-semibold tabular-nums transition-colors", children: [percent, "%"] }));
16
+ }
17
+ //# sourceMappingURL=ZoomChip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ZoomChip.js","sourceRoot":"","sources":["../../src/components/ZoomChip.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAYZ;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAiB;IAC/D,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAA;IACvC,MAAM,IAAI,GAAS,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAE7C,OAAO,CACL,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,gBAE3B,IAAI,KAAK,KAAK;YACZ,CAAC,CAAC,oBAAoB,OAAO,yCAAyC;YACtE,CAAC,CAAC,uCAAuC,EAE7C,KAAK,EAAE,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,gCAAgC,CAAC,CAAC,CAAC,4BAA4B,EACvF,SAAS,EAAC,wMAAwM,aAEjN,OAAO,SACD,CACV,CAAA;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { type LucideIcon } from 'lucide-react';
2
+ export declare function breakpointIcon(name: string): LucideIcon;
3
+ //# sourceMappingURL=breakpoint-icons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breakpoint-icons.d.ts","sourceRoot":"","sources":["../../src/components/breakpoint-icons.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuC,KAAK,UAAU,EAAE,MAAM,cAAc,CAAA;AAcnF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAEvD"}
@@ -0,0 +1,16 @@
1
+ import { Laptop, Monitor, Smartphone, Tablet } from 'lucide-react';
2
+ /**
3
+ * Maps a breakpoint's `icon` name to its component. Kept out of
4
+ * `lib/breakpoints.ts` so that module stays free of React imports — it is pure
5
+ * data and arithmetic, and its tests run without a DOM.
6
+ */
7
+ const ICONS = {
8
+ monitor: Monitor,
9
+ laptop: Laptop,
10
+ tablet: Tablet,
11
+ smartphone: Smartphone,
12
+ };
13
+ export function breakpointIcon(name) {
14
+ return ICONS[name] ?? Monitor;
15
+ }
16
+ //# sourceMappingURL=breakpoint-icons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"breakpoint-icons.js","sourceRoot":"","sources":["../../src/components/breakpoint-icons.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAmB,MAAM,cAAc,CAAA;AAEnF;;;;GAIG;AACH,MAAM,KAAK,GAA+B;IACxC,OAAO,EAAE,OAAO;IAChB,MAAM,EAAE,MAAM;IACd,MAAM,EAAE,MAAM;IACd,UAAU,EAAE,UAAU;CACvB,CAAA;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,OAAO,CAAA;AAC/B,CAAC"}
@@ -0,0 +1,31 @@
1
+ export interface AuthorValue {
2
+ /** Displayed byline. Empty means no byline is set. */
3
+ name: string;
4
+ /** CMS user id, when the byline is one. Empty for generic and guest. */
5
+ id: string;
6
+ }
7
+ export interface AuthorPickerProps {
8
+ value: AuthorValue;
9
+ onChange: (next: AuthorValue) => void;
10
+ /** Brand byline offered under GENERIC — usually the site or org name. */
11
+ brandName?: string;
12
+ disabled?: boolean;
13
+ }
14
+ /**
15
+ * Byline picker: a CMS user, the brand, or a one-off guest name.
16
+ *
17
+ * ── What this does and does not change ────────────────────────────────────
18
+ *
19
+ * It sets the page's DISPLAYED author, which reaches the page's structured
20
+ * data. It does not reassign the document's `createdById` — that records who
21
+ * created the record, and trading an audit trail for a label is not a swap
22
+ * worth making silently. A page written by a staff editor and bylined to the
23
+ * brand is a normal thing; a page whose history says someone else made it is
24
+ * not.
25
+ *
26
+ * The menu renders inline below the trigger rather than floating. The rail is
27
+ * narrow and scrollable, and a floating menu inside a scroll container either
28
+ * clips or detaches from its trigger on scroll.
29
+ */
30
+ export declare function AuthorPicker({ value, onChange, brandName, disabled }: AuthorPickerProps): import("react/jsx-runtime").JSX.Element;
31
+ //# sourceMappingURL=AuthorPicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthorPicker.d.ts","sourceRoot":"","sources":["../../../src/components/page-tab/AuthorPicker.tsx"],"names":[],"mappings":"AAaA,MAAM,WAAW,WAAW;IAC1B,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAA;IACZ,wEAAwE;IACxE,EAAE,EAAE,MAAM,CAAA;CACX;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,WAAW,CAAA;IAClB,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAA;IACrC,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAgB,EAAE,EAAE,iBAAiB,2CAwH/F"}