@drawnagency/primitives 0.1.113 → 0.1.114
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/README.md +1 -1
- package/dist/auth/cookies.d.ts +39 -0
- package/dist/auth/cookies.d.ts.map +1 -1
- package/dist/auth/index.js +3 -3
- package/dist/{chunk-GPPVYLEI.js → chunk-45FA7Z55.js} +1 -1
- package/dist/{chunk-REDDTTGJ.js → chunk-4Y2JQQZL.js} +19 -1
- package/dist/chunk-5GFACGSN.js +6 -0
- package/dist/chunk-5KSAS4VY.js +275 -0
- package/dist/{chunk-Z7W3DQTR.js → chunk-7SAAJVDE.js} +408 -28
- package/dist/{chunk-S2L3BPLS.js → chunk-7SX42OP4.js} +1 -1
- package/dist/{chunk-GGXZZ77Z.js → chunk-AASEGXNT.js} +18 -1
- package/dist/{chunk-WKZ7OENC.js → chunk-BQ5TZZUE.js} +5 -1
- package/dist/chunk-DIMDRBFK.js +11 -0
- package/dist/{chunk-WUJ3XNFI.js → chunk-GGYY3JQJ.js} +19 -24
- package/dist/{chunk-NHN2LSIU.js → chunk-IP2SEBAJ.js} +5 -1
- package/dist/{chunk-IINIZBQB.js → chunk-JCDZAFX2.js} +30 -10
- package/dist/chunk-JR24U7FJ.js +120 -0
- package/dist/{chunk-SW6PCQQD.js → chunk-KQA772WQ.js} +2 -1
- package/dist/{chunk-KR2MHTNJ.js → chunk-N3G7DOU3.js} +609 -61
- package/dist/chunk-OAGJBCSH.js +67 -0
- package/dist/{chunk-6BZ2KNTP.js → chunk-PLANHXS7.js} +191 -61
- package/dist/{chunk-VWWY5TL5.js → chunk-PNSCDBU7.js} +38 -56
- package/dist/{chunk-AHALSCQH.js → chunk-PTY2RG2K.js} +1 -1
- package/dist/{chunk-BDFTMGCL.js → chunk-PZPGJZRG.js} +9 -8
- package/dist/{chunk-FRE2CCLV.js → chunk-QHMKIODQ.js} +17 -4
- package/dist/{chunk-HBULBDGP.js → chunk-S37DZPHW.js} +1 -1
- package/dist/{chunk-4I5OO5UB.js → chunk-TEOAKGZA.js} +0 -1
- package/dist/chunk-VUYCRBJK.js +174 -0
- package/dist/{chunk-4RNF2DRG.js → chunk-XDEPXTBY.js} +0 -2
- package/dist/{chunk-DS4GCYV2.js → chunk-Y5QECJX7.js} +223 -16
- package/dist/collab/rebase.d.ts +2 -1
- package/dist/collab/rebase.d.ts.map +1 -1
- package/dist/collab/rebase.js +20 -5
- package/dist/components/brandguide/ColorsEditable.d.ts.map +1 -1
- package/dist/components/chat/ChatPanel.d.ts.map +1 -1
- package/dist/components/chat/ChatWidget.d.ts.map +1 -1
- package/dist/components/docfile/DocFilePane.d.ts.map +1 -1
- package/dist/components/editor/ChildBlockWrapper.d.ts.map +1 -1
- package/dist/components/editor/ConflictDialog.d.ts.map +1 -1
- package/dist/components/editor/DragHandle.d.ts +12 -2
- package/dist/components/editor/DragHandle.d.ts.map +1 -1
- package/dist/components/editor/DropEdgeIndicator.d.ts +15 -6
- package/dist/components/editor/DropEdgeIndicator.d.ts.map +1 -1
- package/dist/components/editor/NavGroupRow.d.ts +4 -1
- package/dist/components/editor/NavGroupRow.d.ts.map +1 -1
- package/dist/components/editor/PagesModal.d.ts.map +1 -1
- package/dist/components/editor/SectionWrapper.d.ts +1 -1
- package/dist/components/editor/SectionWrapper.d.ts.map +1 -1
- package/dist/components/editor/SettingsForm.d.ts.map +1 -1
- package/dist/components/editor/TagIndicator.d.ts +15 -0
- package/dist/components/editor/TagIndicator.d.ts.map +1 -0
- package/dist/components/editor/index.js +148 -77
- package/dist/components/primitives/DocumentDropZone.d.ts +1 -1
- package/dist/components/primitives/EditableGrid.d.ts.map +1 -1
- package/dist/components/primitives/EditableList.d.ts.map +1 -1
- package/dist/components/primitives/HeadingSection.d.ts +4 -1
- package/dist/components/primitives/HeadingSection.d.ts.map +1 -1
- package/dist/components/primitives/ResolvedMedia.d.ts +5 -1
- package/dist/components/primitives/ResolvedMedia.d.ts.map +1 -1
- package/dist/components/primitives/{defineHeadingSection.d.ts → define-heading-section.d.ts} +1 -1
- package/dist/components/primitives/define-heading-section.d.ts.map +1 -0
- package/dist/components/primitives/{gridDrop.d.ts → grid-drop.d.ts} +1 -1
- package/dist/components/primitives/grid-drop.d.ts.map +1 -0
- package/dist/components/primitives/index.d.ts +1 -1
- package/dist/components/primitives/index.d.ts.map +1 -1
- package/dist/components/primitives/index.js +17 -154
- package/dist/components/sections/Button/CTAButton.d.ts.map +1 -1
- package/dist/components/sections/DoDontMedia/DoDontMediaBlock.d.ts +2 -6
- package/dist/components/sections/DoDontMedia/DoDontMediaBlock.d.ts.map +1 -1
- package/dist/components/sections/DoDontMedia/index.d.ts +8 -0
- package/dist/components/sections/DoDontMedia/index.d.ts.map +1 -1
- package/dist/components/sections/IconList/IconList.d.ts +2 -1
- package/dist/components/sections/IconList/IconList.d.ts.map +1 -1
- package/dist/components/sections/IconList/IconListSettings.d.ts +2 -1
- package/dist/components/sections/IconList/IconListSettings.d.ts.map +1 -1
- package/dist/components/sections/IconList/index.d.ts +1 -0
- package/dist/components/sections/IconList/index.d.ts.map +1 -1
- package/dist/components/sections/Media/MediaBlock.d.ts +2 -1
- package/dist/components/sections/Media/MediaBlock.d.ts.map +1 -1
- package/dist/components/sections/Media/index.d.ts +8 -0
- package/dist/components/sections/Media/index.d.ts.map +1 -1
- package/dist/components/sections/Reference/ReferenceList.d.ts +35 -0
- package/dist/components/sections/Reference/ReferenceList.d.ts.map +1 -0
- package/dist/components/sections/Reference/ReferenceSettings.d.ts +12 -0
- package/dist/components/sections/Reference/ReferenceSettings.d.ts.map +1 -0
- package/dist/components/sections/Reference/index.d.ts +9 -0
- package/dist/components/sections/Reference/index.d.ts.map +1 -0
- package/dist/components/sections/SectionSummary/SectionSummaryBlock.d.ts +38 -0
- package/dist/components/sections/SectionSummary/SectionSummaryBlock.d.ts.map +1 -0
- package/dist/components/sections/SectionSummary/SectionSummarySettings.d.ts +20 -0
- package/dist/components/sections/SectionSummary/SectionSummarySettings.d.ts.map +1 -0
- package/dist/components/sections/SectionSummary/index.d.ts +33 -0
- package/dist/components/sections/SectionSummary/index.d.ts.map +1 -0
- package/dist/components/sections/all-sections.d.ts +53 -0
- package/dist/components/sections/all-sections.d.ts.map +1 -1
- package/dist/components/sections/brand-guide.d.ts +41 -2
- package/dist/components/sections/brand-guide.d.ts.map +1 -1
- package/dist/components/sections/brand-guide.js +12 -10
- package/dist/components/sections/core-sections.d.ts +14 -0
- package/dist/components/sections/core-sections.d.ts.map +1 -1
- package/dist/components/sections/register-schemas.js +19 -17
- package/dist/components/sections/register.js +19 -17
- package/dist/components/shared/BrandSwatchesContext.d.ts +9 -0
- package/dist/components/shared/BrandSwatchesContext.d.ts.map +1 -0
- package/dist/components/shared/Button.d.ts +11 -3
- package/dist/components/shared/Button.d.ts.map +1 -1
- package/dist/components/shared/Checkbox.d.ts +6 -0
- package/dist/components/shared/Checkbox.d.ts.map +1 -1
- package/dist/components/shared/ConfirmDialog.d.ts +25 -0
- package/dist/components/shared/ConfirmDialog.d.ts.map +1 -0
- package/dist/components/shared/ConfirmInline.d.ts +25 -0
- package/dist/components/shared/ConfirmInline.d.ts.map +1 -0
- package/dist/components/shared/ErrorBoundary.d.ts.map +1 -1
- package/dist/components/shared/Fab.d.ts +11 -2
- package/dist/components/shared/Fab.d.ts.map +1 -1
- package/dist/components/shared/FabStack.d.ts +22 -0
- package/dist/components/shared/FabStack.d.ts.map +1 -0
- package/dist/components/shared/HistoryChangesModal.d.ts.map +1 -1
- package/dist/components/shared/HistoryPopover.d.ts.map +1 -1
- package/dist/components/shared/ImageBackgroundField.d.ts +9 -0
- package/dist/components/shared/ImageBackgroundField.d.ts.map +1 -0
- package/dist/components/shared/{UploadRejectionAlert.d.ts → InlineAlert.d.ts} +4 -2
- package/dist/components/shared/InlineAlert.d.ts.map +1 -0
- package/dist/components/shared/Input.d.ts +10 -0
- package/dist/components/shared/Input.d.ts.map +1 -1
- package/dist/components/shared/Navigation.d.ts.map +1 -1
- package/dist/components/shared/Notice.d.ts +10 -0
- package/dist/components/shared/Notice.d.ts.map +1 -0
- package/dist/components/shared/Popover.d.ts.map +1 -1
- package/dist/components/shared/ReferenceSourcesContext.d.ts +25 -0
- package/dist/components/shared/ReferenceSourcesContext.d.ts.map +1 -0
- package/dist/components/shared/Select.d.ts +3 -2
- package/dist/components/shared/Select.d.ts.map +1 -1
- package/dist/components/shared/SplitButton.d.ts.map +1 -1
- package/dist/components/shared/SummaryAnchorsContext.d.ts +10 -0
- package/dist/components/shared/SummaryAnchorsContext.d.ts.map +1 -0
- package/dist/components/shared/Textarea.d.ts +7 -0
- package/dist/components/shared/Textarea.d.ts.map +1 -1
- package/dist/components/shared/ToolbarFrame.d.ts +17 -0
- package/dist/components/shared/ToolbarFrame.d.ts.map +1 -0
- package/dist/components/shared/copy.d.ts +13 -0
- package/dist/components/shared/copy.d.ts.map +1 -0
- package/dist/components/shared/icons.d.ts +0 -12
- package/dist/components/shared/icons.d.ts.map +1 -1
- package/dist/components/shell/AudienceDetailsPanel.d.ts +7 -1
- package/dist/components/shell/AudienceDetailsPanel.d.ts.map +1 -1
- package/dist/components/shell/AudienceRow.d.ts.map +1 -1
- package/dist/components/shell/BugReportFAB.d.ts +7 -1
- package/dist/components/shell/BugReportFAB.d.ts.map +1 -1
- package/dist/components/shell/CredentialList.d.ts.map +1 -1
- package/dist/components/shell/DocInspector.d.ts.map +1 -1
- package/dist/components/shell/DocumentEditorOverlay.d.ts.map +1 -1
- package/dist/components/shell/EditorContext.d.ts +4 -0
- package/dist/components/shell/EditorContext.d.ts.map +1 -1
- package/dist/components/shell/EditorLoginForm.d.ts.map +1 -1
- package/dist/components/shell/EditorModal.d.ts.map +1 -1
- package/dist/components/shell/EditorShell.d.ts.map +1 -1
- package/dist/components/shell/HistoryToolbar.d.ts.map +1 -1
- package/dist/components/shell/MediaFolderTile.d.ts +4 -1
- package/dist/components/shell/MediaFolderTile.d.ts.map +1 -1
- package/dist/components/shell/MediaLibraryModal.d.ts.map +1 -1
- package/dist/components/shell/RestoreModal.d.ts.map +1 -1
- package/dist/components/shell/SiteSettingsChatbot.d.ts.map +1 -1
- package/dist/components/shell/SiteSettingsDisplay.d.ts.map +1 -1
- package/dist/components/shell/SiteSettingsModal.d.ts +3 -1
- package/dist/components/shell/SiteSettingsModal.d.ts.map +1 -1
- package/dist/components/shell/SiteSettingsTags.d.ts +13 -0
- package/dist/components/shell/SiteSettingsTags.d.ts.map +1 -0
- package/dist/components/shell/SiteSettingsUsers.d.ts.map +1 -1
- package/dist/components/shell/ViewerLoginForm.d.ts.map +1 -1
- package/dist/components/shell/{blockMoveDispatch.d.ts → block-move-dispatch.d.ts} +1 -1
- package/dist/components/shell/block-move-dispatch.d.ts.map +1 -0
- package/dist/components/shell/reference-sources.d.ts +22 -0
- package/dist/components/shell/reference-sources.d.ts.map +1 -0
- package/dist/deck/index.d.ts +2 -2
- package/dist/deck/index.d.ts.map +1 -1
- package/dist/deck/index.js +1 -3
- package/dist/deck-builder/index.js +11 -18
- package/dist/document/index.d.ts +2 -2
- package/dist/document/index.d.ts.map +1 -1
- package/dist/hooks/useBlockDnd.d.ts +11 -0
- package/dist/hooks/useBlockDnd.d.ts.map +1 -1
- package/dist/hooks/useBuildStatus.d.ts +1 -1
- package/dist/hooks/useBuildStatus.d.ts.map +1 -1
- package/dist/hooks/useDocumentUploads.d.ts +1 -1
- package/dist/hooks/useDocumentUploads.d.ts.map +1 -1
- package/dist/hooks/useEditorPersistence.d.ts +5 -1
- package/dist/hooks/useEditorPersistence.d.ts.map +1 -1
- package/dist/hooks/useEditorPublish.d.ts +2 -2
- package/dist/hooks/useEditorPublish.d.ts.map +1 -1
- package/dist/hooks/useNotice.d.ts +19 -0
- package/dist/hooks/useNotice.d.ts.map +1 -0
- package/dist/hooks/useSortableNavRow.d.ts +11 -0
- package/dist/hooks/useSortableNavRow.d.ts.map +1 -1
- package/dist/hooks/useSortableRow.d.ts +11 -0
- package/dist/hooks/useSortableRow.d.ts.map +1 -1
- package/dist/hooks/useStackedEscape.d.ts +32 -0
- package/dist/hooks/useStackedEscape.d.ts.map +1 -0
- package/dist/index.js +67 -49
- package/dist/lib/audience-secret.js +2 -2
- package/dist/lib/block-dnd.d.ts +5 -0
- package/dist/lib/block-dnd.d.ts.map +1 -1
- package/dist/lib/block-move.d.ts.map +1 -1
- package/dist/lib/brand-swatches.d.ts +12 -0
- package/dist/lib/brand-swatches.d.ts.map +1 -0
- package/dist/lib/container-ops.d.ts +3 -2
- package/dist/lib/container-ops.d.ts.map +1 -1
- package/dist/lib/dexie.d.ts.map +1 -1
- package/dist/lib/dexie.js +9 -1
- package/dist/lib/env.d.ts +4 -0
- package/dist/lib/env.d.ts.map +1 -1
- package/dist/lib/env.js +5 -3
- package/dist/lib/events.d.ts +1 -2
- package/dist/lib/events.d.ts.map +1 -1
- package/dist/lib/image-background.d.ts +8 -0
- package/dist/lib/image-background.d.ts.map +1 -0
- package/dist/lib/index-ops.js +3 -3
- package/dist/lib/index.d.ts +6 -4
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +29 -21
- package/dist/lib/loader.d.ts +21 -7
- package/dist/lib/loader.d.ts.map +1 -1
- package/dist/lib/nav-tree.d.ts +1 -0
- package/dist/lib/nav-tree.d.ts.map +1 -1
- package/dist/lib/nav.d.ts +38 -0
- package/dist/lib/nav.d.ts.map +1 -1
- package/dist/lib/platform-broker.d.ts +4 -2
- package/dist/lib/platform-broker.d.ts.map +1 -1
- package/dist/lib/platform-broker.js +21 -13
- package/dist/lib/reference-type.d.ts +2 -0
- package/dist/lib/reference-type.d.ts.map +1 -0
- package/dist/lib/reference-validation.d.ts +31 -0
- package/dist/lib/reference-validation.d.ts.map +1 -0
- package/dist/lib/reference-validation.js +39 -0
- package/dist/lib/references.d.ts +74 -0
- package/dist/lib/references.d.ts.map +1 -0
- package/dist/lib/registry.d.ts +35 -0
- package/dist/lib/registry.d.ts.map +1 -1
- package/dist/lib/{safeRedirect.d.ts → safe-redirect.d.ts} +1 -1
- package/dist/lib/safe-redirect.d.ts.map +1 -0
- package/dist/lib/tags.d.ts +58 -0
- package/dist/lib/tags.d.ts.map +1 -0
- package/dist/lib/theme-vars.d.ts +9 -0
- package/dist/lib/theme-vars.d.ts.map +1 -1
- package/dist/media/index.d.ts +1 -1
- package/dist/media/index.d.ts.map +1 -1
- package/dist/media/index.js +8 -4
- package/dist/media/utils.d.ts +2 -0
- package/dist/media/utils.d.ts.map +1 -1
- package/dist/media/{videoPoster.d.ts → video-poster.d.ts} +1 -1
- package/dist/media/video-poster.d.ts.map +1 -0
- package/dist/schemas/audience.js +2 -2
- package/dist/schemas/index.d.ts +1 -0
- package/dist/schemas/index.d.ts.map +1 -1
- package/dist/schemas/index.js +14 -8
- package/dist/schemas/shared.d.ts +26 -0
- package/dist/schemas/shared.d.ts.map +1 -1
- package/dist/schemas/site-config.d.ts +6 -0
- package/dist/schemas/site-config.d.ts.map +1 -1
- package/dist/schemas/tags.d.ts +27 -0
- package/dist/schemas/tags.d.ts.map +1 -0
- package/dist/storage/index.js +1 -1
- package/dist/types/database.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/auth/cookies.ts +111 -0
- package/src/collab/rebase.ts +26 -2
- package/src/components/brandguide/ColorSwatchSettings.tsx +1 -1
- package/src/components/brandguide/ColorsEditable.tsx +1 -0
- package/src/components/chat/ChatPanel.tsx +2 -3
- package/src/components/chat/ChatWidget.tsx +2 -3
- package/src/components/docfile/DocFilePane.tsx +4 -12
- package/src/components/editor/ChildBlockWrapper.tsx +13 -19
- package/src/components/editor/ConflictDialog.tsx +27 -35
- package/src/components/editor/DeleteButton.tsx +2 -2
- package/src/components/editor/DragHandle.tsx +16 -3
- package/src/components/editor/DropEdgeIndicator.tsx +36 -10
- package/src/components/editor/InsertButton.tsx +2 -2
- package/src/components/editor/NavGroupRow.tsx +43 -14
- package/src/components/editor/PagesModal.tsx +99 -26
- package/src/components/editor/SectionWrapper.tsx +64 -23
- package/src/components/editor/SettingsButton.tsx +2 -2
- package/src/components/editor/SettingsForm.tsx +19 -2
- package/src/components/editor/StatusIndicator.tsx +4 -4
- package/src/components/editor/TagIndicator.tsx +80 -0
- package/src/components/primitives/DocumentDropZone.tsx +1 -1
- package/src/components/primitives/EditableGrid.tsx +15 -14
- package/src/components/primitives/EditableList.tsx +27 -74
- package/src/components/primitives/HeadingSection.tsx +5 -2
- package/src/components/primitives/ResolvedMedia.tsx +13 -0
- package/src/components/primitives/{defineHeadingSection.tsx → define-heading-section.tsx} +1 -0
- package/src/components/primitives/index.ts +1 -1
- package/src/components/primitives/useEditablePlainText.ts +5 -5
- package/src/components/sections/Button/CTAButton.tsx +3 -1
- package/src/components/sections/DoDontMedia/DoDontMediaBlock.tsx +4 -4
- package/src/components/sections/DoDontMedia/index.tsx +12 -3
- package/src/components/sections/IconList/DoDontToggle.tsx +2 -2
- package/src/components/sections/IconList/IconList.tsx +70 -37
- package/src/components/sections/IconList/IconListSettings.tsx +55 -17
- package/src/components/sections/IconList/index.tsx +10 -3
- package/src/components/sections/LinkHeading/index.tsx +1 -1
- package/src/components/sections/Media/MediaBlock.tsx +13 -2
- package/src/components/sections/Media/index.tsx +12 -4
- package/src/components/sections/Reference/ReferenceList.tsx +214 -0
- package/src/components/sections/Reference/ReferenceSettings.tsx +159 -0
- package/src/components/sections/Reference/index.tsx +47 -0
- package/src/components/sections/SectionSummary/SectionSummaryBlock.tsx +476 -0
- package/src/components/sections/SectionSummary/SectionSummarySettings.tsx +126 -0
- package/src/components/sections/SectionSummary/index.tsx +149 -0
- package/src/components/sections/SubHeading/index.tsx +1 -1
- package/src/components/sections/SubSubHeading/index.tsx +1 -1
- package/src/components/sections/brand-guide.ts +4 -3
- package/src/components/sections/core-sections.ts +2 -0
- package/src/components/shared/BrandSwatchesContext.ts +13 -0
- package/src/components/shared/Button.tsx +47 -34
- package/src/components/shared/Checkbox.tsx +21 -12
- package/src/components/shared/ConfirmDialog.tsx +87 -0
- package/src/components/shared/ConfirmInline.tsx +90 -0
- package/src/components/shared/ErrorBoundary.tsx +6 -18
- package/src/components/shared/Fab.tsx +15 -5
- package/src/components/shared/FabStack.tsx +28 -0
- package/src/components/shared/HistoryChangesModal.tsx +43 -41
- package/src/components/shared/HistoryPopover.tsx +7 -20
- package/src/components/shared/ImageBackgroundField.tsx +92 -0
- package/src/components/shared/{UploadRejectionAlert.tsx → InlineAlert.tsx} +21 -5
- package/src/components/shared/Input.tsx +23 -7
- package/src/components/shared/Navigation.tsx +22 -17
- package/src/components/shared/Notice.tsx +59 -0
- package/src/components/shared/Popover.tsx +7 -11
- package/src/components/shared/ReferenceSourcesContext.ts +27 -0
- package/src/components/shared/Select.tsx +5 -2
- package/src/components/shared/SplitButton.tsx +11 -17
- package/src/components/shared/SummaryAnchorsContext.ts +11 -0
- package/src/components/shared/Textarea.tsx +17 -2
- package/src/components/shared/ToolbarFrame.tsx +28 -0
- package/src/components/shared/VideoSourceField.tsx +1 -1
- package/src/components/shared/copy.ts +18 -0
- package/src/components/shared/icons.tsx +1 -17
- package/src/components/shell/AudienceAddForm.tsx +2 -2
- package/src/components/shell/AudienceDetailsPanel.tsx +23 -5
- package/src/components/shell/AudienceRow.tsx +22 -15
- package/src/components/shell/BugReportFAB.tsx +50 -40
- package/src/components/shell/CredentialList.tsx +33 -13
- package/src/components/shell/DocInspector.tsx +3 -12
- package/src/components/shell/DocumentEditorOverlay.tsx +33 -23
- package/src/components/shell/EditorContext.tsx +7 -0
- package/src/components/shell/EditorLoginForm.tsx +26 -36
- package/src/components/shell/EditorModal.tsx +8 -10
- package/src/components/shell/EditorShell.tsx +499 -339
- package/src/components/shell/HistoryToolbar.tsx +24 -26
- package/src/components/shell/MediaFolderTile.tsx +8 -3
- package/src/components/shell/MediaLibraryModal.tsx +59 -60
- package/src/components/shell/ProcessingIndicator.tsx +2 -2
- package/src/components/shell/RestoreModal.tsx +17 -16
- package/src/components/shell/SetPasswordForm.tsx +3 -3
- package/src/components/shell/SiteSettingsChatbot.tsx +61 -42
- package/src/components/shell/SiteSettingsDisplay.tsx +10 -20
- package/src/components/shell/SiteSettingsModal.tsx +24 -2
- package/src/components/shell/SiteSettingsTags.tsx +143 -0
- package/src/components/shell/SiteSettingsUsers.tsx +36 -26
- package/src/components/shell/SiteSettingsViewerAccess.tsx +2 -2
- package/src/components/shell/ViewerLoginForm.tsx +5 -4
- package/src/components/shell/reference-sources.ts +36 -0
- package/src/deck/index.ts +2 -2
- package/src/document/index.ts +2 -2
- package/src/hooks/useBlockDnd.ts +11 -0
- package/src/hooks/useBuildStatus.ts +53 -6
- package/src/hooks/useDocumentUploads.ts +1 -1
- package/src/hooks/useEditorPersistence.ts +141 -35
- package/src/hooks/useEditorPublish.ts +18 -27
- package/src/hooks/useMediaPipeline.ts +1 -1
- package/src/hooks/useNotice.ts +45 -0
- package/src/hooks/useSortableNavRow.ts +11 -0
- package/src/hooks/useSortableRow.ts +22 -2
- package/src/hooks/useStackedEscape.ts +62 -0
- package/src/lib/block-dnd.ts +6 -1
- package/src/lib/block-move.ts +3 -0
- package/src/lib/brand-swatches.ts +32 -0
- package/src/lib/container-ops.ts +5 -3
- package/src/lib/dexie.ts +15 -1
- package/src/lib/env.ts +9 -0
- package/src/lib/events.ts +1 -2
- package/src/lib/image-background.ts +31 -0
- package/src/lib/index.ts +5 -4
- package/src/lib/loader.ts +54 -55
- package/src/lib/nav-tree.ts +71 -0
- package/src/lib/nav.ts +196 -36
- package/src/lib/platform-broker.ts +32 -14
- package/src/lib/reference-type.ts +3 -0
- package/src/lib/reference-validation.ts +69 -0
- package/src/lib/references.ts +130 -0
- package/src/lib/registry.ts +37 -0
- package/src/lib/tags.ts +148 -0
- package/src/lib/theme-vars.ts +23 -0
- package/src/media/index.ts +1 -1
- package/src/media/utils.ts +18 -0
- package/src/media/{videoPoster.ts → video-poster.ts} +2 -2
- package/src/schemas/index.ts +1 -0
- package/src/schemas/shared.ts +24 -1
- package/src/schemas/site-config.ts +6 -1
- package/src/schemas/tags.ts +33 -0
- package/src/storage/types.ts +1 -1
- package/src/types/database.ts +2 -0
- package/dist/chunk-DNY72JZN.js +0 -86
- package/dist/chunk-F2LYQPXV.js +0 -77
- package/dist/chunk-JH762VUH.js +0 -128
- package/dist/chunk-K7DYZXVU.js +0 -138
- package/dist/components/primitives/defineHeadingSection.d.ts.map +0 -1
- package/dist/components/primitives/gridDrop.d.ts.map +0 -1
- package/dist/components/shared/UploadRejectionAlert.d.ts.map +0 -1
- package/dist/components/shell/BuildStatusIndicator.d.ts +0 -7
- package/dist/components/shell/BuildStatusIndicator.d.ts.map +0 -1
- package/dist/components/shell/blockMoveDispatch.d.ts.map +0 -1
- package/dist/hooks/index.d.ts +0 -9
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/lib/grid.d.ts +0 -2
- package/dist/lib/grid.d.ts.map +0 -1
- package/dist/lib/safeRedirect.d.ts.map +0 -1
- package/dist/media/videoPoster.d.ts.map +0 -1
- package/src/components/brandguide/ColorSwatchSettings.d.ts.map +0 -1
- package/src/components/brandguide/Colors.d.ts.map +0 -1
- package/src/components/primitives/CustomParagraph.d.ts.map +0 -1
- package/src/components/primitives/EditableGrid.d.ts.map +0 -1
- package/src/components/primitives/EditablePlainText.d.ts.map +0 -1
- package/src/components/primitives/EditableRichText.d.ts.map +0 -1
- package/src/components/primitives/HeadingSection.d.ts.map +0 -1
- package/src/components/primitives/IconPicker.d.ts.map +0 -1
- package/src/components/primitives/LinkPopover.d.ts.map +0 -1
- package/src/components/primitives/MediaSettingsForms.d.ts.map +0 -1
- package/src/components/primitives/ResolvedMedia.d.ts.map +0 -1
- package/src/components/primitives/RichTextToolbar.d.ts.map +0 -1
- package/src/components/primitives/tiptap-presets.d.ts.map +0 -1
- package/src/components/primitives/useEditableCollection.d.ts.map +0 -1
- package/src/components/primitives/useEditablePlainText.d.ts.map +0 -1
- package/src/components/primitives/useEditableRichText.d.ts.map +0 -1
- package/src/components/sections/Button/CTAButton.d.ts.map +0 -1
- package/src/components/sections/Button/index.d.ts.map +0 -1
- package/src/components/sections/Colors/index.d.ts.map +0 -1
- package/src/components/sections/IconList/IconList.d.ts.map +0 -1
- package/src/components/sections/IconList/IconListSettings.d.ts.map +0 -1
- package/src/components/sections/IconList/index.d.ts.map +0 -1
- package/src/components/sections/LinkHeading/index.d.ts.map +0 -1
- package/src/components/sections/Prose/Prose.d.ts.map +0 -1
- package/src/components/sections/Prose/index.d.ts.map +0 -1
- package/src/components/sections/SubHeading/index.d.ts.map +0 -1
- package/src/components/sections/SubSubHeading/index.d.ts.map +0 -1
- package/src/components/sections/register-schemas.d.ts.map +0 -1
- package/src/components/shared/Button.d.ts.map +0 -1
- package/src/components/shared/Checkbox.d.ts.map +0 -1
- package/src/components/shared/FormLabel.d.ts.map +0 -1
- package/src/components/shared/IconButton.d.ts.map +0 -1
- package/src/components/shared/Input.d.ts.map +0 -1
- package/src/components/shared/Select.d.ts.map +0 -1
- package/src/components/shared/icons.d.ts.map +0 -1
- package/src/components/shell/BuildStatusIndicator.tsx +0 -59
- package/src/components/shell/MediaLibraryContext.d.ts.map +0 -1
- package/src/hooks/index.ts +0 -8
- package/src/hooks/useResolvedMedia.d.ts.map +0 -1
- package/src/lib/cn.d.ts.map +0 -1
- package/src/lib/grid.d.ts.map +0 -1
- package/src/lib/grid.ts +0 -7
- package/src/lib/icons.d.ts.map +0 -1
- package/src/lib/loader.d.ts.map +0 -1
- package/src/lib/nav.d.ts.map +0 -1
- package/src/lib/registry.d.ts.map +0 -1
- package/src/lib/sanitize.d.ts.map +0 -1
- package/src/media/github.d.ts.map +0 -1
- package/src/media/index.d.ts.map +0 -1
- package/src/media/queue.d.ts.map +0 -1
- package/src/media/resolve.d.ts.map +0 -1
- package/src/media/types.d.ts.map +0 -1
- package/src/media/utils.d.ts.map +0 -1
- package/src/media/videoPoster.d.ts.map +0 -1
- package/src/schemas/auth.d.ts.map +0 -1
- package/src/schemas/media-grid-options.d.ts.map +0 -1
- package/src/schemas/media.d.ts.map +0 -1
- package/src/schemas/sections.d.ts.map +0 -1
- package/src/schemas/shared.d.ts.map +0 -1
- package/src/schemas/site-config.d.ts.map +0 -1
- /package/dist/{chunk-B7VBSE5X.js → chunk-3JDTXOIG.js} +0 -0
- /package/dist/{chunk-CXZ5YJG4.js → chunk-L45X7DKX.js} +0 -0
- /package/src/components/primitives/{gridDrop.ts → grid-drop.ts} +0 -0
- /package/src/components/shell/{blockMoveDispatch.ts → block-move-dispatch.ts} +0 -0
- /package/src/lib/{safeRedirect.ts → safe-redirect.ts} +0 -0
package/src/lib/nav.ts
CHANGED
|
@@ -4,7 +4,9 @@ import { audiencePasses, pageDisplayTitle } from "./pages";
|
|
|
4
4
|
import { navEntries } from "./nav-tree";
|
|
5
5
|
import { getContainerChildren } from "./container-ops";
|
|
6
6
|
import type { SiteIndex } from "../schemas/site-config";
|
|
7
|
+
import type { Section } from "../schemas/sections";
|
|
7
8
|
import type { SiteDocument } from "./documents";
|
|
9
|
+
import { expandReferences, referenceItems, REFERENCE_TYPE, type ReferenceContent, type ReferenceQuery } from "./references";
|
|
8
10
|
|
|
9
11
|
export interface NavItem {
|
|
10
12
|
href: string;
|
|
@@ -41,35 +43,114 @@ function flattenForNav(sections: LoadedSection[]): LoadedSection[] {
|
|
|
41
43
|
const walk = (loaded: LoadedSection) => {
|
|
42
44
|
flat.push(loaded);
|
|
43
45
|
for (const child of getContainerChildren(loaded.section)) {
|
|
44
|
-
walk({ section: child, meta: loaded.meta });
|
|
46
|
+
walk({ section: child, meta: loaded.meta, referencedBy: loaded.referencedBy });
|
|
45
47
|
}
|
|
46
48
|
};
|
|
47
49
|
sections.forEach(walk);
|
|
48
50
|
return flat;
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
/**
|
|
54
|
+
* The TL;DR / Summary section (`section_summary`) is the one non-heading type
|
|
55
|
+
* that can put itself in the nav, as a Heading (h1 role) or Subheading (h2 role)
|
|
56
|
+
* under its own label. Deliberately special-cased by type rather than added to
|
|
57
|
+
* the registry: the nav-entry option is scoped to this section only.
|
|
58
|
+
*/
|
|
59
|
+
const SUMMARY_TYPE = "section_summary";
|
|
60
|
+
|
|
61
|
+
export type SummaryNavLevel = "off" | "heading" | "subheading";
|
|
62
|
+
|
|
63
|
+
function summaryNav(section: LoadedSection["section"]): { role: "h1" | "h2"; label: string } | null {
|
|
64
|
+
if (section.type !== SUMMARY_TYPE) return null;
|
|
65
|
+
const options = ("options" in section ? section.options : undefined) as
|
|
66
|
+
| { nav?: SummaryNavLevel; navLabel?: string }
|
|
67
|
+
| undefined;
|
|
68
|
+
const label = options?.navLabel?.trim() ?? "";
|
|
69
|
+
// The label is required while nav is on; without one there is nothing to show.
|
|
70
|
+
if (!label || (options?.nav !== "heading" && options?.nav !== "subheading")) return null;
|
|
71
|
+
return { role: options.nav === "heading" ? "h1" : "h2", label };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
type RoleLookup = (type: string) => "h1" | "h2" | "h3" | undefined;
|
|
75
|
+
|
|
76
|
+
function roleLookup(registry?: SectionRegistry): RoleLookup {
|
|
77
|
+
return (type) => (registry ? registry.getSection(type) : getSection(type))?.navRole;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Every anchor the page renders, aligned with `flat` (undefined = none).
|
|
82
|
+
*
|
|
83
|
+
* Headings render id={toSectionId(heading)} unconditionally (even when excluded
|
|
84
|
+
* from nav) and never move, so every native heading anchor is reserved first.
|
|
85
|
+
* Summary anchors are derived from the nav label, so they can collide with a
|
|
86
|
+
* heading's or another summary's: summaries claim theirs next, in document
|
|
87
|
+
* order, suffixing -2, -3… on collision. Copies a Reference pulled in
|
|
88
|
+
* (`referencedBy`) claim last — `<anchor>-<reference id>`, then -2, -3… — for
|
|
89
|
+
* their headings and nav-on summaries alike, so adding a Reference never moves
|
|
90
|
+
* an anchor the page already had. Keying on the Reference rather than a bare
|
|
91
|
+
* suffix keeps two References showing the same section apart, and keeps each
|
|
92
|
+
* copy's anchor stable when References are reordered.
|
|
93
|
+
*
|
|
94
|
+
* Positional rather than keyed by section id: a Reference repeats its sources'
|
|
95
|
+
* ids, so one page can hold the same section twice.
|
|
96
|
+
*/
|
|
97
|
+
function assignAnchors(flat: LoadedSection[], lookupRole: RoleLookup): (string | undefined)[] {
|
|
98
|
+
const anchors: (string | undefined)[] = new Array(flat.length);
|
|
99
|
+
const taken = new Set<string>();
|
|
100
|
+
const claim = (i: number, base: string) => {
|
|
101
|
+
let anchor = base;
|
|
102
|
+
for (let n = 2; taken.has(anchor); n++) anchor = `${base}-${n}`;
|
|
103
|
+
taken.add(anchor);
|
|
104
|
+
anchors[i] = anchor;
|
|
105
|
+
};
|
|
106
|
+
const headingOf = (section: LoadedSection["section"]) => {
|
|
107
|
+
const heading = (section.content as { heading?: string }).heading;
|
|
108
|
+
return heading && lookupRole(section.type) ? heading : undefined;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
flat.forEach(({ section, referencedBy }, i) => {
|
|
112
|
+
const heading = headingOf(section);
|
|
113
|
+
if (referencedBy || !heading) return;
|
|
114
|
+
anchors[i] = toSectionId(heading);
|
|
115
|
+
taken.add(anchors[i]);
|
|
116
|
+
});
|
|
117
|
+
flat.forEach(({ section, referencedBy }, i) => {
|
|
118
|
+
const entry = summaryNav(section);
|
|
119
|
+
if (!referencedBy && entry) claim(i, toSectionId(entry.label));
|
|
120
|
+
});
|
|
121
|
+
flat.forEach(({ section, referencedBy }, i) => {
|
|
122
|
+
const text = summaryNav(section)?.label ?? headingOf(section);
|
|
123
|
+
if (referencedBy && text) claim(i, `${toSectionId(text)}-${toSectionId(referencedBy)}`);
|
|
124
|
+
});
|
|
125
|
+
return anchors;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function buildNavLinks(flat: LoadedSection[], lookupRole: RoleLookup, anchors: (string | undefined)[]): NavItem[] {
|
|
55
129
|
const nav: NavItem[] = [];
|
|
56
130
|
let currentParent: NavItem | null = null;
|
|
57
131
|
let currentChild: NavItem | null = null;
|
|
58
132
|
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
for (const { section, meta } of flattenForNav(sections)) {
|
|
65
|
-
const content = section.content as { heading?: string; excludeFromNav?: boolean };
|
|
66
|
-
if (!content.heading) continue;
|
|
133
|
+
for (const [i, { section, meta }] of flat.entries()) {
|
|
134
|
+
let role: "h1" | "h2" | "h3" | undefined;
|
|
135
|
+
let label: string;
|
|
136
|
+
let excluded = false;
|
|
67
137
|
|
|
68
|
-
const
|
|
69
|
-
if (
|
|
138
|
+
const summary = summaryNav(section);
|
|
139
|
+
if (summary) {
|
|
140
|
+
role = summary.role;
|
|
141
|
+
label = summary.label;
|
|
142
|
+
} else {
|
|
143
|
+
const content = section.content as { heading?: string; excludeFromNav?: boolean };
|
|
144
|
+
if (!content.heading) continue;
|
|
145
|
+
role = lookupRole(section.type);
|
|
146
|
+
label = content.heading;
|
|
147
|
+
excluded = !!content.excludeFromNav;
|
|
148
|
+
}
|
|
149
|
+
const anchor = anchors[i];
|
|
150
|
+
if (!role || !anchor) continue;
|
|
70
151
|
|
|
71
152
|
if (role === "h1") {
|
|
72
|
-
if (
|
|
153
|
+
if (excluded) {
|
|
73
154
|
currentParent = null;
|
|
74
155
|
// Clear the child cursor too: leaving it pointing into the PREVIOUS
|
|
75
156
|
// h1's subtree made an h3 following an excluded h1 nest under that
|
|
@@ -77,44 +158,123 @@ export function generateNavLinks(
|
|
|
77
158
|
currentChild = null;
|
|
78
159
|
continue;
|
|
79
160
|
}
|
|
80
|
-
currentParent = {
|
|
81
|
-
href: `#${toSectionId(content.heading)}`,
|
|
82
|
-
label: content.heading,
|
|
83
|
-
status: meta.status,
|
|
84
|
-
children: [],
|
|
85
|
-
};
|
|
161
|
+
currentParent = { href: `#${anchor}`, label, status: meta.status, children: [] };
|
|
86
162
|
currentChild = null;
|
|
87
163
|
nav.push(currentParent);
|
|
88
164
|
} else if (role === "h2") {
|
|
89
165
|
// Same cursor reset as the excluded h1 above — an h3 under an excluded
|
|
90
166
|
// h2 belongs nowhere, not under the preceding h2.
|
|
91
|
-
if (
|
|
167
|
+
if (excluded) {
|
|
92
168
|
currentChild = null;
|
|
93
169
|
continue;
|
|
94
170
|
}
|
|
95
171
|
if (!currentParent) continue;
|
|
96
|
-
currentChild = {
|
|
97
|
-
href: `#${toSectionId(content.heading)}`,
|
|
98
|
-
label: content.heading,
|
|
99
|
-
status: meta.status,
|
|
100
|
-
children: [],
|
|
101
|
-
};
|
|
172
|
+
currentChild = { href: `#${anchor}`, label, status: meta.status, children: [] };
|
|
102
173
|
currentParent.children.push(currentChild);
|
|
103
174
|
} else if (role === "h3") {
|
|
104
|
-
if (
|
|
175
|
+
if (excluded) continue;
|
|
105
176
|
if (!currentChild) continue;
|
|
106
|
-
currentChild.children.push({
|
|
107
|
-
href: `#${toSectionId(content.heading)}`,
|
|
108
|
-
label: content.heading,
|
|
109
|
-
status: meta.status,
|
|
110
|
-
children: [],
|
|
111
|
-
});
|
|
177
|
+
currentChild.children.push({ href: `#${anchor}`, label, status: meta.status, children: [] });
|
|
112
178
|
}
|
|
113
179
|
}
|
|
114
180
|
|
|
115
181
|
return nav;
|
|
116
182
|
}
|
|
117
183
|
|
|
184
|
+
export function generateNavLinks(
|
|
185
|
+
sections: LoadedSection[],
|
|
186
|
+
registry?: SectionRegistry,
|
|
187
|
+
): NavItem[] {
|
|
188
|
+
const flat = flattenForNav(sections);
|
|
189
|
+
const lookupRole = roleLookup(registry);
|
|
190
|
+
return buildNavLinks(flat, lookupRole, assignAnchors(flat, lookupRole));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export interface SummaryNavAnchor {
|
|
194
|
+
anchor: string;
|
|
195
|
+
/** False for a Subheading with no Link Heading above it: it renders its
|
|
196
|
+
* anchor but is dropped from the nav, so the editor hints about it. */
|
|
197
|
+
inNav: boolean;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Per summary section id (nav on, label set): the anchor it renders and
|
|
201
|
+
* whether it made it into the nav. Same derivation generateNavLinks uses. */
|
|
202
|
+
export function summaryNavAnchors(
|
|
203
|
+
sections: LoadedSection[],
|
|
204
|
+
registry?: SectionRegistry,
|
|
205
|
+
): Record<string, SummaryNavAnchor> {
|
|
206
|
+
const flat = flattenForNav(sections);
|
|
207
|
+
const lookupRole = roleLookup(registry);
|
|
208
|
+
const anchors = assignAnchors(flat, lookupRole);
|
|
209
|
+
const hrefs = new Set<string>();
|
|
210
|
+
const collect = (items: NavItem[]) => items.forEach((i) => { hrefs.add(i.href); collect(i.children); });
|
|
211
|
+
collect(buildNavLinks(flat, lookupRole, anchors));
|
|
212
|
+
const out: Record<string, SummaryNavAnchor> = {};
|
|
213
|
+
flat.forEach(({ section, referencedBy }, i) => {
|
|
214
|
+
const anchor = anchors[i];
|
|
215
|
+
if (!referencedBy && anchor && summaryNav(section)) out[section.id] = { anchor, inNav: hrefs.has(`#${anchor}`) };
|
|
216
|
+
});
|
|
217
|
+
return out;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Render-only copy of the page's sections with `navAnchor` stamped on every
|
|
222
|
+
* section whose rendered anchor it cannot derive alone — nav-bearing summaries
|
|
223
|
+
* and everything a Reference pulled in (container children included) — for
|
|
224
|
+
* renderers that can't share React context with the page: the SSR viewer
|
|
225
|
+
* renders each section as its own React tree. Never written back: the editor
|
|
226
|
+
* reads summary anchors from SummaryAnchorsContext instead, so stored content
|
|
227
|
+
* stays clean.
|
|
228
|
+
*
|
|
229
|
+
* `navAnchor` is not part of any section schema: it survives only because the
|
|
230
|
+
* viewer's later passes (media resolve, sanitizeLoadedSections) spread sections.
|
|
231
|
+
* A Zod re-parse after this point would strip it and leave the section without
|
|
232
|
+
* the id its nav entry links to — stamp after any parse, never before.
|
|
233
|
+
*/
|
|
234
|
+
export function withNavAnchors(sections: LoadedSection[], registry?: SectionRegistry): LoadedSection[] {
|
|
235
|
+
const anchors = assignAnchors(flattenForNav(sections), roleLookup(registry));
|
|
236
|
+
// Walks in flattenForNav's order (each section, then its children), so the
|
|
237
|
+
// running position lines up with `anchors`.
|
|
238
|
+
let at = 0;
|
|
239
|
+
const stamp = (section: Section, referenced: boolean): Section => {
|
|
240
|
+
const anchor = anchors[at++];
|
|
241
|
+
const children = getContainerChildren(section);
|
|
242
|
+
let out = section;
|
|
243
|
+
if (children.length > 0) {
|
|
244
|
+
out = { ...out, content: { ...(out.content as object), children: children.map((c) => stamp(c, referenced)) } } as Section;
|
|
245
|
+
}
|
|
246
|
+
return anchor && (referenced || summaryNav(section)) ? ({ ...out, navAnchor: anchor } as Section) : out;
|
|
247
|
+
};
|
|
248
|
+
return sections.map((s) => ({ ...s, section: stamp(s.section, !!s.referencedBy) }));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Every copy the editor renders for each Reference on the page, keyed by
|
|
253
|
+
* Reference id then source id, with anchors stamped over the WHOLE page — so
|
|
254
|
+
* the editor nav (built from expandReferences) links to exactly these ids, and
|
|
255
|
+
* two References showing one section never share a DOM id. Copies the
|
|
256
|
+
* published site drops (drafts, archived pages) claim after the rest, so a
|
|
257
|
+
* greyed copy can't take an anchor a published one would have.
|
|
258
|
+
* Lives here rather than in references.ts because it needs withNavAnchors, and
|
|
259
|
+
* references.ts importing nav.ts would cycle.
|
|
260
|
+
*/
|
|
261
|
+
export function pageReferenceCopies(
|
|
262
|
+
pageSections: LoadedSection[],
|
|
263
|
+
opts: Omit<ReferenceQuery, "referenceId" | "content">,
|
|
264
|
+
): Record<string, Record<string, Section>> {
|
|
265
|
+
const greyed = pageSections
|
|
266
|
+
.filter((s) => s.section.type === REFERENCE_TYPE)
|
|
267
|
+
.flatMap((r) =>
|
|
268
|
+
referenceItems({ ...opts, referenceId: r.section.id, content: r.section.content as unknown as ReferenceContent, audiences: [] })
|
|
269
|
+
.flatMap((i) => (i.source && i.excluded ? [{ ...i.source, referencedBy: r.section.id }] : [])),
|
|
270
|
+
);
|
|
271
|
+
const out: Record<string, Record<string, Section>> = {};
|
|
272
|
+
for (const s of withNavAnchors([...expandReferences(pageSections, { ...opts, audience: null }), ...greyed])) {
|
|
273
|
+
if (s.referencedBy) (out[s.referencedBy] ??= {})[s.section.id] = s.section;
|
|
274
|
+
}
|
|
275
|
+
return out;
|
|
276
|
+
}
|
|
277
|
+
|
|
118
278
|
export interface PageNavItem {
|
|
119
279
|
id: string;
|
|
120
280
|
title: string;
|
|
@@ -2,6 +2,10 @@ import { env } from "./env";
|
|
|
2
2
|
|
|
3
3
|
const ATTEMPTS = 3;
|
|
4
4
|
const RETRY_BASE_MS = 150;
|
|
5
|
+
const REQUEST_TIMEOUT_MS = 10_000;
|
|
6
|
+
// Gateway failures mean the request never reached a handler, so even a write
|
|
7
|
+
// is safe to replay. A 500 may have failed after the write landed; it isn't.
|
|
8
|
+
const GATEWAY_STATUSES = new Set([502, 503, 504]);
|
|
5
9
|
|
|
6
10
|
function sleep(ms: number): Promise<void> {
|
|
7
11
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -16,8 +20,8 @@ function sleep(ms: number): Promise<void> {
|
|
|
16
20
|
* request never got a response at all.
|
|
17
21
|
*/
|
|
18
22
|
export class PlatformBrokerError extends Error {
|
|
19
|
-
constructor(public readonly status: number | null, message: string) {
|
|
20
|
-
super(message);
|
|
23
|
+
constructor(public readonly status: number | null, message: string, options?: ErrorOptions) {
|
|
24
|
+
super(message, options);
|
|
21
25
|
this.name = "PlatformBrokerError";
|
|
22
26
|
}
|
|
23
27
|
}
|
|
@@ -31,7 +35,9 @@ export function isPlatformMode(): boolean {
|
|
|
31
35
|
* Call a platform broker endpoint scoped to this site. The platform authorizes
|
|
32
36
|
* the op against the site that owns PLATFORM_API_KEY — the client never names
|
|
33
37
|
* another tenant. Retries network/5xx (transient); 4xx is a real auth/config
|
|
34
|
-
* error and is surfaced immediately.
|
|
38
|
+
* error and is surfaced immediately. Writes are retried only on a gateway
|
|
39
|
+
* status: an unconfirmed write (timeout, dropped connection, 500) may already
|
|
40
|
+
* have landed. Mirrors fetchInstallationToken().
|
|
35
41
|
*/
|
|
36
42
|
export async function platformBroker<T = unknown>(
|
|
37
43
|
path: string,
|
|
@@ -52,15 +58,18 @@ export async function platformBroker<T = unknown>(
|
|
|
52
58
|
serializedBody = JSON.stringify(opts.body);
|
|
53
59
|
}
|
|
54
60
|
|
|
55
|
-
let lastError: unknown;
|
|
56
61
|
for (let attempt = 0; attempt < ATTEMPTS; attempt++) {
|
|
57
62
|
const isLast = attempt === ATTEMPTS - 1;
|
|
58
63
|
let res: Response;
|
|
59
64
|
try {
|
|
60
|
-
res = await fetch(url, { method, headers, body: serializedBody });
|
|
65
|
+
res = await fetch(url, { method, headers, body: serializedBody, signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) });
|
|
61
66
|
} catch (err) {
|
|
62
|
-
|
|
63
|
-
if (isLast)
|
|
67
|
+
// A timed-out write may already have landed. Do not automatically replay it.
|
|
68
|
+
if (method !== "GET" || isLast) {
|
|
69
|
+
throw new PlatformBrokerError(null, method === "GET"
|
|
70
|
+
? "Platform request failed — check your connection and try again."
|
|
71
|
+
: "Platform request wasn't confirmed — check its status before trying again.", { cause: err });
|
|
72
|
+
}
|
|
64
73
|
await sleep(RETRY_BASE_MS * 2 ** attempt);
|
|
65
74
|
continue;
|
|
66
75
|
}
|
|
@@ -68,14 +77,17 @@ export async function platformBroker<T = unknown>(
|
|
|
68
77
|
if (res.ok) {
|
|
69
78
|
if (res.status === 204) return undefined as T;
|
|
70
79
|
try {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
return undefined as T;
|
|
80
|
+
// Some endpoints (e.g. bug-report's 201) succeed with an empty body.
|
|
81
|
+
const text = await res.text();
|
|
82
|
+
return (text ? JSON.parse(text) : undefined) as T;
|
|
83
|
+
} catch (err) {
|
|
84
|
+
throw new PlatformBrokerError(null, method === "GET"
|
|
85
|
+
? "Platform response couldn't be read — try again."
|
|
86
|
+
: "Platform response couldn't be read — check the operation's status before trying again.", { cause: err });
|
|
74
87
|
}
|
|
75
88
|
}
|
|
76
89
|
|
|
77
|
-
if (res.status >= 500
|
|
78
|
-
lastError = new PlatformBrokerError(res.status, `Platform broker error (${res.status})`);
|
|
90
|
+
if (!isLast && (method === "GET" ? res.status >= 500 : GATEWAY_STATUSES.has(res.status))) {
|
|
79
91
|
await sleep(RETRY_BASE_MS * 2 ** attempt);
|
|
80
92
|
continue;
|
|
81
93
|
}
|
|
@@ -83,7 +95,8 @@ export async function platformBroker<T = unknown>(
|
|
|
83
95
|
throw new PlatformBrokerError(res.status, `Platform broker error (${res.status}): ${await res.text()}`);
|
|
84
96
|
}
|
|
85
97
|
|
|
86
|
-
|
|
98
|
+
// Unreachable: every final attempt returns or throws above.
|
|
99
|
+
throw new PlatformBrokerError(null, "Platform broker unreachable");
|
|
87
100
|
}
|
|
88
101
|
|
|
89
102
|
/**
|
|
@@ -104,18 +117,23 @@ export async function platformBrokerStream(
|
|
|
104
117
|
let lastError: unknown;
|
|
105
118
|
for (let attempt = 0; attempt < ATTEMPTS; attempt++) {
|
|
106
119
|
let res: Response;
|
|
120
|
+
// Bound only connection establishment; an active LLM stream may take longer.
|
|
121
|
+
const controller = new AbortController();
|
|
122
|
+
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
107
123
|
try {
|
|
108
124
|
res = await fetch(url, {
|
|
125
|
+
signal: controller.signal,
|
|
109
126
|
method: "POST",
|
|
110
127
|
headers: { "x-api-key": apiKey, "x-site-id": siteId, "Content-Type": "application/json" },
|
|
111
128
|
body: JSON.stringify(body),
|
|
112
129
|
});
|
|
113
130
|
} catch (err) {
|
|
114
131
|
lastError = err;
|
|
132
|
+
if (controller.signal.aborted) throw new PlatformBrokerError(null, "Chat connection timed out — try again.");
|
|
115
133
|
if (attempt === ATTEMPTS - 1) throw err;
|
|
116
134
|
await sleep(RETRY_BASE_MS * 2 ** attempt);
|
|
117
135
|
continue;
|
|
118
|
-
}
|
|
136
|
+
} finally { clearTimeout(timer); }
|
|
119
137
|
if (!res.ok || !res.body) throw new Error(`Platform broker error (${res.status})`);
|
|
120
138
|
return res.body;
|
|
121
139
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { SiteIndex } from "../schemas/site-config";
|
|
2
|
+
import { REFERENCE_TYPE } from "./reference-type";
|
|
3
|
+
|
|
4
|
+
/** What one Reference names that it can never show. */
|
|
5
|
+
export interface ReferenceProblem {
|
|
6
|
+
referenceId: string;
|
|
7
|
+
unknownTagIds: string[];
|
|
8
|
+
/** Not a top-level section on any page: deleted, or a container's child. */
|
|
9
|
+
unplacedSectionIds: string[];
|
|
10
|
+
/** Picks that are themselves References, which are never shown. */
|
|
11
|
+
referencePickIds: string[];
|
|
12
|
+
picksItself: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const strings = (v: unknown): string[] =>
|
|
16
|
+
Array.isArray(v) ? v.filter((x): x is string => typeof x === "string") : [];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The dangling-id check for Reference sections — what save_sections rejects on,
|
|
20
|
+
* and what validate_site and `authoring validate` warn about.
|
|
21
|
+
*
|
|
22
|
+
* Deliberately NOT part of applyContentWrite or the schema: on the viewer and in
|
|
23
|
+
* the editor a dangling pick or a deleted tag is ordinary (someone deleted the
|
|
24
|
+
* source), so it drops silently and an editor save must keep succeeding. This
|
|
25
|
+
* is for whoever just chose the ids, where a bad one is a mistake.
|
|
26
|
+
*
|
|
27
|
+
* `sections` are full section bodies; anything that is not a Reference (by body
|
|
28
|
+
* type or index type) is ignored. `tagIds` null means the registry could not
|
|
29
|
+
* be read whole, so tag ids go unchecked rather than being misreported.
|
|
30
|
+
*/
|
|
31
|
+
export function referenceProblems(
|
|
32
|
+
sections: readonly { id: string; content: unknown }[],
|
|
33
|
+
index: Pick<SiteIndex, "pages" | "sections">,
|
|
34
|
+
tagIds: ReadonlySet<string> | null,
|
|
35
|
+
): ReferenceProblem[] {
|
|
36
|
+
const onPage = new Set(index.pages.flatMap((p) => p.order));
|
|
37
|
+
const problems: ReferenceProblem[] = [];
|
|
38
|
+
for (const { id, content } of sections) {
|
|
39
|
+
const body = content as { type?: unknown; content?: { tagIds?: unknown; sectionIds?: unknown } } | null;
|
|
40
|
+
if (body?.type !== REFERENCE_TYPE && index.sections[id]?.type !== REFERENCE_TYPE) continue;
|
|
41
|
+
const picks = strings(body?.content?.sectionIds).filter((pick) => pick !== id);
|
|
42
|
+
const problem: ReferenceProblem = {
|
|
43
|
+
referenceId: id,
|
|
44
|
+
unknownTagIds: tagIds ? strings(body?.content?.tagIds).filter((tag) => !tagIds.has(tag)) : [],
|
|
45
|
+
unplacedSectionIds: picks.filter((pick) => !onPage.has(pick)),
|
|
46
|
+
referencePickIds: picks.filter((pick) => onPage.has(pick) && index.sections[pick]?.type === REFERENCE_TYPE),
|
|
47
|
+
picksItself: strings(body?.content?.sectionIds).includes(id),
|
|
48
|
+
};
|
|
49
|
+
if (
|
|
50
|
+
problem.unknownTagIds.length || problem.unplacedSectionIds.length ||
|
|
51
|
+
problem.referencePickIds.length || problem.picksItself
|
|
52
|
+
) problems.push(problem);
|
|
53
|
+
}
|
|
54
|
+
return problems;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** One line per Reference, naming every bad id. */
|
|
58
|
+
export function describeReferenceProblem(problem: ReferenceProblem): string {
|
|
59
|
+
const list = (ids: string[]) => ids.map((id) => `"${id}"`).join(", ");
|
|
60
|
+
const issues = [
|
|
61
|
+
...(problem.unknownTagIds.length ? [`tagIds ${list(problem.unknownTagIds)} are not tags on this site`] : []),
|
|
62
|
+
...(problem.unplacedSectionIds.length
|
|
63
|
+
? [`sectionIds ${list(problem.unplacedSectionIds)} are not top-level sections on any page (a container's child cannot be picked — pick the container)`]
|
|
64
|
+
: []),
|
|
65
|
+
...(problem.referencePickIds.length ? [`sectionIds ${list(problem.referencePickIds)} are References, which a Reference never shows`] : []),
|
|
66
|
+
...(problem.picksItself ? ["sectionIds includes the Reference itself"] : []),
|
|
67
|
+
];
|
|
68
|
+
return `Reference "${problem.referenceId}": ${issues.join("; ")}.`;
|
|
69
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import type { LoadedSection } from "./loader";
|
|
2
|
+
import type { Page, SiteIndex } from "../schemas/site-config";
|
|
3
|
+
import { audiencePasses } from "./pages";
|
|
4
|
+
import { pagesInNavOrder } from "./nav-tree";
|
|
5
|
+
import { siteTags } from "./tags";
|
|
6
|
+
import { REFERENCE_TYPE } from "./reference-type";
|
|
7
|
+
|
|
8
|
+
export { REFERENCE_TYPE };
|
|
9
|
+
|
|
10
|
+
export interface ReferenceContent {
|
|
11
|
+
tagIds: string[];
|
|
12
|
+
sectionIds: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Why the published site leaves a referenced section out entirely. */
|
|
16
|
+
export type ReferenceExclusion =
|
|
17
|
+
/** Deleted, its file failed to load, or not a top-level section. */
|
|
18
|
+
| "missing"
|
|
19
|
+
/** Another Reference — never nested. */
|
|
20
|
+
| "reference"
|
|
21
|
+
| "archived-page"
|
|
22
|
+
/** A draft or archived section. */
|
|
23
|
+
| "not-live";
|
|
24
|
+
|
|
25
|
+
/** What a Reference is resolved against. */
|
|
26
|
+
export interface ReferenceQuery {
|
|
27
|
+
referenceId: string;
|
|
28
|
+
content: ReferenceContent;
|
|
29
|
+
index: SiteIndex;
|
|
30
|
+
allSections: LoadedSection[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ReferenceItem {
|
|
34
|
+
sectionId: string;
|
|
35
|
+
/** The page the section sits on; null when it is on none (deleted, or an
|
|
36
|
+
* id that was never a top-level section). */
|
|
37
|
+
page: Page | null;
|
|
38
|
+
source: LoadedSection | null;
|
|
39
|
+
excluded: ReferenceExclusion | null;
|
|
40
|
+
/** Of the audiences asked about, those who can't see it on its own page.
|
|
41
|
+
* Always empty for an excluded item. */
|
|
42
|
+
hiddenFrom: string[];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Everything a Reference points at, as the editor lists it: in site order,
|
|
47
|
+
* with what the viewer would silently drop kept and marked `excluded`, and
|
|
48
|
+
* with `hiddenFrom` naming which of `audiences` its source page and section
|
|
49
|
+
* keep it from. Picks that are on no page trail, in stored order. Only the
|
|
50
|
+
* Reference itself is left out. resolveReference is this list filtered, so
|
|
51
|
+
* the two can never disagree about what a Reference shows.
|
|
52
|
+
*/
|
|
53
|
+
export function referenceItems(opts: ReferenceQuery & { audiences: string[] }): ReferenceItem[] {
|
|
54
|
+
const { referenceId, content, index, allSections, audiences } = opts;
|
|
55
|
+
|
|
56
|
+
const wanted = new Set(content.sectionIds);
|
|
57
|
+
const tagIds = new Set(content.tagIds);
|
|
58
|
+
for (const tag of siteTags(index)) {
|
|
59
|
+
if (tagIds.has(tag.id)) for (const id of tag.sectionIds) wanted.add(id);
|
|
60
|
+
}
|
|
61
|
+
wanted.delete(referenceId);
|
|
62
|
+
if (wanted.size === 0) return [];
|
|
63
|
+
|
|
64
|
+
const byId = new Map(allSections.map((s) => [s.section.id, s]));
|
|
65
|
+
const item = (sectionId: string, page: Page | null): ReferenceItem => {
|
|
66
|
+
const source = byId.get(sectionId) ?? null;
|
|
67
|
+
const excluded: ReferenceExclusion | null =
|
|
68
|
+
!source || !page ? "missing"
|
|
69
|
+
: source.section.type === REFERENCE_TYPE ? "reference"
|
|
70
|
+
: page.status !== "live" ? "archived-page"
|
|
71
|
+
: source.meta.status !== "live" ? "not-live"
|
|
72
|
+
: null;
|
|
73
|
+
const hiddenFrom = excluded || !source || !page ? [] : audiences.filter(
|
|
74
|
+
(a) => !audiencePasses(page.access, a) || !audiencePasses(source.meta.access, a),
|
|
75
|
+
);
|
|
76
|
+
return { sectionId, page, source, excluded, hiddenFrom };
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const items: ReferenceItem[] = [];
|
|
80
|
+
const placed = new Set<string>();
|
|
81
|
+
for (const page of pagesInNavOrder(index)) {
|
|
82
|
+
for (const id of page.order) {
|
|
83
|
+
if (!wanted.has(id)) continue;
|
|
84
|
+
items.push(item(id, page));
|
|
85
|
+
placed.add(id);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
for (const id of wanted) if (!placed.has(id)) items.push(item(id, null));
|
|
89
|
+
return items;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* The sections a Reference shows: the union of its tagged and picked sections,
|
|
94
|
+
* resolved against the content it is given (the published content, on every
|
|
95
|
+
* viewer path) in site order — pages in nav order, then position on the page.
|
|
96
|
+
*
|
|
97
|
+
* A Reference stores pointers rather than copies because the index puts each
|
|
98
|
+
* section id on exactly one page. Resolving them is where every exposure rule
|
|
99
|
+
* lives, so a Reference can never widen what a viewer sees:
|
|
100
|
+
* - an item must pass its SOURCE page's access and its source section's access
|
|
101
|
+
* for `audience` (the viewer has already passed the referencing page's and
|
|
102
|
+
* the Reference's own, so the result is the intersection of all four);
|
|
103
|
+
* - only live sections on live pages — hidden (not-in-nav) pages count,
|
|
104
|
+
* archived ones do not;
|
|
105
|
+
* - References in the source set are skipped, the Reference itself included,
|
|
106
|
+
* so there are no cycles. Containers come whole, children and all.
|
|
107
|
+
* Dangling picks and deleted tags drop silently. Items are the source entries
|
|
108
|
+
* marked with `referencedBy`, which is what gives their anchors the
|
|
109
|
+
* `-<reference id>` suffix (lib/nav.ts).
|
|
110
|
+
*/
|
|
111
|
+
export function resolveReference(opts: ReferenceQuery & { audience: string | null }): LoadedSection[] {
|
|
112
|
+
const { audience, ...rest } = opts;
|
|
113
|
+
return referenceItems({ ...rest, audiences: audience === null ? [] : [audience] }).flatMap((i) =>
|
|
114
|
+
i.source && !i.excluded && i.hiddenFrom.length === 0 ? [{ ...i.source, referencedBy: opts.referenceId }] : [],
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** A page's sections with each Reference replaced, in place, by the copies it
|
|
119
|
+
* shows `audience` — what the viewer renders and what the page nav is built
|
|
120
|
+
* from, on both the viewer and the editor. */
|
|
121
|
+
export function expandReferences(
|
|
122
|
+
pageSections: LoadedSection[],
|
|
123
|
+
opts: Omit<ReferenceQuery, "referenceId" | "content"> & { audience: string | null },
|
|
124
|
+
): LoadedSection[] {
|
|
125
|
+
return pageSections.flatMap((s) =>
|
|
126
|
+
s.section.type === REFERENCE_TYPE
|
|
127
|
+
? resolveReference({ ...opts, referenceId: s.section.id, content: s.section.content as unknown as ReferenceContent })
|
|
128
|
+
: [s],
|
|
129
|
+
);
|
|
130
|
+
}
|
package/src/lib/registry.ts
CHANGED
|
@@ -2,7 +2,9 @@ import type { ZodType, z } from "zod";
|
|
|
2
2
|
import type { ComponentType, ReactNode } from "react";
|
|
3
3
|
import type { Audience } from "../schemas/auth";
|
|
4
4
|
import type { LinkValue } from "../schemas/link";
|
|
5
|
+
import type { ImageBackground } from "../schemas/shared";
|
|
5
6
|
import type { SectionContent } from "../schemas/sections";
|
|
7
|
+
import type { TagEntry } from "../schemas/tags";
|
|
6
8
|
import type { VideoSource } from "../components/sections/Video/VideoBlock";
|
|
7
9
|
|
|
8
10
|
// --- Settings field types ---
|
|
@@ -71,6 +73,16 @@ export type SettingsFieldDef =
|
|
|
71
73
|
label: string;
|
|
72
74
|
default: { documentId: string };
|
|
73
75
|
target?: "content" | "options";
|
|
76
|
+
}
|
|
77
|
+
| {
|
|
78
|
+
/** Per-placement backdrop behind an image (ImageBackgroundField). Leave
|
|
79
|
+
* `default` unset: an absent value is "None" and must stay absent on save. */
|
|
80
|
+
type: "image-background";
|
|
81
|
+
label: string;
|
|
82
|
+
default?: ImageBackground;
|
|
83
|
+
target?: "content" | "options";
|
|
84
|
+
/** Hide the field for the current values (e.g. when the ref is a video). */
|
|
85
|
+
hidden?: (values: Record<string, unknown>) => boolean;
|
|
74
86
|
};
|
|
75
87
|
|
|
76
88
|
export type SettingsSchema = Record<string, SettingsFieldDef>;
|
|
@@ -105,6 +117,20 @@ export interface SectionProps<T = unknown> {
|
|
|
105
117
|
onDelete?: () => void;
|
|
106
118
|
}
|
|
107
119
|
|
|
120
|
+
/** The site's tag registry and its edits, as the editor shell hands them to
|
|
121
|
+
* the canvas and Site settings. */
|
|
122
|
+
export interface SiteTagging {
|
|
123
|
+
/** Already reconciled (siteTags). */
|
|
124
|
+
tags: TagEntry[];
|
|
125
|
+
/** False when the registry was never loaded or loaded degraded: a save would
|
|
126
|
+
* wholesale-replace tags.json with what this session couldn't fully read. */
|
|
127
|
+
editable: boolean;
|
|
128
|
+
onCreate: (label: string, sectionId?: string) => void;
|
|
129
|
+
onRename: (tagId: string, label: string) => void;
|
|
130
|
+
onDelete: (tagId: string) => void;
|
|
131
|
+
onToggle: (sectionId: string, tagId: string, tagged: boolean) => void;
|
|
132
|
+
}
|
|
133
|
+
|
|
108
134
|
export interface WrapperProps {
|
|
109
135
|
sectionId: string;
|
|
110
136
|
sectionType: string;
|
|
@@ -112,6 +138,8 @@ export interface WrapperProps {
|
|
|
112
138
|
dirty?: boolean;
|
|
113
139
|
index: number;
|
|
114
140
|
isLast: boolean;
|
|
141
|
+
/** Total sections on the page — Move up/down's "N of M" and end-disabling. */
|
|
142
|
+
sectionCount?: number;
|
|
115
143
|
containerId: string;
|
|
116
144
|
isContainerBlock: boolean;
|
|
117
145
|
definition: SectionDefinition;
|
|
@@ -119,6 +147,15 @@ export interface WrapperProps {
|
|
|
119
147
|
audiences: Audience[];
|
|
120
148
|
access: string[];
|
|
121
149
|
onAccessChange?: (access: string[]) => void;
|
|
150
|
+
/** Section tags (the tags.json sidecar). Only root sections get a
|
|
151
|
+
* SectionWrapper — container children are not index sections and cannot be
|
|
152
|
+
* tagged. Absent handlers make the Tags pill read-only. */
|
|
153
|
+
tagging: {
|
|
154
|
+
tags: TagEntry[];
|
|
155
|
+
assignedIds: string[];
|
|
156
|
+
onToggle?: (tagId: string, tagged: boolean) => void;
|
|
157
|
+
onCreate?: (label: string) => void;
|
|
158
|
+
};
|
|
122
159
|
onStatusChange?: (status: "draft" | "live" | "archived") => void;
|
|
123
160
|
onSectionChange?: (options: Record<string, unknown>) => void;
|
|
124
161
|
onReorder?: (fromIndex: number, toIndex: number) => void;
|