@morphika/webframe 0.1.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 (319) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +46 -0
  3. package/admin/assets.ts +4 -0
  4. package/admin/database.ts +4 -0
  5. package/admin/index.ts +6 -0
  6. package/admin/login.ts +4 -0
  7. package/admin/navigation.ts +4 -0
  8. package/admin/pages-editor.ts +4 -0
  9. package/admin/pages.ts +4 -0
  10. package/admin/projects-editor.ts +4 -0
  11. package/admin/projects.ts +4 -0
  12. package/admin/settings.ts +4 -0
  13. package/admin/setup.ts +4 -0
  14. package/admin/storage.ts +4 -0
  15. package/admin/styles.ts +4 -0
  16. package/app/(site)/[slug]/loading.tsx +20 -0
  17. package/app/(site)/[slug]/page.tsx +83 -0
  18. package/app/(site)/error.tsx +32 -0
  19. package/app/(site)/layout.tsx +53 -0
  20. package/app/(site)/loading.tsx +20 -0
  21. package/app/(site)/not-found.tsx +41 -0
  22. package/app/(site)/page.tsx +43 -0
  23. package/app/(site)/preview/page.tsx +99 -0
  24. package/app/(site)/work/[slug]/loading.tsx +23 -0
  25. package/app/(site)/work/[slug]/page.tsx +84 -0
  26. package/app/admin/assets/page.tsx +573 -0
  27. package/app/admin/database/page.tsx +302 -0
  28. package/app/admin/error.tsx +53 -0
  29. package/app/admin/layout.tsx +273 -0
  30. package/app/admin/login/page.tsx +88 -0
  31. package/app/admin/navigation/page.tsx +157 -0
  32. package/app/admin/page.tsx +17 -0
  33. package/app/admin/pages/[slug]/page.tsx +849 -0
  34. package/app/admin/pages/page.tsx +588 -0
  35. package/app/admin/projects/[slug]/page.tsx +3 -0
  36. package/app/admin/projects/page.tsx +669 -0
  37. package/app/admin/settings/page.tsx +132 -0
  38. package/app/admin/setup/page.tsx +64 -0
  39. package/app/admin/storage/page.tsx +518 -0
  40. package/app/admin/styles/page.tsx +243 -0
  41. package/app/api/admin/assets/file/route.ts +81 -0
  42. package/app/api/admin/assets/health/route.ts +170 -0
  43. package/app/api/admin/assets/register/route.ts +163 -0
  44. package/app/api/admin/assets/registry/route.ts +98 -0
  45. package/app/api/admin/assets/relink/confirm/route.ts +242 -0
  46. package/app/api/admin/assets/relink/route.ts +202 -0
  47. package/app/api/admin/assets/scan/route.ts +271 -0
  48. package/app/api/admin/auth/route.ts +160 -0
  49. package/app/api/admin/custom-sections/[slug]/route.ts +159 -0
  50. package/app/api/admin/custom-sections/route.ts +127 -0
  51. package/app/api/admin/database/route.ts +53 -0
  52. package/app/api/admin/pages/[slug]/duplicate/route.ts +91 -0
  53. package/app/api/admin/pages/[slug]/route.ts +617 -0
  54. package/app/api/admin/pages/[slug]/set-home/route.ts +76 -0
  55. package/app/api/admin/pages/route.ts +129 -0
  56. package/app/api/admin/preview/route.ts +53 -0
  57. package/app/api/admin/r2/connect/route.ts +181 -0
  58. package/app/api/admin/r2/delete/route.ts +198 -0
  59. package/app/api/admin/r2/disconnect/route.ts +42 -0
  60. package/app/api/admin/r2/rename/route.ts +265 -0
  61. package/app/api/admin/r2/status/route.ts +106 -0
  62. package/app/api/admin/r2/upload-url/route.ts +148 -0
  63. package/app/api/admin/revalidate/route.ts +55 -0
  64. package/app/api/admin/settings/route.ts +279 -0
  65. package/app/api/admin/setup/complete/route.ts +51 -0
  66. package/app/api/admin/setup/route.ts +118 -0
  67. package/app/api/admin/storage/switch/route.ts +117 -0
  68. package/app/api/admin/styles/fonts/route.ts +97 -0
  69. package/app/api/admin/styles/route.ts +304 -0
  70. package/app/api/assets/[...path]/route.ts +98 -0
  71. package/app/api/custom-sections/[id]/route.ts +43 -0
  72. package/app/api/draft-mode/disable/route.ts +10 -0
  73. package/app/api/draft-mode/enable/route.ts +26 -0
  74. package/app/api/projects/route.ts +42 -0
  75. package/app/api/styles/route.ts +88 -0
  76. package/app/favicon.ico +0 -0
  77. package/app/globals.css +7 -0
  78. package/app/layout.tsx +53 -0
  79. package/app/robots.ts +17 -0
  80. package/app/sitemap.ts +48 -0
  81. package/app/studio/[[...index]]/page.tsx +8 -0
  82. package/components/admin/MetadataEditor.tsx +173 -0
  83. package/components/admin/PublishToggle.tsx +130 -0
  84. package/components/admin/icons.tsx +40 -0
  85. package/components/admin/nav-builder/NavBuilder.tsx +182 -0
  86. package/components/admin/nav-builder/NavBuilderGrid.tsx +326 -0
  87. package/components/admin/nav-builder/NavGeneralSettings.tsx +275 -0
  88. package/components/admin/nav-builder/NavGridCell.tsx +48 -0
  89. package/components/admin/nav-builder/NavGridItem.tsx +189 -0
  90. package/components/admin/nav-builder/NavItemSettings.tsx +288 -0
  91. package/components/admin/nav-builder/NavItemTypePicker.tsx +102 -0
  92. package/components/admin/nav-builder/NavLivePreview.tsx +125 -0
  93. package/components/admin/nav-builder/NavSettingsFields.tsx +248 -0
  94. package/components/admin/nav-builder/NavSettingsPanel.tsx +127 -0
  95. package/components/admin/nav-builder/index.ts +10 -0
  96. package/components/admin/nav-builder/nav-builder-utils.ts +238 -0
  97. package/components/admin/setup-wizard/BrandingStep.tsx +218 -0
  98. package/components/admin/setup-wizard/DatabaseStep.tsx +331 -0
  99. package/components/admin/setup-wizard/DoneStep.tsx +187 -0
  100. package/components/admin/setup-wizard/SetupWizard.tsx +166 -0
  101. package/components/admin/setup-wizard/StorageStep.tsx +308 -0
  102. package/components/admin/setup-wizard/WelcomeStep.tsx +96 -0
  103. package/components/admin/setup-wizard/index.ts +9 -0
  104. package/components/admin/styles/ColorsEditor.tsx +214 -0
  105. package/components/admin/styles/FontsEditor.tsx +258 -0
  106. package/components/admin/styles/GridLayoutEditor.tsx +292 -0
  107. package/components/admin/styles/LinksButtonsEditor.tsx +120 -0
  108. package/components/admin/styles/TypographyEditor.tsx +266 -0
  109. package/components/admin/styles/index.ts +9 -0
  110. package/components/admin/styles/shared.tsx +68 -0
  111. package/components/blocks/BlockRenderer.tsx +404 -0
  112. package/components/blocks/ButtonBlockRenderer.tsx +52 -0
  113. package/components/blocks/CoverBlockRenderer.tsx +239 -0
  114. package/components/blocks/CustomSectionInstanceRenderer.tsx +82 -0
  115. package/components/blocks/EnterAnimationWrapper.tsx +140 -0
  116. package/components/blocks/HoverAnimationWrapper.tsx +308 -0
  117. package/components/blocks/ImageBlockRenderer.tsx +61 -0
  118. package/components/blocks/ImageGridBlockRenderer.tsx +545 -0
  119. package/components/blocks/PageBackground.tsx +28 -0
  120. package/components/blocks/PageNavAnimation.tsx +35 -0
  121. package/components/blocks/PageNavColor.tsx +24 -0
  122. package/components/blocks/PageRenderer.tsx +142 -0
  123. package/components/blocks/ParallaxGroupRenderer.tsx +448 -0
  124. package/components/blocks/ParallaxSlideRenderer.tsx +175 -0
  125. package/components/blocks/ProjectGridBlockRenderer.tsx +556 -0
  126. package/components/blocks/SectionRenderer.tsx +170 -0
  127. package/components/blocks/SectionV2Renderer.tsx +330 -0
  128. package/components/blocks/ShaderCanvas.tsx +392 -0
  129. package/components/blocks/SpacerBlockRenderer.tsx +17 -0
  130. package/components/blocks/TextBlockRenderer.tsx +87 -0
  131. package/components/blocks/TypewriterRichText.tsx +464 -0
  132. package/components/blocks/TypewriterWrapper.tsx +149 -0
  133. package/components/blocks/VideoBlockRenderer.tsx +304 -0
  134. package/components/blocks/index.ts +2 -0
  135. package/components/builder/AssetBrowser.tsx +2 -0
  136. package/components/builder/BlockLivePreview.tsx +101 -0
  137. package/components/builder/BlockTypePicker.tsx +178 -0
  138. package/components/builder/BuilderCanvas.tsx +354 -0
  139. package/components/builder/CanvasMinimap.tsx +200 -0
  140. package/components/builder/CanvasToolbar.tsx +202 -0
  141. package/components/builder/ColorPicker.tsx +243 -0
  142. package/components/builder/ColorSwatchPicker.tsx +274 -0
  143. package/components/builder/ColumnDragContext.tsx +51 -0
  144. package/components/builder/ColumnDragOverlay.tsx +110 -0
  145. package/components/builder/CustomSectionInstanceCard.tsx +97 -0
  146. package/components/builder/DeviceFrame.tsx +123 -0
  147. package/components/builder/DndWrapper.tsx +337 -0
  148. package/components/builder/InsertionLines.tsx +186 -0
  149. package/components/builder/ParallaxGroupCanvas.tsx +228 -0
  150. package/components/builder/ParallaxSlideHeader.tsx +113 -0
  151. package/components/builder/ReadOnlyFrame.tsx +417 -0
  152. package/components/builder/SectionEditorBar.tsx +288 -0
  153. package/components/builder/SectionTypePicker.tsx +422 -0
  154. package/components/builder/SectionV2Canvas.tsx +297 -0
  155. package/components/builder/SectionV2Column.tsx +488 -0
  156. package/components/builder/SettingsPanel.tsx +911 -0
  157. package/components/builder/SortableBlock.tsx +230 -0
  158. package/components/builder/SortableRow.tsx +362 -0
  159. package/components/builder/VirtualAssetGrid.tsx +397 -0
  160. package/components/builder/asset-browser/AssetBrowser.tsx +178 -0
  161. package/components/builder/asset-browser/FileLightbox.tsx +116 -0
  162. package/components/builder/asset-browser/FolderTreeItem.tsx +55 -0
  163. package/components/builder/asset-browser/R2BrowserContent.tsx +436 -0
  164. package/components/builder/asset-browser/R2ContextMenu.tsx +98 -0
  165. package/components/builder/asset-browser/VideoThumbnail.tsx +63 -0
  166. package/components/builder/asset-browser/helpers.ts +88 -0
  167. package/components/builder/asset-browser/index.ts +1 -0
  168. package/components/builder/asset-browser/types.ts +49 -0
  169. package/components/builder/asset-browser/useAssetBrowser.ts +344 -0
  170. package/components/builder/asset-browser/useR2DragDrop.ts +116 -0
  171. package/components/builder/asset-browser/useR2Operations.ts +189 -0
  172. package/components/builder/blockStyles.tsx +295 -0
  173. package/components/builder/editors/ButtonBlockEditor.tsx +184 -0
  174. package/components/builder/editors/CoverBlockEditor.tsx +488 -0
  175. package/components/builder/editors/EnterAnimationPicker.tsx +297 -0
  176. package/components/builder/editors/HoverEffectPicker.tsx +209 -0
  177. package/components/builder/editors/ImageBlockEditor.tsx +206 -0
  178. package/components/builder/editors/ImageGridBlockEditor.tsx +386 -0
  179. package/components/builder/editors/ProjectGridEditor.tsx +648 -0
  180. package/components/builder/editors/SpacerBlockEditor.tsx +167 -0
  181. package/components/builder/editors/StaggerSettings.tsx +108 -0
  182. package/components/builder/editors/TextAlignmentIcons.tsx +39 -0
  183. package/components/builder/editors/TextBlockEditor.tsx +462 -0
  184. package/components/builder/editors/TextStylePicker.tsx +183 -0
  185. package/components/builder/editors/VideoBlockEditor.tsx +278 -0
  186. package/components/builder/editors/index.ts +10 -0
  187. package/components/builder/editors/shared.tsx +345 -0
  188. package/components/builder/hooks/useColumnDrag.ts +472 -0
  189. package/components/builder/hooks/useColumnResize.ts +221 -0
  190. package/components/builder/index.ts +12 -0
  191. package/components/builder/live-preview/LiveButtonPreview.tsx +38 -0
  192. package/components/builder/live-preview/LiveCoverPreview.tsx +146 -0
  193. package/components/builder/live-preview/LiveImageGridPreview.tsx +123 -0
  194. package/components/builder/live-preview/LiveImagePreview.tsx +107 -0
  195. package/components/builder/live-preview/LiveProjectGridPreview.tsx +1010 -0
  196. package/components/builder/live-preview/LiveSpacerPreview.tsx +9 -0
  197. package/components/builder/live-preview/LiveTextEditor.tsx +198 -0
  198. package/components/builder/live-preview/LiveVideoPreview.tsx +98 -0
  199. package/components/builder/live-preview/index.ts +10 -0
  200. package/components/builder/live-preview/shared.tsx +153 -0
  201. package/components/builder/settings-panel/BlockLayoutTab.tsx +532 -0
  202. package/components/builder/settings-panel/BlockSettings.tsx +94 -0
  203. package/components/builder/settings-panel/ColumnV2Settings.tsx +160 -0
  204. package/components/builder/settings-panel/LayoutTab.tsx +310 -0
  205. package/components/builder/settings-panel/PageSettings.tsx +200 -0
  206. package/components/builder/settings-panel/ParallaxGroupSettings.tsx +118 -0
  207. package/components/builder/settings-panel/ParallaxSlideSettings.tsx +178 -0
  208. package/components/builder/settings-panel/SectionV2AnimationTab.tsx +103 -0
  209. package/components/builder/settings-panel/SectionV2LayoutTab.tsx +312 -0
  210. package/components/builder/settings-panel/SectionV2Settings.tsx +323 -0
  211. package/components/builder/settings-panel/TRBLInputs.tsx +51 -0
  212. package/components/builder/settings-panel/index.ts +19 -0
  213. package/components/builder/settings-panel/responsive-helpers.ts +524 -0
  214. package/components/ui/CustomCursor.tsx +118 -0
  215. package/components/ui/NavContentLightbox.tsx +152 -0
  216. package/components/ui/Navbar.tsx +582 -0
  217. package/components/ui/PortfolioTracker.tsx +87 -0
  218. package/components/ui/ScrollToTop.tsx +47 -0
  219. package/lib/animation/enter-presets.ts +147 -0
  220. package/lib/animation/enter-resolve.ts +90 -0
  221. package/lib/animation/enter-types.ts +128 -0
  222. package/lib/animation/hover-effect-presets.ts +210 -0
  223. package/lib/animation/hover-effect-types.ts +126 -0
  224. package/lib/asset-retry.ts +111 -0
  225. package/lib/assets.ts +92 -0
  226. package/lib/audit.ts +35 -0
  227. package/lib/auth-token.ts +94 -0
  228. package/lib/auth.ts +13 -0
  229. package/lib/builder/cascade-helpers.ts +51 -0
  230. package/lib/builder/cascade.ts +533 -0
  231. package/lib/builder/constants.ts +103 -0
  232. package/lib/builder/defaults.ts +182 -0
  233. package/lib/builder/history.ts +48 -0
  234. package/lib/builder/index.ts +21 -0
  235. package/lib/builder/layout-styles.ts +344 -0
  236. package/lib/builder/masonry.ts +166 -0
  237. package/lib/builder/responsive.ts +156 -0
  238. package/lib/builder/serializer.ts +845 -0
  239. package/lib/builder/store-blocks.ts +193 -0
  240. package/lib/builder/store-canvas.ts +319 -0
  241. package/lib/builder/store-helpers.ts +490 -0
  242. package/lib/builder/store-sections.ts +709 -0
  243. package/lib/builder/store.ts +333 -0
  244. package/lib/builder/templates.ts +297 -0
  245. package/lib/builder/types.ts +374 -0
  246. package/lib/builder/utils.ts +37 -0
  247. package/lib/color-utils.ts +116 -0
  248. package/lib/config/index.ts +57 -0
  249. package/lib/config/types.ts +122 -0
  250. package/lib/contexts/AssetContext.tsx +79 -0
  251. package/lib/contexts/NavAnimationContext.tsx +44 -0
  252. package/lib/contexts/NavColorContext.tsx +38 -0
  253. package/lib/contexts/PageExitContext.tsx +194 -0
  254. package/lib/contexts/ThumbStatusContext.tsx +83 -0
  255. package/lib/csrf-client.ts +34 -0
  256. package/lib/csrf.ts +68 -0
  257. package/lib/format-utils.ts +24 -0
  258. package/lib/hooks/useViewport.ts +42 -0
  259. package/lib/logger.ts +81 -0
  260. package/lib/revalidate.ts +23 -0
  261. package/lib/sanitize.ts +91 -0
  262. package/lib/sanity/client.ts +8 -0
  263. package/lib/sanity/queries.ts +486 -0
  264. package/lib/sanity/types.ts +869 -0
  265. package/lib/sanity/writeClient.ts +24 -0
  266. package/lib/security.ts +402 -0
  267. package/lib/setup/detect.ts +156 -0
  268. package/lib/shader/glsl/index.ts +27 -0
  269. package/lib/shader/glsl/pixelate.ts +51 -0
  270. package/lib/shader/glsl/rgb-shift.ts +45 -0
  271. package/lib/shader/glsl/ripple.ts +46 -0
  272. package/lib/shader/glsl/vertex.ts +14 -0
  273. package/lib/storage/index.ts +211 -0
  274. package/lib/storage/r2-adapter.ts +286 -0
  275. package/lib/storage/types.ts +125 -0
  276. package/lib/styles/provider.tsx +267 -0
  277. package/lib/thumbnails/generate.ts +151 -0
  278. package/lib/utils.ts +6 -0
  279. package/package.json +212 -0
  280. package/sanity/compose.ts +65 -0
  281. package/sanity/sanity.config.ts +126 -0
  282. package/sanity/schemas/assetRegistry.ts +301 -0
  283. package/sanity/schemas/blocks/blockLayout.ts +90 -0
  284. package/sanity/schemas/blocks/buttonBlock.ts +82 -0
  285. package/sanity/schemas/blocks/coverBlock.ts +229 -0
  286. package/sanity/schemas/blocks/imageBlock.ts +58 -0
  287. package/sanity/schemas/blocks/imageGridBlock.ts +112 -0
  288. package/sanity/schemas/blocks/index.ts +9 -0
  289. package/sanity/schemas/blocks/projectGridBlock.ts +251 -0
  290. package/sanity/schemas/blocks/spacerBlock.ts +41 -0
  291. package/sanity/schemas/blocks/textBlock.ts +139 -0
  292. package/sanity/schemas/blocks/videoBlock.ts +80 -0
  293. package/sanity/schemas/customSection.ts +69 -0
  294. package/sanity/schemas/customSectionInstance.ts +163 -0
  295. package/sanity/schemas/index.ts +111 -0
  296. package/sanity/schemas/objects/enterAnimationConfig.ts +72 -0
  297. package/sanity/schemas/objects/hoverEffectConfig.ts +90 -0
  298. package/sanity/schemas/objects/parallaxGroup.ts +66 -0
  299. package/sanity/schemas/objects/parallaxSlide.ts +217 -0
  300. package/sanity/schemas/objects/typewriterConfig.ts +38 -0
  301. package/sanity/schemas/page.ts +162 -0
  302. package/sanity/schemas/pageSection.ts +157 -0
  303. package/sanity/schemas/pageSectionV2.ts +269 -0
  304. package/sanity/schemas/siteSettings.ts +256 -0
  305. package/sanity/schemas/siteStyles.ts +210 -0
  306. package/site/error.ts +4 -0
  307. package/site/index.ts +8 -0
  308. package/site/not-found.ts +4 -0
  309. package/site/page.ts +4 -0
  310. package/site/preview.ts +4 -0
  311. package/site/robots.ts +4 -0
  312. package/site/sitemap.ts +4 -0
  313. package/site/work.ts +4 -0
  314. package/studio/index.ts +4 -0
  315. package/styles/admin.css +85 -0
  316. package/styles/animations.css +237 -0
  317. package/styles/base.css +148 -0
  318. package/styles/globals.css +10 -0
  319. package/tsconfig.json +25 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Morphika Studio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # @morphika/webframe
2
+
3
+ Core library for the **Visual Page Builder** — a reusable website builder built on Next.js and Sanity.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ npx create-visual-builder my-site
9
+ cd my-site
10
+ npm run dev
11
+ ```
12
+
13
+ Then open `http://localhost:3000/admin` to run the setup wizard.
14
+
15
+ ## What's Included
16
+
17
+ - Visual page builder with drag-and-drop sections, columns, and blocks
18
+ - 7 content block types (text, image, image grid, video, spacer, button, cover)
19
+ - Project grid with masonry layout
20
+ - Parallax scroll sections
21
+ - Custom reusable sections
22
+ - Navigation builder (12-column grid)
23
+ - Asset management with Cloudflare R2 storage
24
+ - Enter animations, hover effects, and page transitions
25
+ - Global styles editor (typography, colors, grid)
26
+ - Responsive design with desktop/tablet/phone viewports
27
+ - SEO metadata per page
28
+ - Sanity CMS backend
29
+
30
+ ## Documentation
31
+
32
+ - [Getting Started](https://github.com/MorphikaStudio/morphika-web-builder/blob/main/docs/GETTING-STARTED.md)
33
+ - [Configuration Reference](https://github.com/MorphikaStudio/morphika-web-builder/blob/main/docs/CONFIGURATION.md)
34
+ - [Customization Guide](https://github.com/MorphikaStudio/morphika-web-builder/blob/main/docs/CUSTOMIZATION.md)
35
+ - [Deployment Guide](https://github.com/MorphikaStudio/morphika-web-builder/blob/main/docs/DEPLOYMENT.md)
36
+
37
+ ## Requirements
38
+
39
+ - Node.js 22+
40
+ - Next.js 16+
41
+ - React 19+
42
+ - Sanity v3 project
43
+
44
+ ## License
45
+
46
+ MIT
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @morphika/webframe/admin/assets — Asset browser page.
3
+ */
4
+ export { default as AssetsPage } from "../app/admin/assets/page";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @morphika/webframe/admin/database — Database configurator page.
3
+ */
4
+ export { default as DatabasePage } from "../app/admin/database/page";
package/admin/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @morphika/webframe/admin — Admin page components.
3
+ * Re-exports from the monolith source during the transition period.
4
+ */
5
+ export { default as AdminLayout } from "../app/admin/layout";
6
+ export { default as AdminDashboard } from "../app/admin/page";
package/admin/login.ts ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @morphika/webframe/admin/login — Admin login page.
3
+ */
4
+ export { default as LoginPage } from "../app/admin/login/page";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @morphika/webframe/admin/navigation — Navigation builder page.
3
+ */
4
+ export { default as NavigationPage } from "../app/admin/navigation/page";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @morphika/webframe/admin/pages/editor — Page editor page.
3
+ */
4
+ export { default as PageEditorPage } from "../app/admin/pages/[slug]/page";
package/admin/pages.ts ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @morphika/webframe/admin/pages — Pages list page.
3
+ */
4
+ export { default as PagesListPage } from "../app/admin/pages/page";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @morphika/webframe/admin/projects/editor — Project editor page.
3
+ */
4
+ export { default as ProjectEditorPage } from "../app/admin/projects/[slug]/page";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @morphika/webframe/admin/projects — Projects list page.
3
+ */
4
+ export { default as ProjectsPage } from "../app/admin/projects/page";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @morphika/webframe/admin/settings — Site settings page.
3
+ */
4
+ export { default as AdminSettingsPage } from "../app/admin/settings/page";
package/admin/setup.ts ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @morphika/webframe/admin/setup — Setup wizard page re-export.
3
+ */
4
+ export { default } from "../app/admin/setup/page";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @morphika/webframe/admin/storage — Storage management page.
3
+ */
4
+ export { default as StoragePage } from "../app/admin/storage/page";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @morphika/webframe/admin/styles — Styles editor page.
3
+ */
4
+ export { default as StylesPage } from "../app/admin/styles/page";
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Loading skeleton for dynamic pages.
3
+ * Shown by Next.js while page content loads.
4
+ */
5
+ export default function PageLoading() {
6
+ return (
7
+ <div className="min-h-screen bg-brand-dark" aria-busy="true" aria-label="Loading page">
8
+ {/* Hero placeholder */}
9
+ <div className="w-full h-[50vh] bg-neutral-900 animate-pulse" />
10
+ {/* Content placeholders */}
11
+ <div className="max-w-3xl mx-auto px-6 py-16 space-y-6">
12
+ <div className="h-8 w-1/2 bg-neutral-800 rounded animate-pulse" />
13
+ <div className="h-4 w-full bg-neutral-800/60 rounded animate-pulse" />
14
+ <div className="h-4 w-4/5 bg-neutral-800/60 rounded animate-pulse" />
15
+ <div className="h-96 w-full bg-neutral-800/40 rounded animate-pulse mt-8" />
16
+ <div className="h-4 w-3/4 bg-neutral-800/60 rounded animate-pulse" />
17
+ </div>
18
+ </div>
19
+ );
20
+ }
@@ -0,0 +1,83 @@
1
+ import { notFound } from "next/navigation";
2
+ import type { Metadata } from "next";
3
+ import { client } from "../../../lib/sanity/client";
4
+ import { publishedPageBySlugQuery, allPageSlugsQuery } from "../../../lib/sanity/queries";
5
+ import type { Page } from "../../../lib/sanity/types";
6
+ import { PageRenderer } from "../../../components/blocks";
7
+ import { getSiteConfig } from "../../../lib/config";
8
+ import { assetUrl } from "../../../lib/assets";
9
+
10
+ const cfg = getSiteConfig();
11
+
12
+ // ISR: cache for 1 hour, revalidate in background on next request.
13
+ export const revalidate = 3600;
14
+
15
+ interface PageProps {
16
+ params: Promise<{ slug: string }>;
17
+ }
18
+
19
+ async function getPageBySlug(slug: string): Promise<Page | null> {
20
+ try {
21
+ return await client.fetch<Page>(publishedPageBySlugQuery, { slug });
22
+ } catch (error) {
23
+ console.error(`[Page] Failed to fetch page "${slug}":`, error);
24
+ return null;
25
+ }
26
+ }
27
+
28
+ export async function generateStaticParams() {
29
+ try {
30
+ const slugs: string[] = await client.fetch(allPageSlugsQuery);
31
+ return slugs.map((slug) => ({ slug }));
32
+ } catch {
33
+ return [];
34
+ }
35
+ }
36
+
37
+ export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
38
+ const { slug } = await params;
39
+ const page = await getPageBySlug(slug);
40
+
41
+ if (!page) return {};
42
+
43
+ const title = page.metadata?.seo_title || page.title;
44
+ const description = page.metadata?.seo_description || cfg.defaults.metaDescription;
45
+ const ogImagePath = page.metadata?.og_image_path || page.thumbnail_path;
46
+ const ogImage = ogImagePath ? assetUrl(ogImagePath) : undefined;
47
+ const url = `${cfg.domain}/${slug}`;
48
+
49
+ return {
50
+ title,
51
+ description,
52
+ alternates: { canonical: url },
53
+ openGraph: {
54
+ title,
55
+ description,
56
+ url,
57
+ siteName: cfg.name,
58
+ type: "website",
59
+ ...(ogImage && { images: [{ url: ogImage }] }),
60
+ },
61
+ twitter: {
62
+ card: "summary_large_image",
63
+ title,
64
+ description,
65
+ ...(ogImage && { images: [ogImage] }),
66
+ },
67
+ };
68
+ }
69
+
70
+ export default async function DynamicPage({ params }: PageProps) {
71
+ const { slug } = await params;
72
+ const page = await getPageBySlug(slug);
73
+
74
+ if (!page) {
75
+ notFound();
76
+ }
77
+
78
+ return (
79
+ <main className="min-h-screen">
80
+ <PageRenderer page={page} />
81
+ </main>
82
+ );
83
+ }
@@ -0,0 +1,32 @@
1
+ "use client";
2
+
3
+ import { useEffect } from "react";
4
+
5
+ export default function SiteError({
6
+ error,
7
+ reset,
8
+ }: {
9
+ error: Error & { digest?: string };
10
+ reset: () => void;
11
+ }) {
12
+ useEffect(() => {
13
+ console.error("[SiteError]", error);
14
+ }, [error]);
15
+
16
+ return (
17
+ <div className="min-h-[80vh] flex flex-col items-center justify-center px-6 text-center">
18
+ <h1 className="text-5xl font-bold tracking-tight text-white">
19
+ Something went wrong
20
+ </h1>
21
+ <p className="mt-4 text-lg text-white/60 max-w-md">
22
+ An unexpected error occurred. Please try again.
23
+ </p>
24
+ <button
25
+ onClick={reset}
26
+ className="mt-8 inline-flex items-center gap-2 px-6 py-3 rounded-full text-sm font-medium bg-white text-black transition-colors hover:bg-white/80"
27
+ >
28
+ Try again
29
+ </button>
30
+ </div>
31
+ );
32
+ }
@@ -0,0 +1,53 @@
1
+ import { client } from "../../lib/sanity/client";
2
+ import { siteSettingsQuery } from "../../lib/sanity/queries";
3
+ import type { SiteSettings } from "../../lib/sanity/types";
4
+ import { NavColorProvider } from "../../lib/contexts/NavColorContext";
5
+ import { NavAnimationProvider } from "../../lib/contexts/NavAnimationContext";
6
+ import { PageExitProvider } from "../../lib/contexts/PageExitContext";
7
+ import { StylesProvider } from "../../lib/styles/provider";
8
+ import { getSiteConfig } from "../../lib/config";
9
+ import Navbar from "../../components/ui/Navbar";
10
+ import CustomCursor from "../../components/ui/CustomCursor";
11
+ import ScrollToTop from "../../components/ui/ScrollToTop";
12
+ import PortfolioTracker from "../../components/ui/PortfolioTracker";
13
+
14
+ async function getSiteSettings(): Promise<SiteSettings | null> {
15
+ try {
16
+ return await client.fetch<SiteSettings>(siteSettingsQuery);
17
+ } catch (error) {
18
+ console.error("[SiteLayout] Failed to fetch site settings:", error);
19
+ return null;
20
+ }
21
+ }
22
+
23
+ export default async function SiteLayout({
24
+ children,
25
+ }: {
26
+ children: React.ReactNode;
27
+ }) {
28
+ const settings = await getSiteSettings();
29
+ const navColor = settings?.nav_design?.color || "yellow-lime";
30
+
31
+ const cfg = getSiteConfig();
32
+
33
+ return (
34
+ <StylesProvider>
35
+ <NavColorProvider initialColor={navColor}>
36
+ <NavAnimationProvider>
37
+ <PageExitProvider>
38
+ <div data-site {...(cfg.features.customCursor ? { "data-custom-cursor": true } : {})}>
39
+ <Navbar
40
+ navItems={settings?.nav_items}
41
+ design={settings?.nav_design}
42
+ />
43
+ <main className="flex-1">{children}</main>
44
+ {cfg.features.customCursor && <CustomCursor />}
45
+ <ScrollToTop />
46
+ {cfg.features.portfolioTracking && <PortfolioTracker />}
47
+ </div>
48
+ </PageExitProvider>
49
+ </NavAnimationProvider>
50
+ </NavColorProvider>
51
+ </StylesProvider>
52
+ );
53
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Loading skeleton for public site pages.
3
+ * Shown by Next.js while dynamic page content loads.
4
+ */
5
+ export default function SiteLoading() {
6
+ return (
7
+ <div className="min-h-screen bg-brand-dark" aria-busy="true" aria-label="Loading page">
8
+ {/* Hero placeholder */}
9
+ <div className="w-full h-[60vh] bg-neutral-900 animate-pulse" />
10
+ {/* Content placeholders */}
11
+ <div className="max-w-3xl mx-auto px-6 py-16 space-y-6">
12
+ <div className="h-8 w-2/3 bg-neutral-800 rounded animate-pulse" />
13
+ <div className="h-4 w-full bg-neutral-800/60 rounded animate-pulse" />
14
+ <div className="h-4 w-5/6 bg-neutral-800/60 rounded animate-pulse" />
15
+ <div className="h-4 w-4/6 bg-neutral-800/60 rounded animate-pulse" />
16
+ <div className="h-64 w-full bg-neutral-800/40 rounded animate-pulse mt-8" />
17
+ </div>
18
+ </div>
19
+ );
20
+ }
@@ -0,0 +1,41 @@
1
+ import Link from "next/link";
2
+ import { getSiteConfig } from "../../lib/config";
3
+
4
+ const cfg = getSiteConfig();
5
+
6
+ export default function NotFound() {
7
+ return (
8
+ <div className="min-h-[80vh] flex flex-col items-center justify-center px-6 text-center">
9
+ <h1
10
+ className="text-[120px] leading-none font-bold tracking-tighter"
11
+ style={{ color: cfg.palette.accent }}
12
+ >
13
+ 404
14
+ </h1>
15
+ <p className="mt-4 text-lg text-white/60 max-w-md">
16
+ The page you&apos;re looking for doesn&apos;t exist or has been moved.
17
+ </p>
18
+ <Link
19
+ href="/"
20
+ className="mt-8 inline-flex items-center gap-2 px-6 py-3 rounded-full text-sm font-medium transition-colors hover:opacity-80"
21
+ style={{
22
+ backgroundColor: cfg.palette.primary,
23
+ color: cfg.palette.text,
24
+ }}
25
+ >
26
+ <svg
27
+ width="16"
28
+ height="16"
29
+ viewBox="0 0 24 24"
30
+ fill="none"
31
+ stroke="currentColor"
32
+ strokeWidth="2"
33
+ aria-hidden="true"
34
+ >
35
+ <polyline points="15 18 9 12 15 6" />
36
+ </svg>
37
+ Back to Home
38
+ </Link>
39
+ </div>
40
+ );
41
+ }
@@ -0,0 +1,43 @@
1
+ import { client } from "../../lib/sanity/client";
2
+ import { homePageQuery } from "../../lib/sanity/queries";
3
+ import type { Page } from "../../lib/sanity/types";
4
+ import { PageRenderer } from "../../components/blocks";
5
+ import { getSiteConfig } from "../../lib/config";
6
+
7
+ // ISR: cache for 1 hour, revalidate in background on next request.
8
+ export const revalidate = 3600;
9
+
10
+ async function getHomePage(): Promise<Page | null> {
11
+ try {
12
+ return await client.fetch<Page>(homePageQuery);
13
+ } catch (error) {
14
+ console.error("[Home] Failed to fetch home page:", error);
15
+ return null;
16
+ }
17
+ }
18
+
19
+ export default async function HomePage() {
20
+ const page = await getHomePage();
21
+
22
+ if (!page) {
23
+ const cfg = getSiteConfig();
24
+ return (
25
+ <main className="flex min-h-screen items-center justify-center bg-brand-dark">
26
+ <div className="text-center">
27
+ <h1 className="font-mono text-2xl uppercase tracking-widest text-brand-accent">
28
+ {cfg.name}
29
+ </h1>
30
+ <p className="mt-4 font-mono text-sm uppercase tracking-wider text-brand-muted">
31
+ Coming Soon
32
+ </p>
33
+ </div>
34
+ </main>
35
+ );
36
+ }
37
+
38
+ return (
39
+ <main className="min-h-screen">
40
+ <PageRenderer page={page} />
41
+ </main>
42
+ );
43
+ }
@@ -0,0 +1,99 @@
1
+ "use client";
2
+
3
+ /**
4
+ * Preview Page — renders any page (including drafts) for the builder's
5
+ * "Preview" button. Fetches from /api/admin/preview with slug.
6
+ *
7
+ * URL pattern:
8
+ * /preview?slug=about → preview by slug
9
+ *
10
+ * Renders inside the (site) layout so it includes Navbar, Footer,
11
+ * custom cursor, etc. — exactly like the production page.
12
+ */
13
+
14
+ import { useSearchParams } from "next/navigation";
15
+ import { useEffect, useState, Suspense } from "react";
16
+ import type { Page } from "../../../lib/sanity/types";
17
+ import { PageRenderer } from "../../../components/blocks";
18
+ import { AssetProvider } from "../../../lib/contexts/AssetContext";
19
+
20
+ function PreviewContent() {
21
+ const searchParams = useSearchParams();
22
+ const slug = searchParams.get("slug");
23
+
24
+ const [page, setPage] = useState<Page | null>(null);
25
+ const [error, setError] = useState<string | null>(null);
26
+ const [loading, setLoading] = useState(true);
27
+
28
+ useEffect(() => {
29
+ if (!slug) {
30
+ setError("Missing ?slug= parameter");
31
+ setLoading(false);
32
+ return;
33
+ }
34
+
35
+ const params = new URLSearchParams();
36
+ params.set("slug", slug);
37
+
38
+ fetch(`/api/admin/preview?${params}`)
39
+ .then((res) => {
40
+ if (!res.ok) throw new Error(`Failed to load (${res.status})`);
41
+ return res.json();
42
+ })
43
+ .then((data) => {
44
+ setPage(data.page);
45
+ setLoading(false);
46
+ })
47
+ .catch((err) => {
48
+ setError(err.message);
49
+ setLoading(false);
50
+ });
51
+ }, [slug]);
52
+
53
+ if (loading) {
54
+ return (
55
+ <div className="flex min-h-[50vh] items-center justify-center">
56
+ <p className="font-mono text-sm text-neutral-400 animate-pulse">
57
+ Loading preview...
58
+ </p>
59
+ </div>
60
+ );
61
+ }
62
+
63
+ if (error || !page) {
64
+ return (
65
+ <div className="flex min-h-[50vh] items-center justify-center">
66
+ <div className="text-center">
67
+ <p className="font-mono text-sm text-[var(--admin-error)]">
68
+ {error || "Page not found"}
69
+ </p>
70
+ <p className="mt-2 font-mono text-xs text-neutral-400">
71
+ Make sure the page has been saved at least once.
72
+ </p>
73
+ </div>
74
+ </div>
75
+ );
76
+ }
77
+
78
+ return (
79
+ <AssetProvider mode="admin">
80
+ <PageRenderer page={page} />
81
+ </AssetProvider>
82
+ );
83
+ }
84
+
85
+ export default function PreviewPage() {
86
+ return (
87
+ <Suspense
88
+ fallback={
89
+ <div className="flex min-h-[50vh] items-center justify-center">
90
+ <p className="font-mono text-sm text-neutral-400 animate-pulse">
91
+ Loading preview...
92
+ </p>
93
+ </div>
94
+ }
95
+ >
96
+ <PreviewContent />
97
+ </Suspense>
98
+ );
99
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Loading skeleton for project detail pages.
3
+ * Shown by Next.js while project content loads.
4
+ */
5
+ export default function ProjectLoading() {
6
+ return (
7
+ <div className="min-h-screen bg-brand-dark" aria-busy="true" aria-label="Loading project">
8
+ {/* Hero placeholder */}
9
+ <div className="w-full h-[80vh] bg-neutral-900 animate-pulse" />
10
+ {/* Content placeholders */}
11
+ <div className="max-w-4xl mx-auto px-6 py-16 space-y-8">
12
+ <div className="h-6 w-1/3 bg-neutral-800 rounded animate-pulse" />
13
+ <div className="h-4 w-2/3 bg-neutral-800/60 rounded animate-pulse" />
14
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6 mt-8">
15
+ <div className="h-72 bg-neutral-800/40 rounded animate-pulse" />
16
+ <div className="h-72 bg-neutral-800/40 rounded animate-pulse" />
17
+ </div>
18
+ <div className="h-4 w-full bg-neutral-800/60 rounded animate-pulse" />
19
+ <div className="h-4 w-5/6 bg-neutral-800/60 rounded animate-pulse" />
20
+ </div>
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,84 @@
1
+ import { notFound } from "next/navigation";
2
+ import type { Metadata } from "next";
3
+ import { client } from "../../../../lib/sanity/client";
4
+ import { publishedProjectBySlugQuery, allProjectSlugsQuery } from "../../../../lib/sanity/queries";
5
+ import type { Page } from "../../../../lib/sanity/types";
6
+ import { PageRenderer } from "../../../../components/blocks";
7
+ import { getSiteConfig } from "../../../../lib/config";
8
+ import { assetUrl } from "../../../../lib/assets";
9
+
10
+ const cfg = getSiteConfig();
11
+
12
+ // ISR: cache for 1 hour, revalidate in background on next request.
13
+ // Replaces force-dynamic — reduces SSR invocations by ~95%.
14
+ export const revalidate = 3600;
15
+
16
+ interface ProjectPageProps {
17
+ params: Promise<{ slug: string }>;
18
+ }
19
+
20
+ async function getProjectPage(slug: string): Promise<Page | null> {
21
+ try {
22
+ return await client.fetch<Page>(publishedProjectBySlugQuery, { slug });
23
+ } catch (error) {
24
+ console.error(`[Project] Failed to fetch project "${slug}":`, error);
25
+ return null;
26
+ }
27
+ }
28
+
29
+ export async function generateStaticParams() {
30
+ try {
31
+ const slugs = await client.fetch<string[]>(allProjectSlugsQuery);
32
+ return slugs.map((slug) => ({ slug }));
33
+ } catch {
34
+ return [];
35
+ }
36
+ }
37
+
38
+ export async function generateMetadata({ params }: ProjectPageProps): Promise<Metadata> {
39
+ const { slug } = await params;
40
+ const page = await getProjectPage(slug);
41
+
42
+ if (!page) return {};
43
+
44
+ const title = page.metadata?.seo_title || `${page.title} — ${cfg.name}`;
45
+ const description = page.metadata?.seo_description || cfg.defaults.metaDescription;
46
+ const ogImagePath = page.metadata?.og_image_path || page.thumbnail_path;
47
+ const ogImage = ogImagePath ? assetUrl(ogImagePath) : undefined;
48
+ const url = `${cfg.domain}/work/${slug}`;
49
+
50
+ return {
51
+ title,
52
+ description,
53
+ alternates: { canonical: url },
54
+ openGraph: {
55
+ title,
56
+ description,
57
+ url,
58
+ siteName: cfg.name,
59
+ type: "article",
60
+ ...(ogImage && { images: [{ url: ogImage }] }),
61
+ },
62
+ twitter: {
63
+ card: "summary_large_image",
64
+ title,
65
+ description,
66
+ ...(ogImage && { images: [ogImage] }),
67
+ },
68
+ };
69
+ }
70
+
71
+ export default async function ProjectPage({ params }: ProjectPageProps) {
72
+ const { slug } = await params;
73
+ const page = await getProjectPage(slug);
74
+
75
+ if (!page) {
76
+ notFound();
77
+ }
78
+
79
+ return (
80
+ <main className="min-h-screen">
81
+ <PageRenderer page={page} />
82
+ </main>
83
+ );
84
+ }