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.
Files changed (411) 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 +1734 -0
  68. package/dist/features/canvas/CanvasBlockFrame.js +353 -0
  69. package/dist/features/canvas/CanvasBlockHeader.js +55 -0
  70. package/dist/features/canvas/CanvasLoadingScreen.js +35 -0
  71. package/dist/features/canvas/CanvasOverlays.js +254 -0
  72. package/dist/features/canvas/CanvasPageEdge.js +87 -0
  73. package/dist/features/canvas/CanvasPageFrame.js +335 -0
  74. package/dist/features/canvas/CanvasPageHeader.js +125 -0
  75. package/dist/features/canvas/CanvasPreviewPageSlot.js +103 -0
  76. package/dist/features/canvas/CanvasShelfMarker.js +255 -0
  77. package/dist/features/canvas/DerivedPagePathInput.js +238 -0
  78. package/dist/features/canvas/canvasCamera.js +229 -0
  79. package/dist/features/canvas/canvasFlight.js +104 -0
  80. package/dist/features/canvas/canvasFrameCapture.js +364 -0
  81. package/dist/features/canvas/canvasFrameDocument.js +317 -0
  82. package/dist/features/canvas/canvasFrameMode.js +53 -0
  83. package/dist/features/canvas/canvasInsertionSeams.js +44 -0
  84. package/dist/features/canvas/canvasItems.js +120 -0
  85. package/dist/features/canvas/canvasOverlayGeometry.js +240 -0
  86. package/dist/features/canvas/canvasPages.js +79 -0
  87. package/dist/features/canvas/canvasScreenSpace.js +26 -0
  88. package/dist/features/canvas/canvasSelection.js +18 -0
  89. package/dist/features/canvas/canvasStore.js +149 -0
  90. package/dist/features/canvas/canvasThumbnails.js +304 -0
  91. package/dist/features/canvas/canvasZoom.js +10 -0
  92. package/dist/features/canvas/sidebar/CanvasInspector.js +202 -0
  93. package/dist/features/canvas/sidebar/CanvasNavigator.js +226 -0
  94. package/dist/features/canvas/sidebar/CanvasSidebar.js +60 -0
  95. package/dist/features/canvas/sidebar/CanvasSidebarFrame.js +48 -0
  96. package/dist/features/canvas/sidebar/CanvasSidebarSkeleton.js +39 -0
  97. package/dist/features/canvas/sidebar/CatalogBlockInspector.js +189 -0
  98. package/dist/features/canvas/sidebar/CuratedPageInspector.js +69 -0
  99. package/dist/features/canvas/sidebar/DerivedPageInspector.js +37 -0
  100. package/dist/features/canvas/sidebar/LayoutPageBlocks.js +78 -0
  101. package/dist/features/canvas/sidebar/LayoutPageOverview.js +71 -0
  102. package/dist/features/canvas/sidebar/PageInspector.js +157 -0
  103. package/dist/features/canvas/sidebar/PageInspectorSkeleton.js +90 -0
  104. package/dist/features/canvas/sidebar/PageOverview.js +27 -0
  105. package/dist/features/canvas/sidebar/SingletonPageInspector.js +31 -0
  106. package/dist/features/canvas/useCanvasCamera.js +803 -0
  107. package/dist/features/canvas/useCanvasFrame.js +149 -0
  108. package/dist/features/canvas/useCanvasFrameInput.js +97 -0
  109. package/dist/features/canvas/useCanvasNearness.js +57 -0
  110. package/dist/features/canvas/useDiscoveredPreviewPaths.js +62 -0
  111. package/dist/features/content/CamoxContent.d.ts +2 -5
  112. package/dist/features/content/CamoxContent.js +174 -271
  113. package/dist/features/content/CollectionItemSheetContext.js +106 -0
  114. package/dist/features/content/ContentAssets.js +315 -0
  115. package/dist/features/content/ContentCollection.js +663 -0
  116. package/dist/features/content/collection-form.js +58 -0
  117. package/dist/features/content/components/AssetCard.js +80 -84
  118. package/dist/features/content/components/AssetCardSkeleton.js +4 -8
  119. package/dist/features/content/components/ContentSidebar.js +129 -18
  120. package/dist/features/content/components/ContentSidebarGroup.js +37 -0
  121. package/dist/features/content/components/DeleteCollectionItemButton.js +202 -0
  122. package/dist/features/content/components/MissingFromLocale.js +282 -0
  123. package/dist/features/content/components/UploadDropZone.js +27 -31
  124. package/dist/features/content/components/UploadProgressDrawer.js +90 -98
  125. package/dist/features/navigation/navigation.d.ts +66 -20
  126. package/dist/features/navigation/navigation.js +257 -90
  127. package/dist/features/og/defaultFonts.js +8 -8
  128. package/dist/features/og/geist-variable-ttf-base64.js +1 -1
  129. package/dist/features/og/imageResponse.d.ts +8 -0
  130. package/dist/features/og/imageResponse.js +26 -0
  131. package/dist/features/page/CuratedPageContent.js +167 -0
  132. package/dist/features/page/DerivedPageContent.js +26 -30
  133. package/dist/features/page/SharedLayoutContent.js +25 -27
  134. package/dist/features/preview/CamoxPreview.d.ts +23 -61
  135. package/dist/features/preview/CamoxPreview.js +816 -537
  136. package/dist/features/preview/EditablePageContent.js +1 -1
  137. package/dist/features/preview/EditablePageExperience.js +16 -22
  138. package/dist/features/preview/components/AddBlockDialog.js +270 -0
  139. package/dist/features/preview/components/AssetFieldEditor.js +173 -154
  140. package/dist/features/preview/components/AssetLightbox.js +181 -134
  141. package/dist/features/preview/components/AssetPickerModal.js +153 -170
  142. package/dist/features/preview/components/BlockActionsPopover.js +150 -149
  143. package/dist/features/preview/components/BlockErrorBoundary.js +8 -12
  144. package/dist/features/preview/components/BlockThumbnail.js +176 -100
  145. package/dist/features/preview/components/CanvasPreview.js +243 -0
  146. package/dist/features/preview/components/CreatePageModal.js +128 -137
  147. package/dist/features/preview/components/DebouncedFieldEditor.js +29 -33
  148. package/dist/features/preview/components/DerivedLayoutDialog.js +21 -19
  149. package/dist/features/preview/components/DrillRow.js +150 -0
  150. package/dist/features/preview/components/FieldOverlayStyles.js +11 -8
  151. package/dist/features/preview/components/FieldToolbar.js +160 -110
  152. package/dist/features/preview/components/Frame.js +108 -20
  153. package/dist/features/preview/components/IconFieldEditor.js +62 -66
  154. package/dist/features/preview/components/ItemFieldsEditor.js +231 -205
  155. package/dist/features/preview/components/LinkFieldEditor.js +135 -139
  156. package/dist/features/preview/components/Metadata.js +22 -30
  157. package/dist/features/preview/components/MobilePreviewDrawer.js +89 -108
  158. package/dist/features/preview/components/MultipleAssetFieldEditor.js +334 -246
  159. package/dist/features/preview/components/OverlayTracker.js +9 -41
  160. package/dist/features/preview/components/Overlays.js +81 -69
  161. package/dist/features/preview/components/PageInfo.js +1748 -0
  162. package/dist/features/preview/components/PageInfoSkeleton.js +51 -0
  163. package/dist/features/preview/components/PageLocationFieldset.js +112 -116
  164. package/dist/features/preview/components/PageNavigatorHeader.js +24 -0
  165. package/dist/features/preview/components/PageNicknameField.js +22 -26
  166. package/dist/features/preview/components/PagePicker.js +128 -130
  167. package/dist/features/preview/components/PageTree.js +525 -532
  168. package/dist/features/preview/components/PreviewActivation.js +28 -32
  169. package/dist/features/preview/components/PreviewPanel.js +620 -319
  170. package/dist/features/preview/components/PreviewToolbar.js +105 -333
  171. package/dist/features/preview/components/RecordCard.js +80 -0
  172. package/dist/features/preview/components/RecordCombobox.js +178 -0
  173. package/dist/features/preview/components/ReferenceFieldEditor.js +149 -0
  174. package/dist/features/preview/components/ReferenceListFieldEditor.js +432 -0
  175. package/dist/features/preview/components/RemovedBlockPlaceholder.js +95 -0
  176. package/dist/features/preview/components/RepeatableItemsList.js +125 -138
  177. package/dist/features/preview/components/SelectionEditor.js +993 -0
  178. package/dist/features/preview/components/ShikiMarkdown.js +14 -18
  179. package/dist/features/preview/components/SidebarSection.js +52 -64
  180. package/dist/features/preview/components/StudioEditingFallback.js +56 -0
  181. package/dist/features/preview/components/UnlinkAssetButton.js +104 -107
  182. package/dist/features/preview/components/blockGridNavigation.js +15 -0
  183. package/dist/features/preview/components/contentFieldSchema.js +8 -0
  184. package/dist/features/preview/components/useFieldWriter.js +200 -0
  185. package/dist/features/preview/components/useRecordView.js +383 -0
  186. package/dist/features/preview/components/useRepeatableItemActions.js +35 -37
  187. package/dist/features/preview/components/useUpdateBlockPosition.js +37 -37
  188. package/dist/features/preview/editingFrame.js +197 -0
  189. package/dist/features/preview/overlayHighlights.js +17 -44
  190. package/dist/features/preview/overlayMessages.js +72 -1
  191. package/dist/features/preview/pageRecord.js +189 -0
  192. package/dist/features/preview/previewConstants.js +6 -2
  193. package/dist/features/preview/previewPageFrame.js +95 -0
  194. package/dist/features/preview/previewPreparation.js +7 -0
  195. package/dist/features/preview/previewSelection.js +32 -63
  196. package/dist/features/preview/previewStore.js +225 -123
  197. package/dist/features/preview/referencePickerFocus.js +29 -0
  198. package/dist/features/preview/repeatableItemTargets.js +56 -0
  199. package/dist/features/preview/selectionReveal.js +21 -0
  200. package/dist/features/preview/usePreviewViewportActions.js +114 -0
  201. package/dist/features/preview/viewportContinuity.js +109 -0
  202. package/dist/features/provider/AuthenticatedCamoxProvider.js +69 -44
  203. package/dist/features/provider/CamoxProvider.d.ts +3 -6
  204. package/dist/features/provider/CamoxProvider.js +38 -50
  205. package/dist/features/provider/CoreCamoxProvider.js +66 -50
  206. package/dist/features/provider/LocalhostPreviewProvider.js +78 -114
  207. package/dist/features/provider/PreviewSignInPage.js +54 -0
  208. package/dist/features/provider/components/CamoxAppContext.js +11 -11
  209. package/dist/features/provider/components/CommandPalette.js +75 -82
  210. package/dist/features/provider/useAdminShortcuts.js +7 -11
  211. package/dist/features/routes/pageRuntime.d.ts +44 -4
  212. package/dist/features/routes/pageRuntime.js +244 -137
  213. package/dist/features/runtime/RuntimeChrome.js +117 -115
  214. package/dist/features/runtime/alternates.js +64 -0
  215. package/dist/features/runtime/analytics.d.ts +7 -0
  216. package/dist/features/runtime/analytics.js +66 -0
  217. package/dist/features/runtime/artifactSources.js +87 -0
  218. package/dist/features/runtime/editingFallback.js +20 -0
  219. package/dist/features/runtime/linkPrefetch.js +97 -0
  220. package/dist/features/runtime/livePages.js +62 -0
  221. package/dist/features/runtime/llmsTxt.js +40 -0
  222. package/dist/features/runtime/loopbackFetch.js +18 -0
  223. package/dist/features/runtime/navigationAccessibility.js +51 -0
  224. package/dist/features/runtime/navigationTarget.js +23 -2
  225. package/dist/features/runtime/pageApp.js +229 -158
  226. package/dist/features/runtime/pageBlockTypes.js +20 -0
  227. package/dist/features/runtime/pageClient.d.ts +2 -4
  228. package/dist/features/runtime/pageClient.js +18 -3
  229. package/dist/features/runtime/pageNavigation.js +254 -32
  230. package/dist/features/runtime/pageServer.d.ts +2 -4
  231. package/dist/features/runtime/pageServer.js +6 -2
  232. package/dist/features/runtime/pageViewport.js +7 -0
  233. package/dist/features/runtime/runtime.d.ts +59 -24
  234. package/dist/features/runtime/runtime.js +779 -191
  235. package/dist/features/runtime/runtimeChromeLoader.js +58 -0
  236. package/dist/features/runtime/runtimeRoutes.d.ts +19 -0
  237. package/dist/features/runtime/runtimeRoutes.js +25 -0
  238. package/dist/features/runtime/scrollRestoration.js +133 -0
  239. package/dist/features/runtime/studioApp.d.ts +4 -3
  240. package/dist/features/runtime/studioApp.js +5 -9
  241. package/dist/features/runtime/studioClient.d.ts +2 -4
  242. package/dist/features/runtime/studioClient.js +12 -3
  243. package/dist/features/runtime/studioServer.d.ts +2 -4
  244. package/dist/features/runtime/studioServer.js +6 -1
  245. package/dist/features/runtime/worker.d.ts +6 -0
  246. package/dist/features/runtime/worker.js +23 -0
  247. package/dist/features/runtime/workerRequest.js +44 -0
  248. package/dist/features/studio/CamoxStudio.d.ts +2 -6
  249. package/dist/features/studio/CamoxStudio.js +34 -49
  250. package/dist/features/studio/FollowAgentProvider.js +338 -0
  251. package/dist/features/studio/components/ChatSidebar.js +356 -0
  252. package/dist/features/studio/components/DraftsControl.js +676 -0
  253. package/dist/features/studio/components/FaviconDialog.js +318 -0
  254. package/dist/features/studio/components/LocalePicker.js +785 -0
  255. package/dist/features/studio/components/Navbar.js +123 -103
  256. package/dist/features/studio/components/ProjectMenu.js +239 -260
  257. package/dist/features/studio/components/RenameDraftDialog.js +206 -0
  258. package/dist/features/studio/components/ReviewDraftSheet.js +1472 -0
  259. package/dist/features/studio/components/ThreadComposer.js +157 -0
  260. package/dist/features/studio/components/ThreadMarkdown.js +113 -0
  261. package/dist/features/studio/components/ThreadMessages.js +270 -0
  262. package/dist/features/studio/components/UserButton.js +34 -23
  263. package/dist/features/studio/draftLabel.js +33 -0
  264. package/dist/features/studio/draftNotice.js +13 -0
  265. package/dist/features/studio/followAgent.js +250 -0
  266. package/dist/features/studio/followAgentContext.js +7 -0
  267. package/dist/features/studio/localeOptions.js +118 -0
  268. package/dist/features/studio/localeSwitch.js +19 -0
  269. package/dist/features/studio/routes.js +48 -1
  270. package/dist/features/studio/studioStore.js +64 -1
  271. package/dist/features/studio/useTheme.js +24 -27
  272. package/dist/features/vite/artifactRuntimeMinification.js +36 -0
  273. package/dist/features/vite/blockBoilerplate.js +6 -5
  274. package/dist/features/vite/blockChunks.js +31 -0
  275. package/dist/features/vite/bundlerLogs.js +17 -0
  276. package/dist/features/vite/definitionsSync.js +158 -226
  277. package/dist/features/vite/devAuthentication.js +4 -16
  278. package/dist/features/vite/devSignIn.js +87 -0
  279. package/dist/features/vite/iconGeneration.js +1 -1
  280. package/dist/features/vite/layoutGeneration.js +11 -1
  281. package/dist/features/vite/publishSeededContent.js +30 -0
  282. package/dist/features/vite/routeGeneration.js +6 -7
  283. package/dist/features/vite/runtimeDev.js +80 -99
  284. package/dist/features/vite/vite.d.ts +51 -31
  285. package/dist/features/vite/vite.js +224 -127
  286. package/dist/features/vite/workerBuild.js +39 -0
  287. package/dist/hooks/use-debounced-field.js +116 -47
  288. package/dist/hooks/use-file-upload.js +114 -67
  289. package/dist/hooks/use-marquee-selection.js +32 -36
  290. package/dist/hooks/use-page-destinations.js +69 -37
  291. package/dist/lib/api-client-server.d.ts +5 -0
  292. package/dist/lib/api-client-server.js +10 -4
  293. package/dist/lib/api-client.d.ts +6 -0
  294. package/dist/lib/api-client.js +51 -16
  295. package/dist/lib/api-socket.js +17 -0
  296. package/dist/lib/auth-page-styles.js +39 -0
  297. package/dist/lib/auth.js +117 -61
  298. package/dist/lib/collection-cache.js +23 -0
  299. package/dist/lib/draft-scope.d.ts +4 -0
  300. package/dist/lib/draft-scope.js +222 -0
  301. package/dist/lib/html.js +12 -0
  302. package/dist/lib/link-pages.js +154 -0
  303. package/dist/lib/locale-path.d.ts +22 -0
  304. package/dist/lib/locale-path.js +57 -0
  305. package/dist/lib/normalized-data.js +252 -152
  306. package/dist/lib/preview-handoff.js +2 -2
  307. package/dist/lib/queries.d.ts +11 -0
  308. package/dist/lib/queries.js +70 -141
  309. package/dist/lib/studio-reads.d.ts +14 -0
  310. package/dist/lib/studio-reads.js +574 -0
  311. package/dist/lib/use-project-room.js +50 -50
  312. package/dist/lib/use-thread.js +310 -0
  313. package/dist/lib/utils.js +25 -29
  314. package/dist/lib/view-locale-context.js +22 -0
  315. package/dist/lib/view-locale.js +46 -0
  316. package/dist/package.js +5 -0
  317. package/dist/studio-overlays.css +41 -151
  318. package/dist/studio.css +2 -2
  319. package/package.json +70 -73
  320. package/skills/camox/SKILL.md +16 -11
  321. package/skills/camox/references/artifacts.md +179 -0
  322. package/skills/camox/references/block-definitions.md +75 -59
  323. package/skills/camox/references/cli-common.md +9 -11
  324. package/skills/camox/references/cli-content.md +92 -69
  325. package/skills/camox/references/cli-publishing.md +26 -0
  326. package/skills/camox/references/dom-integrations.md +27 -0
  327. package/skills/camox/references/field-styling.md +4 -4
  328. package/skills/camox/references/layout-definitions.md +16 -14
  329. package/skills/camox/references/localization.md +56 -0
  330. package/skills/camox/references/routed-layouts.md +133 -15
  331. package/skills/camox-expand/SKILL.md +69 -0
  332. package/skills/camox-expand/references/jobs.md +39 -0
  333. package/skills/camoxify/SKILL.md +29 -7
  334. package/dist/core/components/AddBlockControlBar.js +0 -78
  335. package/dist/core/hooks/useRequireDraftSource.js +0 -23
  336. package/dist/features/metadata/sitemap.d.ts +0 -8
  337. package/dist/features/metadata/sitemap.js +0 -17
  338. package/dist/features/og/imageResponse.node.d.ts +0 -12
  339. package/dist/features/og/imageResponse.node.js +0 -49
  340. package/dist/features/og/imageResponse.workerd.d.ts +0 -12
  341. package/dist/features/og/imageResponse.workerd.js +0 -40
  342. package/dist/features/page/PublishedPageContent.js +0 -205
  343. package/dist/features/page/PublishedPageExperience.js +0 -23
  344. package/dist/features/preview/DerivedPreview.js +0 -134
  345. package/dist/features/preview/commentsEnabled.js +0 -8
  346. package/dist/features/preview/components/AddBlockSidebar.js +0 -381
  347. package/dist/features/preview/components/AttachedComments.js +0 -331
  348. package/dist/features/preview/components/CommentHeader.js +0 -122
  349. package/dist/features/preview/components/CommentSidebar.js +0 -29
  350. package/dist/features/preview/components/CommentTargetQuote.js +0 -167
  351. package/dist/features/preview/components/DerivedLayoutSidebar.d.ts +0 -5
  352. package/dist/features/preview/components/DerivedLayoutSidebar.js +0 -179
  353. package/dist/features/preview/components/DerivedPageInfoSidebar.js +0 -69
  354. package/dist/features/preview/components/LeftSidebar.js +0 -68
  355. package/dist/features/preview/components/PageEditorSidebar.js +0 -563
  356. package/dist/features/preview/components/PageInfoSidebar.js +0 -1551
  357. package/dist/features/preview/components/PageNavigatorSidebar.js +0 -78
  358. package/dist/features/preview/components/PageStatusBadge.js +0 -97
  359. package/dist/features/preview/components/PeekedBlock.js +0 -217
  360. package/dist/features/preview/components/PreviewComments.js +0 -56
  361. package/dist/features/preview/components/PreviewToolbarSpacer.js +0 -43
  362. package/dist/features/preview/components/PublicationRow.js +0 -422
  363. package/dist/features/preview/components/PublishDialog.js +0 -194
  364. package/dist/features/preview/components/RightSidebar.js +0 -59
  365. package/dist/features/preview/components/SendFeedbackDialog.js +0 -83
  366. package/dist/features/preview/previewCommentsStore.js +0 -135
  367. package/dist/features/preview/publication.js +0 -81
  368. package/dist/features/preview/usePageComments.js +0 -39
  369. package/dist/features/runtime/SharedChromeContext.js +0 -7
  370. package/dist/features/studio/components/EnvironmentMenu.js +0 -425
  371. package/dist/features/vite/appGeneration.js +0 -89
  372. package/dist/lib/view-queries.js +0 -28
  373. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/Matches.d.ts +0 -114
  374. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/RouterProvider.d.ts +0 -25
  375. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/config.d.ts +0 -1
  376. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/defer.d.ts +0 -1
  377. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/fileRoute.d.ts +0 -20
  378. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/global.d.ts +0 -8
  379. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/hash-scroll.d.ts +0 -1
  380. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/index.d.ts +0 -18
  381. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/link.d.ts +0 -161
  382. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/load-matches.d.ts +0 -6
  383. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/location.d.ts +0 -54
  384. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/lru-cache.d.ts +0 -8
  385. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/manifest.d.ts +0 -32
  386. 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
  387. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/not-found.d.ts +0 -1
  388. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/redirect.d.ts +0 -49
  389. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/rewrite.d.ts +0 -1
  390. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/root.d.ts +0 -6
  391. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/route.d.ts +0 -343
  392. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/routeInfo.d.ts +0 -48
  393. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/router.d.ts +0 -695
  394. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/scroll-restoration.d.ts +0 -1
  395. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/searchMiddleware.d.ts +0 -1
  396. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/searchParams.d.ts +0 -5
  397. 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
  398. 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
  399. 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
  400. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/stores.d.ts +0 -69
  401. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/structuralSharing.d.ts +0 -1
  402. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/typePrimitives.d.ts +0 -1
  403. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useLoaderData.d.ts +0 -1
  404. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useLoaderDeps.d.ts +0 -1
  405. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useNavigate.d.ts +0 -10
  406. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useParams.d.ts +0 -1
  407. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useRouteContext.d.ts +0 -1
  408. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/useSearch.d.ts +0 -1
  409. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/utils.d.ts +0 -28
  410. package/dist/node_modules/.pnpm/@tanstack_router-core@1.168.14/node_modules/@tanstack/router-core/dist/esm/validators.d.ts +0 -49
  411. package/skills/camox/references/cli-environments.md +0 -37
@@ -0,0 +1,31 @@
1
+ import { relative } from "node:path";
2
+
3
+ //#region src/features/vite/blockChunks.ts
4
+ const BLOCK_MODULE = /^src\/(camox\/)?blocks\/[^/]+\.tsx?$/;
5
+ /**
6
+ * The chunks the browser must load to render each block, by the block's module path (as the
7
+ * app's globs name it). Chunks every entry already loads are left out: they need no preload.
8
+ */
9
+ function collectBlockChunks(bundle, root) {
10
+ const chunks = /* @__PURE__ */ new Map();
11
+ for (const output of Object.values(bundle)) if (output.type === "chunk") chunks.set(output.fileName, output);
12
+ const withImports = (fileName, into = /* @__PURE__ */ new Set()) => {
13
+ if (into.has(fileName)) return into;
14
+ into.add(fileName);
15
+ for (const imported of chunks.get(fileName)?.imports ?? []) withImports(imported, into);
16
+ return into;
17
+ };
18
+ const loadedByEntries = /* @__PURE__ */ new Set();
19
+ for (const chunk of chunks.values()) if (chunk.isEntry) withImports(chunk.fileName, loadedByEntries);
20
+ const blockChunks = {};
21
+ for (const chunk of chunks.values()) {
22
+ if (!chunk.isDynamicEntry || !chunk.facadeModuleId) continue;
23
+ const modulePath = relative(root, chunk.facadeModuleId).replaceAll("\\", "/");
24
+ if (!BLOCK_MODULE.test(modulePath)) continue;
25
+ blockChunks[`/${modulePath}`] = [...withImports(chunk.fileName)].filter((fileName) => !loadedByEntries.has(fileName));
26
+ }
27
+ return blockChunks;
28
+ }
29
+
30
+ //#endregion
31
+ export { collectBlockChunks };
@@ -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 };