@morphika/andami 0.1.2

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 +50 -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 +212 -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
@@ -0,0 +1,182 @@
1
+ import type { ContentBlock, ParallaxSlideV2, SectionV2Settings } from "../../lib/sanity/types";
2
+ import type { BlockType } from "./types";
3
+ import { generateKey } from "./utils";
4
+ import type { EnterAnimationConfig, TypewriterConfig } from "../../lib/animation/enter-types";
5
+ import type { HoverEffectConfig } from "../../lib/animation/hover-effect-types";
6
+
7
+ // ============================================
8
+ // Parallax slide defaults (Session 128)
9
+ // ============================================
10
+
11
+ /** Default section settings for a new parallax slide */
12
+ export const DEFAULT_PARALLAX_SLIDE_SETTINGS: SectionV2Settings = {
13
+ preset: "full",
14
+ grid_columns: 12,
15
+ col_gap: 20,
16
+ row_gap: 20,
17
+ spacing_top: "0",
18
+ spacing_bottom: "0",
19
+ };
20
+
21
+ /** Create a new empty parallax slide with default values */
22
+ export function createDefaultParallaxSlide(): ParallaxSlideV2 {
23
+ return {
24
+ _key: generateKey(),
25
+ _type: "parallaxSlide",
26
+ background_type: "image",
27
+ background_position: "center center",
28
+ background_overlay_color: "#000000",
29
+ background_overlay_opacity: 0,
30
+ columns: [{
31
+ _key: generateKey(),
32
+ grid_column: 1,
33
+ grid_row: 1,
34
+ span: 12,
35
+ blocks: [],
36
+ }],
37
+ section_settings: { ...DEFAULT_PARALLAX_SLIDE_SETTINGS },
38
+ };
39
+ }
40
+
41
+ // ============================================
42
+ // Default animation configs per block type (Session 117)
43
+ // ============================================
44
+
45
+ /**
46
+ * Default enter animation configs per block type.
47
+ * Returns undefined for block types with no block-specific enter animation.
48
+ * Generic cascade (page → section → column) applies when block has no specific config.
49
+ */
50
+ export function getDefaultEnterAnimation(_blockType: BlockType): EnterAnimationConfig | undefined {
51
+ // No defaults — blocks start with no block-specific enter animation.
52
+ // They inherit from the generic cascade (column → section → page).
53
+ // Users opt-in via the Animation tab in the builder.
54
+ return undefined;
55
+ }
56
+
57
+ /**
58
+ * Default hover effect configs per block type.
59
+ * Returns undefined for block types with no hover effects.
60
+ */
61
+ export function getDefaultHoverEffect(_blockType: BlockType): HoverEffectConfig | undefined {
62
+ // No defaults — blocks start with no hover effect.
63
+ // Users opt-in via the Animation tab in the builder.
64
+ return undefined;
65
+ }
66
+
67
+ /**
68
+ * Default typewriter config (for textBlock when enter preset is "typewriter").
69
+ */
70
+ export const DEFAULT_TYPEWRITER_CONFIG: Required<TypewriterConfig> = {
71
+ speed: 40,
72
+ mode: "character",
73
+ };
74
+
75
+ /**
76
+ * Create a new block with sensible defaults for the given block type.
77
+ */
78
+ export function createDefaultBlock(blockType: BlockType): ContentBlock {
79
+ const _key = generateKey();
80
+
81
+ switch (blockType) {
82
+ case "textBlock":
83
+ return {
84
+ _type: "textBlock",
85
+ _key,
86
+ text: [],
87
+ style: { fontSize: 14, alignment: "left", fontWeight: "400" },
88
+ };
89
+ case "imageBlock":
90
+ return {
91
+ _type: "imageBlock",
92
+ _key,
93
+ asset_path: "",
94
+ alt: "",
95
+ width: "full",
96
+ aspect_ratio: "auto",
97
+ lazy: true,
98
+ shadow: false,
99
+ border_radius: "",
100
+ };
101
+ case "imageGridBlock":
102
+ return {
103
+ _type: "imageGridBlock",
104
+ _key,
105
+ images: [],
106
+ h_gutter: 10,
107
+ v_gutter: 10,
108
+ images_per_row: 2,
109
+ random_grid: "disabled",
110
+ random_seed: 1,
111
+ lightbox: false,
112
+ object_fit: "cover",
113
+ };
114
+ case "videoBlock":
115
+ return {
116
+ _type: "videoBlock",
117
+ _key,
118
+ video_type: "vimeo",
119
+ url_or_path: "",
120
+ autoplay: false,
121
+ loop: false,
122
+ muted: true,
123
+ controls: true,
124
+ aspect_ratio: "16:9",
125
+ };
126
+ case "spacerBlock":
127
+ return {
128
+ _type: "spacerBlock",
129
+ _key,
130
+ height: "medium",
131
+ };
132
+ case "buttonBlock":
133
+ return {
134
+ _type: "buttonBlock",
135
+ _key,
136
+ text: "Button",
137
+ url: "#",
138
+ style: "primary",
139
+ size: "medium",
140
+ };
141
+ case "coverBlock":
142
+ return {
143
+ _type: "coverBlock",
144
+ _key,
145
+ headline: "",
146
+ subheadline: "",
147
+ media_type: "image",
148
+ media_path: "",
149
+ background_size: "cover",
150
+ background_position: "center center",
151
+ background_repeat: "no-repeat",
152
+ overlay: "dark",
153
+ overlay_opacity: 50,
154
+ content_align_h: "center",
155
+ content_align_v: "center",
156
+ content_max_width: "800px",
157
+ height: "100vh",
158
+ mobile_height: "same",
159
+ text_color: "#ffffff",
160
+ show_scroll_indicator: false,
161
+ };
162
+ case "projectGridBlock":
163
+ return {
164
+ _type: "projectGridBlock",
165
+ _key,
166
+ columns: 3,
167
+ aspect_ratios: ["16/9"],
168
+ gap_v: 16,
169
+ gap_h: 16,
170
+ hover_effect: "scale",
171
+ show_subtitle: true,
172
+ border_radius: 0,
173
+ video_mode: "off",
174
+ projects: [],
175
+ };
176
+ default:
177
+ return {
178
+ _type: blockType,
179
+ _key,
180
+ } as ContentBlock;
181
+ }
182
+ }
@@ -0,0 +1,48 @@
1
+ // ============================================
2
+ // Undo/Redo History for Builder Store
3
+ // ============================================
4
+ // Custom Zustand middleware that captures snapshots of rows + pageSettings state.
5
+ // Stores up to MAX_HISTORY snapshots for undo, and a separate redo stack.
6
+ //
7
+ // BUG-010 fix (Session 78): Snapshots now include pageSettings alongside rows
8
+ // so that undo/redo correctly reverts page-level changes (colors, nav_color, animations).
9
+
10
+ import type { ContentItem } from "../../lib/sanity/types";
11
+ import type { PageSettings } from "./types";
12
+
13
+ export const MAX_HISTORY = 50;
14
+
15
+ /** A single snapshot captures both content rows and page-level settings. */
16
+ export interface HistorySnapshot {
17
+ rows: ContentItem[];
18
+ pageSettings: PageSettings;
19
+ }
20
+
21
+ export interface HistoryState {
22
+ /** Past snapshots (for undo). Last element = most recent past state. */
23
+ _history: HistorySnapshot[];
24
+ /** Future snapshots (for redo). Last element = next redo. */
25
+ _future: HistorySnapshot[];
26
+ /** Whether we're currently applying undo/redo (skip snapshot). */
27
+ _isTimeTraveling: boolean;
28
+ }
29
+
30
+ export const initialHistoryState: HistoryState = {
31
+ _history: [],
32
+ _future: [],
33
+ _isTimeTraveling: false,
34
+ };
35
+
36
+ /**
37
+ * Push a snapshot onto the history stack.
38
+ */
39
+ export function pushSnapshot(
40
+ history: HistorySnapshot[],
41
+ snapshot: HistorySnapshot
42
+ ): HistorySnapshot[] {
43
+ const next = [...history, structuredClone(snapshot)];
44
+ if (next.length > MAX_HISTORY) {
45
+ next.shift();
46
+ }
47
+ return next;
48
+ }
@@ -0,0 +1,21 @@
1
+ export { useBuilderStore } from "./store";
2
+ export { stateToDocument, documentToState } from "./serializer";
3
+ export { createDefaultBlock } from "./defaults";
4
+ export { generateKey } from "./utils";
5
+ export {
6
+ moveBlockInState,
7
+ resizeColumnV2LeftInState,
8
+ addSectionV2InState,
9
+ moveColumnV2InState,
10
+ } from "./store-helpers";
11
+ export { BLOCK_TYPE_REGISTRY, ALL_BLOCK_INFO, SECTION_TYPE_REGISTRY, isSectionBlockType } from "./types";
12
+ export type {
13
+ BuilderStore,
14
+ BuilderState,
15
+ BuilderActions,
16
+ BlockType,
17
+ BlockTypeInfo,
18
+ SectionType,
19
+ SectionTypeInfo,
20
+ SectionBlockType,
21
+ } from "./types";
@@ -0,0 +1,344 @@
1
+ /**
2
+ * Shared layout style helpers for row and block rendering.
3
+ *
4
+ * Converts layout properties (spacing, background, offset, border)
5
+ * into React.CSSProperties objects. Used by:
6
+ * - RowRenderer (public site)
7
+ * - SortableRow (builder active frame)
8
+ * - ReadOnlyFrame (builder read-only frames)
9
+ * - BlockRenderer (public site — block wrapper)
10
+ * - BlockLivePreview (builder — block wrapper)
11
+ */
12
+
13
+ import type { BlockLayout } from "../../lib/sanity/types";
14
+ import { hexToRgba } from "../../lib/color-utils";
15
+
16
+ /** Row-level settings shape (section settings, V2 settings, etc.) */
17
+ type RowSettings = LayoutProps & { padding?: string };
18
+
19
+ /**
20
+ * Common layout shape — both RowSettings and BlockLayout share these fields.
21
+ */
22
+ type LayoutProps = {
23
+ spacing_top?: string;
24
+ spacing_right?: string;
25
+ spacing_bottom?: string;
26
+ spacing_left?: string;
27
+ background_color?: string;
28
+ background_opacity?: number;
29
+ background_image?: string;
30
+ background_size?: string;
31
+ background_position?: string;
32
+ background_repeat?: string;
33
+ offset_top?: string;
34
+ offset_right?: string;
35
+ offset_bottom?: string;
36
+ offset_left?: string;
37
+ border_color?: string;
38
+ border_width?: string;
39
+ border_style?: string;
40
+ border_sides?: string;
41
+ border_radius?: string;
42
+ };
43
+
44
+ /**
45
+ * Convert a TRBL px string value to a CSS px string.
46
+ * Returns undefined if value is falsy or "0".
47
+ */
48
+ function px(value?: string): string | undefined {
49
+ if (!value || value === "0") return undefined;
50
+ return `${value}px`;
51
+ }
52
+
53
+ /**
54
+ * Legacy padding enum → px mapping. Kept for backward-compat with rows
55
+ * that still use the old `padding` enum but haven't been migrated to TRBL yet.
56
+ */
57
+ const LEGACY_PADDING_PX: Record<string, string> = {
58
+ none: "0",
59
+ small: "16",
60
+ medium: "32",
61
+ large: "64",
62
+ xlarge: "96",
63
+ };
64
+
65
+ /**
66
+ * Build spacing (padding) styles from fine-grained TRBL values.
67
+ * Always uses TRBL values as the source of truth.
68
+ * If no TRBL is set, falls back to resolving the legacy padding enum to TRBL.
69
+ */
70
+ export function getSpacingStyles(s: LayoutProps & { padding?: string }): React.CSSProperties {
71
+ // If explicit TRBL values exist, use them directly
72
+ if (hasCustomSpacing(s)) {
73
+ return {
74
+ paddingTop: px(s.spacing_top),
75
+ paddingRight: px(s.spacing_right),
76
+ paddingBottom: px(s.spacing_bottom),
77
+ paddingLeft: px(s.spacing_left),
78
+ };
79
+ }
80
+
81
+ // Migrate legacy padding enum → TRBL (top + bottom only)
82
+ const legacyPx = LEGACY_PADDING_PX[(s as RowSettings).padding || "none"] || "0";
83
+ if (legacyPx === "0") return {};
84
+ return {
85
+ paddingTop: `${legacyPx}px`,
86
+ paddingBottom: `${legacyPx}px`,
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Check whether the layout has custom TRBL spacing set.
92
+ * When true (on rows), the old enum-based padding should be skipped.
93
+ */
94
+ export function hasCustomSpacing(s: LayoutProps): boolean {
95
+ return !!(
96
+ (s.spacing_top && s.spacing_top !== "0") ||
97
+ (s.spacing_right && s.spacing_right !== "0") ||
98
+ (s.spacing_bottom && s.spacing_bottom !== "0") ||
99
+ (s.spacing_left && s.spacing_left !== "0")
100
+ );
101
+ }
102
+
103
+ /**
104
+ * Build background styles (color w/ opacity + background image).
105
+ * Returns styles that layer image behind/over the color.
106
+ */
107
+ export function getBackgroundStyles(
108
+ s: LayoutProps,
109
+ assetBaseUrl?: string
110
+ ): React.CSSProperties {
111
+ const styles: React.CSSProperties = {};
112
+
113
+ // Background color + opacity
114
+ if (s.background_color && s.background_color !== "transparent") {
115
+ const opacity = s.background_opacity ?? 100;
116
+ if (opacity < 100) {
117
+ styles.backgroundColor = hexToRgba(s.background_color, opacity / 100);
118
+ } else {
119
+ styles.backgroundColor = s.background_color;
120
+ }
121
+ }
122
+
123
+ // Background image
124
+ if (s.background_image) {
125
+ const imgUrl = assetBaseUrl
126
+ ? `${assetBaseUrl.replace(/\/$/, "")}/${s.background_image}`
127
+ : s.background_image;
128
+ styles.backgroundImage = `url(${imgUrl})`;
129
+ styles.backgroundSize = s.background_size || "cover";
130
+ styles.backgroundPosition = s.background_position || "center center";
131
+ styles.backgroundRepeat = s.background_repeat || "no-repeat";
132
+ }
133
+
134
+ return styles;
135
+ }
136
+
137
+ /**
138
+ * Build offset (margin) styles from TRBL values.
139
+ */
140
+ export function getOffsetStyles(s: LayoutProps): React.CSSProperties {
141
+ const hasOffset =
142
+ (s.offset_top && s.offset_top !== "0") ||
143
+ (s.offset_right && s.offset_right !== "0") ||
144
+ (s.offset_bottom && s.offset_bottom !== "0") ||
145
+ (s.offset_left && s.offset_left !== "0");
146
+
147
+ if (!hasOffset) return {};
148
+
149
+ return {
150
+ marginTop: px(s.offset_top),
151
+ marginRight: px(s.offset_right),
152
+ marginBottom: px(s.offset_bottom),
153
+ marginLeft: px(s.offset_left),
154
+ };
155
+ }
156
+
157
+ /**
158
+ * Build border styles from color/width/style/sides/radius.
159
+ * `border_sides` controls which sides get the border:
160
+ * "all" (default), "top", "right", "bottom", "left", "top-bottom", "left-right"
161
+ */
162
+ export function getBorderStyles(s: LayoutProps): React.CSSProperties {
163
+ const styles: React.CSSProperties = {};
164
+
165
+ const bw = parseInt(s.border_width || "0");
166
+ const bs = s.border_style || "none";
167
+
168
+ if (bw > 0 && bs !== "none") {
169
+ const bc = s.border_color || "#000000";
170
+ const sides = s.border_sides || "all";
171
+ const borderValue = `${bw}px ${bs} ${bc}`;
172
+
173
+ switch (sides) {
174
+ case "top":
175
+ styles.borderTop = borderValue;
176
+ break;
177
+ case "right":
178
+ styles.borderRight = borderValue;
179
+ break;
180
+ case "bottom":
181
+ styles.borderBottom = borderValue;
182
+ break;
183
+ case "left":
184
+ styles.borderLeft = borderValue;
185
+ break;
186
+ case "top-bottom":
187
+ styles.borderTop = borderValue;
188
+ styles.borderBottom = borderValue;
189
+ break;
190
+ case "left-right":
191
+ styles.borderLeft = borderValue;
192
+ styles.borderRight = borderValue;
193
+ break;
194
+ default: // "all"
195
+ styles.border = borderValue;
196
+ break;
197
+ }
198
+ }
199
+
200
+ const br = parseInt(s.border_radius || "0");
201
+ if (br > 0) {
202
+ styles.borderRadius = `${br}px`;
203
+ }
204
+
205
+ return styles;
206
+ }
207
+
208
+ /**
209
+ * Build ALL layout styles — works for both Row settings and Block layout.
210
+ */
211
+ export function getRowLayoutStyles(
212
+ s: RowSettings,
213
+ assetBaseUrl?: string
214
+ ): React.CSSProperties {
215
+ return {
216
+ ...getBackgroundStyles(s, assetBaseUrl),
217
+ ...getOffsetStyles(s),
218
+ ...getBorderStyles(s),
219
+ ...getSpacingStyles(s),
220
+ };
221
+ }
222
+
223
+ /**
224
+ * Build ALL layout styles for a block's layout object.
225
+ */
226
+ export function getBlockLayoutStyles(
227
+ layout?: BlockLayout,
228
+ assetBaseUrl?: string
229
+ ): React.CSSProperties {
230
+ if (!layout) return {};
231
+ return {
232
+ ...getBackgroundStyles(layout, assetBaseUrl),
233
+ ...getOffsetStyles(layout),
234
+ ...getBorderStyles(layout),
235
+ ...getSpacingStyles(layout),
236
+ };
237
+ }
238
+
239
+ /**
240
+ * Get alignment styles for a block within its column.
241
+ *
242
+ * Horizontal: uses `alignSelf` on the block (column is flex-col).
243
+ *
244
+ * NOTE: Vertical alignment is NOT handled here. It's applied as `justify-content`
245
+ * on the column flex container (in RowRenderer, SectionV2Renderer, SectionV2Column).
246
+ * Per-block margin-auto caused interactions between siblings — changing one block's
247
+ * alignment shifted others. Column-level justify-content groups all blocks together.
248
+ *
249
+ * Returns empty object if no alignment is set or everything is default (left).
250
+ */
251
+ export function getBlockAlignmentStyles(layout?: BlockLayout): React.CSSProperties {
252
+ if (!layout) return {};
253
+ const styles: React.CSSProperties = {};
254
+
255
+ // Horizontal alignment → align-self within flex-col parent
256
+ if (layout.align_h === "center") {
257
+ styles.alignSelf = "center";
258
+ } else if (layout.align_h === "right") {
259
+ styles.alignSelf = "flex-end";
260
+ }
261
+ // "left" = flex-start = default, no style needed
262
+
263
+ return styles;
264
+ }
265
+
266
+ /**
267
+ * Check if a block has horizontal alignment set (non-default).
268
+ */
269
+ export function hasBlockAlignment(layout?: BlockLayout): boolean {
270
+ if (!layout) return false;
271
+ return !!layout.align_h && layout.align_h !== "left";
272
+ }
273
+
274
+ /**
275
+ * Compute the column-level `justifyContent` from block vertical alignment settings.
276
+ *
277
+ * Scans all blocks in a column and derives the column's justify-content:
278
+ * - Any block has "bottom" → flex-end (all blocks grouped at bottom)
279
+ * - Any block has "center" → center (all blocks grouped in center)
280
+ * - Otherwise → flex-start (default, top)
281
+ *
282
+ * Priority: bottom > center > top (if mixed, bottom wins).
283
+ */
284
+ export function getColumnVerticalAlign(blocks: Array<{ layout?: BlockLayout }>): string | undefined {
285
+ let hasBottom = false;
286
+ let hasCenter = false;
287
+
288
+ for (const block of blocks) {
289
+ const layout = (block as unknown as Record<string, unknown>).layout as BlockLayout | undefined;
290
+ if (!layout) continue;
291
+ if (layout.align_v === "bottom") hasBottom = true;
292
+ if (layout.align_v === "center") hasCenter = true;
293
+ }
294
+
295
+ if (hasBottom) return "flex-end";
296
+ if (hasCenter) return "center";
297
+ return undefined; // default (flex-start)
298
+ }
299
+
300
+ /**
301
+ * Check if a block has any layout styles set.
302
+ */
303
+ export function hasBlockLayout(layout?: BlockLayout): boolean {
304
+ if (!layout) return false;
305
+ return Object.values(layout).some((v) => v !== undefined && v !== "" && v !== "0" && v !== 0 && v !== 100);
306
+ }
307
+
308
+ /**
309
+ * Resolve effective TRBL spacing for a row, accounting for the legacy padding enum.
310
+ *
311
+ * When a row has the old `padding` enum set (e.g. "medium") but no explicit TRBL
312
+ * spacing, this function converts the enum to concrete px values.
313
+ * Used by the SettingsPanel to show the user what padding is actually active.
314
+ */
315
+ export function resolveEffectiveSpacing(s: RowSettings): {
316
+ top: string;
317
+ right: string;
318
+ bottom: string;
319
+ left: string;
320
+ } {
321
+ // If explicit TRBL spacing is set, use it directly
322
+ if (hasCustomSpacing(s)) {
323
+ return {
324
+ top: s.spacing_top || "0",
325
+ right: s.spacing_right || "0",
326
+ bottom: s.spacing_bottom || "0",
327
+ left: s.spacing_left || "0",
328
+ };
329
+ }
330
+
331
+ // Otherwise, derive from the legacy padding enum (default "none" = 0)
332
+ const legacyPx = LEGACY_PADDING_PX[s.padding || "none"] || "0";
333
+ return {
334
+ top: legacyPx,
335
+ right: "0",
336
+ bottom: legacyPx,
337
+ left: "0",
338
+ };
339
+ }
340
+
341
+ // ---- Helpers ----
342
+
343
+ // Re-export hexToRgba for backward compatibility (imported at top from @/lib/color-utils)
344
+ export { hexToRgba };