@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.
- package/CHANGELOG.md +552 -0
- package/design-system/tokens.css +173 -71
- package/design-system/tokens.json +244 -63
- package/design-system/tokens.scss +203 -98
- package/design-system/tokens.tailwind.cjs +93 -38
- package/dist/AdminRoot.d.ts +27 -6
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/components/breakpoint-controls.render.test.d.ts +2 -0
- package/dist/__tests__/components/breakpoint-controls.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/breakpoint-controls.render.test.js +51 -0
- package/dist/__tests__/components/breakpoint-controls.render.test.js.map +1 -0
- package/dist/__tests__/components/canvas-edit-tip.render.test.d.ts +2 -0
- package/dist/__tests__/components/canvas-edit-tip.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/canvas-edit-tip.render.test.js +37 -0
- package/dist/__tests__/components/canvas-edit-tip.render.test.js.map +1 -0
- package/dist/__tests__/components/editor-rail.render.test.d.ts +2 -0
- package/dist/__tests__/components/editor-rail.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/editor-rail.render.test.js +138 -0
- package/dist/__tests__/components/editor-rail.render.test.js.map +1 -0
- package/dist/__tests__/components/media-block.render.test.d.ts +2 -0
- package/dist/__tests__/components/media-block.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/media-block.render.test.js +108 -0
- package/dist/__tests__/components/media-block.render.test.js.map +1 -0
- package/dist/__tests__/components/page-audit-panel.render.test.d.ts +2 -0
- package/dist/__tests__/components/page-audit-panel.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/page-audit-panel.render.test.js +59 -0
- package/dist/__tests__/components/page-audit-panel.render.test.js.map +1 -0
- package/dist/__tests__/components/reflow-meter.render.test.d.ts +2 -0
- package/dist/__tests__/components/reflow-meter.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/reflow-meter.render.test.js +94 -0
- package/dist/__tests__/components/reflow-meter.render.test.js.map +1 -0
- package/dist/__tests__/components/seo-char-count.test.js +24 -6
- package/dist/__tests__/components/seo-char-count.test.js.map +1 -1
- package/dist/__tests__/components/serp-preview.render.test.d.ts +2 -0
- package/dist/__tests__/components/serp-preview.render.test.d.ts.map +1 -0
- package/dist/__tests__/components/serp-preview.render.test.js +25 -0
- package/dist/__tests__/components/serp-preview.render.test.js.map +1 -0
- package/dist/__tests__/lib/breakpoints.test.d.ts +2 -0
- package/dist/__tests__/lib/breakpoints.test.d.ts.map +1 -0
- package/dist/__tests__/lib/breakpoints.test.js +67 -0
- package/dist/__tests__/lib/breakpoints.test.js.map +1 -0
- package/dist/__tests__/lib/canvas-edit-chrome.test.d.ts +2 -0
- package/dist/__tests__/lib/canvas-edit-chrome.test.d.ts.map +1 -0
- package/dist/__tests__/lib/canvas-edit-chrome.test.js +44 -0
- package/dist/__tests__/lib/canvas-edit-chrome.test.js.map +1 -0
- package/dist/__tests__/lib/media-format.test.d.ts +2 -0
- package/dist/__tests__/lib/media-format.test.d.ts.map +1 -0
- package/dist/__tests__/lib/media-format.test.js +70 -0
- package/dist/__tests__/lib/media-format.test.js.map +1 -0
- package/dist/__tests__/lib/page-audit-input.test.d.ts +2 -0
- package/dist/__tests__/lib/page-audit-input.test.d.ts.map +1 -0
- package/dist/__tests__/lib/page-audit-input.test.js +122 -0
- package/dist/__tests__/lib/page-audit-input.test.js.map +1 -0
- package/dist/__tests__/lib/page-editor-service.test.js +2 -0
- package/dist/__tests__/lib/page-editor-service.test.js.map +1 -1
- package/dist/__tests__/lib/page-redirect.test.d.ts +2 -0
- package/dist/__tests__/lib/page-redirect.test.d.ts.map +1 -0
- package/dist/__tests__/lib/page-redirect.test.js +26 -0
- package/dist/__tests__/lib/page-redirect.test.js.map +1 -0
- package/dist/__tests__/lib/rail-width.test.d.ts +2 -0
- package/dist/__tests__/lib/rail-width.test.d.ts.map +1 -0
- package/dist/__tests__/lib/rail-width.test.js +69 -0
- package/dist/__tests__/lib/rail-width.test.js.map +1 -0
- package/dist/__tests__/lib/reflow-baseline.test.d.ts +2 -0
- package/dist/__tests__/lib/reflow-baseline.test.d.ts.map +1 -0
- package/dist/__tests__/lib/reflow-baseline.test.js +56 -0
- package/dist/__tests__/lib/reflow-baseline.test.js.map +1 -0
- package/dist/__tests__/lib/reflow-probe.test.d.ts +2 -0
- package/dist/__tests__/lib/reflow-probe.test.d.ts.map +1 -0
- package/dist/__tests__/lib/reflow-probe.test.js +73 -0
- package/dist/__tests__/lib/reflow-probe.test.js.map +1 -0
- package/dist/__tests__/styles/canvas-container-queries.test.d.ts +2 -0
- package/dist/__tests__/styles/canvas-container-queries.test.d.ts.map +1 -0
- package/dist/__tests__/styles/canvas-container-queries.test.js +87 -0
- package/dist/__tests__/styles/canvas-container-queries.test.js.map +1 -0
- package/dist/__tests__/styles/packaging.test.d.ts +2 -0
- package/dist/__tests__/styles/packaging.test.d.ts.map +1 -0
- package/dist/__tests__/styles/packaging.test.js +31 -0
- package/dist/__tests__/styles/packaging.test.js.map +1 -0
- package/dist/__tests__/styles/tokens-drift.test.d.ts +2 -0
- package/dist/__tests__/styles/tokens-drift.test.d.ts.map +1 -0
- package/dist/__tests__/styles/tokens-drift.test.js +92 -0
- package/dist/__tests__/styles/tokens-drift.test.js.map +1 -0
- package/dist/__tests__/views/admin-renderer-compat.test.d.ts +2 -0
- package/dist/__tests__/views/admin-renderer-compat.test.d.ts.map +1 -0
- package/dist/__tests__/views/admin-renderer-compat.test.js +43 -0
- package/dist/__tests__/views/admin-renderer-compat.test.js.map +1 -0
- package/dist/__tests__/views/admin-section-renderers.test.js +52 -4
- package/dist/__tests__/views/admin-section-renderers.test.js.map +1 -1
- package/dist/__tests__/views/editor-top-bar.render.test.js +1 -1
- package/dist/__tests__/views/editor-top-bar.render.test.js.map +1 -1
- package/dist/__tests__/views/page-tab.render.test.d.ts +2 -0
- package/dist/__tests__/views/page-tab.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/page-tab.render.test.js +151 -0
- package/dist/__tests__/views/page-tab.render.test.js.map +1 -0
- package/dist/__tests__/views/section-class-parity.test.d.ts +2 -0
- package/dist/__tests__/views/section-class-parity.test.d.ts.map +1 -0
- package/dist/__tests__/views/section-class-parity.test.js +81 -0
- package/dist/__tests__/views/section-class-parity.test.js.map +1 -0
- package/dist/__tests__/views/sections-panel-design-owned.render.test.d.ts +2 -0
- package/dist/__tests__/views/sections-panel-design-owned.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/sections-panel-design-owned.render.test.js +86 -0
- package/dist/__tests__/views/sections-panel-design-owned.render.test.js.map +1 -0
- package/dist/components/BreakpointPicker.d.ts +18 -0
- package/dist/components/BreakpointPicker.d.ts.map +1 -0
- package/dist/components/BreakpointPicker.js +31 -0
- package/dist/components/BreakpointPicker.js.map +1 -0
- package/dist/components/CanvasEditTip.d.ts +6 -0
- package/dist/components/CanvasEditTip.d.ts.map +1 -0
- package/dist/components/CanvasEditTip.js +22 -0
- package/dist/components/CanvasEditTip.js.map +1 -0
- package/dist/components/EditorRail.d.ts +36 -0
- package/dist/components/EditorRail.d.ts.map +1 -0
- package/dist/components/EditorRail.js +104 -0
- package/dist/components/EditorRail.js.map +1 -0
- package/dist/components/EditorRailTabs.d.ts +32 -0
- package/dist/components/EditorRailTabs.d.ts.map +1 -0
- package/dist/components/EditorRailTabs.js +45 -0
- package/dist/components/EditorRailTabs.js.map +1 -0
- package/dist/components/MediaBlock.d.ts +49 -0
- package/dist/components/MediaBlock.d.ts.map +1 -0
- package/dist/components/MediaBlock.js +50 -0
- package/dist/components/MediaBlock.js.map +1 -0
- package/dist/components/ReflowMeter.d.ts +28 -0
- package/dist/components/ReflowMeter.d.ts.map +1 -0
- package/dist/components/ReflowMeter.js +42 -0
- package/dist/components/ReflowMeter.js.map +1 -0
- package/dist/components/ZoomChip.d.ts +17 -0
- package/dist/components/ZoomChip.d.ts.map +1 -0
- package/dist/components/ZoomChip.js +17 -0
- package/dist/components/ZoomChip.js.map +1 -0
- package/dist/components/breakpoint-icons.d.ts +3 -0
- package/dist/components/breakpoint-icons.d.ts.map +1 -0
- package/dist/components/breakpoint-icons.js +16 -0
- package/dist/components/breakpoint-icons.js.map +1 -0
- package/dist/components/page-tab/AuthorPicker.d.ts +31 -0
- package/dist/components/page-tab/AuthorPicker.d.ts.map +1 -0
- package/dist/components/page-tab/AuthorPicker.js +95 -0
- package/dist/components/page-tab/AuthorPicker.js.map +1 -0
- package/dist/components/page-tab/DirectiveSwitch.d.ts +38 -0
- package/dist/components/page-tab/DirectiveSwitch.d.ts.map +1 -0
- package/dist/components/page-tab/DirectiveSwitch.js +29 -0
- package/dist/components/page-tab/DirectiveSwitch.js.map +1 -0
- package/dist/components/seo/EditorSeoScoreBadge.d.ts +8 -1
- package/dist/components/seo/EditorSeoScoreBadge.d.ts.map +1 -1
- package/dist/components/seo/EditorSeoScoreBadge.js +2 -2
- package/dist/components/seo/EditorSeoScoreBadge.js.map +1 -1
- package/dist/components/seo/PageAuditPanel.d.ts +14 -0
- package/dist/components/seo/PageAuditPanel.d.ts.map +1 -0
- package/dist/components/seo/PageAuditPanel.js +59 -0
- package/dist/components/seo/PageAuditPanel.js.map +1 -0
- package/dist/components/seo/SeoCharCount.d.ts +19 -7
- package/dist/components/seo/SeoCharCount.d.ts.map +1 -1
- package/dist/components/seo/SeoCharCount.js +30 -16
- package/dist/components/seo/SeoCharCount.js.map +1 -1
- package/dist/components/seo/SeoEditorPane.d.ts +15 -1
- package/dist/components/seo/SeoEditorPane.d.ts.map +1 -1
- package/dist/components/seo/SeoEditorPane.js +118 -21
- package/dist/components/seo/SeoEditorPane.js.map +1 -1
- package/dist/components/seo/SerpPreview.d.ts +20 -0
- package/dist/components/seo/SerpPreview.d.ts.map +1 -0
- package/dist/components/seo/SerpPreview.js +6 -0
- package/dist/components/seo/SerpPreview.js.map +1 -0
- package/dist/hooks/usePageAudit.d.ts +41 -0
- package/dist/hooks/usePageAudit.d.ts.map +1 -0
- package/dist/hooks/usePageAudit.js +68 -0
- package/dist/hooks/usePageAudit.js.map +1 -0
- package/dist/hooks/useReflowGuard.d.ts +47 -0
- package/dist/hooks/useReflowGuard.d.ts.map +1 -0
- package/dist/hooks/useReflowGuard.js +52 -0
- package/dist/hooks/useReflowGuard.js.map +1 -0
- package/dist/lib/breakpoints.d.ts +51 -0
- package/dist/lib/breakpoints.d.ts.map +1 -0
- package/dist/lib/breakpoints.js +84 -0
- package/dist/lib/breakpoints.js.map +1 -0
- package/dist/lib/canvas-edit-chrome.d.ts +22 -0
- package/dist/lib/canvas-edit-chrome.d.ts.map +1 -0
- package/dist/lib/canvas-edit-chrome.js +81 -0
- package/dist/lib/canvas-edit-chrome.js.map +1 -0
- package/dist/lib/media-format.d.ts +55 -0
- package/dist/lib/media-format.d.ts.map +1 -0
- package/dist/lib/media-format.js +93 -0
- package/dist/lib/media-format.js.map +1 -0
- package/dist/lib/page-audit-input.d.ts +45 -0
- package/dist/lib/page-audit-input.d.ts.map +1 -0
- package/dist/lib/page-audit-input.js +53 -0
- package/dist/lib/page-audit-input.js.map +1 -0
- package/dist/lib/page-editor-service.d.ts +12 -0
- package/dist/lib/page-editor-service.d.ts.map +1 -1
- package/dist/lib/page-editor-service.js +6 -0
- package/dist/lib/page-editor-service.js.map +1 -1
- package/dist/lib/page-redirect.d.ts +39 -0
- package/dist/lib/page-redirect.d.ts.map +1 -0
- package/dist/lib/page-redirect.js +38 -0
- package/dist/lib/page-redirect.js.map +1 -0
- package/dist/lib/rail-width.d.ts +23 -0
- package/dist/lib/rail-width.d.ts.map +1 -0
- package/dist/lib/rail-width.js +59 -0
- package/dist/lib/rail-width.js.map +1 -0
- package/dist/lib/reflow-baseline.d.ts +45 -0
- package/dist/lib/reflow-baseline.d.ts.map +1 -0
- package/dist/lib/reflow-baseline.js +38 -0
- package/dist/lib/reflow-baseline.js.map +1 -0
- package/dist/lib/reflow-probe.d.ts +159 -0
- package/dist/lib/reflow-probe.d.ts.map +1 -0
- package/dist/lib/reflow-probe.js +228 -0
- package/dist/lib/reflow-probe.js.map +1 -0
- package/dist/lib/seo-service.d.ts +10 -0
- package/dist/lib/seo-service.d.ts.map +1 -1
- package/dist/lib/seo-service.js.map +1 -1
- package/dist/prenta-admin.css +1 -1
- package/dist/views/page-editor/CanvasMetaLine.d.ts +19 -0
- package/dist/views/page-editor/CanvasMetaLine.d.ts.map +1 -0
- package/dist/views/page-editor/CanvasMetaLine.js +23 -0
- package/dist/views/page-editor/CanvasMetaLine.js.map +1 -0
- package/dist/views/page-editor/EditorCanvas.d.ts +34 -8
- package/dist/views/page-editor/EditorCanvas.d.ts.map +1 -1
- package/dist/views/page-editor/EditorCanvas.js +68 -34
- package/dist/views/page-editor/EditorCanvas.js.map +1 -1
- package/dist/views/page-editor/EditorTopBar.d.ts +14 -1
- package/dist/views/page-editor/EditorTopBar.d.ts.map +1 -1
- package/dist/views/page-editor/EditorTopBar.js +8 -17
- package/dist/views/page-editor/EditorTopBar.js.map +1 -1
- package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
- package/dist/views/page-editor/PageSectionEditor.js +275 -53
- package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
- package/dist/views/page-editor/PageTab.d.ts +36 -0
- package/dist/views/page-editor/PageTab.d.ts.map +1 -0
- package/dist/views/page-editor/PageTab.js +65 -0
- package/dist/views/page-editor/PageTab.js.map +1 -0
- package/dist/views/page-editor/SectionInspector.d.ts.map +1 -1
- package/dist/views/page-editor/SectionInspector.js +63 -19
- package/dist/views/page-editor/SectionInspector.js.map +1 -1
- package/dist/views/page-editor/SectionsPanel.d.ts +30 -5
- package/dist/views/page-editor/SectionsPanel.d.ts.map +1 -1
- package/dist/views/page-editor/SectionsPanel.js +38 -7
- package/dist/views/page-editor/SectionsPanel.js.map +1 -1
- package/dist/views/page-editor/SitePreviewFrame.d.ts.map +1 -1
- package/dist/views/page-editor/SitePreviewFrame.js +3 -0
- package/dist/views/page-editor/SitePreviewFrame.js.map +1 -1
- package/dist/views/page-editor/reflow-context.d.ts +28 -0
- package/dist/views/page-editor/reflow-context.d.ts.map +1 -0
- package/dist/views/page-editor/reflow-context.js +14 -0
- package/dist/views/page-editor/reflow-context.js.map +1 -0
- package/dist/views/page-editor/sections/ByTheNumbersSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/ByTheNumbersSection.js +1 -1
- package/dist/views/page-editor/sections/ByTheNumbersSection.js.map +1 -1
- package/dist/views/page-editor/sections/CtaBannerSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/CtaBannerSection.js +1 -1
- package/dist/views/page-editor/sections/CtaBannerSection.js.map +1 -1
- package/dist/views/page-editor/sections/FeatureHighlightSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/FeatureHighlightSection.js +2 -2
- package/dist/views/page-editor/sections/FeatureHighlightSection.js.map +1 -1
- package/dist/views/page-editor/sections/GallerySection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/GallerySection.js +7 -3
- package/dist/views/page-editor/sections/GallerySection.js.map +1 -1
- package/dist/views/page-editor/sections/HeroBannerSection.js +1 -1
- package/dist/views/page-editor/sections/HeroBannerSection.js.map +1 -1
- package/dist/views/page-editor/sections/MissionSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/MissionSection.js +1 -1
- package/dist/views/page-editor/sections/MissionSection.js.map +1 -1
- package/dist/views/page-editor/sections/PullQuoteSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/PullQuoteSection.js +1 -1
- package/dist/views/page-editor/sections/PullQuoteSection.js.map +1 -1
- package/dist/views/page-editor/sections/RelatedPostsSection.d.ts.map +1 -1
- package/dist/views/page-editor/sections/RelatedPostsSection.js +1 -1
- package/dist/views/page-editor/sections/RelatedPostsSection.js.map +1 -1
- package/dist/views/page-editor/sections/SectionRenderer.d.ts.map +1 -1
- package/dist/views/page-editor/sections/SectionRenderer.js +49 -24
- package/dist/views/page-editor/sections/SectionRenderer.js.map +1 -1
- package/dist/views/page-editor/sections/admin-renderers.d.ts +47 -15
- package/dist/views/page-editor/sections/admin-renderers.d.ts.map +1 -1
- package/dist/views/page-editor/sections/admin-renderers.js +27 -15
- package/dist/views/page-editor/sections/admin-renderers.js.map +1 -1
- package/dist/views/page-editor/sections/parts.d.ts +0 -1
- package/dist/views/page-editor/sections/parts.d.ts.map +1 -1
- package/dist/views/page-editor/sections/parts.js +41 -16
- package/dist/views/page-editor/sections/parts.js.map +1 -1
- package/dist/views/page-editor/viewports.d.ts +7 -14
- package/dist/views/page-editor/viewports.d.ts.map +1 -1
- package/dist/views/page-editor/viewports.js +7 -12
- package/dist/views/page-editor/viewports.js.map +1 -1
- package/dist/views/post-editor/PostEditorCanvas.d.ts +2 -1
- package/dist/views/post-editor/PostEditorCanvas.d.ts.map +1 -1
- package/dist/views/post-editor/PostEditorCanvas.js +5 -2
- package/dist/views/post-editor/PostEditorCanvas.js.map +1 -1
- package/dist/views/post-editor/PostSectionEditor.d.ts.map +1 -1
- package/dist/views/post-editor/PostSectionEditor.js +2 -20
- package/dist/views/post-editor/PostSectionEditor.js.map +1 -1
- package/dist/views/post-editor/PostTemplateEditor.d.ts.map +1 -1
- package/dist/views/post-editor/PostTemplateEditor.js +4 -1
- package/dist/views/post-editor/PostTemplateEditor.js.map +1 -1
- package/package.json +5 -3
- package/src/AdminRoot.tsx +27 -6
- package/src/__tests__/components/breakpoint-controls.render.test.tsx +57 -0
- package/src/__tests__/components/canvas-edit-tip.render.test.tsx +41 -0
- package/src/__tests__/components/editor-rail.render.test.tsx +192 -0
- package/src/__tests__/components/media-block.render.test.tsx +126 -0
- package/src/__tests__/components/page-audit-panel.render.test.tsx +64 -0
- package/src/__tests__/components/reflow-meter.render.test.tsx +144 -0
- package/src/__tests__/components/seo-char-count.test.tsx +26 -6
- package/src/__tests__/components/serp-preview.render.test.tsx +32 -0
- package/src/__tests__/lib/breakpoints.test.ts +84 -0
- package/src/__tests__/lib/canvas-edit-chrome.test.ts +56 -0
- package/src/__tests__/lib/media-format.test.ts +89 -0
- package/src/__tests__/lib/page-audit-input.test.ts +144 -0
- package/src/__tests__/lib/page-editor-service.test.ts +2 -0
- package/src/__tests__/lib/page-redirect.test.ts +29 -0
- package/src/__tests__/lib/rail-width.test.ts +86 -0
- package/src/__tests__/lib/reflow-baseline.test.ts +64 -0
- package/src/__tests__/lib/reflow-probe.test.ts +95 -0
- package/src/__tests__/styles/canvas-container-queries.test.ts +103 -0
- package/src/__tests__/styles/packaging.test.ts +38 -0
- package/src/__tests__/styles/tokens-drift.test.ts +110 -0
- package/src/__tests__/views/admin-renderer-compat.test.ts +48 -0
- package/src/__tests__/views/admin-section-renderers.test.tsx +70 -4
- package/src/__tests__/views/editor-top-bar.render.test.tsx +1 -1
- package/src/__tests__/views/page-tab.render.test.tsx +226 -0
- package/src/__tests__/views/section-class-parity.test.ts +98 -0
- package/src/__tests__/views/sections-panel-design-owned.render.test.tsx +104 -0
- package/src/components/BreakpointPicker.tsx +114 -0
- package/src/components/CanvasEditTip.tsx +46 -0
- package/src/components/EditorRail.tsx +219 -0
- package/src/components/EditorRailTabs.tsx +117 -0
- package/src/components/MediaBlock.tsx +223 -0
- package/src/components/ReflowMeter.tsx +125 -0
- package/src/components/ZoomChip.tsx +39 -0
- package/src/components/breakpoint-icons.ts +17 -0
- package/src/components/page-tab/AuthorPicker.tsx +240 -0
- package/src/components/page-tab/DirectiveSwitch.tsx +86 -0
- package/src/components/seo/EditorSeoScoreBadge.tsx +9 -1
- package/src/components/seo/PageAuditPanel.tsx +192 -0
- package/src/components/seo/SeoCharCount.tsx +55 -24
- package/src/components/seo/SeoEditorPane.tsx +213 -31
- package/src/components/seo/SerpPreview.tsx +41 -0
- package/src/hooks/usePageAudit.ts +98 -0
- package/src/hooks/useReflowGuard.ts +96 -0
- package/src/lib/breakpoints.ts +107 -0
- package/src/lib/canvas-edit-chrome.ts +82 -0
- package/src/lib/media-format.ts +99 -0
- package/src/lib/page-audit-input.ts +104 -0
- package/src/lib/page-editor-service.ts +18 -0
- package/src/lib/page-redirect.ts +60 -0
- package/src/lib/rail-width.ts +58 -0
- package/src/lib/reflow-baseline.ts +70 -0
- package/src/lib/reflow-probe.ts +299 -0
- package/src/lib/seo-service.ts +10 -0
- package/src/styles/prenta-tokens.css +120 -42
- package/src/styles/theme.css +130 -0
- package/src/views/page-editor/CanvasMetaLine.tsx +50 -0
- package/src/views/page-editor/EditorCanvas.tsx +109 -50
- package/src/views/page-editor/EditorTopBar.tsx +35 -43
- package/src/views/page-editor/PageSectionEditor.tsx +419 -148
- package/src/views/page-editor/PageTab.tsx +433 -0
- package/src/views/page-editor/SectionInspector.tsx +82 -41
- package/src/views/page-editor/SectionsPanel.tsx +281 -140
- package/src/views/page-editor/SitePreviewFrame.tsx +3 -0
- package/src/views/page-editor/reflow-context.ts +43 -0
- package/src/views/page-editor/sections/ByTheNumbersSection.tsx +3 -2
- package/src/views/page-editor/sections/CtaBannerSection.tsx +3 -1
- package/src/views/page-editor/sections/FeatureHighlightSection.tsx +3 -2
- package/src/views/page-editor/sections/GallerySection.tsx +7 -3
- package/src/views/page-editor/sections/HeroBannerSection.tsx +2 -2
- package/src/views/page-editor/sections/MissionSection.tsx +4 -2
- package/src/views/page-editor/sections/PullQuoteSection.tsx +3 -1
- package/src/views/page-editor/sections/RelatedPostsSection.tsx +2 -1
- package/src/views/page-editor/sections/SectionRenderer.tsx +69 -29
- package/src/views/page-editor/sections/admin-renderers.tsx +54 -15
- package/src/views/page-editor/sections/parts.tsx +46 -18
- package/src/views/page-editor/viewports.ts +13 -26
- package/src/views/post-editor/PostEditorCanvas.tsx +18 -11
- package/src/views/post-editor/PostSectionEditor.tsx +1 -56
- package/src/views/post-editor/PostTemplateEditor.tsx +3 -0
package/src/styles/theme.css
CHANGED
|
@@ -162,6 +162,29 @@
|
|
|
162
162
|
--text-xs: var(--fs-hint);
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
/*
|
|
166
|
+
* Canvas reflow steps — the container-query breakpoints the previewed PAGE
|
|
167
|
+
* reflows at, as distinct from Tailwind's `sm:`/`md:` viewport breakpoints,
|
|
168
|
+
* which describe the admin chrome around it.
|
|
169
|
+
*
|
|
170
|
+
* The canvas renders at a device's real CSS width and is scaled with a
|
|
171
|
+
* transform, so a viewport query there is meaningless: it would report the
|
|
172
|
+
* editor window's width, and picking "Mobile" on a wide monitor would render
|
|
173
|
+
* a 390px-wide canvas still laid out for desktop. Section renderers under
|
|
174
|
+
* `views/page-editor/sections/` must use `@pv-*`, never `sm:`/`md:`/`lg:`.
|
|
175
|
+
*
|
|
176
|
+
* Values come from the page-builder design handoff. Any element carrying
|
|
177
|
+
* these variants needs an ancestor with `@container` — EditorCanvas,
|
|
178
|
+
* PostEditorCanvas, PagePreview and PostPreview all declare it.
|
|
179
|
+
*/
|
|
180
|
+
@theme {
|
|
181
|
+
--container-pv-sm: 460px; /* below: stats 1-up, buttons full width */
|
|
182
|
+
--container-pv-md: 700px; /* below: image+text stacks, quotes 1-up */
|
|
183
|
+
--container-pv-lg: 900px; /* below: quote grid 2-up, stats collapse */
|
|
184
|
+
--container-pv-xl: 1100px; /* below: general step-down */
|
|
185
|
+
--container-pv-2xl: 2000px; /* above: type and padding scale up */
|
|
186
|
+
}
|
|
187
|
+
|
|
165
188
|
@layer base {
|
|
166
189
|
.prenta-admin * {
|
|
167
190
|
@apply border-border outline-ring/50;
|
|
@@ -255,6 +278,23 @@
|
|
|
255
278
|
* The canvas re-establishes the site's typographic context: headings and
|
|
256
279
|
* body text fall back to inheritance rather than the admin scale above.
|
|
257
280
|
*/
|
|
281
|
+
/*
|
|
282
|
+
* The canvas is the query container for every previewed section (`@pv-*`
|
|
283
|
+
* variants — see the step scale above). It lives here rather than as an
|
|
284
|
+
* `@container` utility on each host because FOUR surfaces render these
|
|
285
|
+
* sections — EditorCanvas, PostEditorCanvas, PagePreview, PostPreview — and
|
|
286
|
+
* a host that forgets it degrades silently: with no container ancestor,
|
|
287
|
+
* every `@pv-*` variant simply never matches and the page renders at its
|
|
288
|
+
* narrowest layout regardless of the selected breakpoint. Tying containment
|
|
289
|
+
* to the class that defines the surface makes that unforgettable.
|
|
290
|
+
*
|
|
291
|
+
* `inline-size` only: the canvas must stay free to grow as tall as its
|
|
292
|
+
* content, which `size` containment would prevent.
|
|
293
|
+
*/
|
|
294
|
+
.prenta-admin .prenta-canvas {
|
|
295
|
+
container-type: inline-size;
|
|
296
|
+
}
|
|
297
|
+
|
|
258
298
|
.prenta-admin .prenta-canvas :is(h1, h2, h3, h4) {
|
|
259
299
|
font-size: revert-layer;
|
|
260
300
|
font-weight: revert-layer;
|
|
@@ -383,6 +423,96 @@
|
|
|
383
423
|
border-radius: var(--radius-md);
|
|
384
424
|
margin: 1em 0;
|
|
385
425
|
}
|
|
426
|
+
|
|
427
|
+
/*
|
|
428
|
+
* Structure canvas — lite hover outline on editable sections.
|
|
429
|
+
* Selected section chrome stays on the element (Tailwind outline-violet).
|
|
430
|
+
* Field-level chrome applies when renderers expose data-cms-field.
|
|
431
|
+
*/
|
|
432
|
+
.prenta-admin .prenta-canvas [data-section-id][data-editable='true']:not([data-selected='true']) {
|
|
433
|
+
outline: 1px solid transparent;
|
|
434
|
+
outline-offset: -1px;
|
|
435
|
+
transition: outline-color 120ms ease;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.prenta-admin
|
|
439
|
+
.prenta-canvas
|
|
440
|
+
[data-section-id][data-editable='true']:not([data-selected='true']):hover {
|
|
441
|
+
outline-color: color-mix(in oklab, var(--acc) 40%, transparent);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.prenta-admin .prenta-canvas [data-cms-field] {
|
|
445
|
+
outline: 1px solid transparent;
|
|
446
|
+
outline-offset: 2px;
|
|
447
|
+
border-radius: 2px;
|
|
448
|
+
transition: outline-color 120ms ease;
|
|
449
|
+
cursor: text;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
.prenta-admin .prenta-canvas [data-cms-field]:hover,
|
|
453
|
+
.prenta-admin .prenta-canvas [data-cms-field]:focus,
|
|
454
|
+
.prenta-admin .prenta-canvas [data-cms-field]:focus-within {
|
|
455
|
+
outline-color: color-mix(in oklab, var(--acc) 45%, transparent);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.prenta-admin .prenta-canvas [data-cms-field][data-cms-field-kind='media'] {
|
|
459
|
+
cursor: pointer;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
@media (prefers-reduced-motion: reduce) {
|
|
463
|
+
.prenta-admin .prenta-canvas [data-section-id][data-editable='true'],
|
|
464
|
+
.prenta-admin .prenta-canvas [data-cms-field] {
|
|
465
|
+
transition: none;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/*
|
|
471
|
+
* Restore the stock Tailwind type scale inside the canvas.
|
|
472
|
+
*
|
|
473
|
+
* The `@theme inline` block above deliberately remaps six size keys onto admin
|
|
474
|
+
* design tokens so `text-base` means "admin body text, 13px" across the shell.
|
|
475
|
+
* That aliasing is right for admin chrome and wrong for the canvas, which
|
|
476
|
+
* renders the consumer's WEBSITE: there, `text-base` must mean 16px, the size
|
|
477
|
+
* a visitor actually reads. Left unscoped, every section renderer styles
|
|
478
|
+
* website copy on the admin scale — body text 3px small, `text-2xl` headings
|
|
479
|
+
* at 21px instead of 24px.
|
|
480
|
+
*
|
|
481
|
+
* This matters beyond looks. The reflow guard measures how copy wraps by
|
|
482
|
+
* laying it out in the canvas's own class chain; if the canvas renders type at
|
|
483
|
+
* the admin scale, every line count it reports is measured against a font size
|
|
484
|
+
* the live page never uses, and the guard is confidently wrong.
|
|
485
|
+
*
|
|
486
|
+
* Must live in `@layer utilities`, not `@layer base`: layer order beats
|
|
487
|
+
* specificity, so a base-layer rule — however specific — loses to the utility
|
|
488
|
+
* it is trying to correct. Inside the same layer, specificity decides, and
|
|
489
|
+
* three classes beat one.
|
|
490
|
+
*
|
|
491
|
+
* Only `font-size` is restated. The line-height half of each utility is a
|
|
492
|
+
* unitless ratio that scales correctly on its own.
|
|
493
|
+
*
|
|
494
|
+
* Values are Tailwind's defaults; the remapped keys are the only ones listed,
|
|
495
|
+
* because every other step (`text-3xl` and up) was never aliased.
|
|
496
|
+
*/
|
|
497
|
+
@layer utilities {
|
|
498
|
+
.prenta-admin .prenta-canvas .text-xs {
|
|
499
|
+
font-size: 0.75rem;
|
|
500
|
+
}
|
|
501
|
+
.prenta-admin .prenta-canvas .text-sm {
|
|
502
|
+
font-size: 0.875rem;
|
|
503
|
+
}
|
|
504
|
+
.prenta-admin .prenta-canvas .text-base {
|
|
505
|
+
font-size: 1rem;
|
|
506
|
+
}
|
|
507
|
+
.prenta-admin .prenta-canvas .text-lg {
|
|
508
|
+
font-size: 1.125rem;
|
|
509
|
+
}
|
|
510
|
+
.prenta-admin .prenta-canvas .text-xl {
|
|
511
|
+
font-size: 1.25rem;
|
|
512
|
+
}
|
|
513
|
+
.prenta-admin .prenta-canvas .text-2xl {
|
|
514
|
+
font-size: 1.5rem;
|
|
515
|
+
}
|
|
386
516
|
}
|
|
387
517
|
|
|
388
518
|
.prenta-admin.reduce-motion,
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Laptop, Monitor, Smartphone, Tablet, type LucideIcon } from 'lucide-react'
|
|
4
|
+
import type { Breakpoint } from '../../lib/breakpoints.js'
|
|
5
|
+
|
|
6
|
+
const ICONS: Record<string, LucideIcon> = {
|
|
7
|
+
monitor: Monitor,
|
|
8
|
+
laptop: Laptop,
|
|
9
|
+
tablet: Tablet,
|
|
10
|
+
smartphone: Smartphone,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface CanvasMetaLineProps {
|
|
14
|
+
breakpoint: Breakpoint
|
|
15
|
+
/** Unscaled page height in px, measured from the canvas surface. */
|
|
16
|
+
pageHeight: number
|
|
17
|
+
/** Current transform scale, 0–1. */
|
|
18
|
+
scale: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Reads out what the canvas is currently showing:
|
|
23
|
+
* `▣ Wide Screen | 1920 × 2174 px | 42% scale`.
|
|
24
|
+
*
|
|
25
|
+
* The dimensions are the page's own, before scaling — the number an editor
|
|
26
|
+
* needs when reasoning about the live page. The scale is the separate fact
|
|
27
|
+
* that the preview is shrunk to fit.
|
|
28
|
+
*/
|
|
29
|
+
export function CanvasMetaLine({ breakpoint, pageHeight, scale }: CanvasMetaLineProps) {
|
|
30
|
+
const Icon = ICONS[breakpoint.icon] ?? Monitor
|
|
31
|
+
const percent = Math.round(scale * 100)
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<p
|
|
35
|
+
className="text-muted-foreground mb-3 inline-flex items-center gap-1.5 text-[11px] font-semibold tracking-[0.3px] whitespace-nowrap"
|
|
36
|
+
// The canvas is decorative chrome around the real content; announcing
|
|
37
|
+
// these numbers on every keystroke would flood a screen reader.
|
|
38
|
+
aria-hidden
|
|
39
|
+
>
|
|
40
|
+
<Icon className="h-3 w-3" />
|
|
41
|
+
<span className="text-foreground/70">{breakpoint.name}</span>
|
|
42
|
+
<span aria-hidden>|</span>
|
|
43
|
+
<span className="tabular-nums">
|
|
44
|
+
{breakpoint.width} × {pageHeight ? Math.round(pageHeight) : '—'} px
|
|
45
|
+
</span>
|
|
46
|
+
<span aria-hidden>|</span>
|
|
47
|
+
<span className="tabular-nums">{percent}% scale</span>
|
|
48
|
+
</p>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
@@ -1,71 +1,100 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'
|
|
4
|
-
import { LayoutTemplate
|
|
4
|
+
import { LayoutTemplate } from 'lucide-react'
|
|
5
|
+
import { CanvasEditTip } from '../../components/CanvasEditTip.js'
|
|
6
|
+
import { CanvasMetaLine } from './CanvasMetaLine.js'
|
|
7
|
+
import { fitScale, getBreakpoint, type BreakpointId, type Zoom } from '../../lib/breakpoints.js'
|
|
5
8
|
import type { PageSection } from '../../lib/page-editor-service.js'
|
|
6
9
|
import { SectionRenderer } from './sections/index.js'
|
|
7
10
|
import { StructureNotice } from './StructureNotice.js'
|
|
8
|
-
import { getViewport, type ViewportId } from './viewports.js'
|
|
9
11
|
|
|
10
12
|
interface EditorCanvasProps {
|
|
11
13
|
sections: PageSection[]
|
|
12
14
|
selectedId: string | null
|
|
13
|
-
|
|
15
|
+
breakpoint: BreakpointId
|
|
16
|
+
zoom?: Zoom
|
|
14
17
|
onSelect: (id: string) => void
|
|
15
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Reports the measured scale so the header's zoom chip can show a real
|
|
20
|
+
* percentage. Only the canvas knows the available width, so the number has
|
|
21
|
+
* to travel up rather than being recomputed by the header.
|
|
22
|
+
*/
|
|
23
|
+
onScaleChange?: (scale: number) => void
|
|
24
|
+
/**
|
|
25
|
+
* Reports the canvas surface element, so the reflow guard can clone it. The
|
|
26
|
+
* guard needs the real element rather than a `document.querySelector`, which
|
|
27
|
+
* would also match the site-preview frame's canvas when that is mounted.
|
|
28
|
+
*/
|
|
29
|
+
onCanvasElement?: (el: HTMLElement | null) => void
|
|
16
30
|
/** Section types rendered as placeholders — shows the "Structure view" notice. */
|
|
17
31
|
structuralTypes?: string[]
|
|
18
32
|
sitePreviewAvailable?: boolean
|
|
19
33
|
}
|
|
20
34
|
|
|
21
35
|
/**
|
|
22
|
-
* The preview canvas.
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* a
|
|
36
|
+
* The preview canvas.
|
|
37
|
+
*
|
|
38
|
+
* The canvas renders at the breakpoint's **real CSS width** and is scaled down
|
|
39
|
+
* with a transform. Combined with `container-type: inline-size`, that makes the
|
|
40
|
+
* page reflow against its own width — the same container queries that style the
|
|
41
|
+
* live page decide the layout. A max-width clamp cannot do this: it yields a
|
|
42
|
+
* narrow desktop layout rather than the mobile one.
|
|
43
|
+
*
|
|
44
|
+
* Two measurement rules, both learned the hard way:
|
|
45
|
+
*
|
|
46
|
+
* 1. Available width comes from a **zero-height sentinel** rendered as the
|
|
47
|
+
* panel's first child, not from `panel.clientWidth - padding`. The two
|
|
48
|
+
* disagree once a scrollbar appears or the host scales the preview, and
|
|
49
|
+
* the padding arithmetic silently rots whenever the gutter changes.
|
|
50
|
+
* 2. Both measurements are re-taken on **every render**, not only from the
|
|
51
|
+
* ResizeObserver. Page height changes with every text edit, image swap and
|
|
52
|
+
* show/hide — none of which resize the observed elements. The observer
|
|
53
|
+
* exists for later window resizes; its first callback is not a substitute.
|
|
26
54
|
*/
|
|
27
55
|
export function EditorCanvas({
|
|
28
56
|
sections,
|
|
29
57
|
selectedId,
|
|
30
|
-
|
|
58
|
+
breakpoint,
|
|
59
|
+
zoom = 'fit',
|
|
31
60
|
onSelect,
|
|
32
|
-
|
|
61
|
+
onScaleChange,
|
|
62
|
+
onCanvasElement,
|
|
33
63
|
structuralTypes = [],
|
|
34
64
|
sitePreviewAvailable = false,
|
|
35
65
|
}: EditorCanvasProps) {
|
|
36
|
-
const
|
|
37
|
-
const
|
|
66
|
+
const bp = getBreakpoint(breakpoint)
|
|
67
|
+
const sentinelRef = useRef<HTMLDivElement>(null)
|
|
38
68
|
const surfaceRef = useRef<HTMLDivElement>(null)
|
|
39
69
|
const sectionRefs = useRef<Map<string, HTMLElement>>(new Map())
|
|
40
|
-
|
|
41
|
-
const [
|
|
70
|
+
|
|
71
|
+
const [available, setAvailable] = useState(0)
|
|
72
|
+
const [pageHeight, setPageHeight] = useState(0)
|
|
42
73
|
|
|
43
74
|
const registerRef = useCallback((id: string, el: HTMLElement | null) => {
|
|
44
75
|
if (el) sectionRefs.current.set(id, el)
|
|
45
76
|
else sectionRefs.current.delete(id)
|
|
46
77
|
}, [])
|
|
47
78
|
|
|
48
|
-
//
|
|
79
|
+
// No dependency array: page height changes on edits that resize nothing the
|
|
80
|
+
// observer watches. Both setters bail when the value is unchanged, so this
|
|
81
|
+
// settles after one extra pass instead of looping.
|
|
49
82
|
useLayoutEffect(() => {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const next = Math.min(1, available / vp.width)
|
|
57
|
-
setScale(next > 0 ? next : 1)
|
|
58
|
-
if (surfaceRef.current) {
|
|
59
|
-
setSurfaceHeight(surfaceRef.current.offsetHeight * (next > 0 ? next : 1))
|
|
60
|
-
}
|
|
83
|
+
const measure = () => {
|
|
84
|
+
const sentinelWidth = sentinelRef.current?.offsetWidth ?? 0
|
|
85
|
+
if (sentinelWidth > 0) setAvailable((prev) => (prev === sentinelWidth ? prev : sentinelWidth))
|
|
86
|
+
|
|
87
|
+
const height = surfaceRef.current?.offsetHeight ?? 0
|
|
88
|
+
if (height > 0) setPageHeight((prev) => (prev === height ? prev : height))
|
|
61
89
|
}
|
|
62
90
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
ro
|
|
91
|
+
measure()
|
|
92
|
+
|
|
93
|
+
const ro = new ResizeObserver(measure)
|
|
94
|
+
if (sentinelRef.current) ro.observe(sentinelRef.current)
|
|
66
95
|
if (surfaceRef.current) ro.observe(surfaceRef.current)
|
|
67
96
|
return () => ro.disconnect()
|
|
68
|
-
}
|
|
97
|
+
})
|
|
69
98
|
|
|
70
99
|
// Scroll the selected section into view within the canvas.
|
|
71
100
|
useEffect(() => {
|
|
@@ -74,28 +103,61 @@ export function EditorCanvas({
|
|
|
74
103
|
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
|
|
75
104
|
}, [selectedId])
|
|
76
105
|
|
|
106
|
+
const scale = fitScale(available, bp.width, zoom)
|
|
107
|
+
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
onScaleChange?.(scale)
|
|
110
|
+
}, [scale, onScaleChange])
|
|
111
|
+
|
|
77
112
|
return (
|
|
78
|
-
<div
|
|
113
|
+
<div className="bg-muted h-full overflow-auto px-6 pt-4.5 pb-10 text-center">
|
|
114
|
+
{/*
|
|
115
|
+
* Zero-height sentinel. Its offsetWidth IS the usable width — it is
|
|
116
|
+
* subject to exactly the same padding, scrollbar and host scaling as the
|
|
117
|
+
* canvas, so there is no arithmetic to get wrong. Must stay the first
|
|
118
|
+
* child and must stay zero-height.
|
|
119
|
+
*/}
|
|
120
|
+
<div ref={sentinelRef} aria-hidden className="h-0" />
|
|
121
|
+
|
|
79
122
|
{sections.length === 0 ? (
|
|
80
|
-
<EmptyCanvas
|
|
123
|
+
<EmptyCanvas />
|
|
81
124
|
) : (
|
|
82
125
|
<>
|
|
126
|
+
<CanvasEditTip />
|
|
83
127
|
<StructureNotice
|
|
84
128
|
structuralTypes={structuralTypes}
|
|
85
129
|
sitePreviewAvailable={sitePreviewAvailable}
|
|
86
130
|
/>
|
|
131
|
+
<CanvasMetaLine breakpoint={bp} pageHeight={pageHeight} scale={scale} />
|
|
132
|
+
|
|
133
|
+
{/*
|
|
134
|
+
* Fit box: reserves the scaled footprint so the surrounding scroll
|
|
135
|
+
* area sizes correctly against a transformed child. Deliberately has
|
|
136
|
+
* NO width/height transition — a transition frozen mid-flight (a
|
|
137
|
+
* preview that is not visible never finishes one) reports a bogus
|
|
138
|
+
* height and corrupts the next measurement.
|
|
139
|
+
*/}
|
|
87
140
|
<div
|
|
88
|
-
className="mx-auto"
|
|
89
|
-
style={{
|
|
141
|
+
className="mx-auto block"
|
|
142
|
+
style={{
|
|
143
|
+
width: Math.round(bp.width * scale),
|
|
144
|
+
height: pageHeight ? Math.round(pageHeight * scale) : undefined,
|
|
145
|
+
}}
|
|
90
146
|
>
|
|
91
147
|
{/* `prenta-canvas` is the public hook for consumer brand CSS:
|
|
92
148
|
site styles that should render in this preview scope to
|
|
93
149
|
`.prenta-admin .prenta-canvas` instead of the admin root. */}
|
|
94
150
|
<div
|
|
95
|
-
ref={
|
|
96
|
-
|
|
151
|
+
ref={(el) => {
|
|
152
|
+
surfaceRef.current = el
|
|
153
|
+
onCanvasElement?.(el)
|
|
154
|
+
}}
|
|
155
|
+
// `prenta-canvas` carries `container-type: inline-size` (theme.css)
|
|
156
|
+
// — that is what makes the `@pv-*` steps in the section renderers
|
|
157
|
+
// resolve against the simulated width instead of the viewport.
|
|
158
|
+
className="prenta-canvas border-border bg-card origin-top-left overflow-hidden rounded-lg text-left shadow-[0_2px_20px_rgba(0,0,0,0.12)] motion-safe:transition-[width,transform] motion-safe:duration-(--mo-slow) motion-safe:ease-(--ease)"
|
|
97
159
|
style={{
|
|
98
|
-
width:
|
|
160
|
+
width: bp.width,
|
|
99
161
|
transform: `scale(${scale})`,
|
|
100
162
|
transformOrigin: 'top left',
|
|
101
163
|
}}
|
|
@@ -118,25 +180,22 @@ export function EditorCanvas({
|
|
|
118
180
|
)
|
|
119
181
|
}
|
|
120
182
|
|
|
121
|
-
|
|
183
|
+
/**
|
|
184
|
+
* Design-owned pages always ship the template's sections, so an empty canvas
|
|
185
|
+
* means the template resolved to nothing — a configuration problem, not an
|
|
186
|
+
* invitation to add a section. There is deliberately no "Add section" button.
|
|
187
|
+
*/
|
|
188
|
+
function EmptyCanvas() {
|
|
122
189
|
return (
|
|
123
190
|
<div className="mx-auto flex h-full max-w-md flex-col items-center justify-center text-center">
|
|
124
191
|
<div className="bg-card border-border mb-4 flex h-14 w-14 items-center justify-center rounded-full border">
|
|
125
192
|
<LayoutTemplate className="text-muted-foreground h-6 w-6" aria-hidden />
|
|
126
193
|
</div>
|
|
127
|
-
<h2 className="text-foreground text-lg font-medium">This
|
|
128
|
-
<p className="text-muted-foreground mt-1
|
|
129
|
-
|
|
130
|
-
|
|
194
|
+
<h2 className="text-foreground text-lg font-medium">This template renders no sections</h2>
|
|
195
|
+
<p className="text-muted-foreground mt-1 text-sm">
|
|
196
|
+
Layout is design-owned — sections come from the page's template in code. Check that the page
|
|
197
|
+
has a template assigned, or pick a different one in Page settings.
|
|
131
198
|
</p>
|
|
132
|
-
<button
|
|
133
|
-
type="button"
|
|
134
|
-
onClick={onAddSection}
|
|
135
|
-
className="bg-primary text-primary-foreground hover:bg-primary/90 inline-flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors"
|
|
136
|
-
>
|
|
137
|
-
<Plus className="h-4 w-4" aria-hidden />
|
|
138
|
-
Add Section
|
|
139
|
-
</button>
|
|
140
199
|
</div>
|
|
141
200
|
)
|
|
142
201
|
}
|
|
@@ -7,18 +7,16 @@ import {
|
|
|
7
7
|
Eye,
|
|
8
8
|
Globe,
|
|
9
9
|
History,
|
|
10
|
-
Laptop,
|
|
11
10
|
Loader2,
|
|
12
|
-
type LucideIcon,
|
|
13
|
-
Monitor,
|
|
14
11
|
Settings2,
|
|
15
|
-
Smartphone,
|
|
16
|
-
Tablet,
|
|
17
12
|
TriangleAlert,
|
|
18
13
|
} from 'lucide-react'
|
|
19
14
|
import { EditorSeoScoreBadge } from '../../components/seo/EditorSeoScoreBadge.js'
|
|
15
|
+
import { BreakpointPicker } from '../../components/BreakpointPicker.js'
|
|
16
|
+
import { ZoomChip } from '../../components/ZoomChip.js'
|
|
20
17
|
import type { PageStatus } from '../../lib/page-editor-service.js'
|
|
21
|
-
import {
|
|
18
|
+
import type { Zoom } from '../../lib/breakpoints.js'
|
|
19
|
+
import { type ViewportId } from './viewports.js'
|
|
22
20
|
|
|
23
21
|
export type SaveState = 'idle' | 'saving' | 'saved' | 'error'
|
|
24
22
|
|
|
@@ -35,6 +33,11 @@ interface EditorTopBarProps {
|
|
|
35
33
|
/** Shown on hover/focus when publish is blocked by validation (not role). */
|
|
36
34
|
publishDisabledReason?: string
|
|
37
35
|
viewport: ViewportId
|
|
36
|
+
/** Canvas zoom mode; omit to hide the zoom chip (e.g. the site-preview iframe). */
|
|
37
|
+
zoom?: Zoom
|
|
38
|
+
/** Scale the canvas actually measured, 0–1 — displayed by the zoom chip. */
|
|
39
|
+
canvasScale?: number
|
|
40
|
+
onZoom?: (zoom: Zoom) => void
|
|
38
41
|
/** Breadcrumb root label + back-button text (e.g. "Pages" or "Posts"). */
|
|
39
42
|
backLabel?: string
|
|
40
43
|
onViewport: (v: ViewportId) => void
|
|
@@ -63,7 +66,14 @@ interface EditorTopBarProps {
|
|
|
63
66
|
onOpenSeo?: () => void
|
|
64
67
|
seoOpen?: boolean
|
|
65
68
|
/** Live 0–100 SEO score shown left of the SEO label. */
|
|
69
|
+
/**
|
|
70
|
+
* 0-100 for the header chip. `undefined` means not known yet — the chip
|
|
71
|
+
* renders without a number rather than defaulting to 0, which would read as
|
|
72
|
+
* a catastrophic score for a page nobody has audited.
|
|
73
|
+
*/
|
|
66
74
|
seoScore?: number
|
|
75
|
+
/** Band for `seoScore`; omit to derive from the content-analysis scale. */
|
|
76
|
+
seoScoreBand?: 'good' | 'fair' | 'poor'
|
|
67
77
|
onSaveDraft: () => void
|
|
68
78
|
onPublish: () => void
|
|
69
79
|
/** Open the schedule-publish dialog. Omitted when the document is unsaved. */
|
|
@@ -81,13 +91,6 @@ interface EditorTopBarProps {
|
|
|
81
91
|
structuralTypes?: string[]
|
|
82
92
|
}
|
|
83
93
|
|
|
84
|
-
const VIEWPORT_ICONS: Record<string, LucideIcon> = {
|
|
85
|
-
monitor: Monitor,
|
|
86
|
-
laptop: Laptop,
|
|
87
|
-
tablet: Tablet,
|
|
88
|
-
smartphone: Smartphone,
|
|
89
|
-
}
|
|
90
|
-
|
|
91
94
|
const STATUS_TEXT: Record<PageStatus, { label: string; className: string }> = {
|
|
92
95
|
DRAFT: { label: 'Draft', className: 'text-warning dark:text-amber-400' },
|
|
93
96
|
PUBLISHED: { label: 'Published', className: 'text-emerald-600 dark:text-emerald-400' },
|
|
@@ -106,6 +109,9 @@ export function EditorTopBar({
|
|
|
106
109
|
publishReady = true,
|
|
107
110
|
publishDisabledReason,
|
|
108
111
|
viewport,
|
|
112
|
+
zoom,
|
|
113
|
+
canvasScale,
|
|
114
|
+
onZoom,
|
|
109
115
|
backLabel = 'Pages',
|
|
110
116
|
onViewport,
|
|
111
117
|
onBack,
|
|
@@ -117,7 +123,8 @@ export function EditorTopBar({
|
|
|
117
123
|
onOpenSettings,
|
|
118
124
|
onOpenSeo,
|
|
119
125
|
seoOpen = false,
|
|
120
|
-
seoScore
|
|
126
|
+
seoScore,
|
|
127
|
+
seoScoreBand,
|
|
121
128
|
onSaveDraft,
|
|
122
129
|
onPublish,
|
|
123
130
|
onSchedule,
|
|
@@ -209,32 +216,11 @@ export function EditorTopBar({
|
|
|
209
216
|
|
|
210
217
|
{/* Right controls */}
|
|
211
218
|
<div className="flex items-center gap-2">
|
|
212
|
-
<div
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
{VIEWPORTS.map((v) => {
|
|
218
|
-
const Icon = VIEWPORT_ICONS[v.icon] ?? Monitor
|
|
219
|
-
const active = v.id === viewport
|
|
220
|
-
return (
|
|
221
|
-
<button
|
|
222
|
-
key={v.id}
|
|
223
|
-
type="button"
|
|
224
|
-
onClick={() => onViewport(v.id)}
|
|
225
|
-
aria-pressed={active}
|
|
226
|
-
title={`${v.label} (${v.width}px)`}
|
|
227
|
-
className={`flex items-center gap-1 rounded-md px-2 py-1 text-xs font-medium transition-colors ${
|
|
228
|
-
active
|
|
229
|
-
? 'bg-primary text-primary-foreground'
|
|
230
|
-
: 'text-muted-foreground hover:text-foreground hover:bg-accent'
|
|
231
|
-
}`}
|
|
232
|
-
>
|
|
233
|
-
<Icon className="h-3.5 w-3.5" aria-hidden />
|
|
234
|
-
<span className="hidden lg:inline">{v.label}</span>
|
|
235
|
-
</button>
|
|
236
|
-
)
|
|
237
|
-
})}
|
|
219
|
+
<div className="hidden items-center gap-1.5 md:flex">
|
|
220
|
+
<BreakpointPicker value={viewport} onChange={onViewport} />
|
|
221
|
+
{zoom !== undefined && onZoom && (
|
|
222
|
+
<ZoomChip zoom={zoom} scale={canvasScale ?? 1} onChange={onZoom} />
|
|
223
|
+
)}
|
|
238
224
|
</div>
|
|
239
225
|
|
|
240
226
|
<button
|
|
@@ -252,15 +238,21 @@ export function EditorTopBar({
|
|
|
252
238
|
type="button"
|
|
253
239
|
onClick={onOpenSeo}
|
|
254
240
|
aria-pressed={seoOpen}
|
|
255
|
-
aria-label={`SEO, score ${seoScore} out of 100`}
|
|
256
|
-
title={
|
|
241
|
+
aria-label={seoScore === undefined ? 'SEO' : `SEO, score ${seoScore} out of 100`}
|
|
242
|
+
title={
|
|
243
|
+
seoScore === undefined
|
|
244
|
+
? 'SEO — meta tags, social preview, schema, and analysis.'
|
|
245
|
+
: `SEO — score ${seoScore}/100. Meta tags, social preview, schema, and analysis.`
|
|
246
|
+
}
|
|
257
247
|
className={`inline-flex items-center gap-1.5 rounded-lg px-2 py-1.5 text-sm font-medium transition-colors ${
|
|
258
248
|
seoOpen
|
|
259
249
|
? 'bg-primary text-primary-foreground'
|
|
260
250
|
: 'text-muted-foreground hover:text-foreground hover:bg-accent'
|
|
261
251
|
}`}
|
|
262
252
|
>
|
|
263
|
-
|
|
253
|
+
{seoScore !== undefined && (
|
|
254
|
+
<EditorSeoScoreBadge score={seoScore} band={seoScoreBand} inverted={seoOpen} />
|
|
255
|
+
)}
|
|
264
256
|
<span className="hidden sm:inline">SEO</span>
|
|
265
257
|
</button>
|
|
266
258
|
)}
|