camox 0.44.0 → 0.45.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/dist/components/AuthGate.js +0 -6
- package/dist/components/DeviceSelector.js +96 -0
- package/dist/components/PlatformLabel.js +20 -24
- package/dist/core/components/lexical/HighlightNode.js +1 -1
- package/dist/core/components/lexical/InlineContentEditable.js +15 -19
- package/dist/core/components/lexical/InlineLexicalEditor.js +54 -74
- package/dist/core/components/lexical/InlineStylesPlugin.js +53 -53
- package/dist/core/components/lexical/MaxLengthPlugin.js +59 -0
- package/dist/core/components/lexical/SelectionBroadcaster.js +53 -54
- package/dist/core/components/lexical/SidebarLexicalEditor.js +322 -229
- package/dist/core/components/lexical/SummaryToggleGuard.js +42 -0
- package/dist/core/components/lexical/TextLinkPopover.js +169 -180
- package/dist/core/createApp.d.ts +241 -312
- package/dist/core/createApp.js +123 -29
- package/dist/core/createBlock.d.ts +5 -5
- package/dist/core/createBlock.js +385 -152
- package/dist/core/createCollection.d.ts +55 -0
- package/dist/core/createCollection.js +93 -0
- package/dist/core/createLayout.d.ts +197 -61
- package/dist/core/createLayout.js +114 -56
- package/dist/core/defineDocument.d.ts +3 -5
- package/dist/core/derivedRoutes.js +49 -1
- package/dist/core/dom.d.ts +13 -4
- package/dist/core/dom.js +44 -1
- package/dist/core/editing/BlockEditingRuntime.d.ts +16 -0
- package/dist/core/editing/BlockEditingRuntime.js +6 -15
- package/dist/core/editing/CompleteBlockEditingRuntime.js +28 -23
- package/dist/core/editing/createEditableBlock.d.ts +754 -633
- package/dist/core/editing/createEditableBlock.js +2113 -1066
- package/dist/core/editing/referenceWrites.js +51 -0
- package/dist/core/editing/useLatestRecord.js +90 -0
- package/dist/core/hooks/useArtifactHost.js +139 -0
- package/dist/core/hooks/useFieldSelection.js +43 -18
- package/dist/core/hooks/useIsEditable.js +12 -10
- package/dist/core/hooks/useOverlayMessage.js +15 -19
- package/dist/core/lib/artifact.d.ts +21 -0
- package/dist/core/lib/artifact.js +154 -0
- package/dist/core/lib/artifactColors.js +123 -0
- package/dist/core/lib/artifactData.d.ts +1 -0
- package/dist/core/lib/artifactData.js +131 -0
- package/dist/core/lib/artifactHost.js +205 -0
- package/dist/core/lib/artifactRuntime.js +36 -0
- package/dist/core/lib/artifactTheme.js +57 -0
- package/dist/core/lib/contentType.d.ts +302 -106
- package/dist/core/lib/contentType.js +201 -173
- package/dist/core/lib/fieldTypes.js +3 -0
- package/dist/core/lib/iconTypes.d.ts +3 -3
- package/dist/core/lib/icons.d.ts +4 -4
- package/dist/core/lib/icons.js +40 -44
- package/dist/core/lib/imagePlaceholder.js +177 -0
- package/dist/core/lib/imageTransform.js +1 -1
- package/dist/core/lib/lexicalReact.d.ts +3 -1
- package/dist/core/lib/lexicalState.js +1 -1
- package/dist/core/lib/previewPaused.js +9 -0
- package/dist/core/lib/recordScope.js +316 -0
- package/dist/core/lib/reference.d.ts +81 -0
- package/dist/core/lib/reference.js +29 -0
- package/dist/core/lib/textLinks.js +7 -5
- package/dist/core/lib/textStyles.d.ts +5 -7
- package/dist/core/lib/textStyles.js +2 -2
- package/dist/core/notFound.d.ts +7 -0
- package/dist/core/notFound.js +11 -0
- package/dist/core/pageDestinations.js +6 -3
- package/dist/core/recordItem.js +34 -0
- package/dist/core/requestContext.d.ts +26 -0
- package/dist/core/requestContext.js +20 -0
- package/dist/features/canvas/CamoxCanvas.js +1734 -0
- package/dist/features/canvas/CanvasBlockFrame.js +353 -0
- package/dist/features/canvas/CanvasBlockHeader.js +55 -0
- package/dist/features/canvas/CanvasLoadingScreen.js +35 -0
- package/dist/features/canvas/CanvasOverlays.js +254 -0
- package/dist/features/canvas/CanvasPageEdge.js +87 -0
- package/dist/features/canvas/CanvasPageFrame.js +335 -0
- package/dist/features/canvas/CanvasPageHeader.js +125 -0
- package/dist/features/canvas/CanvasPreviewPageSlot.js +103 -0
- package/dist/features/canvas/CanvasShelfMarker.js +255 -0
- package/dist/features/canvas/DerivedPagePathInput.js +238 -0
- package/dist/features/canvas/canvasCamera.js +229 -0
- package/dist/features/canvas/canvasFlight.js +104 -0
- package/dist/features/canvas/canvasFrameCapture.js +364 -0
- package/dist/features/canvas/canvasFrameDocument.js +317 -0
- package/dist/features/canvas/canvasFrameMode.js +53 -0
- package/dist/features/canvas/canvasInsertionSeams.js +44 -0
- package/dist/features/canvas/canvasItems.js +120 -0
- package/dist/features/canvas/canvasOverlayGeometry.js +240 -0
- package/dist/features/canvas/canvasPages.js +79 -0
- package/dist/features/canvas/canvasScreenSpace.js +26 -0
- package/dist/features/canvas/canvasSelection.js +18 -0
- package/dist/features/canvas/canvasStore.js +149 -0
- package/dist/features/canvas/canvasThumbnails.js +304 -0
- package/dist/features/canvas/canvasZoom.js +10 -0
- package/dist/features/canvas/sidebar/CanvasInspector.js +202 -0
- package/dist/features/canvas/sidebar/CanvasNavigator.js +226 -0
- package/dist/features/canvas/sidebar/CanvasSidebar.js +60 -0
- package/dist/features/canvas/sidebar/CanvasSidebarFrame.js +48 -0
- package/dist/features/canvas/sidebar/CanvasSidebarSkeleton.js +39 -0
- package/dist/features/canvas/sidebar/CatalogBlockInspector.js +189 -0
- package/dist/features/canvas/sidebar/CuratedPageInspector.js +69 -0
- package/dist/features/canvas/sidebar/DerivedPageInspector.js +37 -0
- package/dist/features/canvas/sidebar/LayoutPageBlocks.js +78 -0
- package/dist/features/canvas/sidebar/LayoutPageOverview.js +71 -0
- package/dist/features/canvas/sidebar/PageInspector.js +157 -0
- package/dist/features/canvas/sidebar/PageInspectorSkeleton.js +90 -0
- package/dist/features/canvas/sidebar/PageOverview.js +27 -0
- package/dist/features/canvas/sidebar/SingletonPageInspector.js +31 -0
- package/dist/features/canvas/useCanvasCamera.js +803 -0
- package/dist/features/canvas/useCanvasFrame.js +149 -0
- package/dist/features/canvas/useCanvasFrameInput.js +97 -0
- package/dist/features/canvas/useCanvasNearness.js +57 -0
- package/dist/features/canvas/useDiscoveredPreviewPaths.js +62 -0
- package/dist/features/content/CamoxContent.d.ts +2 -5
- package/dist/features/content/CamoxContent.js +174 -271
- package/dist/features/content/CollectionItemSheetContext.js +106 -0
- package/dist/features/content/ContentAssets.js +315 -0
- package/dist/features/content/ContentCollection.js +663 -0
- package/dist/features/content/collection-form.js +58 -0
- package/dist/features/content/components/AssetCard.js +80 -84
- package/dist/features/content/components/AssetCardSkeleton.js +4 -8
- package/dist/features/content/components/ContentSidebar.js +129 -18
- package/dist/features/content/components/ContentSidebarGroup.js +37 -0
- package/dist/features/content/components/DeleteCollectionItemButton.js +202 -0
- package/dist/features/content/components/MissingFromLocale.js +282 -0
- package/dist/features/content/components/UploadDropZone.js +27 -31
- package/dist/features/content/components/UploadProgressDrawer.js +90 -98
- package/dist/features/navigation/navigation.d.ts +66 -20
- package/dist/features/navigation/navigation.js +257 -90
- package/dist/features/og/defaultFonts.js +8 -8
- package/dist/features/og/geist-variable-ttf-base64.js +1 -1
- package/dist/features/og/imageResponse.d.ts +8 -0
- package/dist/features/og/imageResponse.js +26 -0
- package/dist/features/page/CuratedPageContent.js +167 -0
- package/dist/features/page/DerivedPageContent.js +26 -30
- package/dist/features/page/SharedLayoutContent.js +25 -27
- package/dist/features/preview/CamoxPreview.d.ts +23 -61
- package/dist/features/preview/CamoxPreview.js +816 -537
- package/dist/features/preview/EditablePageContent.js +1 -1
- package/dist/features/preview/EditablePageExperience.js +16 -22
- package/dist/features/preview/components/AddBlockDialog.js +270 -0
- package/dist/features/preview/components/AssetFieldEditor.js +173 -154
- package/dist/features/preview/components/AssetLightbox.js +181 -134
- package/dist/features/preview/components/AssetPickerModal.js +153 -170
- package/dist/features/preview/components/BlockActionsPopover.js +150 -149
- package/dist/features/preview/components/BlockErrorBoundary.js +8 -12
- package/dist/features/preview/components/BlockThumbnail.js +176 -100
- package/dist/features/preview/components/CanvasPreview.js +243 -0
- package/dist/features/preview/components/CreatePageModal.js +128 -137
- package/dist/features/preview/components/DebouncedFieldEditor.js +29 -33
- package/dist/features/preview/components/DerivedLayoutDialog.js +21 -19
- package/dist/features/preview/components/DrillRow.js +150 -0
- package/dist/features/preview/components/FieldOverlayStyles.js +11 -8
- package/dist/features/preview/components/FieldToolbar.js +160 -110
- package/dist/features/preview/components/Frame.js +108 -20
- package/dist/features/preview/components/IconFieldEditor.js +62 -66
- package/dist/features/preview/components/ItemFieldsEditor.js +231 -205
- package/dist/features/preview/components/LinkFieldEditor.js +135 -139
- package/dist/features/preview/components/Metadata.js +22 -30
- package/dist/features/preview/components/MobilePreviewDrawer.js +89 -108
- package/dist/features/preview/components/MultipleAssetFieldEditor.js +334 -246
- package/dist/features/preview/components/OverlayTracker.js +9 -41
- package/dist/features/preview/components/Overlays.js +81 -69
- package/dist/features/preview/components/PageInfo.js +1748 -0
- package/dist/features/preview/components/PageInfoSkeleton.js +51 -0
- package/dist/features/preview/components/PageLocationFieldset.js +112 -116
- package/dist/features/preview/components/PageNavigatorHeader.js +24 -0
- package/dist/features/preview/components/PageNicknameField.js +22 -26
- package/dist/features/preview/components/PagePicker.js +128 -130
- package/dist/features/preview/components/PageTree.js +525 -532
- package/dist/features/preview/components/PreviewActivation.js +28 -32
- package/dist/features/preview/components/PreviewPanel.js +620 -319
- package/dist/features/preview/components/PreviewToolbar.js +105 -333
- package/dist/features/preview/components/RecordCard.js +80 -0
- package/dist/features/preview/components/RecordCombobox.js +178 -0
- package/dist/features/preview/components/ReferenceFieldEditor.js +149 -0
- package/dist/features/preview/components/ReferenceListFieldEditor.js +432 -0
- package/dist/features/preview/components/RemovedBlockPlaceholder.js +95 -0
- package/dist/features/preview/components/RepeatableItemsList.js +125 -138
- package/dist/features/preview/components/SelectionEditor.js +993 -0
- package/dist/features/preview/components/ShikiMarkdown.js +14 -18
- package/dist/features/preview/components/SidebarSection.js +52 -64
- package/dist/features/preview/components/StudioEditingFallback.js +56 -0
- package/dist/features/preview/components/UnlinkAssetButton.js +104 -107
- package/dist/features/preview/components/blockGridNavigation.js +15 -0
- package/dist/features/preview/components/contentFieldSchema.js +8 -0
- package/dist/features/preview/components/useFieldWriter.js +200 -0
- package/dist/features/preview/components/useRecordView.js +383 -0
- package/dist/features/preview/components/useRepeatableItemActions.js +35 -37
- package/dist/features/preview/components/useUpdateBlockPosition.js +37 -37
- package/dist/features/preview/editingFrame.js +197 -0
- package/dist/features/preview/overlayHighlights.js +17 -44
- package/dist/features/preview/overlayMessages.js +72 -1
- package/dist/features/preview/pageRecord.js +189 -0
- package/dist/features/preview/previewConstants.js +6 -2
- package/dist/features/preview/previewPageFrame.js +95 -0
- package/dist/features/preview/previewPreparation.js +7 -0
- package/dist/features/preview/previewSelection.js +32 -63
- package/dist/features/preview/previewStore.js +225 -123
- package/dist/features/preview/referencePickerFocus.js +29 -0
- package/dist/features/preview/repeatableItemTargets.js +56 -0
- package/dist/features/preview/selectionReveal.js +21 -0
- package/dist/features/preview/usePreviewViewportActions.js +114 -0
- package/dist/features/preview/viewportContinuity.js +109 -0
- package/dist/features/provider/AuthenticatedCamoxProvider.js +69 -44
- package/dist/features/provider/CamoxProvider.d.ts +3 -6
- package/dist/features/provider/CamoxProvider.js +38 -50
- package/dist/features/provider/CoreCamoxProvider.js +66 -50
- package/dist/features/provider/LocalhostPreviewProvider.js +78 -114
- package/dist/features/provider/PreviewSignInPage.js +54 -0
- package/dist/features/provider/components/CamoxAppContext.js +11 -11
- package/dist/features/provider/components/CommandPalette.js +75 -82
- package/dist/features/provider/useAdminShortcuts.js +7 -11
- package/dist/features/routes/pageRuntime.d.ts +44 -4
- package/dist/features/routes/pageRuntime.js +244 -137
- package/dist/features/runtime/RuntimeChrome.js +117 -115
- package/dist/features/runtime/alternates.js +64 -0
- package/dist/features/runtime/analytics.d.ts +7 -0
- package/dist/features/runtime/analytics.js +66 -0
- package/dist/features/runtime/artifactSources.js +87 -0
- package/dist/features/runtime/editingFallback.js +20 -0
- package/dist/features/runtime/linkPrefetch.js +97 -0
- package/dist/features/runtime/livePages.js +62 -0
- package/dist/features/runtime/llmsTxt.js +40 -0
- package/dist/features/runtime/loopbackFetch.js +18 -0
- package/dist/features/runtime/navigationAccessibility.js +51 -0
- package/dist/features/runtime/navigationTarget.js +23 -2
- package/dist/features/runtime/pageApp.js +229 -158
- package/dist/features/runtime/pageBlockTypes.js +20 -0
- package/dist/features/runtime/pageClient.d.ts +2 -4
- package/dist/features/runtime/pageClient.js +18 -3
- package/dist/features/runtime/pageNavigation.js +254 -32
- package/dist/features/runtime/pageServer.d.ts +2 -4
- package/dist/features/runtime/pageServer.js +6 -2
- package/dist/features/runtime/pageViewport.js +7 -0
- package/dist/features/runtime/runtime.d.ts +59 -24
- package/dist/features/runtime/runtime.js +779 -191
- package/dist/features/runtime/runtimeChromeLoader.js +58 -0
- package/dist/features/runtime/runtimeRoutes.d.ts +19 -0
- package/dist/features/runtime/runtimeRoutes.js +25 -0
- package/dist/features/runtime/scrollRestoration.js +133 -0
- package/dist/features/runtime/studioApp.d.ts +4 -3
- package/dist/features/runtime/studioApp.js +5 -9
- package/dist/features/runtime/studioClient.d.ts +2 -4
- package/dist/features/runtime/studioClient.js +12 -3
- package/dist/features/runtime/studioServer.d.ts +2 -4
- package/dist/features/runtime/studioServer.js +6 -1
- package/dist/features/runtime/worker.d.ts +6 -0
- package/dist/features/runtime/worker.js +23 -0
- package/dist/features/runtime/workerRequest.js +44 -0
- package/dist/features/studio/CamoxStudio.d.ts +2 -6
- package/dist/features/studio/CamoxStudio.js +34 -49
- package/dist/features/studio/FollowAgentProvider.js +338 -0
- package/dist/features/studio/components/ChatSidebar.js +356 -0
- package/dist/features/studio/components/DraftsControl.js +676 -0
- package/dist/features/studio/components/FaviconDialog.js +318 -0
- package/dist/features/studio/components/LocalePicker.js +785 -0
- package/dist/features/studio/components/Navbar.js +123 -103
- package/dist/features/studio/components/ProjectMenu.js +239 -260
- package/dist/features/studio/components/RenameDraftDialog.js +206 -0
- package/dist/features/studio/components/ReviewDraftSheet.js +1472 -0
- package/dist/features/studio/components/ThreadComposer.js +157 -0
- package/dist/features/studio/components/ThreadMarkdown.js +113 -0
- package/dist/features/studio/components/ThreadMessages.js +270 -0
- package/dist/features/studio/components/UserButton.js +34 -23
- package/dist/features/studio/draftLabel.js +33 -0
- package/dist/features/studio/draftNotice.js +13 -0
- package/dist/features/studio/followAgent.js +250 -0
- package/dist/features/studio/followAgentContext.js +7 -0
- package/dist/features/studio/localeOptions.js +118 -0
- package/dist/features/studio/localeSwitch.js +19 -0
- package/dist/features/studio/routes.js +48 -1
- package/dist/features/studio/studioStore.js +64 -1
- package/dist/features/studio/useTheme.js +24 -27
- package/dist/features/vite/artifactRuntimeMinification.js +36 -0
- package/dist/features/vite/blockBoilerplate.js +6 -5
- package/dist/features/vite/blockChunks.js +31 -0
- package/dist/features/vite/bundlerLogs.js +17 -0
- package/dist/features/vite/definitionsSync.js +158 -226
- package/dist/features/vite/devAuthentication.js +4 -16
- package/dist/features/vite/devSignIn.js +87 -0
- package/dist/features/vite/iconGeneration.js +1 -1
- package/dist/features/vite/layoutGeneration.js +11 -1
- package/dist/features/vite/publishSeededContent.js +30 -0
- package/dist/features/vite/routeGeneration.js +6 -7
- package/dist/features/vite/runtimeDev.js +80 -99
- package/dist/features/vite/vite.d.ts +51 -31
- package/dist/features/vite/vite.js +224 -127
- package/dist/features/vite/workerBuild.js +39 -0
- package/dist/hooks/use-debounced-field.js +116 -47
- package/dist/hooks/use-file-upload.js +114 -67
- package/dist/hooks/use-marquee-selection.js +32 -36
- package/dist/hooks/use-page-destinations.js +69 -37
- package/dist/lib/api-client-server.d.ts +5 -0
- package/dist/lib/api-client-server.js +10 -4
- package/dist/lib/api-client.d.ts +6 -0
- package/dist/lib/api-client.js +51 -16
- package/dist/lib/api-socket.js +17 -0
- package/dist/lib/auth-page-styles.js +39 -0
- package/dist/lib/auth.js +117 -61
- package/dist/lib/collection-cache.js +23 -0
- package/dist/lib/draft-scope.d.ts +4 -0
- package/dist/lib/draft-scope.js +222 -0
- package/dist/lib/html.js +12 -0
- package/dist/lib/link-pages.js +154 -0
- package/dist/lib/locale-path.d.ts +22 -0
- package/dist/lib/locale-path.js +57 -0
- package/dist/lib/normalized-data.js +252 -152
- package/dist/lib/preview-handoff.js +2 -2
- package/dist/lib/queries.d.ts +11 -0
- package/dist/lib/queries.js +70 -141
- package/dist/lib/studio-reads.d.ts +14 -0
- package/dist/lib/studio-reads.js +574 -0
- package/dist/lib/use-project-room.js +50 -50
- package/dist/lib/use-thread.js +310 -0
- package/dist/lib/utils.js +25 -29
- package/dist/lib/view-locale-context.js +22 -0
- package/dist/lib/view-locale.js +46 -0
- package/dist/package.js +5 -0
- package/dist/studio-overlays.css +41 -151
- package/dist/studio.css +2 -2
- package/package.json +70 -73
- package/skills/camox/SKILL.md +16 -11
- package/skills/camox/references/artifacts.md +179 -0
- package/skills/camox/references/block-definitions.md +75 -59
- package/skills/camox/references/cli-common.md +9 -11
- package/skills/camox/references/cli-content.md +92 -69
- package/skills/camox/references/cli-publishing.md +26 -0
- package/skills/camox/references/dom-integrations.md +27 -0
- package/skills/camox/references/field-styling.md +4 -4
- package/skills/camox/references/layout-definitions.md +16 -14
- package/skills/camox/references/localization.md +56 -0
- package/skills/camox/references/routed-layouts.md +133 -15
- package/skills/camox-expand/SKILL.md +69 -0
- package/skills/camox-expand/references/jobs.md +39 -0
- package/skills/camoxify/SKILL.md +29 -7
- package/dist/core/components/AddBlockControlBar.js +0 -78
- package/dist/core/hooks/useRequireDraftSource.js +0 -23
- package/dist/features/metadata/sitemap.d.ts +0 -8
- package/dist/features/metadata/sitemap.js +0 -17
- package/dist/features/og/imageResponse.node.d.ts +0 -12
- package/dist/features/og/imageResponse.node.js +0 -49
- package/dist/features/og/imageResponse.workerd.d.ts +0 -12
- package/dist/features/og/imageResponse.workerd.js +0 -40
- package/dist/features/page/PublishedPageContent.js +0 -205
- package/dist/features/page/PublishedPageExperience.js +0 -23
- package/dist/features/preview/DerivedPreview.js +0 -134
- package/dist/features/preview/commentsEnabled.js +0 -8
- package/dist/features/preview/components/AddBlockSidebar.js +0 -381
- package/dist/features/preview/components/AttachedComments.js +0 -331
- package/dist/features/preview/components/CommentHeader.js +0 -122
- package/dist/features/preview/components/CommentSidebar.js +0 -29
- package/dist/features/preview/components/CommentTargetQuote.js +0 -167
- package/dist/features/preview/components/DerivedLayoutSidebar.d.ts +0 -5
- package/dist/features/preview/components/DerivedLayoutSidebar.js +0 -179
- package/dist/features/preview/components/DerivedPageInfoSidebar.js +0 -69
- package/dist/features/preview/components/LeftSidebar.js +0 -68
- package/dist/features/preview/components/PageEditorSidebar.js +0 -563
- package/dist/features/preview/components/PageInfoSidebar.js +0 -1551
- package/dist/features/preview/components/PageNavigatorSidebar.js +0 -78
- package/dist/features/preview/components/PageStatusBadge.js +0 -97
- package/dist/features/preview/components/PeekedBlock.js +0 -217
- package/dist/features/preview/components/PreviewComments.js +0 -56
- package/dist/features/preview/components/PreviewToolbarSpacer.js +0 -43
- package/dist/features/preview/components/PublicationRow.js +0 -422
- package/dist/features/preview/components/PublishDialog.js +0 -194
- package/dist/features/preview/components/RightSidebar.js +0 -59
- package/dist/features/preview/components/SendFeedbackDialog.js +0 -83
- package/dist/features/preview/previewCommentsStore.js +0 -135
- package/dist/features/preview/publication.js +0 -81
- package/dist/features/preview/usePageComments.js +0 -39
- package/dist/features/runtime/SharedChromeContext.js +0 -7
- package/dist/features/studio/components/EnvironmentMenu.js +0 -425
- package/dist/features/vite/appGeneration.js +0 -89
- package/dist/lib/view-queries.js +0 -28
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/Matches.d.ts +0 -114
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/RouterProvider.d.ts +0 -25
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/config.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/defer.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/fileRoute.d.ts +0 -20
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/global.d.ts +0 -8
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/hash-scroll.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/index.d.ts +0 -18
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/link.d.ts +0 -161
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/load-matches.d.ts +0 -6
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/location.d.ts +0 -54
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/lru-cache.d.ts +0 -8
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/manifest.d.ts +0 -32
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/new-process-route-tree.d.ts +0 -115
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/not-found.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/redirect.d.ts +0 -49
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/rewrite.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/root.d.ts +0 -6
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/route.d.ts +0 -343
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/routeInfo.d.ts +0 -48
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/router.d.ts +0 -695
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/scroll-restoration.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/searchMiddleware.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/searchParams.d.ts +0 -5
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/ssr/serializer/RawStream.d.ts +0 -38
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/ssr/serializer/seroval-plugins.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/ssr/serializer/transformer.d.ts +0 -65
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/stores.d.ts +0 -69
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/structuralSharing.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/typePrimitives.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useLoaderData.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useLoaderDeps.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useNavigate.d.ts +0 -10
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useParams.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useRouteContext.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useSearch.d.ts +0 -1
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/utils.d.ts +0 -28
- package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/validators.d.ts +0 -49
- package/skills/camox/references/cli-environments.md +0 -37
|
@@ -14,7 +14,7 @@ Spread the field's render-prop `props` onto the element to preserve inline editi
|
|
|
14
14
|
<myBlock.Field name="title">{(props) => <h1 {...props} />}</myBlock.Field>
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
Camox owns inline markup so editing and published output have the same appearance. Customize appearance with `textStyle` and `linkStyle` (these replace the old `components` prop):
|
|
17
|
+
Camox owns inline markup so editing and published output have the same appearance. Customize appearance with `textStyle` and `linkStyle` (these replace the old `components` prop). Utilities below, such as `text-brand`, are examples; use the ones the project's Theme defines:
|
|
18
18
|
|
|
19
19
|
```tsx
|
|
20
20
|
<myBlock.Field
|
|
@@ -31,12 +31,12 @@ Camox owns inline markup so editing and published output have the same appearanc
|
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
return {
|
|
34
|
-
className: bold ? "text-
|
|
34
|
+
className: bold ? "text-brand" : undefined,
|
|
35
35
|
style: bold && italic ? { letterSpacing: "0.02em" } : undefined,
|
|
36
36
|
};
|
|
37
37
|
}}
|
|
38
38
|
linkStyle={({ external }) => ({
|
|
39
|
-
className: external ? "text-
|
|
39
|
+
className: external ? "text-ink-muted" : "text-brand font-medium",
|
|
40
40
|
})}
|
|
41
41
|
>
|
|
42
42
|
{(props) => <p {...props} />}
|
|
@@ -45,6 +45,6 @@ Camox owns inline markup so editing and published output have the same appearanc
|
|
|
45
45
|
|
|
46
46
|
- `textStyle` receives `{ bold, italic, strikethrough, highlight }` and returns `{ className?, style? }` or `undefined`. For text runs, `highlight` is false and the other flags describe explicit content formatting, not inherited heading styles. Unmarked text keeps its parent's typography.
|
|
47
47
|
- `linkStyle` receives `{ target, href, external, pageId }` and returns the same appearance-only object. Links are underlined by default; override `style.textDecorationLine` to change that.
|
|
48
|
-
- Camox also calls `textStyle` once for each shared highlight wrapper with `{ highlight: true, bold: false, italic: false, strikethrough: false }`. That result styles the whole range; its children receive their own text-run calls. `highlight` identifies the wrapper being styled, not whether a run is inside a highlight. This keeps one continuous background when bold/italic formatting changes within the range. Highlights default to the
|
|
48
|
+
- Camox also calls `textStyle` once for each shared highlight wrapper with `{ highlight: true, bold: false, italic: false, strikethrough: false }`. That result styles the whole range; its children receive their own text-run calls. `highlight` identifies the wrapper being styled, not whether a run is inside a highlight. This keeps one continuous background when bold/italic formatting changes within the range. Highlights default to the website's `--camox-highlight` color, falling back to `currentColor`. Return a custom `style.color` to change it, or use a clipped background with transparent text fill to render a gradient, as shown above.
|
|
49
49
|
- Styles apply to inline children; the outer element's `className` stays on that element and is inherited normally. Explicit formatting defaults (combined italic, strikethrough, highlight color) use inline styles; use `style` to override those defaults rather than competing utility classes.
|
|
50
50
|
- These props accept appearance only, never replacement elements, children, or event handlers. They also work on `item.Field` inside repeaters. The sidebar editor shows default formatting; field-specific styles apply on the page.
|
|
@@ -12,9 +12,9 @@ Use one `.tsx` file per layout in `src/layouts/`, a named `layout` or `Layout` e
|
|
|
12
12
|
|
|
13
13
|
```tsx
|
|
14
14
|
import { createLayout } from "camox/createLayout";
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
import { block as footerBlock } from "../blocks/footer";
|
|
17
|
-
import { block as
|
|
17
|
+
import { block as navbarBlock } from "../blocks/navbar";
|
|
18
18
|
|
|
19
19
|
const myLayout = createLayout({
|
|
20
20
|
id: "my-layout",
|
|
@@ -23,7 +23,7 @@ const myLayout = createLayout({
|
|
|
23
23
|
blocks: {
|
|
24
24
|
before: [navbarBlock],
|
|
25
25
|
after: [footerBlock],
|
|
26
|
-
initial: [
|
|
26
|
+
initial: [() => import("../blocks/hero")],
|
|
27
27
|
},
|
|
28
28
|
component: MyLayoutComponent,
|
|
29
29
|
buildMetaTitle: ({ pageMetaTitle, projectName }) => `${pageMetaTitle} | ${projectName}`,
|
|
@@ -44,26 +44,28 @@ export { myLayout as layout };
|
|
|
44
44
|
|
|
45
45
|
## The `createLayout` options
|
|
46
46
|
|
|
47
|
-
| Option
|
|
48
|
-
|
|
|
49
|
-
| `id`
|
|
50
|
-
| `title`
|
|
51
|
-
| `description`
|
|
52
|
-
| `blocks`
|
|
53
|
-
| `component`
|
|
54
|
-
| `buildMetaTitle`
|
|
55
|
-
| `
|
|
47
|
+
| Option | Required | Description |
|
|
48
|
+
| ---------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
|
49
|
+
| `id` | yes | Unique kebab-case identifier matching the filename without extension. |
|
|
50
|
+
| `title` | yes | CMS display name. |
|
|
51
|
+
| `description` | yes | Guidance for users and agents on which pages suit this layout. |
|
|
52
|
+
| `blocks` | yes | `before`, `after`, and optional `initial` arrays; see layout slot rules below. |
|
|
53
|
+
| `component` | yes | Named function component receiving `{ children }`. |
|
|
54
|
+
| `buildMetaTitle` | yes | `({ pageMetaTitle, projectName, pageFullPath }) => string` for the browser title. |
|
|
55
|
+
| `buildMetaDescription` | no | `({ pageMetaDescription, projectName, pageFullPath }) => string \| undefined`. Without it, the editor's description is used. |
|
|
56
|
+
| `buildOgImage` | no | `({ title, description, projectName }) => JSX` for a sharing image. |
|
|
56
57
|
|
|
57
|
-
##
|
|
58
|
+
## Layout Slots — `before`, `after`, and `initial`
|
|
58
59
|
|
|
59
60
|
- `before` and `after` require blocks with `layoutOnly: true`. Either array may be empty.
|
|
60
61
|
- Render groups with `<myLayout.BeforeBlocks />` and `<myLayout.AfterBlocks />`, not individual blocks. Array order controls rendering order; edit the config to add or reorder blocks.
|
|
61
|
-
- For identical navbar/footer content across layouts, also set `synced: true`. `layoutOnly` restricts
|
|
62
|
+
- For identical navbar/footer content across layouts, also set `synced: true`. `layoutOnly` restricts a block to layout slots; it does not enable syncing. See [Synced blocks](block-definitions.md#synced-blocks-synced-optional) for sharing and existing-placement behavior.
|
|
62
63
|
|
|
63
64
|
## Initial Blocks — `blocks.initial` (optional)
|
|
64
65
|
|
|
65
66
|
`initial` seeds the homepage at project creation, using block defaults in array order. It does not populate existing Pages.
|
|
66
67
|
|
|
68
|
+
- List each block as an import of its module, `() => import("../blocks/hero")`, never a statically imported block. Layouts load on every page, so a static import would ship the block to every visitor.
|
|
67
69
|
- Accepts only blocks with `layoutOnly` omitted or `false`.
|
|
68
70
|
- At most one layout per app may define `initial`; multiple definitions cause a startup error.
|
|
69
71
|
- Without `initial`, Camox seeds an empty homepage using the first curated layout. Apps with only singleton/derived layouts do not seed a curated homepage.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Localization in code
|
|
2
|
+
|
|
3
|
+
A project publishes in one or more locales, added and removed as content, never in code. The default locale's pages sit at unprefixed paths (`/pricing`); every other locale's sit under its lowercase tag (`/de/preise`). Camox translates everything in blocks and records, writes `<html lang>`, `hreflang` alternates and the combined sitemap itself. Code only has to keep text in blocks and links in the locale.
|
|
4
|
+
|
|
5
|
+
## Visible text lives in blocks
|
|
6
|
+
|
|
7
|
+
Every text a visitor can read is block content, layout blocks included: navbar and footer labels, a singleton's heading, a "Back to articles" link, a record page's "Written by". Camox translates block content; it never translates JSX. Put the text in a `layoutOnly` block placed in the layout's `before` or `after` slots, or in a field of the block that shows it. JSX keeps only structure, styling and data a loader returns.
|
|
8
|
+
|
|
9
|
+
## Internal links stay in the locale
|
|
10
|
+
|
|
11
|
+
Write root-relative paths without a locale prefix, and pass them through `useLocalizedHref` from `camox/navigation`. On a `de` page, `/articles` becomes `/de/articles`; on a default-locale page it stays `/articles`. External URLs, `#anchors` and `mailto:` links come back as is.
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { Link, useLocalizedHref } from "camox/navigation";
|
|
15
|
+
|
|
16
|
+
function BackToArticles({ children }: { children: React.ReactNode }) {
|
|
17
|
+
const localized = useLocalizedHref();
|
|
18
|
+
return <Link to={localized("/articles")}>{children}</Link>;
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Link fields and `camox:page:` text links already resolve per locale: render them as they come. The locale's prefix comes from the helper alone, so a `/de` written into code or content links German visitors to `/de/de/...` and English visitors to German.
|
|
23
|
+
|
|
24
|
+
## Locale switcher
|
|
25
|
+
|
|
26
|
+
`useAlternates()` returns the current page in every locale it exists in, the current one included, as `{ locale, href, label }`. `label` is the locale's name in its own language (`Deutsch`, `français`), from `Intl.DisplayNames`; capitalize it with CSS if the design needs it. A locale the page was removed from isn't listed, so the switcher never links to a 404. `useLocale()` returns the current locale's tag, such as `de`.
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { Link, useAlternates, useLocale } from "camox/navigation";
|
|
30
|
+
|
|
31
|
+
function LocaleSwitcher() {
|
|
32
|
+
const current = useLocale();
|
|
33
|
+
const alternates = useAlternates();
|
|
34
|
+
if (alternates.length < 2) return null;
|
|
35
|
+
return (
|
|
36
|
+
<nav aria-label="Language">
|
|
37
|
+
{alternates.map(({ locale, href, label }) => (
|
|
38
|
+
<Link key={locale} to={href} hrefLang={locale} aria-current={locale === current}>
|
|
39
|
+
{label}
|
|
40
|
+
</Link>
|
|
41
|
+
))}
|
|
42
|
+
</nav>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Loaders
|
|
48
|
+
|
|
49
|
+
A singleton or derived layout's `loader` receives `locale`, the page's locale tag. Use it to fetch localized data when the data source has it. Camox renders what a loader returns as it is and never translates it. Route patterns aren't localized: `routes` lists the same paths for every locale, and `/de/pokemon/pikachu` renders the `pokemon.$name` layout in German.
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
loader: async ({ params, locale }) => ({
|
|
53
|
+
kind: "data",
|
|
54
|
+
data: await loadPokemon(params.name, { language: locale }),
|
|
55
|
+
}),
|
|
56
|
+
```
|
|
@@ -5,7 +5,7 @@ Use this reference for code-owned pages in `src/layouts/`. For editor-created pa
|
|
|
5
5
|
- **`singleton`**: one code-owned page at a fixed file-defined URL. Editors change its blocks, not its existence, URL, or structure.
|
|
6
6
|
- **`derived`**: code-owned routes resolved through a required server-side loader, typically using file parameters such as `$name`.
|
|
7
7
|
|
|
8
|
-
All editable content lives in blocks. Layouts do not expose editable `content` schemas or `.Field` helpers. Read [Block Definitions](block-definitions.md) when creating or changing those blocks.
|
|
8
|
+
All editable content lives in blocks. Layouts do not expose editable `content` schemas or `.Field` helpers; [record pages](#record-pages) render their collection record through `layout.Item`. Read [Block Definitions](block-definitions.md) when creating or changing those blocks.
|
|
9
9
|
|
|
10
10
|
## Singleton pages
|
|
11
11
|
|
|
@@ -13,13 +13,14 @@ Declare a singleton in `src/layouts/pokedex.tsx`. The file identity defines `/po
|
|
|
13
13
|
|
|
14
14
|
```tsx
|
|
15
15
|
import { createLayout } from "camox/createLayout";
|
|
16
|
+
|
|
17
|
+
import { block as footerBlock } from "../blocks/footer";
|
|
16
18
|
import { block as navbarBlock } from "../blocks/navbar";
|
|
17
19
|
import { block as pokedexIntroBlock } from "../blocks/pokedex-intro";
|
|
18
|
-
import { block as footerBlock } from "../blocks/footer";
|
|
19
|
-
import { loadPokemon } from "../lib/pokemon";
|
|
20
20
|
import { PokemonBrowser } from "../components/pokemon-browser";
|
|
21
|
+
import { loadPokemon } from "../lib/pokemon";
|
|
21
22
|
|
|
22
|
-
export const
|
|
23
|
+
export const layout = createLayout("pokedex")({
|
|
23
24
|
kind: "singleton",
|
|
24
25
|
title: "Pokédex",
|
|
25
26
|
description: "The code-owned Pokémon directory.",
|
|
@@ -27,41 +28,158 @@ export const Layout = createLayout("pokedex")({
|
|
|
27
28
|
before: [navbarBlock, pokedexIntroBlock],
|
|
28
29
|
after: [footerBlock],
|
|
29
30
|
},
|
|
30
|
-
loader: () => loadPokemon(),
|
|
31
|
-
|
|
31
|
+
loader: async () => ({ kind: "data", data: await loadPokemon() }),
|
|
32
|
+
// After the loader: these callbacks' `data` is inferred from it.
|
|
32
33
|
buildMetaTitle: ({ pageMetaTitle }) => pageMetaTitle,
|
|
34
|
+
toMarkdown: ({ data }) => [`${data.length} Pokémon`, data.map((p) => `- ${p.name}`).join("\n")],
|
|
35
|
+
component: PokedexPage,
|
|
33
36
|
});
|
|
34
37
|
|
|
35
38
|
function PokedexPage() {
|
|
36
|
-
const pokemon =
|
|
39
|
+
const pokemon = layout.useData();
|
|
37
40
|
return (
|
|
38
41
|
<>
|
|
39
|
-
<
|
|
42
|
+
<layout.BeforeBlocks />
|
|
40
43
|
<main>
|
|
41
44
|
<PokemonBrowser pokemon={pokemon} />
|
|
42
45
|
</main>
|
|
43
|
-
<
|
|
46
|
+
<layout.AfterBlocks />
|
|
44
47
|
</>
|
|
45
48
|
);
|
|
46
49
|
}
|
|
47
50
|
```
|
|
48
51
|
|
|
49
|
-
- `loader` is optional.
|
|
52
|
+
- `loader` is optional. Return `{ kind: "data", data }`, synchronously or asynchronously. `layout.useData()` infers and returns the unwrapped `data`, not the envelope. It runs server-side for document requests and client navigation; the entire envelope travels through JSON to hydration. Keep data JSON-serializable (no functions, symbols, BigInt, or cyclic objects). Without a loader, `useData()` returns `undefined`. The loader also receives `locale`, the page's locale tag; Camox renders its data untranslated (see [Localization](localization.md#loaders)).
|
|
50
53
|
- Before/after blocks must be `layoutOnly: true`. Put editable headings, introductions, and other bespoke content in these blocks; keep structural application UI in the component. See [Block Definitions](block-definitions.md).
|
|
51
|
-
- For a navbar or footer whose content must stay identical across different layouts, also set `synced: true` in its `createBlock` options. `layoutOnly: true` alone only restricts placement. See [Synced blocks](block-definitions.md#synced-blocks-synced-optional) for sharing
|
|
54
|
+
- For a navbar or footer whose content must stay identical across different layouts, also set `synced: true` in its `createBlock` options. `layoutOnly: true` alone only restricts placement. See [Synced blocks](block-definitions.md#synced-blocks-synced-optional) for sharing behavior.
|
|
52
55
|
- No `children` slot or `blocks.initial`: editors do not compose or create these pages.
|
|
53
56
|
- No curated page record is created. Do not use the pages CLI to create, duplicate, move, or assign a singleton.
|
|
54
|
-
- Studio lists singletons as directly navigable pages with block editing
|
|
57
|
+
- Studio lists singletons as directly navigable pages with block editing, without deletion or URL/layout controls. They also appear in link destination pickers (including inline text links), “Go to page” actions, and the runtime sitemap. Derived pages appear in the sitemap only when their layout declares `routes` (see [Route discovery](#route-discovery)).
|
|
55
58
|
- Singleton links are saved as fixed internal URLs, not synthetic database page IDs: link fields use `{ type: "external", href: "/pokedex", text: "Pokédex", newTab: false }` (the existing URL representation), while inline text uses `[Pokédex](/pokedex)`. Renaming a code-owned URL requires updating links or adding a redirect. Curated-page links retain their page-ID references.
|
|
56
59
|
- Curated-only mutation surfaces, such as page-parent selectors and the pages record API/CLI, remain separate from the navigable destination list.
|
|
57
|
-
-
|
|
60
|
+
- The route renders the current editable blocks.
|
|
58
61
|
- Code deployment controls route existence. Removing the definition and syncing releases its reserved URL. Existing curated URL collisions or layouts still assigned to curated pages must be resolved explicitly before sync can succeed.
|
|
59
62
|
- Curated pages cannot claim singleton URLs or use singleton/derived layouts. Literal singleton routes take precedence over parameterized derived routes.
|
|
60
|
-
-
|
|
63
|
+
- See [Metadata and Markdown](#metadata-and-markdown) for page titles, descriptions, and the page's Markdown Representation.
|
|
61
64
|
|
|
62
65
|
## Derived routes
|
|
63
66
|
|
|
64
|
-
Use the same file-based form with `kind: "derived"`, for example `createLayout("pokemon.$name")`. Its required `loader: ({ params }) => ...` receives typed `params.name`. Read its
|
|
67
|
+
Use the same file-based form with `kind: "derived"`, for example `createLayout("pokemon.$name")`. Its required `loader: ({ params }) => ...` receives typed `params.name` and returns `{ kind: "data", data }`. Read its unwrapped data through `layout.useData()`. Use `throw notFound()` (imported from `camox/createLayout`) for a missing record; other loader errors remain server errors. Like singletons, derived routes have before/after blocks but no page-owned `children` or `blocks.initial`. Unlike singletons, curated URLs retain precedence over derived routes.
|
|
68
|
+
|
|
69
|
+
### Route discovery
|
|
70
|
+
|
|
71
|
+
A derived layout can list the pages it renders with an optional `routes` function, a sibling of `loader`:
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
export const Layout = createLayout("pokemon.$name")({
|
|
75
|
+
kind: "derived",
|
|
76
|
+
// Other options omitted.
|
|
77
|
+
loader: async ({ params }) => ({ kind: "data", data: await loadPokemon(params.name) }),
|
|
78
|
+
routes: async () => ({
|
|
79
|
+
data: (await listPokemon()).map((pokemon) => ({ params: { name: pokemon.name } })),
|
|
80
|
+
}),
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
- Return `{ data: [{ params }] }`, synchronously or asynchronously. `params` is typed from the file identity: `pokemon.$name` needs exactly `{ name: string }`. Return raw values; Camox builds and encodes the URL (`mr mime` becomes `/pokemon/mr%20mime`).
|
|
85
|
+
- Only derived layouts accept `routes`; singletons and curated layouts are a type error.
|
|
86
|
+
- The runtime sitemap lists every discovered page, without `<lastmod>`. The Canvas previews the layout's first discovered page until a path is chosen.
|
|
87
|
+
- Entries may carry an optional `target` naming the collection record they render (see [Record pages](#record-pages)).
|
|
88
|
+
- An invalid entry (missing, extra or empty params, or a value containing `/`) or a throwing `routes` makes `/sitemap.xml` respond 500 with a message naming the layout, rather than silently omitting pages.
|
|
89
|
+
- `routes` lists pages; it does not authorize them. The loader still validates every request, and URLs left out of `routes` keep rendering. A curated page at a discovered URL still takes precedence and may be listed twice.
|
|
90
|
+
- `routes` returns every page at once.
|
|
91
|
+
- `loader` receives `{ params, context }` and `routes` receives `{ context }`. `context` is an opaque request context: ignore it, or pass it through unchanged to Camox helpers. The sitemap and Markdown always get live content; HTML and Studio navigation follow the editor's session.
|
|
92
|
+
|
|
93
|
+
### Record pages
|
|
94
|
+
|
|
95
|
+
A **Record Page** renders one collection record, found by one of its fields. Spread `collection.pages({ by, param })` into a derived layout instead of writing `loader` and `routes`:
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
import { createLayout } from "camox/createLayout";
|
|
99
|
+
|
|
100
|
+
import { collection as articles } from "../collections/articles";
|
|
101
|
+
|
|
102
|
+
export const Layout = createLayout("articles.$slug")({
|
|
103
|
+
kind: "derived",
|
|
104
|
+
title: "Article",
|
|
105
|
+
description: "One article, found by its slug.",
|
|
106
|
+
blocks: { before: [navbarBlock], after: [footerBlock] },
|
|
107
|
+
...articles.pages({ by: "slug", param: "slug" }),
|
|
108
|
+
buildMetaTitle: ({ item, projectName }) => `${item.title} | ${projectName}`,
|
|
109
|
+
buildMetaDescription: ({ item }) => item.excerpt || undefined,
|
|
110
|
+
toMarkdown: ({ item }) => [`# ${item.title}`, `By ${item.author.name}`, item.body],
|
|
111
|
+
component: ArticlePage,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
function ArticlePage() {
|
|
115
|
+
return (
|
|
116
|
+
<Layout.Item>
|
|
117
|
+
{(article) => (
|
|
118
|
+
<article>
|
|
119
|
+
<article.Field name="title">{(props) => <h1 {...props} />}</article.Field>
|
|
120
|
+
<article.Reference name="author">
|
|
121
|
+
{(author) => <author.Field name="name">{(props) => <p {...props} />}</author.Field>}
|
|
122
|
+
</article.Reference>
|
|
123
|
+
<article.Field name="body">{(props) => <p {...props} />}</article.Field>
|
|
124
|
+
</article>
|
|
125
|
+
)}
|
|
126
|
+
</Layout.Item>
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- `by` names a String field of the collection; any other field is a type error. `param` names the route parameter holding its value; a layout whose route lacks it fails to type-check.
|
|
132
|
+
- The loader finds the record whose `by` field's plain text equals the param: the draft value for a signed-in editor previewing, the published value for everyone else. A draft-only record is reachable in preview at its draft value; a never-published or unknown value answers 404. Records whose value is empty or contains `/` never get a page.
|
|
133
|
+
- Several records sharing a value in the same source is a server error naming the collection, field, value and records. Camox does not enforce uniqueness: keep the field unique.
|
|
134
|
+
- The loader returns a `collection-item` envelope, `{ kind: "collection-item", collectionId, data: { id, content } }`, typed as `LayoutCollectionItemResult` from `camox/createLayout`. `layout.useData()` returns the unwrapped record, `{ id, content }`. Camox rejects an envelope naming an unregistered collection or a malformed record id.
|
|
135
|
+
- `routes` lists one entry per record that can have a page, in the request's source: the sitemap lists published records only, even when an editor asks, while the Canvas previews a signed-in editor's first record, draft-only records included. Each entry is `{ params, target: { kind: "collection-item", collectionId, id } }`; `target` names the record independently of its current value. Records whose value is empty or contains `/` are left out. Several records sharing a value fail discovery, so `/sitemap.xml` responds 500 naming the layout and the records. An empty collection returns `{ data: [] }`, and the Canvas asks for a path.
|
|
136
|
+
- `layout.Item` renders the page's record with the same typed scope as a block's `Reference`: `Field`, `Image`, `File`, `Embed`, `ImageList`, `FileList`, `Reference` and `ReferenceList`, plus `id` and `label`. Field names are checked against the collection. The record is the first hop: `Reference` and `ReferenceList` render the records it links, which link nothing further. Prefer it to `useData()`, whose `content` holds stored values (String fields may be rich-text state).
|
|
137
|
+
- `layout.Item` exists only when every return of the loader is a record of one collection. On a loader that may return external data or records of several collections, using it is a type error. At runtime it renders nothing when the result isn't a record.
|
|
138
|
+
- In Studio, editors edit record page fields in place, like a referenced record's: purple overlays, selection, and the record's fields in the sidebar. Edits change the shared record everywhere it appears.
|
|
139
|
+
- `toMarkdown` also receives `item`, the same Markdown builder as a reference list's `toMarkdown`: interpolate fields (`item.title`, `item.author.name`, `item.cover`) and Camox fills them with the live record, rich text included. `data` stays the raw record. Markdown always shows live content, whoever asks.
|
|
140
|
+
- `buildMetaTitle` and `buildMetaDescription` also receive `item`: the record's String fields as plain text.
|
|
141
|
+
- To wrap the helper, pass the whole loader input through, `context` included, and return the envelope unchanged. Type it as the helper's loader (`ReturnType<typeof articlePages.loader>`) to keep `layout.Item` and `item` typed:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
const articlePages = articles.pages({ by: "slug", param: "slug" });
|
|
145
|
+
// …
|
|
146
|
+
loader: async (input) => {
|
|
147
|
+
const result = await articlePages.loader(input);
|
|
148
|
+
return result;
|
|
149
|
+
},
|
|
150
|
+
routes: articlePages.routes,
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Migrating existing loaders
|
|
154
|
+
|
|
155
|
+
This is a breaking loader contract: replace `return pokemon` with `return { kind: "data", data: pokemon }`. For asynchronous data, await the payload inside the envelope: `return { kind: "data", data: await loadPokemon() }`, not a promise in `data`. Components using `layout.useData()` do not change. Existing layouts without loaders do not change.
|
|
156
|
+
|
|
157
|
+
For an explicit return annotation, import `type LayoutLoaderResult` from `camox/createLayout` and use `Promise<LayoutLoaderResult<Pokemon>>` instead of `Promise<Pokemon>`. Inline loaders infer their payload automatically. Standalone loader functions can use `LayoutLoaderResult<Pokemon>` (or `satisfies LayoutLoaderResult<Pokemon>`) to keep `kind` a literal rather than a widened string. Wrappers are ordinary functions returning the same envelope; no attached properties or discovery phase are involved.
|
|
158
|
+
|
|
159
|
+
The playground's `/pokedex`, `/pokemon/pikachu`, and `/pokemon-types/electric` are working external-data examples, and both derived layouts declare `routes`, so `/sitemap.xml` lists every Pokémon and type. Load a URL directly, follow a Pokémon/type link, and refresh: `useData()` receives the same payload in server rendering, navigation, and hydration. Search and “Show more” still run on client state. A nonexistent Pokémon still returns 404; a PokéAPI failure is still a server error.
|
|
160
|
+
|
|
161
|
+
`kind: "data"` does not make external data editable. For collection records, use [Record pages](#record-pages).
|
|
162
|
+
|
|
163
|
+
## Metadata and Markdown
|
|
164
|
+
|
|
165
|
+
Singleton and derived pages get the same head as curated pages: title, description, `og:*` tags, the `/og` image, and the favicon. They have no editor metadata, so the callbacks receive the loader's unwrapped `data` and the route `params` instead:
|
|
166
|
+
|
|
167
|
+
```tsx
|
|
168
|
+
export const Layout = createLayout("pokemon.$name")({
|
|
169
|
+
kind: "derived",
|
|
170
|
+
// Other options omitted.
|
|
171
|
+
loader: async ({ params }) => ({ kind: "data", data: await loadPokemon(params.name) }),
|
|
172
|
+
buildMetaTitle: ({ data, pageMetaTitle }) => `${data.name} – ${pageMetaTitle}`,
|
|
173
|
+
buildMetaDescription: ({ data }) => `${data.name} is a ${data.types.join("/")} type Pokémon.`,
|
|
174
|
+
toMarkdown: ({ data, params }) => [`# ${data.name}`, `Types: ${data.types.join(", ")}`],
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
- **Put `loader` before `buildMetaTitle`, `buildMetaDescription`, and `toMarkdown`.** TypeScript infers their `data` from the loader only in that order. Otherwise the loader is inferred as `never` and fails to type-check.
|
|
179
|
+
- `buildMetaTitle` receives `pageMetaTitle` (the layout's `title` on these pages), `projectName`, `pageFullPath`, `data`, and `params`. The `/og` image shows its result.
|
|
180
|
+
- `buildMetaDescription` is optional; without it these pages have no description.
|
|
181
|
+
- `toMarkdown` returns the page's Markdown Representation as a list of strings. Empty strings are dropped and the rest joined with blank lines. A request with `Accept: text/markdown` gets YAML front matter (the `title` and `description` the head shows, and the page's `url`), then the live before-block Markdown, then this output, then the live after-block Markdown. Curated pages' Markdown opens with the same front matter. Without `toMarkdown`, the page answers HTML only. `throw notFound()` in the loader answers 404. Curated layouts cannot declare `toMarkdown`: their Markdown comes from their blocks.
|
|
182
|
+
- The playground's Pokémon, Pokémon type, and Pokédex layouts declare these. Try `curl -H "Accept: text/markdown" http://localhost:3000/pokemon/pikachu`.
|
|
65
183
|
|
|
66
184
|
## File and component conventions
|
|
67
185
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: camox-expand
|
|
3
|
+
description: Expand a Camox project's Block Definitions ahead of need by finding jobs they can't do yet, proposing new blocks and Settings, and building the approved ones. Use when asked to add more blocks or prepare blocks before a launch.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Camox Expand
|
|
7
|
+
|
|
8
|
+
Work in an existing Camox project. Grow its Block Definitions so editors can do more with content changes alone. Every addition fills a real **gap**: a job the current Block Definitions can't do, or can only do in a shape or visual treatment the site already repeats. Each one must keep the site's own design.
|
|
9
|
+
|
|
10
|
+
## 1. Ask about what's next
|
|
11
|
+
|
|
12
|
+
Ask the operator one question: what is being launched or published next? Use the answer to rank gaps. If they have nothing planned, the other evidence decides.
|
|
13
|
+
|
|
14
|
+
## 2. Inventory the Block Definitions
|
|
15
|
+
|
|
16
|
+
Read every Block Definition in `src/blocks/`. For each one, note its job, content shape, visual treatment, and Settings. Also look at the rendered site to learn its design language. You're done when you can say which jobs each block does and how two blocks for the same job differ.
|
|
17
|
+
|
|
18
|
+
## 3. Find gaps
|
|
19
|
+
|
|
20
|
+
Collect evidence in this order:
|
|
21
|
+
|
|
22
|
+
1. **Upcoming needs** from the operator's answer.
|
|
23
|
+
2. **The jobs checklist.** Compare [Jobs](references/jobs.md) with the Block Definitions. A job counts as covered only when an existing block, or an existing block with a Setting, does it well.
|
|
24
|
+
3. **The source site**, when the project was migrated from one. Look for sections and page types that `camoxify-progress.md` or the current Pages don't reproduce.
|
|
25
|
+
4. **Competitors' sites**, only when the site's category is clear. Take the jobs their pages and sections do, and leave their designs and copy alone. Every suggestion is designed in the site's own visual language.
|
|
26
|
+
|
|
27
|
+
Decide between a new block and a Setting with Camoxify's "Reuse before creating" rule in [Inspect and decompose](../camoxify/SKILL.md#inspect-and-decompose).
|
|
28
|
+
|
|
29
|
+
You're done when every evidence source that applies has been checked, every job in the checklist is marked covered or a gap, and every gap is marked as a new block or a Setting.
|
|
30
|
+
|
|
31
|
+
## 4. Propose and wait for approval
|
|
32
|
+
|
|
33
|
+
List the suggestions in the chat. Each one has:
|
|
34
|
+
|
|
35
|
+
- its **job**,
|
|
36
|
+
- its **kind**: new block, or new Setting on an existing block,
|
|
37
|
+
- the **closest existing block**,
|
|
38
|
+
- a **text wireframe** for a new block, or a **before/after sketch** for a new Setting, in a fenced code block so it renders in any harness.
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
Logo wall (new block). Job: social proof. Closest: feature-grid
|
|
42
|
+
+----------------------------------------------+
|
|
43
|
+
| Trusted by teams shipping daily |
|
|
44
|
+
| [logo] [logo] [logo] [logo] [logo] |
|
|
45
|
+
+----------------------------------------------+
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Stop suggesting when the next idea would be filler; there is no target count. Then wait. The operator approves, cuts items, or asks for more or fewer. Revise until they approve a list. Nothing is built before that.
|
|
49
|
+
|
|
50
|
+
## 5. Build the approved list
|
|
51
|
+
|
|
52
|
+
Make only these kinds of change:
|
|
53
|
+
|
|
54
|
+
- **New Block Definitions**, following [Block Definitions](../camox/references/block-definitions.md), [Localized code](../camox/SKILL.md#localized-code) (every visible text in fields, internal links through `useLocalizedHref` without a locale prefix), and Camoxify's rules on [styling](../camoxify/SKILL.md#use-the-existing-styling-stack), [genericity and default content](../camoxify/SKILL.md#keep-definitions-generic), [repeaters](../camoxify/SKILL.md#model-repetition-not-a-fixed-screenshot), [Settings](../camoxify/SKILL.md#use-settings-for-constrained-choices), and [formatted text](../camoxify/SKILL.md#keep-formatted-text-in-one-field).
|
|
55
|
+
- **New Settings on existing Block Definitions**, with defaults that leave current rendering unchanged. Existing content schemas stay exactly as they are. Before editing the block, capture it in the Block Catalog at desktop and mobile, opened at `/camox/blocks/<id>` on the running dev server; step 6 compares against this capture.
|
|
56
|
+
- **Description edits.** A new block's `description` says when to choose it over its siblings. Edit the siblings' descriptions so each one names what sets it apart, and the Agent can choose between them.
|
|
57
|
+
|
|
58
|
+
New blocks live only in code and the Block Catalog. Leave Pages, layouts, and publishing alone, and create no temporary Pages. This rules out new Layout-only Blocks: the Block Catalog never shows them, and they only sync once a layout uses them. An existing Layout-only Block that gains a Setting has no Block Catalog page either, so capture and check it on a Page whose layout uses it.
|
|
59
|
+
|
|
60
|
+
## 6. Verify
|
|
61
|
+
|
|
62
|
+
Finish with a Block Catalog link to each new block, so the operator and the product marketing team can review them live. Delete any block the operator rejects, along with any description changes made only for it.
|
|
63
|
+
|
|
64
|
+
The run is complete when:
|
|
65
|
+
|
|
66
|
+
- every approved item is built, and every rejected one is deleted,
|
|
67
|
+
- the project's `check` script passes, and so does `vp lint --type-aware --type-check`, since `vp check` skips type errors unless the project's Vite config sets `lint.options.typeCheck`,
|
|
68
|
+
- every new block renders correctly with its default content at desktop and mobile in the Block Catalog at `/camox/blocks/<id>`,
|
|
69
|
+
- every existing block that gained a Setting renders the same as its capture with default Settings.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Jobs
|
|
2
|
+
|
|
3
|
+
Jobs that marketing pages and sections commonly do. Each entry describes what the content achieves for the visitor, never how it looks. Design every block for a job in the site's own visual language.
|
|
4
|
+
|
|
5
|
+
## Section jobs
|
|
6
|
+
|
|
7
|
+
- **Introduction**: say what the product or page is and who it is for.
|
|
8
|
+
- **Explanation**: show how the product works or what a feature does.
|
|
9
|
+
- **Benefits**: turn features into outcomes for the visitor.
|
|
10
|
+
- **Social proof**: show that others use and trust the product, through customers, quotes, numbers, ratings, or press.
|
|
11
|
+
- **Credibility**: back claims with certifications, security, compliance, or awards.
|
|
12
|
+
- **Objection handling**: answer the doubts that stop a visitor from acting, such as questions, risks, or "why not the alternative".
|
|
13
|
+
- **Comparison**: set the product against alternatives, plans, or the status quo.
|
|
14
|
+
- **Pricing**: say what it costs and what each option includes.
|
|
15
|
+
- **Conversion**: ask the visitor to act, such as sign up, buy, book, or download.
|
|
16
|
+
- **Lead capture**: collect contact details in exchange for something.
|
|
17
|
+
- **Demonstration**: let the visitor see the product working, through media, a demo, or an interactive sample.
|
|
18
|
+
- **Process**: walk through the steps to get started or to get a result.
|
|
19
|
+
- **Integration**: show what the product connects to.
|
|
20
|
+
- **Team and story**: introduce the people and the reasons behind the product.
|
|
21
|
+
- **Announcement**: flag something new or time-limited.
|
|
22
|
+
- **Navigation aid**: help visitors find related pages, resources, or the next step.
|
|
23
|
+
- **Long-form reading**: carry an article, guide, or document.
|
|
24
|
+
|
|
25
|
+
## Page jobs
|
|
26
|
+
|
|
27
|
+
- **Launch page**: present a new product or feature.
|
|
28
|
+
- **Comparison page**: argue for the product against a named alternative.
|
|
29
|
+
- **Customer story**: tell one customer's problem, solution, and results.
|
|
30
|
+
- **Use-case or industry page**: speak to one audience or problem.
|
|
31
|
+
- **Pricing page**: let visitors choose a plan.
|
|
32
|
+
- **Event registration**: get visitors to sign up for a webinar, meetup, or conference.
|
|
33
|
+
- **Changelog**: record what changed and when.
|
|
34
|
+
- **Resource or download page**: offer a guide, report, or template.
|
|
35
|
+
- **Integration page**: explain one integration.
|
|
36
|
+
- **About page**: explain who is behind the product.
|
|
37
|
+
- **Careers page**: attract applicants.
|
|
38
|
+
- **Contact page**: route visitors to the right channel.
|
|
39
|
+
- **Legal page**: carry terms, privacy, or policy text.
|
package/skills/camoxify/SKILL.md
CHANGED
|
@@ -7,6 +7,14 @@ description: Turn an existing website, screenshot, or design into an editable Ca
|
|
|
7
7
|
|
|
8
8
|
Work in the existing Camox project; project creation and authentication are onboarding prerequisites. Match the reference while making the result reusable and easy to edit.
|
|
9
9
|
|
|
10
|
+
## Scope the run
|
|
11
|
+
|
|
12
|
+
Take the scope from the request. A first run usually covers the shell and the homepage; a later run may cover the whole site, reusing what earlier runs built.
|
|
13
|
+
|
|
14
|
+
For multi-page runs, build the page list before coding: read the sitemap (`/sitemap.xml`, or the one named in `/robots.txt`) and follow navigation and footer links. Group pages that share one structure, such as blog posts, case studies, or docs articles, into a **page family**: one layout and set of blocks, built and verified on a representative page, then repeated for the rest through the CLI. Confirm the list and its families with the user before building.
|
|
15
|
+
|
|
16
|
+
Track progress in `camoxify-progress.md` at the project root: one line per page with its status (pending, built, populated, verified) and any new definitions it introduced. Update it as each page advances. When resuming, start from that file and the project's current definitions.
|
|
17
|
+
|
|
10
18
|
## Inspect and decompose
|
|
11
19
|
|
|
12
20
|
Inspect the source URL with available browsing tools, or use the supplied screenshot/design. If the URL is inaccessible, ask for screenshots. Check multiple pages and viewport sizes when available; don't infer a whole site's structure from one crop.
|
|
@@ -16,18 +24,26 @@ For URL references, use a browser when available to explore interactive states:
|
|
|
16
24
|
Before coding, map the reference to:
|
|
17
25
|
|
|
18
26
|
- **Layout:** the consistent shell across pages, such as navigation and footer. Editable shell content still lives in blocks, placed by the layout.
|
|
19
|
-
- **Page blocks:** independently useful sections such as a hero, feature grid, testimonials, or pricing.
|
|
27
|
+
- **Page blocks:** independently useful sections such as a hero, feature grid, testimonials, or pricing. Avoid both a monolithic page block and separate blocks for every heading or card.
|
|
28
|
+
|
|
29
|
+
Reuse before creating. When a section matches an existing definition's content shape and visual treatment, use it, adding a setting for a small variation. Create a new definition when the visual treatment or the content shape is meaningfully different; distinct designs for the same job keep pages varied. A new definition's `description` says when to choose it over similar existing blocks.
|
|
30
|
+
|
|
20
31
|
- **Fields and repeaters:** editable content within each section, grouped by meaning rather than visual position.
|
|
32
|
+
- **Artifacts:** bespoke visuals found on the reference, such as animated illustrations, 3D scenes, or one-off interactive widgets. Recreate each as an artifact source on a `field.artifact` of the block around it, not as a one-off block. Read [Artifacts](../camox/references/artifacts.md).
|
|
21
33
|
|
|
22
34
|
Read [Block Definitions](../camox/references/block-definitions.md) and, when creating or changing a shell, [Layout Definitions](../camox/references/layout-definitions.md).
|
|
23
35
|
|
|
24
36
|
## Use the existing styling stack
|
|
25
37
|
|
|
26
|
-
|
|
38
|
+
Learn the project's **Theme** before styling: read its main stylesheet, the CSS file that imports Tailwind. Its tokens and the comments beside them say what each color, font, text size, radius and spacing is for, and which **Color Schemes** a block can switch to. Take each token's meaning from its comment, not from its name.
|
|
27
39
|
|
|
28
|
-
|
|
40
|
+
Reuse the project's **Components**, such as its button or card, and compose or extend them to match the reference. Build a new Component only when none fits.
|
|
29
41
|
|
|
30
|
-
|
|
42
|
+
Style through the Theme's utilities rather than raw colors or stock palette classes, so a rebrand stays one stylesheet edit. To match the reference's colors, fonts and radii, change the Theme's values in the stylesheet; when the reference needs a role the Theme lacks, add a token there with a comment explaining it.
|
|
43
|
+
|
|
44
|
+
If the reference uses another styling system, recreate its appearance and behavior with the project's Components and Tailwind utilities; do not copy or introduce the reference's styling dependencies.
|
|
45
|
+
|
|
46
|
+
Express layout, spacing, typography, colors, responsive behavior, and interactive states through Tailwind and the project's Components. Do not install other styling libraries or add separate stylesheets or inline styles.
|
|
31
47
|
|
|
32
48
|
## Keep definitions generic
|
|
33
49
|
|
|
@@ -35,17 +51,19 @@ Block names, schemas, rendering, and **default content** must work on other page
|
|
|
35
51
|
|
|
36
52
|
Apply reference-specific content **only to instances through the CLI**, never by changing definition defaults or hardcoding it in JSX.
|
|
37
53
|
|
|
54
|
+
Keep the result translatable, following [Localized code](../camox/SKILL.md#localized-code): every visible text in the shell, navigation and footer included, lives in blocks, and internal links go through `useLocalizedHref`, written without a locale prefix.
|
|
55
|
+
|
|
38
56
|
## Model repetition, not a fixed screenshot
|
|
39
57
|
|
|
40
|
-
Use `
|
|
58
|
+
Use `field.repeater` for every repeated content structure: lists, rows, columns, tables, carousels, navigation items, and cards. Never use `field1`, `field2`, etc., or hardcoded content arrays. Nest repeaters for structures such as footer columns containing links or table rows containing cells.
|
|
41
59
|
|
|
42
60
|
Choose bounds from real constraints, not the number of items visible in the reference. Many sibling fields are a code smell: look for repeated groups or separate sections. Model enough flexibility to add, remove, reorder, and rewrite content without code changes; don't expose every CSS value as a field.
|
|
43
61
|
|
|
44
62
|
## Use settings for constrained choices
|
|
45
63
|
|
|
46
|
-
Before adding a `content` field, ask whether the editor is writing free-form content or choosing from known options. For configuration choices such as alignment, theme, size, or variant, use `settings` with `
|
|
64
|
+
Before adding a `content` field, ask whether the editor is writing free-form content or choosing from known options. For configuration choices such as alignment, theme, size, or variant, use `settings` with `setting.enum`. For on/off choices such as showing a background or enabling autoplay, use `settings` with `setting.boolean`.
|
|
47
65
|
|
|
48
|
-
Do not model these choices as `content` fields using `
|
|
66
|
+
Do not model these choices as `content` fields using `field.string`, even if rendering code could interpret strings such as `"left"`, `"dark"`, or `"true"`. Editors should choose a valid option or toggle a setting, not type a magic string. Reserve `content`'s `field.string` for genuinely free-form text, such as headings and descriptions. Enum defaults must match an option key.
|
|
49
67
|
|
|
50
68
|
## Keep formatted text in one field
|
|
51
69
|
|
|
@@ -64,3 +82,7 @@ Use inline formatting and `textStyle`, including highlights, instead of fields s
|
|
|
64
82
|
Follow the [Camox CLI workflow](../camox/SKILL.md#content-and-cli) to assign layouts, place blocks, and populate content, including shared navigation/footer content. Don't publish unless asked.
|
|
65
83
|
|
|
66
84
|
Compare the rendered site with the reference at desktop and mobile sizes. Verify that fields remain editable and that longer text and different repeater counts still work. Fix the reusable implementation, not just the one captured arrangement.
|
|
85
|
+
|
|
86
|
+
In multi-page runs, give this full check to every page that introduced or changed a definition, and to each page family's representative page. Compare the remaining pages with their source at desktop size. The run is complete when every page in `camoxify-progress.md` is verified.
|
|
87
|
+
|
|
88
|
+
Once the run is complete, suggest running `camox-expand` to build blocks for jobs the site will need next.
|