camox 0.44.0 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (407) hide show
  1. package/dist/components/AuthGate.js +0 -6
  2. package/dist/components/DeviceSelector.js +96 -0
  3. package/dist/components/PlatformLabel.js +20 -24
  4. package/dist/core/components/lexical/HighlightNode.js +1 -1
  5. package/dist/core/components/lexical/InlineContentEditable.js +15 -19
  6. package/dist/core/components/lexical/InlineLexicalEditor.js +54 -74
  7. package/dist/core/components/lexical/InlineStylesPlugin.js +53 -53
  8. package/dist/core/components/lexical/MaxLengthPlugin.js +59 -0
  9. package/dist/core/components/lexical/SelectionBroadcaster.js +53 -54
  10. package/dist/core/components/lexical/SidebarLexicalEditor.js +322 -229
  11. package/dist/core/components/lexical/SummaryToggleGuard.js +42 -0
  12. package/dist/core/components/lexical/TextLinkPopover.js +169 -180
  13. package/dist/core/createApp.d.ts +241 -312
  14. package/dist/core/createApp.js +123 -29
  15. package/dist/core/createBlock.d.ts +5 -5
  16. package/dist/core/createBlock.js +385 -152
  17. package/dist/core/createCollection.d.ts +55 -0
  18. package/dist/core/createCollection.js +93 -0
  19. package/dist/core/createLayout.d.ts +197 -61
  20. package/dist/core/createLayout.js +114 -56
  21. package/dist/core/defineDocument.d.ts +3 -5
  22. package/dist/core/derivedRoutes.js +49 -1
  23. package/dist/core/dom.d.ts +13 -4
  24. package/dist/core/dom.js +44 -1
  25. package/dist/core/editing/BlockEditingRuntime.d.ts +16 -0
  26. package/dist/core/editing/BlockEditingRuntime.js +6 -15
  27. package/dist/core/editing/CompleteBlockEditingRuntime.js +28 -23
  28. package/dist/core/editing/createEditableBlock.d.ts +754 -633
  29. package/dist/core/editing/createEditableBlock.js +2113 -1066
  30. package/dist/core/editing/referenceWrites.js +51 -0
  31. package/dist/core/editing/useLatestRecord.js +90 -0
  32. package/dist/core/hooks/useArtifactHost.js +139 -0
  33. package/dist/core/hooks/useFieldSelection.js +43 -18
  34. package/dist/core/hooks/useIsEditable.js +12 -10
  35. package/dist/core/hooks/useOverlayMessage.js +15 -19
  36. package/dist/core/lib/artifact.d.ts +21 -0
  37. package/dist/core/lib/artifact.js +154 -0
  38. package/dist/core/lib/artifactColors.js +123 -0
  39. package/dist/core/lib/artifactData.d.ts +1 -0
  40. package/dist/core/lib/artifactData.js +131 -0
  41. package/dist/core/lib/artifactHost.js +205 -0
  42. package/dist/core/lib/artifactRuntime.js +36 -0
  43. package/dist/core/lib/artifactTheme.js +57 -0
  44. package/dist/core/lib/contentType.d.ts +302 -106
  45. package/dist/core/lib/contentType.js +201 -173
  46. package/dist/core/lib/fieldTypes.js +3 -0
  47. package/dist/core/lib/iconTypes.d.ts +3 -3
  48. package/dist/core/lib/icons.d.ts +4 -4
  49. package/dist/core/lib/icons.js +40 -44
  50. package/dist/core/lib/imagePlaceholder.js +177 -0
  51. package/dist/core/lib/imageTransform.js +1 -1
  52. package/dist/core/lib/lexicalReact.d.ts +3 -1
  53. package/dist/core/lib/lexicalState.js +1 -1
  54. package/dist/core/lib/previewPaused.js +9 -0
  55. package/dist/core/lib/recordScope.js +316 -0
  56. package/dist/core/lib/reference.d.ts +81 -0
  57. package/dist/core/lib/reference.js +29 -0
  58. package/dist/core/lib/textLinks.js +7 -5
  59. package/dist/core/lib/textStyles.d.ts +5 -7
  60. package/dist/core/lib/textStyles.js +2 -2
  61. package/dist/core/notFound.d.ts +7 -0
  62. package/dist/core/notFound.js +11 -0
  63. package/dist/core/pageDestinations.js +6 -3
  64. package/dist/core/recordItem.js +34 -0
  65. package/dist/core/requestContext.d.ts +26 -0
  66. package/dist/core/requestContext.js +20 -0
  67. package/dist/features/canvas/CamoxCanvas.js +1401 -0
  68. package/dist/features/canvas/CanvasLoadingScreen.js +35 -0
  69. package/dist/features/canvas/CanvasOverlays.js +254 -0
  70. package/dist/features/canvas/CanvasPageEdge.js +87 -0
  71. package/dist/features/canvas/CanvasPageFrame.js +333 -0
  72. package/dist/features/canvas/CanvasPageHeader.js +125 -0
  73. package/dist/features/canvas/CanvasPreviewPageSlot.js +123 -0
  74. package/dist/features/canvas/DerivedPagePathInput.js +238 -0
  75. package/dist/features/canvas/canvasCamera.js +229 -0
  76. package/dist/features/canvas/canvasFlight.js +104 -0
  77. package/dist/features/canvas/canvasFrameCapture.js +364 -0
  78. package/dist/features/canvas/canvasFrameDocument.js +302 -0
  79. package/dist/features/canvas/canvasFrameMode.js +53 -0
  80. package/dist/features/canvas/canvasInsertionSeams.js +44 -0
  81. package/dist/features/canvas/canvasItems.js +30 -0
  82. package/dist/features/canvas/canvasOverlayGeometry.js +240 -0
  83. package/dist/features/canvas/canvasPages.js +79 -0
  84. package/dist/features/canvas/canvasScreenSpace.js +26 -0
  85. package/dist/features/canvas/canvasSelection.js +18 -0
  86. package/dist/features/canvas/canvasStore.js +60 -0
  87. package/dist/features/canvas/canvasThumbnails.js +304 -0
  88. package/dist/features/canvas/canvasZoom.js +10 -0
  89. package/dist/features/canvas/sidebar/CanvasInspector.js +202 -0
  90. package/dist/features/canvas/sidebar/CanvasNavigator.js +124 -0
  91. package/dist/features/canvas/sidebar/CanvasSidebar.js +54 -0
  92. package/dist/features/canvas/sidebar/CanvasSidebarFrame.js +48 -0
  93. package/dist/features/canvas/sidebar/CanvasSidebarSkeleton.js +39 -0
  94. package/dist/features/canvas/sidebar/CuratedPageInspector.js +69 -0
  95. package/dist/features/canvas/sidebar/DerivedPageInspector.js +37 -0
  96. package/dist/features/canvas/sidebar/LayoutPageBlocks.js +78 -0
  97. package/dist/features/canvas/sidebar/LayoutPageOverview.js +71 -0
  98. package/dist/features/canvas/sidebar/PageInspector.js +157 -0
  99. package/dist/features/canvas/sidebar/PageInspectorSkeleton.js +90 -0
  100. package/dist/features/canvas/sidebar/PageOverview.js +27 -0
  101. package/dist/features/canvas/sidebar/SingletonPageInspector.js +31 -0
  102. package/dist/features/canvas/useCanvasCamera.js +783 -0
  103. package/dist/features/canvas/useCanvasFrame.js +148 -0
  104. package/dist/features/canvas/useCanvasFrameInput.js +97 -0
  105. package/dist/features/canvas/useCanvasNearness.js +57 -0
  106. package/dist/features/canvas/useDiscoveredPreviewPaths.js +62 -0
  107. package/dist/features/content/CamoxContent.d.ts +2 -5
  108. package/dist/features/content/CamoxContent.js +174 -271
  109. package/dist/features/content/CollectionItemSheetContext.js +106 -0
  110. package/dist/features/content/ContentAssets.js +315 -0
  111. package/dist/features/content/ContentCollection.js +663 -0
  112. package/dist/features/content/collection-form.js +58 -0
  113. package/dist/features/content/components/AssetCard.js +80 -84
  114. package/dist/features/content/components/AssetCardSkeleton.js +4 -8
  115. package/dist/features/content/components/ContentSidebar.js +129 -18
  116. package/dist/features/content/components/ContentSidebarGroup.js +37 -0
  117. package/dist/features/content/components/DeleteCollectionItemButton.js +202 -0
  118. package/dist/features/content/components/MissingFromLocale.js +282 -0
  119. package/dist/features/content/components/UploadDropZone.js +27 -31
  120. package/dist/features/content/components/UploadProgressDrawer.js +90 -98
  121. package/dist/features/navigation/navigation.d.ts +66 -20
  122. package/dist/features/navigation/navigation.js +257 -90
  123. package/dist/features/og/defaultFonts.js +8 -8
  124. package/dist/features/og/geist-variable-ttf-base64.js +1 -1
  125. package/dist/features/og/imageResponse.d.ts +8 -0
  126. package/dist/features/og/imageResponse.js +26 -0
  127. package/dist/features/page/CuratedPageContent.js +167 -0
  128. package/dist/features/page/DerivedPageContent.js +26 -30
  129. package/dist/features/page/SharedLayoutContent.js +25 -27
  130. package/dist/features/preview/CamoxPreview.d.ts +23 -61
  131. package/dist/features/preview/CamoxPreview.js +816 -537
  132. package/dist/features/preview/EditablePageContent.js +1 -1
  133. package/dist/features/preview/EditablePageExperience.js +16 -22
  134. package/dist/features/preview/components/AddBlockSheet.js +273 -0
  135. package/dist/features/preview/components/AssetFieldEditor.js +173 -154
  136. package/dist/features/preview/components/AssetLightbox.js +181 -134
  137. package/dist/features/preview/components/AssetPickerModal.js +153 -170
  138. package/dist/features/preview/components/BlockActionsPopover.js +150 -149
  139. package/dist/features/preview/components/BlockErrorBoundary.js +8 -12
  140. package/dist/features/preview/components/BlockThumbnail.js +172 -98
  141. package/dist/features/preview/components/CanvasPreview.js +243 -0
  142. package/dist/features/preview/components/CreatePageModal.js +128 -137
  143. package/dist/features/preview/components/DebouncedFieldEditor.js +29 -33
  144. package/dist/features/preview/components/DerivedLayoutDialog.js +21 -19
  145. package/dist/features/preview/components/DrillRow.js +150 -0
  146. package/dist/features/preview/components/FieldOverlayStyles.js +11 -8
  147. package/dist/features/preview/components/FieldToolbar.js +160 -110
  148. package/dist/features/preview/components/Frame.js +108 -20
  149. package/dist/features/preview/components/IconFieldEditor.js +62 -66
  150. package/dist/features/preview/components/ItemFieldsEditor.js +231 -205
  151. package/dist/features/preview/components/LinkFieldEditor.js +135 -139
  152. package/dist/features/preview/components/Metadata.js +22 -30
  153. package/dist/features/preview/components/MobilePreviewDrawer.js +89 -108
  154. package/dist/features/preview/components/MultipleAssetFieldEditor.js +334 -246
  155. package/dist/features/preview/components/OverlayTracker.js +9 -41
  156. package/dist/features/preview/components/Overlays.js +81 -69
  157. package/dist/features/preview/components/PageInfo.js +1748 -0
  158. package/dist/features/preview/components/PageInfoSkeleton.js +51 -0
  159. package/dist/features/preview/components/PageLocationFieldset.js +112 -116
  160. package/dist/features/preview/components/PageNavigatorHeader.js +24 -0
  161. package/dist/features/preview/components/PageNicknameField.js +22 -26
  162. package/dist/features/preview/components/PagePicker.js +128 -130
  163. package/dist/features/preview/components/PageTree.js +525 -532
  164. package/dist/features/preview/components/PreviewActivation.js +28 -32
  165. package/dist/features/preview/components/PreviewPanel.js +620 -319
  166. package/dist/features/preview/components/PreviewToolbar.js +105 -333
  167. package/dist/features/preview/components/RecordCard.js +80 -0
  168. package/dist/features/preview/components/RecordCombobox.js +178 -0
  169. package/dist/features/preview/components/ReferenceFieldEditor.js +149 -0
  170. package/dist/features/preview/components/ReferenceListFieldEditor.js +432 -0
  171. package/dist/features/preview/components/RemovedBlockPlaceholder.js +95 -0
  172. package/dist/features/preview/components/RepeatableItemsList.js +125 -138
  173. package/dist/features/preview/components/SelectionEditor.js +993 -0
  174. package/dist/features/preview/components/ShikiMarkdown.js +14 -18
  175. package/dist/features/preview/components/SidebarSection.js +52 -64
  176. package/dist/features/preview/components/StudioEditingFallback.js +56 -0
  177. package/dist/features/preview/components/UnlinkAssetButton.js +104 -107
  178. package/dist/features/preview/components/blockGridNavigation.js +15 -0
  179. package/dist/features/preview/components/contentFieldSchema.js +8 -0
  180. package/dist/features/preview/components/useFieldWriter.js +200 -0
  181. package/dist/features/preview/components/useRecordView.js +383 -0
  182. package/dist/features/preview/components/useRepeatableItemActions.js +35 -37
  183. package/dist/features/preview/components/useUpdateBlockPosition.js +37 -37
  184. package/dist/features/preview/editingFrame.js +197 -0
  185. package/dist/features/preview/overlayHighlights.js +17 -44
  186. package/dist/features/preview/overlayMessages.js +72 -1
  187. package/dist/features/preview/pageRecord.js +189 -0
  188. package/dist/features/preview/previewConstants.js +6 -2
  189. package/dist/features/preview/previewPageFrame.js +95 -0
  190. package/dist/features/preview/previewPreparation.js +7 -0
  191. package/dist/features/preview/previewSelection.js +32 -63
  192. package/dist/features/preview/previewStore.js +225 -123
  193. package/dist/features/preview/referencePickerFocus.js +29 -0
  194. package/dist/features/preview/repeatableItemTargets.js +56 -0
  195. package/dist/features/preview/selectionReveal.js +21 -0
  196. package/dist/features/preview/usePreviewViewportActions.js +114 -0
  197. package/dist/features/preview/viewportContinuity.js +109 -0
  198. package/dist/features/provider/AuthenticatedCamoxProvider.js +69 -44
  199. package/dist/features/provider/CamoxProvider.d.ts +3 -6
  200. package/dist/features/provider/CamoxProvider.js +38 -50
  201. package/dist/features/provider/CoreCamoxProvider.js +69 -50
  202. package/dist/features/provider/LocalhostPreviewProvider.js +78 -114
  203. package/dist/features/provider/PreviewSignInPage.js +54 -0
  204. package/dist/features/provider/components/CamoxAppContext.js +11 -11
  205. package/dist/features/provider/components/CommandPalette.js +75 -82
  206. package/dist/features/provider/useAdminShortcuts.js +7 -11
  207. package/dist/features/routes/pageRuntime.d.ts +44 -4
  208. package/dist/features/routes/pageRuntime.js +244 -137
  209. package/dist/features/runtime/RuntimeChrome.js +117 -115
  210. package/dist/features/runtime/alternates.js +64 -0
  211. package/dist/features/runtime/analytics.d.ts +7 -0
  212. package/dist/features/runtime/analytics.js +66 -0
  213. package/dist/features/runtime/artifactSources.js +87 -0
  214. package/dist/features/runtime/editingFallback.js +20 -0
  215. package/dist/features/runtime/linkPrefetch.js +97 -0
  216. package/dist/features/runtime/livePages.js +62 -0
  217. package/dist/features/runtime/llmsTxt.js +40 -0
  218. package/dist/features/runtime/loopbackFetch.js +18 -0
  219. package/dist/features/runtime/navigationAccessibility.js +51 -0
  220. package/dist/features/runtime/navigationTarget.js +23 -2
  221. package/dist/features/runtime/pageApp.js +229 -158
  222. package/dist/features/runtime/pageBlockTypes.js +20 -0
  223. package/dist/features/runtime/pageClient.d.ts +2 -4
  224. package/dist/features/runtime/pageClient.js +18 -3
  225. package/dist/features/runtime/pageNavigation.js +254 -32
  226. package/dist/features/runtime/pageServer.d.ts +2 -4
  227. package/dist/features/runtime/pageServer.js +6 -2
  228. package/dist/features/runtime/pageViewport.js +7 -0
  229. package/dist/features/runtime/runtime.d.ts +54 -24
  230. package/dist/features/runtime/runtime.js +766 -191
  231. package/dist/features/runtime/runtimeChromeLoader.js +58 -0
  232. package/dist/features/runtime/runtimeRoutes.d.ts +19 -0
  233. package/dist/features/runtime/runtimeRoutes.js +25 -0
  234. package/dist/features/runtime/scrollRestoration.js +133 -0
  235. package/dist/features/runtime/studioApp.d.ts +4 -3
  236. package/dist/features/runtime/studioApp.js +5 -9
  237. package/dist/features/runtime/studioClient.d.ts +2 -4
  238. package/dist/features/runtime/studioClient.js +12 -3
  239. package/dist/features/runtime/studioServer.d.ts +2 -4
  240. package/dist/features/runtime/studioServer.js +6 -1
  241. package/dist/features/runtime/worker.d.ts +6 -0
  242. package/dist/features/runtime/worker.js +23 -0
  243. package/dist/features/runtime/workerRequest.js +44 -0
  244. package/dist/features/studio/CamoxStudio.d.ts +2 -6
  245. package/dist/features/studio/CamoxStudio.js +34 -49
  246. package/dist/features/studio/FollowAgentProvider.js +327 -0
  247. package/dist/features/studio/components/ChatSidebar.js +356 -0
  248. package/dist/features/studio/components/DraftsControl.js +676 -0
  249. package/dist/features/studio/components/FaviconDialog.js +318 -0
  250. package/dist/features/studio/components/LocalePicker.js +785 -0
  251. package/dist/features/studio/components/Navbar.js +123 -103
  252. package/dist/features/studio/components/ProjectMenu.js +239 -260
  253. package/dist/features/studio/components/RenameDraftDialog.js +206 -0
  254. package/dist/features/studio/components/ReviewDraftSheet.js +1472 -0
  255. package/dist/features/studio/components/ThreadComposer.js +157 -0
  256. package/dist/features/studio/components/ThreadMarkdown.js +113 -0
  257. package/dist/features/studio/components/ThreadMessages.js +270 -0
  258. package/dist/features/studio/components/UserButton.js +34 -23
  259. package/dist/features/studio/draftLabel.js +33 -0
  260. package/dist/features/studio/draftNotice.js +13 -0
  261. package/dist/features/studio/followAgent.js +241 -0
  262. package/dist/features/studio/followAgentContext.js +7 -0
  263. package/dist/features/studio/localeOptions.js +118 -0
  264. package/dist/features/studio/localeSwitch.js +19 -0
  265. package/dist/features/studio/routes.js +26 -1
  266. package/dist/features/studio/studioStore.js +64 -1
  267. package/dist/features/studio/useTheme.js +24 -27
  268. package/dist/features/vite/artifactRuntimeMinification.js +36 -0
  269. package/dist/features/vite/blockBoilerplate.js +6 -5
  270. package/dist/features/vite/blockChunks.js +31 -0
  271. package/dist/features/vite/bundlerLogs.js +17 -0
  272. package/dist/features/vite/definitionsSync.js +158 -226
  273. package/dist/features/vite/devAuthentication.js +4 -16
  274. package/dist/features/vite/devSignIn.js +87 -0
  275. package/dist/features/vite/iconGeneration.js +1 -1
  276. package/dist/features/vite/layoutGeneration.js +11 -1
  277. package/dist/features/vite/publishSeededContent.js +30 -0
  278. package/dist/features/vite/routeGeneration.js +6 -7
  279. package/dist/features/vite/runtimeDev.js +80 -99
  280. package/dist/features/vite/vite.d.ts +51 -31
  281. package/dist/features/vite/vite.js +224 -127
  282. package/dist/features/vite/workerBuild.js +39 -0
  283. package/dist/hooks/use-debounced-field.js +116 -47
  284. package/dist/hooks/use-file-upload.js +114 -67
  285. package/dist/hooks/use-marquee-selection.js +32 -36
  286. package/dist/hooks/use-page-destinations.js +69 -37
  287. package/dist/lib/api-client-server.d.ts +5 -0
  288. package/dist/lib/api-client-server.js +10 -4
  289. package/dist/lib/api-client.d.ts +6 -0
  290. package/dist/lib/api-client.js +51 -16
  291. package/dist/lib/api-socket.js +17 -0
  292. package/dist/lib/auth-page-styles.js +39 -0
  293. package/dist/lib/auth.js +129 -61
  294. package/dist/lib/collection-cache.js +23 -0
  295. package/dist/lib/draft-scope.d.ts +4 -0
  296. package/dist/lib/draft-scope.js +222 -0
  297. package/dist/lib/html.js +12 -0
  298. package/dist/lib/link-pages.js +154 -0
  299. package/dist/lib/locale-path.d.ts +22 -0
  300. package/dist/lib/locale-path.js +57 -0
  301. package/dist/lib/normalized-data.js +252 -152
  302. package/dist/lib/preview-handoff.js +2 -2
  303. package/dist/lib/queries.d.ts +11 -0
  304. package/dist/lib/queries.js +70 -141
  305. package/dist/lib/studio-reads.d.ts +14 -0
  306. package/dist/lib/studio-reads.js +554 -0
  307. package/dist/lib/use-project-room.js +50 -50
  308. package/dist/lib/use-thread.js +310 -0
  309. package/dist/lib/utils.js +25 -29
  310. package/dist/lib/view-locale-context.js +22 -0
  311. package/dist/lib/view-locale.js +46 -0
  312. package/dist/package.js +5 -0
  313. package/dist/studio-overlays.css +41 -151
  314. package/dist/studio.css +2 -2
  315. package/package.json +70 -73
  316. package/skills/camox/SKILL.md +16 -11
  317. package/skills/camox/references/artifacts.md +179 -0
  318. package/skills/camox/references/block-definitions.md +75 -59
  319. package/skills/camox/references/cli-common.md +9 -11
  320. package/skills/camox/references/cli-content.md +92 -69
  321. package/skills/camox/references/cli-publishing.md +26 -0
  322. package/skills/camox/references/dom-integrations.md +27 -0
  323. package/skills/camox/references/field-styling.md +4 -4
  324. package/skills/camox/references/layout-definitions.md +16 -14
  325. package/skills/camox/references/localization.md +56 -0
  326. package/skills/camox/references/routed-layouts.md +133 -15
  327. package/skills/camox-expand/SKILL.md +70 -0
  328. package/skills/camox-expand/references/jobs.md +39 -0
  329. package/skills/camoxify/SKILL.md +29 -7
  330. package/dist/core/components/AddBlockControlBar.js +0 -78
  331. package/dist/core/hooks/useRequireDraftSource.js +0 -23
  332. package/dist/features/metadata/sitemap.d.ts +0 -8
  333. package/dist/features/metadata/sitemap.js +0 -17
  334. package/dist/features/og/imageResponse.node.d.ts +0 -12
  335. package/dist/features/og/imageResponse.node.js +0 -49
  336. package/dist/features/og/imageResponse.workerd.d.ts +0 -12
  337. package/dist/features/og/imageResponse.workerd.js +0 -40
  338. package/dist/features/page/PublishedPageContent.js +0 -205
  339. package/dist/features/page/PublishedPageExperience.js +0 -23
  340. package/dist/features/preview/DerivedPreview.js +0 -134
  341. package/dist/features/preview/commentsEnabled.js +0 -8
  342. package/dist/features/preview/components/AddBlockSidebar.js +0 -381
  343. package/dist/features/preview/components/AttachedComments.js +0 -331
  344. package/dist/features/preview/components/CommentHeader.js +0 -122
  345. package/dist/features/preview/components/CommentSidebar.js +0 -29
  346. package/dist/features/preview/components/CommentTargetQuote.js +0 -167
  347. package/dist/features/preview/components/DerivedLayoutSidebar.d.ts +0 -5
  348. package/dist/features/preview/components/DerivedLayoutSidebar.js +0 -179
  349. package/dist/features/preview/components/DerivedPageInfoSidebar.js +0 -69
  350. package/dist/features/preview/components/LeftSidebar.js +0 -68
  351. package/dist/features/preview/components/PageEditorSidebar.js +0 -563
  352. package/dist/features/preview/components/PageInfoSidebar.js +0 -1551
  353. package/dist/features/preview/components/PageNavigatorSidebar.js +0 -78
  354. package/dist/features/preview/components/PageStatusBadge.js +0 -97
  355. package/dist/features/preview/components/PeekedBlock.js +0 -217
  356. package/dist/features/preview/components/PreviewComments.js +0 -56
  357. package/dist/features/preview/components/PreviewToolbarSpacer.js +0 -43
  358. package/dist/features/preview/components/PublicationRow.js +0 -422
  359. package/dist/features/preview/components/PublishDialog.js +0 -194
  360. package/dist/features/preview/components/RightSidebar.js +0 -59
  361. package/dist/features/preview/components/SendFeedbackDialog.js +0 -83
  362. package/dist/features/preview/previewCommentsStore.js +0 -135
  363. package/dist/features/preview/publication.js +0 -81
  364. package/dist/features/preview/usePageComments.js +0 -39
  365. package/dist/features/runtime/SharedChromeContext.js +0 -7
  366. package/dist/features/studio/components/EnvironmentMenu.js +0 -425
  367. package/dist/features/vite/appGeneration.js +0 -89
  368. package/dist/lib/view-queries.js +0 -28
  369. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/Matches.d.ts +0 -114
  370. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/RouterProvider.d.ts +0 -25
  371. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/config.d.ts +0 -1
  372. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/defer.d.ts +0 -1
  373. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/fileRoute.d.ts +0 -20
  374. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/global.d.ts +0 -8
  375. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/hash-scroll.d.ts +0 -1
  376. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/index.d.ts +0 -18
  377. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/link.d.ts +0 -161
  378. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/load-matches.d.ts +0 -6
  379. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/location.d.ts +0 -54
  380. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/lru-cache.d.ts +0 -8
  381. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/manifest.d.ts +0 -32
  382. 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
  383. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/not-found.d.ts +0 -1
  384. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/redirect.d.ts +0 -49
  385. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/rewrite.d.ts +0 -1
  386. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/root.d.ts +0 -6
  387. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/route.d.ts +0 -343
  388. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/routeInfo.d.ts +0 -48
  389. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/router.d.ts +0 -695
  390. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/scroll-restoration.d.ts +0 -1
  391. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/searchMiddleware.d.ts +0 -1
  392. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/searchParams.d.ts +0 -5
  393. 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
  394. 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
  395. 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
  396. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/stores.d.ts +0 -69
  397. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/structuralSharing.d.ts +0 -1
  398. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/typePrimitives.d.ts +0 -1
  399. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useLoaderData.d.ts +0 -1
  400. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useLoaderDeps.d.ts +0 -1
  401. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useNavigate.d.ts +0 -10
  402. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useParams.d.ts +0 -1
  403. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useRouteContext.d.ts +0 -1
  404. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useSearch.d.ts +0 -1
  405. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/utils.d.ts +0 -28
  406. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/validators.d.ts +0 -49
  407. package/skills/camox/references/cli-environments.md +0 -37
@@ -0,0 +1,17 @@
1
+ //#region src/features/vite/bundlerLogs.ts
2
+ /** Wraps the app's own `onLog`, if any, so it never sees warnings every Camox app produces. */
3
+ function withoutExpectedCamoxLogs(onLog) {
4
+ return (level, log, handler) => {
5
+ if (isExpectedCamoxLog(log)) return;
6
+ if (onLog) return onLog(level, log, handler);
7
+ handler(level, log);
8
+ };
9
+ }
10
+ function isExpectedCamoxLog(log) {
11
+ if (log.code === "MODULE_LEVEL_DIRECTIVE") return log.message.includes("\"use client\"");
12
+ if (log.code === "INEFFECTIVE_DYNAMIC_IMPORT") return log.message.includes("virtual:camox/app");
13
+ return false;
14
+ }
15
+
16
+ //#endregion
17
+ export { withoutExpectedCamoxLogs };
@@ -1,279 +1,211 @@
1
1
  import { createServerApiClient } from "../../lib/api-client-server.js";
2
- import path from "node:path";
3
- import { createServer, isRunnableDevEnvironment } from "vite-plus";
2
+ import { DEVELOPMENT_DRAFT } from "@camox/api-contract/drafts";
3
+ import { isRunnableDevEnvironment } from "vite-plus";
4
4
 
5
5
  //#region src/features/vite/definitionsSync.ts
6
6
  const SYNC_DEBOUNCE_DELAY_MS = 100;
7
7
  /**
8
- * Sync block and layout definitions to the API.
9
- * This is the core sync logic, independent of ViteDevServer.
8
+ * The client definitions sync with. A release (deploy token) syncs Live's definitions; dev mode
9
+ * sends the developer's session and syncs their development draft, which its first sync
10
+ * creates (ADR 0006).
10
11
  */
12
+ function createSyncClient(options) {
13
+ const draftId = options.deployToken ? void 0 : DEVELOPMENT_DRAFT;
14
+ return createServerApiClient(options.apiUrl, options.authToken, draftId);
15
+ }
11
16
  function throwIfSyncAuthError(error) {
12
17
  if (error instanceof Error && error.name === "ORPCError" && error.message.toLowerCase().includes("unauthorized")) throw new Error("[camox] Definition sync failed: invalid or missing credentials.");
13
18
  }
14
19
  function isNotFoundError(error) {
15
20
  return error instanceof Error && error.name === "ORPCError" && error.message.toLowerCase().includes("not found");
16
21
  }
17
- function throwUnknownEnvironmentError(environmentName) {
18
- throw new Error(`[camox] Environment "${environmentName}" does not exist. Run the dev server while authenticated to create your personal environment first.`);
22
+ /**
23
+ * A sync that may not create the development draft finds nothing to write to: a release names a
24
+ * project that doesn't exist, or dev mode has no development draft yet (ADR 0006).
25
+ */
26
+ function throwSyncTargetNotFoundError(projectUuid, deployToken) {
27
+ if (deployToken) throw new Error(`[camox] No project has the Project ID "${projectUuid}". Check the camox() projectId.`);
28
+ throw new Error(`[camox] You have no development draft in the project with Project ID "${projectUuid}" yet. Run the dev server while authenticated to create it first.`);
29
+ }
30
+ /** `count` of `noun`, pluralized: "1 block", "2 blocks". */
31
+ function counted(count, noun) {
32
+ return `${count} ${noun}${count === 1 ? "" : "s"}`;
33
+ }
34
+ /** Names quoted and listed: `"hero", "footer"`. */
35
+ function quoted(names) {
36
+ return names.map((name) => `"${name}"`).join(", ");
19
37
  }
38
+ /**
39
+ * Send a Definition Set to the API: Live's for a release, the development draft's in dev mode.
40
+ * It goes in one `definitions.sync` call, which either fully applies it or changes nothing.
41
+ */
20
42
  async function syncDefinitionsToApi(options) {
21
- const { camoxApp, projectSlug, apiUrl, environmentName, autoCreate, authToken, deployToken, logger } = options;
22
- const client = createServerApiClient(apiUrl, environmentName, authToken);
23
- const blocks = camoxApp.getBlocks();
24
- const layoutDefinitions = camoxApp.getSerializableLayoutDefinitions();
25
- const typesUsedInLayouts = /* @__PURE__ */ new Set();
26
- for (const layout of layoutDefinitions) for (const blockDef of layout.blocks) typesUsedInLayouts.add(blockDef.type);
27
- const definitions = blocks.filter((block) => !block._internal.layoutOnly || typesUsedInLayouts.has(block._internal.id)).map((block) => ({
28
- blockId: block._internal.id,
29
- title: block._internal.title,
30
- description: block._internal.description,
31
- contentSchema: block._internal.contentSchema,
32
- settingsSchema: block._internal.settingsSchema,
33
- defaultContent: block._internal.getInitialContent(),
34
- defaultSettings: block._internal.getInitialSettings(),
35
- layoutOnly: block._internal.layoutOnly || void 0,
36
- synced: block._internal.synced
37
- }));
38
- let environmentCreated = false;
39
- let deletedDefinitionTypes = [];
40
- let blockedDefinitionDeletions = [];
43
+ const { definitionSet, projectUuid, autoCreate, deployToken, logger } = options;
44
+ const client = createSyncClient(options);
45
+ let result;
41
46
  try {
42
- const result = await client.blockDefinitions.sync({
43
- projectSlug,
47
+ result = await client.definitions.sync({
48
+ projectUuid,
44
49
  deployToken,
45
50
  autoCreate,
46
- definitions
51
+ definitionSet
47
52
  });
48
- environmentCreated = result.environmentCreated;
49
- deletedDefinitionTypes = result.deletedDefinitionTypes;
50
- blockedDefinitionDeletions = result.blockedDefinitionDeletions;
51
53
  } catch (error) {
52
54
  throwIfSyncAuthError(error);
53
- if (!autoCreate && isNotFoundError(error)) throwUnknownEnvironmentError(environmentName);
55
+ if (!autoCreate && isNotFoundError(error)) throwSyncTargetNotFoundError(projectUuid, deployToken);
54
56
  throw error;
55
57
  }
56
- if (environmentCreated) logger.info(`[camox] Created empty environment "${environmentName}"`, { timestamp: true });
57
- logger.info(`[camox] Synced ${definitions.length} block definition${definitions.length === 1 ? "" : "s"}`, { timestamp: true });
58
- if (deletedDefinitionTypes.length > 0) {
59
- const blockList = deletedDefinitionTypes.map((t) => `"${t}"`).join(", ");
60
- logger.info(`[camox] Removed ${deletedDefinitionTypes.length} orphaned block definition${deletedDefinitionTypes.length === 1 ? "" : "s"} (no longer in code): ${blockList}`, { timestamp: true });
61
- }
62
- for (const blocked of blockedDefinitionDeletions) logger.warn(`[camox] Cannot delete block definition "${blocked.blockId}": still used by ${blocked.blockCount} block${blocked.blockCount === 1 ? "" : "s"}. Delete those blocks first.`, { timestamp: true });
63
- if (layoutDefinitions.length > 0) {
64
- let layoutSyncResults;
65
- try {
66
- layoutSyncResults = await client.layouts.sync({
67
- projectSlug,
68
- deployToken,
69
- autoCreate,
70
- layouts: layoutDefinitions
71
- });
72
- } catch (error) {
73
- throwIfSyncAuthError(error);
74
- if (!autoCreate && isNotFoundError(error)) throwUnknownEnvironmentError(environmentName);
75
- throw error;
76
- }
77
- logger.info(`[camox] Synced ${layoutDefinitions.length} layout${layoutDefinitions.length === 1 ? "" : "s"} to Camox API`, { timestamp: true });
78
- for (const result of layoutSyncResults.layouts) {
79
- if (result.wasExisting && result.createdBlockTypes.length > 0) {
80
- const blockList = result.createdBlockTypes.map((t) => `"${t}"`).join(", ");
81
- logger.info(`[camox] Added ${result.createdBlockTypes.length} block${result.createdBlockTypes.length === 1 ? "" : "s"} to existing layout "${result.layout.layoutId}": ${blockList}`, { timestamp: true });
82
- }
83
- if (result.removedBlockTypes.length > 0) {
84
- const blockList = result.removedBlockTypes.map((t) => `"${t}"`).join(", ");
85
- logger.info(`[camox] Removed ${result.removedBlockTypes.length} layoutOnly block${result.removedBlockTypes.length === 1 ? "" : "s"} from layout "${result.layout.layoutId}": ${blockList}`, { timestamp: true });
86
- }
87
- if (result.skippedOrphanTypes.length > 0) {
88
- const blockList = result.skippedOrphanTypes.map((t) => `"${t}"`).join(", ");
89
- logger.info(`[camox] Layout "${result.layout.layoutId}" has ${result.skippedOrphanTypes.length} block${result.skippedOrphanTypes.length === 1 ? "" : "s"} still in DB but removed from code: ${blockList} (kept because not layoutOnly)`, { timestamp: true });
90
- }
91
- }
92
- for (const layoutId of layoutSyncResults.deletedLayoutIds) logger.info(`[camox] Deleted layout "${layoutId}"`, { timestamp: true });
93
- for (const blocked of layoutSyncResults.blockedLayoutDeletions) logger.warn(`[camox] Cannot delete layout "${blocked.layoutId}": still used by ${blocked.pageCount} page${blocked.pageCount === 1 ? "" : "s"}. Reassign or delete those pages first.`, { timestamp: true });
94
- if (layoutSyncResults.deletedDefinitionTypes.length > 0) {
95
- const blockList = layoutSyncResults.deletedDefinitionTypes.map((t) => `"${t}"`).join(", ");
96
- logger.info(`[camox] Removed ${layoutSyncResults.deletedDefinitionTypes.length} layoutOnly block definition${layoutSyncResults.deletedDefinitionTypes.length === 1 ? "" : "s"} (no layout uses ${layoutSyncResults.deletedDefinitionTypes.length === 1 ? "it" : "them"} anymore): ${blockList}`, { timestamp: true });
58
+ if (result.draftCreated) logger.info("[camox] Created your development draft", { timestamp: true });
59
+ logger.info(`[camox] Synced ${counted(result.blocks.count, "block definition")}`, { timestamp: true });
60
+ const { deletedDefinitionTypes } = result.blocks;
61
+ if (deletedDefinitionTypes.length > 0) logger.info(`[camox] Removed ${counted(deletedDefinitionTypes.length, "block definition")} no longer in code: ${quoted(deletedDefinitionTypes)}. Their blocks are kept and render nothing until the definition comes back.`, { timestamp: true });
62
+ logger.info(`[camox] Synced ${result.collections.count} collection definitions`, { timestamp: true });
63
+ for (const id of result.collections.retired) logger.warn(`[camox] Removed collection "${id}"; its records are kept and hidden`, { timestamp: true });
64
+ const layoutSyncResults = result.layouts;
65
+ if (layoutSyncResults) {
66
+ logger.info(`[camox] Synced ${counted(layoutSyncResults.count, "layout")} to Camox API`, { timestamp: true });
67
+ for (const layout of layoutSyncResults.layouts) {
68
+ const { layoutId, createdBlockTypes, removedBlockTypes, skippedOrphanTypes } = layout;
69
+ if (layout.wasExisting && createdBlockTypes.length > 0) logger.info(`[camox] Added ${counted(createdBlockTypes.length, "block")} to existing layout "${layoutId}": ${quoted(createdBlockTypes)}`, { timestamp: true });
70
+ if (removedBlockTypes.length > 0) logger.info(`[camox] Removed ${counted(removedBlockTypes.length, "layoutOnly block")} from layout "${layoutId}": ${quoted(removedBlockTypes)}`, { timestamp: true });
71
+ if (skippedOrphanTypes.length > 0) logger.info(`[camox] Layout "${layoutId}" has ${counted(skippedOrphanTypes.length, "block")} still in DB but removed from code: ${quoted(skippedOrphanTypes)} (kept because not layoutOnly)`, { timestamp: true });
97
72
  }
73
+ for (const layoutId of layoutSyncResults.removedLayoutIds) logger.info(`[camox] Removed layout "${layoutId}"; its pages are kept and listed as needing a layout`, { timestamp: true });
74
+ const unused = layoutSyncResults.deletedDefinitionTypes;
75
+ if (unused.length > 0) logger.info(`[camox] Removed ${counted(unused.length, "layoutOnly block definition")} (no layout uses ${unused.length === 1 ? "it" : "them"} anymore): ${quoted(unused)}`, { timestamp: true });
98
76
  }
99
- const initialPage = camoxApp.getInitialPageBundles();
100
- if (initialPage) try {
101
- const result = await client.projects.initializeContent({
102
- projectSlug,
103
- deployToken,
104
- layoutId: initialPage.layoutId,
105
- blocks: initialPage.blocks
106
- });
107
- if (result.created) if (initialPage.hasInitialBlocks) logger.info(`[camox] Initialized content: homepage with ${result.blockCount} block${result.blockCount === 1 ? "" : "s"}`, { timestamp: true });
77
+ const { homepage } = definitionSet;
78
+ if (homepage && result.homepage?.created) {
79
+ if (homepage.hasInitialBlocks) logger.info(`[camox] Initialized content: homepage with ${counted(result.homepage.blockCount, "block")}`, { timestamp: true });
108
80
  else {
109
81
  logger.info("[camox] Created empty homepage (using first layout)", { timestamp: true });
110
82
  logger.info("[camox] Tip: add blocks.initial to your layout to pre-populate pages with content", { timestamp: true });
111
83
  }
112
- } catch (error) {
113
- throwIfSyncAuthError(error);
114
- throw error;
115
84
  }
116
85
  }
117
- function getBlockIdFromFilePath(filePath) {
118
- return path.basename(filePath, path.extname(filePath));
119
- }
120
- const CAMOX_APP_PATH = "./src/camox/app.ts";
86
+ const VIRTUAL_CAMOX_APP = "virtual:camox/app";
87
+ const RESOLVED_VIRTUAL_CAMOX_APP = "\0virtual:camox/app";
121
88
  /**
122
- * Load a module using SSR. Uses the server's SSR environment runner if available,
123
- * otherwise falls back to a temporary Vite server (needed when Nitro or other
124
- * frameworks configure the SSR environment as non-runnable).
89
+ * The dev server's Node `ssr` environment, which runs alongside the Worker's own environment:
90
+ * the Cloudflare plugin runs the Worker in workerd, where the dev server can't import modules.
125
91
  */
126
- async function ssrLoadModule(server, modulePath) {
92
+ function getNodeEnvironment(server) {
127
93
  const ssrEnv = server.environments.ssr;
128
- if (ssrEnv && isRunnableDevEnvironment(ssrEnv)) return ssrEnv.runner.import(modulePath);
129
- const tempServer = await createServer({
130
- configFile: false,
131
- root: server.config.root,
132
- cacheDir: path.join(server.config.root, "node_modules", ".vite-camox-temp"),
133
- resolve: server.config.resolve,
134
- server: { middlewareMode: true },
135
- logLevel: "silent"
136
- });
137
- try {
138
- return await tempServer.ssrLoadModule(modulePath);
139
- } finally {
140
- await tempServer.close();
94
+ if (!ssrEnv || !isRunnableDevEnvironment(ssrEnv)) throw new Error("[camox] The dev server has no Node ssr environment to load the app in.");
95
+ return ssrEnv;
96
+ }
97
+ /**
98
+ * The app's current Definition Set, evaluated afresh in the dev server's Node `ssr` environment.
99
+ */
100
+ async function loadDefinitionSet(server) {
101
+ const nodeEnvironment = getNodeEnvironment(server);
102
+ const appModule = nodeEnvironment.moduleGraph.getModuleById(RESOLVED_VIRTUAL_CAMOX_APP);
103
+ if (appModule) nodeEnvironment.moduleGraph.invalidateModule(appModule);
104
+ nodeEnvironment.runner.evaluatedModules.clear();
105
+ const { camoxApp } = await nodeEnvironment.runner.import(VIRTUAL_CAMOX_APP);
106
+ return camoxApp.getDefinitionSet();
107
+ }
108
+ /**
109
+ * A Definition Set as a string that is equal for equal sets: object keys are sorted, and only
110
+ * what reaches the API (JSON) counts.
111
+ */
112
+ function serializeDefinitionSet(definitionSet) {
113
+ return JSON.stringify(definitionSet, (_key, value) => value && typeof value === "object" && !Array.isArray(value) ? Object.fromEntries(Object.keys(value).sort().map((key) => [key, value[key]])) : value);
114
+ }
115
+ /** Whether the app, as the `ssr` environment last loaded it, imports `file`, even indirectly. */
116
+ function isAppDependency(server, file) {
117
+ const { moduleGraph } = getNodeEnvironment(server);
118
+ const pending = [...moduleGraph.getModulesByFile(file) ?? []];
119
+ const seen = new Set(pending);
120
+ for (let module = pending.pop(); module; module = pending.pop()) {
121
+ if (module.id === RESOLVED_VIRTUAL_CAMOX_APP) return true;
122
+ for (const importer of module.importers) {
123
+ if (seen.has(importer)) continue;
124
+ seen.add(importer);
125
+ pending.push(importer);
126
+ }
141
127
  }
128
+ return false;
142
129
  }
130
+ /** Whether a file is a script module, which the app's globs may pick up when it appears. */
131
+ function isScriptFile(file) {
132
+ return /\.[cm]?[jt]sx?$/.test(file) && !file.endsWith(".d.ts");
133
+ }
134
+ /** Whether a project's Live has no pages, as on a fresh database. */
135
+ async function liveHasNoPages(apiUrl, projectUuid) {
136
+ const { pages } = await createServerApiClient(apiUrl).pages.listBySlug({ projectUuid });
137
+ return pages.length === 0;
138
+ }
139
+ /**
140
+ * Sync the app's whole Definition Set to the development draft now, then again whenever a module
141
+ * the app depends on changes (debounced), skipping a sync when the set equals the last one sent
142
+ * (ADR 0006). Resolves once the initial sync settles: `synced` if definitions reached the API,
143
+ * and `releasedToLive` if it first released them to an empty Live (`releaseToEmptyLive`).
144
+ */
143
145
  async function syncDefinitions(server, options) {
144
- const { projectSlug, apiUrl, environmentName, autoCreate, authToken, deployToken } = options;
145
- const blocksDirs = [path.resolve(server.config.root, "src/blocks"), path.resolve(server.config.root, "src/camox/blocks")];
146
- const layoutsDirs = [path.resolve(server.config.root, "src/layouts"), path.resolve(server.config.root, "src/camox/layouts")];
147
- const client = createServerApiClient(apiUrl, environmentName, authToken);
148
- async function performInitialSync() {
149
- const appModule = server.moduleGraph.getModuleById(CAMOX_APP_PATH);
150
- if (appModule) server.moduleGraph.invalidateModule(appModule);
151
- const camoxModule = await ssrLoadModule(server, CAMOX_APP_PATH);
152
- if (!camoxModule.camoxApp) {
153
- server.config.logger.warn(`[camox] No camoxApp export found in ${CAMOX_APP_PATH}`, { timestamp: true });
154
- return;
146
+ const { projectUuid, apiUrl, autoCreate, authToken, deployToken, releaseToEmptyLive } = options;
147
+ const logger = server.config.logger;
148
+ /** What the development draft last received, so an unchanged set isn't sent again. */
149
+ let lastSynced;
150
+ let releasedToLive = false;
151
+ async function sync({ initial = false } = {}) {
152
+ const definitionSet = await loadDefinitionSet(server);
153
+ const serialized = serializeDefinitionSet(definitionSet);
154
+ if (serialized === lastSynced) return;
155
+ if (initial && releaseToEmptyLive && await liveHasNoPages(apiUrl, projectUuid)) {
156
+ logger.info("[camox] Live is empty: releasing your definitions to it", { timestamp: true });
157
+ await syncDefinitionsToApi({
158
+ definitionSet,
159
+ projectUuid,
160
+ apiUrl,
161
+ autoCreate: false,
162
+ deployToken: releaseToEmptyLive,
163
+ logger
164
+ });
165
+ releasedToLive = true;
155
166
  }
156
167
  await syncDefinitionsToApi({
157
- camoxApp: camoxModule.camoxApp,
158
- projectSlug,
168
+ definitionSet,
169
+ projectUuid,
159
170
  apiUrl,
160
- environmentName,
161
171
  autoCreate,
162
172
  authToken,
163
173
  deployToken,
164
- logger: server.config.logger
174
+ logger
165
175
  });
176
+ lastSynced = serialized;
166
177
  }
167
- async function upsertBlock(filePath) {
168
- const relativePath = "./" + path.relative(server.config.root, filePath);
169
- const moduleNode = server.moduleGraph.getModuleById(relativePath);
170
- if (moduleNode) server.moduleGraph.invalidateModule(moduleNode);
171
- const blockModule = await ssrLoadModule(server, relativePath);
172
- if (!blockModule.block) {
173
- server.config.logger.warn(`[camox] No block export found in ${relativePath}`, { timestamp: true });
174
- return;
175
- }
176
- const block = blockModule.block;
177
- if (block._internal.layoutOnly) return;
178
- let result;
179
- try {
180
- result = await client.blockDefinitions.upsert({
181
- projectSlug,
182
- deployToken,
183
- blockId: block._internal.id,
184
- title: block._internal.title,
185
- description: block._internal.description,
186
- contentSchema: block._internal.contentSchema,
187
- settingsSchema: block._internal.settingsSchema,
188
- defaultContent: block._internal.getInitialContent(),
189
- defaultSettings: block._internal.getInitialSettings(),
190
- layoutOnly: block._internal.layoutOnly || void 0,
191
- synced: block._internal.synced
192
- });
193
- } catch (error) {
194
- throwIfSyncAuthError(error);
195
- throw error;
196
- }
197
- server.config.logger.info(`[camox] ${result.action === "created" ? "Created" : "Updated"} block definition "${block._internal.id}"`, { timestamp: true });
198
- }
199
- async function deleteBlock(filePath) {
200
- const blockId = getBlockIdFromFilePath(filePath);
201
- let result;
202
- try {
203
- result = await client.blockDefinitions.delete({
204
- projectSlug,
205
- deployToken,
206
- blockId
207
- });
208
- } catch (error) {
209
- throwIfSyncAuthError(error);
210
- throw error;
211
- }
212
- if (result.deleted) server.config.logger.info(`[camox] Deleted block definition "${blockId}"`, { timestamp: true });
213
- }
178
+ let synced = false;
214
179
  try {
215
- await performInitialSync();
180
+ await sync({ initial: true });
181
+ synced = true;
216
182
  } catch (error) {
217
- server.config.logger.error(`[camox] Failed to sync block definitions: ${String(error)}`, { timestamp: true });
183
+ logger.error(`[camox] Failed to sync block definitions: ${String(error)}`, { timestamp: true });
218
184
  }
219
- const debounceTimers = /* @__PURE__ */ new Map();
220
- function isBlockFile(filePath) {
221
- return blocksDirs.some((blocksDir) => filePath.startsWith(blocksDir)) && /\.tsx?$/.test(filePath);
222
- }
223
- function isLayoutFile(filePath) {
224
- return layoutsDirs.some((layoutsDir) => filePath.startsWith(layoutsDir)) && /\.tsx?$/.test(filePath);
225
- }
226
- const handleBlockFileUpsert = (filePath) => {
227
- if (!isBlockFile(filePath)) return;
228
- const existingTimer = debounceTimers.get(filePath);
229
- if (existingTimer) clearTimeout(existingTimer);
230
- debounceTimers.set(filePath, setTimeout(async () => {
231
- debounceTimers.delete(filePath);
232
- try {
233
- await upsertBlock(filePath);
234
- } catch (error) {
235
- server.config.logger.error(`[camox] Failed to sync block: ${String(error)}`, { timestamp: true });
236
- }
237
- }, SYNC_DEBOUNCE_DELAY_MS));
238
- };
239
- const handleBlockFileDelete = (filePath) => {
240
- if (!isBlockFile(filePath)) return;
241
- const existingTimer = debounceTimers.get(filePath);
242
- if (existingTimer) {
243
- clearTimeout(existingTimer);
244
- debounceTimers.delete(filePath);
245
- }
246
- setTimeout(async () => {
247
- try {
248
- await deleteBlock(filePath);
249
- } catch (error) {
250
- server.config.logger.error(`[camox] Failed to delete block: ${String(error)}`, { timestamp: true });
251
- }
185
+ let running = Promise.resolve();
186
+ let timer;
187
+ const scheduleSync = () => {
188
+ clearTimeout(timer);
189
+ timer = setTimeout(() => {
190
+ running = running.then(() => sync().catch((error) => {
191
+ logger.error(`[camox] Failed to sync block definitions: ${String(error)}`, { timestamp: true });
192
+ }));
252
193
  }, SYNC_DEBOUNCE_DELAY_MS);
253
194
  };
254
- let layoutSyncTimer = null;
255
- const handleLayoutFileChange = (filePath) => {
256
- if (!isLayoutFile(filePath)) return;
257
- const relativePath = "./" + path.relative(server.config.root, filePath);
258
- const moduleNode = server.moduleGraph.getModuleById(relativePath);
259
- if (moduleNode) server.moduleGraph.invalidateModule(moduleNode);
260
- if (layoutSyncTimer) clearTimeout(layoutSyncTimer);
261
- layoutSyncTimer = setTimeout(async () => {
262
- layoutSyncTimer = null;
263
- try {
264
- await performInitialSync();
265
- } catch (error) {
266
- server.config.logger.error(`[camox] Failed to sync layouts: ${String(error)}`, { timestamp: true });
267
- }
268
- }, SYNC_DEBOUNCE_DELAY_MS);
195
+ server.watcher.on("change", (file) => {
196
+ if (isAppDependency(server, file)) scheduleSync();
197
+ });
198
+ server.watcher.on("add", (file) => {
199
+ if (isScriptFile(file)) scheduleSync();
200
+ });
201
+ server.watcher.on("unlink", (file) => {
202
+ if (isScriptFile(file) || isAppDependency(server, file)) scheduleSync();
203
+ });
204
+ return {
205
+ synced,
206
+ releasedToLive
269
207
  };
270
- server.watcher.on("change", handleBlockFileUpsert);
271
- server.watcher.on("change", handleLayoutFileChange);
272
- server.watcher.on("add", handleBlockFileUpsert);
273
- server.watcher.on("add", handleLayoutFileChange);
274
- server.watcher.on("unlink", handleBlockFileDelete);
275
- server.watcher.on("unlink", handleLayoutFileChange);
276
208
  }
277
209
 
278
210
  //#endregion
279
- export { syncDefinitions, syncDefinitionsToApi };
211
+ export { VIRTUAL_CAMOX_APP, syncDefinitions, syncDefinitionsToApi };
@@ -1,10 +1,11 @@
1
+ import { authPageStyles } from "../../lib/auth-page-styles.js";
1
2
  import { verifyOneTimeToken, writeAuthTokenForUrl } from "./auth.js";
2
3
  import { randomBytes } from "node:crypto";
3
4
 
4
5
  //#region src/features/vite/devAuthentication.ts
5
6
  const CALLBACK_PATH = "/__camox/auth/callback";
6
7
  const READY_PATH = "/__camox/auth/ready";
7
- const AUTH_REQUEST_TTL_MS = 600 * 1e3;
8
+ const AUTH_REQUEST_TTL_MS = 6e5;
8
9
  function normalizeUrl(url) {
9
10
  return url.replace(/\/+$/, "");
10
11
  }
@@ -57,17 +58,10 @@ function sendRestartingPage(res, returnPath) {
57
58
  <meta charset="utf-8" />
58
59
  <meta name="viewport" content="width=device-width, initial-scale=1" />
59
60
  <title>Connecting to Camox…</title>
60
- <style>
61
- :root { color-scheme: dark; font-family: Inter, ui-sans-serif, system-ui, sans-serif; }
62
- body { align-items: center; background: #09090b; color: #fafafa; display: flex; justify-content: center; margin: 0; min-height: 100vh; }
63
- main { align-items: center; display: flex; flex-direction: column; gap: 16px; text-align: center; }
64
- .spinner { animation: spin 800ms linear infinite; border: 2px solid #3f3f46; border-radius: 999px; border-top-color: #fafafa; height: 24px; width: 24px; }
65
- p { color: #a1a1aa; margin: 0; }
66
- @keyframes spin { to { transform: rotate(360deg); } }
67
- </style>
61
+ <style>${authPageStyles}</style>
68
62
  </head>
69
63
  <body>
70
- <main><div class="spinner"></div><p>Starting your Camox site…</p></main>
64
+ <main class="camox-auth-page" role="status"><div class="camox-auth-spinner" aria-hidden="true"></div><p>Starting your Camox site…</p></main>
71
65
  <script>
72
66
  const returnPath = ${serializedReturnPath};
73
67
  async function waitUntilReady() {
@@ -144,12 +138,6 @@ function installDevAuthenticationMiddleware(server, options) {
144
138
  }
145
139
  redirect(res, authorizationUrl.toString());
146
140
  });
147
- const authenticationLayer = server.middlewares.stack.at(-1);
148
- const nitroMiddlewareIndex = server.middlewares.stack.findIndex((layer) => typeof layer.handle === "function" && layer.handle.name === "nitroDevMiddlewarePre");
149
- if (authenticationLayer && nitroMiddlewareIndex >= 0) {
150
- server.middlewares.stack.pop();
151
- server.middlewares.stack.splice(nitroMiddlewareIndex, 0, authenticationLayer);
152
- }
153
141
  }
154
142
  async function handleAuthenticationCallback(req, res, server, options, pendingAuthentications) {
155
143
  const requestUrl = new URL(req.url ?? "/", "http://localhost");
@@ -0,0 +1,87 @@
1
+ import { createPreviewSignInUrl, isLoopbackUrl } from "@camox/cli/auth";
2
+ import childProcess from "node:child_process";
3
+ import { open } from "node:fs/promises";
4
+ import { setTimeout } from "node:timers/promises";
5
+
6
+ //#region src/features/vite/devSignIn.ts
7
+ const openedKey = Symbol.for("camox.devSignIn.opened");
8
+ const processState = globalThis;
9
+ const opened = processState[openedKey] ??= /* @__PURE__ */ new Set();
10
+ function openBrowser(url, onError) {
11
+ const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "rundll32.exe" : "xdg-open";
12
+ const args = process.platform === "win32" ? ["url.dll,FileProtocolHandler", url] : [url];
13
+ try {
14
+ const child = childProcess.spawn(command, args, {
15
+ stdio: "ignore",
16
+ detached: true
17
+ });
18
+ child.once("error", onError);
19
+ child.once("exit", (code) => {
20
+ if (code !== 0) onError();
21
+ });
22
+ child.unref();
23
+ } catch {
24
+ onError();
25
+ }
26
+ }
27
+ /** Mint only after Vite has resolved its actual port, including port fallback. */
28
+ function installDevSignInLink(server, options) {
29
+ const controller = new AbortController();
30
+ server.httpServer?.once("close", () => controller.abort());
31
+ const printUrls = server.printUrls.bind(server);
32
+ let started = false;
33
+ server.printUrls = () => {
34
+ printUrls();
35
+ if (started) return;
36
+ const urls = server.resolvedUrls;
37
+ const destination = [...urls?.local ?? [], ...urls?.network ?? []].find((url) => isLoopbackUrl(new URL(url)));
38
+ if (!destination) return;
39
+ started = true;
40
+ printSignInLink(destination);
41
+ };
42
+ async function printSignInLink(destination) {
43
+ const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(3e4)]);
44
+ while (!signal.aborted) try {
45
+ const url = await createPreviewSignInUrl(destination, {
46
+ projectUuid: options.projectUuid,
47
+ apiUrl: options.apiUrl
48
+ }, options.authToken, signal);
49
+ if (signal.aborted) return;
50
+ server.config.logger.info(` ➜ Camox sign in (single-use, expires in 3 minutes; do not share): ${url}`);
51
+ if (options.open) await openSignInLink(url);
52
+ return;
53
+ } catch {
54
+ try {
55
+ await setTimeout(1e3, void 0, { signal });
56
+ } catch {
57
+ break;
58
+ }
59
+ }
60
+ if (controller.signal.aborted) return;
61
+ server.config.logger.warn("Camox sign-in link unavailable. Use the normal local URL to sign in, or run camox preview for a fresh link.");
62
+ }
63
+ async function openSignInLink(url) {
64
+ let warned = false;
65
+ const warn = () => {
66
+ if (warned || controller.signal.aborted) return;
67
+ warned = true;
68
+ server.config.logger.warn("Camox could not open your browser. Use the sign-in link printed above.");
69
+ };
70
+ try {
71
+ if (options.openOnceFile) await (await open(options.openOnceFile, "wx", 384)).close();
72
+ else {
73
+ const key = JSON.stringify([server.config.root, options.projectUuid]);
74
+ if (opened.has(key)) return;
75
+ opened.add(key);
76
+ }
77
+ if (controller.signal.aborted) return;
78
+ openBrowser(url, warn);
79
+ } catch (error) {
80
+ if (error.code === "EEXIST") return;
81
+ warn();
82
+ }
83
+ }
84
+ }
85
+
86
+ //#endregion
87
+ export { installDevSignInLink };
@@ -1,6 +1,6 @@
1
- import { iconCollectionIds, loadIconCollection } from "@camox/api-contract";
2
1
  import { mkdirSync, writeFileSync } from "node:fs";
3
2
  import { resolve } from "node:path";
3
+ import { iconCollectionIds, loadIconCollection } from "@camox/api-contract/icon-catalog";
4
4
 
5
5
  //#region src/features/vite/iconGeneration.ts
6
6
  async function generateIconTypes(root, prefix) {
@@ -37,6 +37,16 @@ export const Layout = createLayout(${JSON.stringify(id)})({
37
37
  }
38
38
  writeIfChanged(resolve(appRoot, "src/camox/layouts.gen.d.ts"), `// Generated by Camox.\nimport "camox/createLayout";\ndeclare module "camox/createLayout" {\n interface LayoutFileRoutes ${declarations.length ? `{\n${declarations.join("\n")}\n }` : "{}"}\n}\n`);
39
39
  }
40
+ /** Regenerate layout files whenever a layout file is added, changed or deleted. */
41
+ function watchLayoutFiles(server, appRoot) {
42
+ for (const event of [
43
+ "add",
44
+ "change",
45
+ "unlink"
46
+ ]) server.watcher.on(event, (path) => {
47
+ if (path.startsWith(resolve(appRoot, "src/layouts") + "/")) generateLayoutFiles(appRoot);
48
+ });
49
+ }
40
50
 
41
51
  //#endregion
42
- export { generateLayoutFiles };
52
+ export { generateLayoutFiles, watchLayoutFiles };
@@ -0,0 +1,30 @@
1
+ //#region src/features/vite/publishSeededContent.ts
2
+ /**
3
+ * Publishes the content dev mode seeded into the development draft, so a fresh database starts
4
+ * with it on Live and the draft only holds the developer's own changes. Failures are warnings:
5
+ * the content stays in the draft, where it can still be published by hand.
6
+ */
7
+ async function publishSeededContent(options) {
8
+ const { client, projectUuid, logger } = options;
9
+ try {
10
+ const project = await client.projects.getByUuid({ projectUuid });
11
+ const preview = await client.drafts.previewPublish({ projectId: project.id });
12
+ if (preview.changes.length === 0) return;
13
+ if (preview.blockers.length > 0) {
14
+ const blockers = preview.blockers.map((blocker) => blocker.message).join("; ");
15
+ logger.warn(`[camox] Seeded content stays in your development draft; publishing is blocked: ${blockers}`, { timestamp: true });
16
+ return;
17
+ }
18
+ await client.drafts.publish({
19
+ projectId: project.id,
20
+ token: preview.token,
21
+ publishAnyway: true
22
+ });
23
+ logger.info(`[camox] Published ${preview.changes.length} seeded change${preview.changes.length === 1 ? "" : "s"} to Live`, { timestamp: true });
24
+ } catch (error) {
25
+ logger.warn(`[camox] Publishing seeded content failed: ${String(error)}`, { timestamp: true });
26
+ }
27
+ }
28
+
29
+ //#endregion
30
+ export { publishSeededContent };