@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,47 @@
1
+ "use client";
2
+
3
+ import { useState, useEffect, useCallback } from "react";
4
+
5
+ export default function ScrollToTop() {
6
+ const [isVisible, setIsVisible] = useState(false);
7
+
8
+ const handleScroll = useCallback(() => {
9
+ setIsVisible(window.scrollY > 400);
10
+ }, []);
11
+
12
+ useEffect(() => {
13
+ window.addEventListener("scroll", handleScroll, { passive: true });
14
+ return () => window.removeEventListener("scroll", handleScroll);
15
+ }, [handleScroll]);
16
+
17
+ const scrollToTop = () => {
18
+ window.scrollTo({ top: 0, behavior: "smooth" });
19
+ };
20
+
21
+ return (
22
+ <button
23
+ onClick={scrollToTop}
24
+ className={`fixed bottom-8 right-8 z-50 flex h-12 w-12 items-center justify-center rounded-full bg-white/10 hover:bg-white/20 transition-all duration-300 ${
25
+ isVisible
26
+ ? "opacity-100 translate-y-0"
27
+ : "opacity-0 translate-y-4 pointer-events-none"
28
+ }`}
29
+ aria-label="Scroll to top"
30
+ >
31
+ {/* Chevron up */}
32
+ <svg
33
+ width="28"
34
+ height="28"
35
+ viewBox="0 0 24 24"
36
+ fill="none"
37
+ stroke="currentColor"
38
+ strokeWidth="1.5"
39
+ strokeLinecap="round"
40
+ strokeLinejoin="round"
41
+ className="text-brand-text"
42
+ >
43
+ <polyline points="18 15 12 9 6 15" />
44
+ </svg>
45
+ </button>
46
+ );
47
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Enter Animation Presets
3
+ *
4
+ * Metadata + keyframe definitions for each enter animation preset.
5
+ * Used by EnterAnimationWrapper (public site) and EnterAnimationPicker (builder UI).
6
+ *
7
+ * Session 116 — Animation UX Refactor.
8
+ */
9
+
10
+ import type {
11
+ EnterPreset,
12
+ AnimationEasing,
13
+ EnterAnimationConfig,
14
+ } from "./enter-types";
15
+ import { ENTER_DEFAULTS } from "./enter-types";
16
+
17
+ // ── Default config ─────────────────────────────────────────────────
18
+
19
+ export const DEFAULT_ENTER_ANIMATION: Required<EnterAnimationConfig> = {
20
+ preset: "none",
21
+ duration: ENTER_DEFAULTS.duration,
22
+ delay: ENTER_DEFAULTS.delay,
23
+ easing: ENTER_DEFAULTS.easing,
24
+ };
25
+
26
+ // ── Preset metadata (for builder UI dropdowns) ─────────────────────
27
+
28
+ export interface EnterPresetInfo {
29
+ id: EnterPreset;
30
+ label: string;
31
+ description: string;
32
+ }
33
+
34
+ /**
35
+ * Human-readable metadata for every enter preset.
36
+ * Ordered for UI display. Generic presets first, then block-specific.
37
+ */
38
+ export const ENTER_PRESET_INFO: EnterPresetInfo[] = [
39
+ // Generic presets
40
+ { id: "none", label: "None", description: "No animation" },
41
+ { id: "fade", label: "Fade", description: "Opacity reveal" },
42
+ { id: "slide-up", label: "Slide Up", description: "Slide in from below" },
43
+ { id: "slide-down", label: "Slide Down", description: "Slide in from above" },
44
+ { id: "scale", label: "Scale", description: "Subtle zoom-in" },
45
+ { id: "blur", label: "Blur", description: "Blur dissolve reveal" },
46
+ { id: "reveal", label: "Reveal", description: "Clip-path wipe" },
47
+ // Block-specific presets
48
+ { id: "typewriter", label: "Typewriter", description: "Character-by-character typing" },
49
+ { id: "blur-in", label: "Blur In", description: "Text blur dissolve" },
50
+ ];
51
+
52
+ /**
53
+ * Quick lookup: preset ID → metadata.
54
+ */
55
+ export const ENTER_PRESET_MAP: Record<EnterPreset, EnterPresetInfo> = Object.fromEntries(
56
+ ENTER_PRESET_INFO.map((p) => [p.id, p])
57
+ ) as Record<EnterPreset, EnterPresetInfo>;
58
+
59
+ // ── Easing options (for builder UI) ────────────────────────────────
60
+
61
+ export const ENTER_EASINGS: { id: AnimationEasing; label: string }[] = [
62
+ { id: "ease-out", label: "Ease Out" },
63
+ { id: "ease", label: "Ease" },
64
+ { id: "ease-in", label: "Ease In" },
65
+ { id: "ease-in-out", label: "Ease In Out" },
66
+ { id: "linear", label: "Linear" },
67
+ ];
68
+
69
+ // ── Duration / delay ranges (for builder UI sliders) ───────────────
70
+
71
+ export const DURATION_RANGE = { min: 100, max: 5000, step: 50, default: 600 } as const;
72
+ export const DELAY_RANGE = { min: 0, max: 5000, step: 50, default: 0 } as const;
73
+
74
+ // ── Keyframe definitions ───────────────────────────────────────────
75
+
76
+ export interface PresetKeyframes {
77
+ /** CSS properties at animation start (hidden state) */
78
+ from: Record<string, string>;
79
+ /** CSS properties at animation end (visible state) */
80
+ to: Record<string, string>;
81
+ }
82
+
83
+ /**
84
+ * Returns the CSS keyframe from/to values for an enter preset.
85
+ *
86
+ * Intensity is removed in the new system — presets use fixed,
87
+ * sensible transform values.
88
+ *
89
+ * Returns null for "none" and for presets handled by custom
90
+ * renderers (typewriter).
91
+ */
92
+ export function getEnterKeyframes(preset: EnterPreset): PresetKeyframes | null {
93
+ switch (preset) {
94
+ case "none":
95
+ return null;
96
+
97
+ case "fade":
98
+ return {
99
+ from: { opacity: "0" },
100
+ to: { opacity: "1" },
101
+ };
102
+
103
+ case "slide-up":
104
+ return {
105
+ from: { opacity: "0", transform: "translateY(30px)" },
106
+ to: { opacity: "1", transform: "translateY(0)" },
107
+ };
108
+
109
+ case "slide-down":
110
+ return {
111
+ from: { opacity: "0", transform: "translateY(-30px)" },
112
+ to: { opacity: "1", transform: "translateY(0)" },
113
+ };
114
+
115
+ case "scale":
116
+ return {
117
+ from: { opacity: "0", transform: "scale(0.85)" },
118
+ to: { opacity: "1", transform: "scale(1)" },
119
+ };
120
+
121
+ case "blur":
122
+ return {
123
+ from: { opacity: "0", filter: "blur(10px)" },
124
+ to: { opacity: "1", filter: "blur(0px)" },
125
+ };
126
+
127
+ case "blur-in":
128
+ // Similar to blur but tuned for text — lighter blur, no opacity
129
+ return {
130
+ from: { opacity: "0", filter: "blur(6px)" },
131
+ to: { opacity: "1", filter: "blur(0px)" },
132
+ };
133
+
134
+ case "reveal":
135
+ return {
136
+ from: { opacity: "0", clipPath: "inset(0 100% 0 0)" },
137
+ to: { opacity: "1", clipPath: "inset(0 0% 0 0)" },
138
+ };
139
+
140
+ case "typewriter":
141
+ // Handled by TypewriterWrapper, not CSS keyframes
142
+ return null;
143
+
144
+ default:
145
+ return null;
146
+ }
147
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Enter Animation Cascade Resolution
3
+ *
4
+ * 4-level cascade: block → column → section → page → null.
5
+ * Full override at each level — no field-level merging.
6
+ *
7
+ * Session 116 — Animation UX Refactor.
8
+ */
9
+
10
+ import type {
11
+ EnterAnimationConfig,
12
+ ResolvedEnterAnimationConfig,
13
+ } from "./enter-types";
14
+ import { ENTER_DEFAULTS } from "./enter-types";
15
+
16
+ /**
17
+ * Resolves the effective enter animation for a block.
18
+ *
19
+ * Cascade order (first defined wins):
20
+ * 1. Block-level enter_animation
21
+ * 2. Column-level enter_animation
22
+ * 3. Section-level enter_animation
23
+ * 4. Page-level enter_animation
24
+ * 5. null (no animation)
25
+ *
26
+ * Full override semantics — if a level defines any property,
27
+ * that entire config is used (no merging with parent levels).
28
+ *
29
+ * Returns null if no level has an animation configured.
30
+ */
31
+ export function resolveEnterAnimation(
32
+ blockConfig?: EnterAnimationConfig,
33
+ columnConfig?: EnterAnimationConfig,
34
+ sectionConfig?: EnterAnimationConfig,
35
+ pageConfig?: EnterAnimationConfig,
36
+ ): ResolvedEnterAnimationConfig | null {
37
+ // Block-level wins
38
+ if (blockConfig && isConfigured(blockConfig)) {
39
+ return normalizeEnterConfig(blockConfig);
40
+ }
41
+
42
+ // Column-level
43
+ if (columnConfig && isConfigured(columnConfig)) {
44
+ return normalizeEnterConfig(columnConfig);
45
+ }
46
+
47
+ // Section-level
48
+ if (sectionConfig && isConfigured(sectionConfig)) {
49
+ return normalizeEnterConfig(sectionConfig);
50
+ }
51
+
52
+ // Page-level
53
+ if (pageConfig && isConfigured(pageConfig)) {
54
+ return normalizeEnterConfig(pageConfig);
55
+ }
56
+
57
+ // No animation anywhere
58
+ return null;
59
+ }
60
+
61
+ /**
62
+ * Check if a config has any meaningful property set.
63
+ * A config with only undefined values is treated as "not configured".
64
+ */
65
+ function isConfigured(config: EnterAnimationConfig): boolean {
66
+ return (
67
+ config.preset !== undefined ||
68
+ config.duration !== undefined ||
69
+ config.delay !== undefined ||
70
+ config.easing !== undefined
71
+ );
72
+ }
73
+
74
+ /**
75
+ * Fill defaults for any missing fields in a partial config.
76
+ * Returns a fully resolved config with all fields present.
77
+ *
78
+ * If preset is "none", still resolves — the wrapper component
79
+ * will detect "none" and skip rendering.
80
+ */
81
+ export function normalizeEnterConfig(
82
+ config: EnterAnimationConfig,
83
+ ): ResolvedEnterAnimationConfig {
84
+ return {
85
+ preset: config.preset ?? "none",
86
+ duration: config.duration ?? ENTER_DEFAULTS.duration,
87
+ delay: config.delay ?? ENTER_DEFAULTS.delay,
88
+ easing: config.easing ?? ENTER_DEFAULTS.easing,
89
+ };
90
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Enter Animation System — Type definitions
3
+ *
4
+ * Replaces the old scroll/enter/load trigger system with a unified "enter"
5
+ * behavior: IntersectionObserver with above-the-fold detection.
6
+ *
7
+ * Session 116 — Animation UX Refactor.
8
+ */
9
+
10
+ import type { ContentBlock } from "../../lib/sanity/types";
11
+
12
+ // ── Block type alias (mirrors builder types.ts) ────────────────────
13
+
14
+ type BlockType = ContentBlock["_type"];
15
+
16
+ // ── Easing ─────────────────────────────────────────────────────────
17
+
18
+ export type AnimationEasing =
19
+ | "linear"
20
+ | "ease"
21
+ | "ease-in"
22
+ | "ease-out"
23
+ | "ease-in-out";
24
+
25
+ // ── Generic enter presets (page / section / column) ────────────────
26
+
27
+ export type GenericEnterPreset =
28
+ | "none"
29
+ | "fade"
30
+ | "slide-up"
31
+ | "slide-down"
32
+ | "scale"
33
+ | "blur"
34
+ | "reveal";
35
+
36
+ /** Array of generic enter presets for builder UI dropdowns */
37
+ export const GENERIC_ENTER_PRESETS: GenericEnterPreset[] = [
38
+ "none",
39
+ "fade",
40
+ "slide-up",
41
+ "slide-down",
42
+ "scale",
43
+ "blur",
44
+ "reveal",
45
+ ];
46
+
47
+ // ── Block-specific enter presets ───────────────────────────────────
48
+
49
+ /** Presets exclusive to specific block types */
50
+ export type BlockSpecificEnterPreset =
51
+ | "typewriter" // textBlock only
52
+ | "blur-in"; // textBlock only (separate from generic "blur")
53
+
54
+ /**
55
+ * Union of ALL enter presets — generic + block-specific.
56
+ * Used as the field type in EnterAnimationConfig so any preset
57
+ * can be stored regardless of context.
58
+ */
59
+ export type EnterPreset = GenericEnterPreset | BlockSpecificEnterPreset;
60
+
61
+ // ── Per-block-type preset registry ─────────────────────────────────
62
+
63
+ /**
64
+ * Which enter presets are available for each block type.
65
+ * Block types not listed here have no block-specific enter animation
66
+ * (they rely on the generic cascade from column/section/page).
67
+ *
68
+ * Based on the registry table in ANIMATION-UX-ROADMAP.md.
69
+ */
70
+ export const BLOCK_ENTER_PRESETS: Record<BlockType, readonly EnterPreset[]> = {
71
+ textBlock: ["typewriter", "fade", "blur-in", "slide-up"],
72
+ imageBlock: ["fade", "blur", "reveal", "scale", "slide-up"],
73
+ imageGridBlock: ["fade", "scale", "slide-up"],
74
+ videoBlock: ["fade", "slide-up"],
75
+ buttonBlock: ["fade", "slide-up", "scale"],
76
+ coverBlock: ["fade", "blur", "reveal", "scale"],
77
+ spacerBlock: [], // invisible — no animation
78
+ projectGridBlock: [], // uses card_entrance system
79
+ };
80
+
81
+ // ── Enter animation config ─────────────────────────────────────────
82
+
83
+ /**
84
+ * Stored in Sanity on pages, sections, columns, and blocks.
85
+ * All fields optional — consumers use resolveEnterAnimation()
86
+ * or fill defaults at the call-site.
87
+ */
88
+ export interface EnterAnimationConfig {
89
+ preset?: EnterPreset;
90
+ duration?: number; // ms, default 600
91
+ delay?: number; // ms, default 0
92
+ easing?: AnimationEasing; // default "ease-out"
93
+ }
94
+
95
+ /**
96
+ * Fully resolved config — all fields guaranteed present.
97
+ * Returned by resolveEnterAnimation().
98
+ */
99
+ export interface ResolvedEnterAnimationConfig {
100
+ preset: EnterPreset;
101
+ duration: number;
102
+ delay: number;
103
+ easing: AnimationEasing;
104
+ }
105
+
106
+ // ── Typewriter config (textBlock only) ─────────────────────────────
107
+
108
+ /**
109
+ * Extra config for the "typewriter" enter preset.
110
+ * Stored on textBlock.typewriter_config when preset === "typewriter".
111
+ */
112
+ export interface TypewriterConfig {
113
+ speed?: number; // chars/sec, default 40, range 10–100
114
+ mode?: "character" | "word"; // default "character"
115
+ }
116
+
117
+ // ── Defaults ───────────────────────────────────────────────────────
118
+
119
+ export const ENTER_DEFAULTS = {
120
+ duration: 600,
121
+ delay: 0,
122
+ easing: "ease-out" as AnimationEasing,
123
+ } as const;
124
+
125
+ export const TYPEWRITER_DEFAULTS: Required<TypewriterConfig> = {
126
+ speed: 40,
127
+ mode: "character",
128
+ } as const;
@@ -0,0 +1,210 @@
1
+ /**
2
+ * Hover Effect Presets
3
+ *
4
+ * Unified registry of CSS-based and shader-based hover effects.
5
+ * The builder UI uses this for dropdowns. The public site uses
6
+ * getHoverEffectStyles() for CSS hovers and delegates to
7
+ * ShaderCanvas for shader hovers.
8
+ *
9
+ * Session 116 — Animation UX Refactor.
10
+ */
11
+
12
+ import type {
13
+ HoverPreset,
14
+ CSSHoverPreset,
15
+ ShaderHoverPreset,
16
+ HoverEasing,
17
+ HoverEffectConfig,
18
+ ShaderSmoothness,
19
+ } from "./hover-effect-types";
20
+ import { HOVER_EFFECT_DEFAULTS } from "./hover-effect-types";
21
+
22
+ // ── Default config ─────────────────────────────────────────────────
23
+
24
+ export const DEFAULT_HOVER_EFFECT: Required<HoverEffectConfig> = {
25
+ preset: HOVER_EFFECT_DEFAULTS.preset,
26
+ duration: HOVER_EFFECT_DEFAULTS.duration,
27
+ easing: HOVER_EFFECT_DEFAULTS.easing,
28
+ shader_speed: HOVER_EFFECT_DEFAULTS.shader_speed,
29
+ shader_smoothness: HOVER_EFFECT_DEFAULTS.shader_smoothness,
30
+ };
31
+
32
+ // ── Preset metadata (for builder UI) ───────────────────────────────
33
+
34
+ export interface HoverPresetInfo {
35
+ id: HoverPreset;
36
+ label: string;
37
+ description: string;
38
+ /** Whether this preset uses WebGL shaders */
39
+ isShader: boolean;
40
+ }
41
+
42
+ export const HOVER_PRESET_INFO: HoverPresetInfo[] = [
43
+ // CSS-based presets
44
+ { id: "none", label: "None", description: "No hover effect", isShader: false },
45
+ { id: "scale-up", label: "Scale Up", description: "Zoom in on hover", isShader: false },
46
+ { id: "scale-down", label: "Scale Down", description: "Zoom out on hover", isShader: false },
47
+ { id: "lift", label: "Lift", description: "Lifts element with shadow", isShader: false },
48
+ { id: "tilt-3d", label: "Tilt 3D", description: "Perspective tilt following cursor", isShader: false },
49
+ { id: "color-shift", label: "Color Shift", description: "Brightness and saturation change", isShader: false },
50
+ { id: "blur-reveal", label: "Blur Reveal", description: "Blurred until hovered", isShader: false },
51
+ { id: "border-glow", label: "Border Glow", description: "Glowing border on hover", isShader: false },
52
+ // Shader-based presets
53
+ { id: "ripple", label: "Ripple", description: "Ripple distortion from cursor", isShader: true },
54
+ { id: "rgb-shift", label: "RGB Shift", description: "Chromatic aberration on hover", isShader: true },
55
+ { id: "pixelate", label: "Pixelate", description: "Pixelation dissolve effect", isShader: true },
56
+ ];
57
+
58
+ /** Quick lookup: preset ID → metadata */
59
+ export const HOVER_PRESET_MAP: Record<HoverPreset, HoverPresetInfo> = Object.fromEntries(
60
+ HOVER_PRESET_INFO.map((p) => [p.id, p])
61
+ ) as Record<HoverPreset, HoverPresetInfo>;
62
+
63
+ // ── Shader detection ───────────────────────────────────────────────
64
+
65
+ const SHADER_PRESETS: ReadonlySet<string> = new Set<ShaderHoverPreset>([
66
+ "ripple",
67
+ "rgb-shift",
68
+ "pixelate",
69
+ ]);
70
+
71
+ /**
72
+ * Check if a hover preset uses WebGL shaders.
73
+ * Used by the builder to show/hide shader-specific controls
74
+ * and by the renderer to delegate to ShaderCanvas.
75
+ */
76
+ export function isShaderPreset(preset: HoverPreset): preset is ShaderHoverPreset {
77
+ return SHADER_PRESETS.has(preset);
78
+ }
79
+
80
+ // ── Easing options (for builder UI) ────────────────────────────────
81
+
82
+ export const HOVER_EFFECT_EASINGS: { id: HoverEasing; label: string }[] = [
83
+ { id: "ease-out", label: "Ease Out" },
84
+ { id: "ease", label: "Ease" },
85
+ { id: "ease-in", label: "Ease In" },
86
+ { id: "ease-in-out", label: "Ease In Out" },
87
+ { id: "linear", label: "Linear" },
88
+ ];
89
+
90
+ // ── Duration range (for builder UI) ────────────────────────────────
91
+
92
+ export const HOVER_DURATION_RANGE = { min: 50, max: 5000, step: 25, default: 300 } as const;
93
+
94
+ // ── Shader speed / smoothness ranges (for builder UI) ──────────────
95
+
96
+ export const SHADER_SPEED_RANGE = { min: 0.5, max: 3.0, step: 0.1, default: 1.0 } as const;
97
+
98
+ export const SHADER_SMOOTHNESS_OPTIONS: { id: ShaderSmoothness; label: string }[] = [
99
+ { id: "snappy", label: "Snappy" },
100
+ { id: "normal", label: "Normal" },
101
+ { id: "smooth", label: "Smooth" },
102
+ ];
103
+
104
+ /** Smoothness label → lerp factor (used by ShaderCanvas renderer) */
105
+ export const SMOOTHNESS_VALUES: Record<ShaderSmoothness, number> = {
106
+ snappy: 0.15,
107
+ normal: 0.05,
108
+ smooth: 0.02,
109
+ };
110
+
111
+ // ── CSS hover styles ───────────────────────────────────────────────
112
+
113
+ export interface HoverStyles {
114
+ /** Base state CSS (always applied) */
115
+ base: Record<string, string>;
116
+ /** Hover state CSS (applied on :hover) */
117
+ hover: Record<string, string>;
118
+ /** Whether this preset needs JS mouse tracking (tilt-3d) */
119
+ needsJS?: boolean;
120
+ }
121
+
122
+ /**
123
+ * Returns CSS properties for a CSS-based hover preset.
124
+ * Returns null for "none" and shader presets (they use WebGL, not CSS).
125
+ *
126
+ * Intensity is removed in the new system — presets use fixed,
127
+ * sensible values. The old intensity multiplier created confusion
128
+ * without meaningful user value.
129
+ */
130
+ export function getHoverEffectStyles(
131
+ preset: HoverPreset,
132
+ duration: number = 300,
133
+ easing: string = "ease-out",
134
+ ): HoverStyles | null {
135
+ // Shader presets don't use CSS
136
+ if (isShaderPreset(preset)) return null;
137
+
138
+ const cssPreset = preset as CSSHoverPreset;
139
+ const transition = `all ${duration}ms ${easing}`;
140
+
141
+ switch (cssPreset) {
142
+ case "none":
143
+ return null;
144
+
145
+ case "scale-up":
146
+ return {
147
+ base: { transition, transform: "scale(1)" },
148
+ hover: { transform: "scale(1.05)" },
149
+ };
150
+
151
+ case "scale-down":
152
+ return {
153
+ base: { transition, transform: "scale(1)" },
154
+ hover: { transform: "scale(0.95)" },
155
+ };
156
+
157
+ case "lift":
158
+ return {
159
+ base: {
160
+ transition,
161
+ transform: "translateY(0)",
162
+ boxShadow: "0 0 0 rgba(0,0,0,0)",
163
+ },
164
+ hover: {
165
+ transform: "translateY(-4px)",
166
+ boxShadow: "0 8px 20px rgba(0,0,0,0.15)",
167
+ },
168
+ };
169
+
170
+ case "tilt-3d":
171
+ return {
172
+ base: {
173
+ transition,
174
+ transform: "perspective(800px) rotateX(0deg) rotateY(0deg)",
175
+ transformStyle: "preserve-3d",
176
+ },
177
+ hover: {
178
+ // Overridden by JS mousemove handler
179
+ transform: "perspective(800px) rotateX(0deg) rotateY(0deg)",
180
+ },
181
+ needsJS: true,
182
+ };
183
+
184
+ case "color-shift":
185
+ return {
186
+ base: { transition, filter: "brightness(1) saturate(1)" },
187
+ hover: { filter: "brightness(1.10) saturate(1.20)" },
188
+ };
189
+
190
+ case "blur-reveal":
191
+ return {
192
+ base: { transition, filter: "blur(3px)", opacity: "0.85" },
193
+ hover: { filter: "blur(0px)", opacity: "1" },
194
+ };
195
+
196
+ case "border-glow":
197
+ return {
198
+ base: {
199
+ transition,
200
+ boxShadow: "0 0 0 0 rgba(7,107,255,0)",
201
+ },
202
+ hover: {
203
+ boxShadow: "0 0 8px 4px rgba(7,107,255,0.40)",
204
+ },
205
+ };
206
+
207
+ default:
208
+ return null;
209
+ }
210
+ }