@morphika/andami 0.5.1 → 0.5.3

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 (147) hide show
  1. package/README.md +27 -2
  2. package/app/admin/assets/page.tsx +6 -6
  3. package/app/admin/database/page.tsx +302 -302
  4. package/app/admin/error.tsx +53 -53
  5. package/app/admin/layout.tsx +332 -320
  6. package/app/admin/navigation/page.tsx +255 -255
  7. package/app/admin/pages/[slug]/page.tsx +44 -27
  8. package/app/admin/pages/page.tsx +24 -19
  9. package/app/admin/projects/page.tsx +30 -21
  10. package/app/admin/setup/page.tsx +1 -1
  11. package/app/admin/styles/page.tsx +1 -1
  12. package/app/api/admin/assets/register/route.ts +51 -14
  13. package/app/api/admin/assets/registry/route.ts +4 -1
  14. package/app/api/admin/assets/relink/confirm/route.ts +4 -1
  15. package/app/api/admin/assets/relink/route.ts +4 -1
  16. package/app/api/admin/assets/scan/route.ts +4 -1
  17. package/app/api/admin/backups/restore-data/route.ts +4 -1
  18. package/app/api/admin/r2/connect/route.ts +4 -1
  19. package/app/api/admin/r2/delete/route.ts +4 -1
  20. package/app/api/admin/r2/rename/route.ts +4 -1
  21. package/app/api/admin/r2/upload-url/route.ts +4 -1
  22. package/app/api/admin/revalidate/route.ts +4 -1
  23. package/app/api/admin/storage/switch/route.ts +4 -1
  24. package/app/api/custom-sections/[id]/route.ts +5 -6
  25. package/components/admin/MetadataEditor.tsx +6 -6
  26. package/components/admin/PublishToggle.tsx +2 -2
  27. package/components/admin/nav-builder/NavBuilder.tsx +1 -1
  28. package/components/admin/nav-builder/NavBuilderGrid.tsx +3 -3
  29. package/components/admin/nav-builder/NavGridCell.tsx +48 -48
  30. package/components/admin/nav-builder/NavGridItem.tsx +8 -6
  31. package/components/admin/nav-builder/NavItemSettings.tsx +331 -331
  32. package/components/admin/nav-builder/NavItemTypePicker.tsx +102 -102
  33. package/components/admin/nav-builder/NavLivePreview.tsx +1 -1
  34. package/components/admin/nav-builder/NavMobileLivePreview.tsx +226 -226
  35. package/components/admin/nav-builder/NavMobileSettings.tsx +242 -242
  36. package/components/admin/nav-builder/NavSettingsFields.tsx +518 -514
  37. package/components/admin/setup-wizard/BrandingStep.tsx +3 -3
  38. package/components/admin/setup-wizard/DatabaseStep.tsx +2 -2
  39. package/components/admin/setup-wizard/DoneStep.tsx +1 -1
  40. package/components/admin/setup-wizard/SetupWizard.tsx +4 -4
  41. package/components/admin/setup-wizard/StorageStep.tsx +2 -2
  42. package/components/admin/setup-wizard/WelcomeStep.tsx +2 -2
  43. package/components/admin/styles/ColorsEditor.tsx +9 -8
  44. package/components/admin/styles/FontsEditor.tsx +9 -7
  45. package/components/admin/styles/GridLayoutEditor.tsx +9 -9
  46. package/components/admin/styles/LinksButtonsEditor.tsx +5 -5
  47. package/components/admin/styles/TypographyEditor.tsx +6 -6
  48. package/components/admin/styles/shared.tsx +68 -68
  49. package/components/blocks/AudioBlockRenderer.tsx +286 -286
  50. package/components/blocks/CoverSectionRenderer.tsx +7 -1
  51. package/components/blocks/MarqueeBlockRenderer.tsx +316 -0
  52. package/components/blocks/ProjectCarouselBlockRenderer.tsx +1 -1
  53. package/components/blocks/SectionV2Renderer.tsx +8 -1
  54. package/components/builder/BlockCardIcons.tsx +316 -316
  55. package/components/builder/BlockTypePicker.tsx +1 -1
  56. package/components/builder/BubbleIcons.tsx +104 -0
  57. package/components/builder/BuilderCanvas.tsx +2 -0
  58. package/components/builder/CanvasMinimap.tsx +66 -49
  59. package/components/builder/CanvasToolbar.tsx +31 -41
  60. package/components/builder/CoverSectionCanvas.tsx +363 -363
  61. package/components/builder/DeviceFrame.tsx +1 -1
  62. package/components/builder/DndWrapper.tsx +3 -3
  63. package/components/builder/InsertionLines.tsx +1 -1
  64. package/components/builder/SectionCardIcons.tsx +421 -320
  65. package/components/builder/SectionEditorBar.tsx +5 -3
  66. package/components/builder/SectionTypePicker.tsx +7 -5
  67. package/components/builder/SectionV2Canvas.tsx +1 -1
  68. package/components/builder/SectionV2Column.tsx +82 -68
  69. package/components/builder/SettingsPanel.tsx +21 -17
  70. package/components/builder/SortableBlock.tsx +93 -73
  71. package/components/builder/SortableRow.tsx +33 -35
  72. package/components/builder/VirtualAssetGrid.tsx +10 -4
  73. package/components/builder/asset-browser/R2BrowserContent.tsx +18 -14
  74. package/components/builder/blockStyles.tsx +192 -185
  75. package/components/builder/color-picker/AlphaSlider.tsx +141 -141
  76. package/components/builder/color-picker/ColorInputs.tsx +105 -105
  77. package/components/builder/color-picker/EyedropperButton.tsx +75 -74
  78. package/components/builder/color-picker/HueSlider.tsx +124 -124
  79. package/components/builder/color-picker/SaturationCanvas.tsx +142 -142
  80. package/components/builder/color-picker/SwatchBar.tsx +98 -93
  81. package/components/builder/color-picker/UnifiedColorPicker.tsx +11 -6
  82. package/components/builder/editors/AudioBlockEditor.tsx +242 -242
  83. package/components/builder/editors/BeforeAfterBlockEditor.tsx +360 -360
  84. package/components/builder/editors/ButtonBlockEditor.tsx +4 -4
  85. package/components/builder/editors/EnterAnimationPicker.tsx +2 -2
  86. package/components/builder/editors/HoverEffectPicker.tsx +2 -2
  87. package/components/builder/editors/ImageBlockEditor.tsx +2 -2
  88. package/components/builder/editors/ImageGridBlockEditor.tsx +8 -6
  89. package/components/builder/editors/MarqueeBlockEditor.tsx +622 -0
  90. package/components/builder/editors/ProjectCarouselBlockEditor.tsx +443 -443
  91. package/components/builder/editors/ProjectGridEditor.tsx +21 -16
  92. package/components/builder/editors/SpacerBlockEditor.tsx +29 -27
  93. package/components/builder/editors/StaggerSettings.tsx +109 -109
  94. package/components/builder/editors/TextBlockEditor.tsx +22 -17
  95. package/components/builder/editors/TextStylePicker.tsx +1 -1
  96. package/components/builder/editors/VideoBlockEditor.tsx +2 -2
  97. package/components/builder/editors/index.ts +11 -10
  98. package/components/builder/editors/shared.tsx +10 -8
  99. package/components/builder/live-preview/LiveAudioPreview.tsx +120 -120
  100. package/components/builder/live-preview/LiveBeforeAfterPreview.tsx +1 -1
  101. package/components/builder/live-preview/LiveImageGridPreview.tsx +10 -2
  102. package/components/builder/live-preview/LiveImagePreview.tsx +4 -2
  103. package/components/builder/live-preview/LiveMarqueePreview.tsx +39 -0
  104. package/components/builder/live-preview/LiveProjectCarouselPreview.tsx +1 -1
  105. package/components/builder/live-preview/LiveVideoPreview.tsx +1 -1
  106. package/components/builder/live-preview/ProjectCardWrapper.tsx +293 -291
  107. package/components/builder/live-preview/RichTextBubbleMenu.tsx +10 -6
  108. package/components/builder/live-preview/shared.tsx +5 -2
  109. package/components/builder/settings-panel/AnimationTab.tsx +138 -138
  110. package/components/builder/settings-panel/BlockLayoutTab.tsx +11 -9
  111. package/components/builder/settings-panel/CardEntranceSection.tsx +114 -114
  112. package/components/builder/settings-panel/ColumnV2LayoutTab.tsx +242 -0
  113. package/components/builder/settings-panel/ColumnV2Settings.tsx +5 -5
  114. package/components/builder/settings-panel/CoverSectionLayoutTab.tsx +71 -71
  115. package/components/builder/settings-panel/CoverSectionSettings.tsx +337 -335
  116. package/components/builder/settings-panel/PageSettings.tsx +3 -3
  117. package/components/builder/settings-panel/ParallaxSlideSettings.tsx +2 -2
  118. package/components/builder/settings-panel/SectionV2AnimationTab.tsx +4 -4
  119. package/components/builder/settings-panel/SectionV2LayoutTab.tsx +356 -356
  120. package/components/builder/settings-panel/SectionV2Settings.tsx +25 -20
  121. package/components/builder/settings-panel/TRBLInputs.tsx +1 -1
  122. package/components/builder/settings-panel/index.ts +1 -0
  123. package/lib/animation/enter-types.ts +1 -0
  124. package/lib/animation/hover-effect-presets.ts +210 -210
  125. package/lib/animation/hover-effect-types.ts +1 -0
  126. package/lib/builder/block-registrations.ts +468 -417
  127. package/lib/builder/constants.ts +111 -111
  128. package/lib/builder/serializer/normalizers.ts +14 -0
  129. package/lib/builder/serializer/serializers.ts +27 -0
  130. package/lib/builder/store-sections.ts +23 -2
  131. package/lib/builder/types-slices.ts +428 -414
  132. package/lib/builder/types.ts +4 -1
  133. package/lib/config/index.ts +27 -27
  134. package/lib/sanity/queries.ts +48 -0
  135. package/lib/sanity/types.ts +112 -1
  136. package/lib/version.ts +1 -1
  137. package/package.json +7 -5
  138. package/sanity/schemas/blocks/audioBlock.ts +69 -69
  139. package/sanity/schemas/blocks/index.ts +12 -11
  140. package/sanity/schemas/blocks/marqueeBlock.ts +292 -0
  141. package/sanity/schemas/index.ts +120 -117
  142. package/sanity/schemas/objects/coverSection.ts +32 -0
  143. package/sanity/schemas/objects/parallaxSlide.ts +32 -0
  144. package/sanity/schemas/pageSectionV2.ts +32 -0
  145. package/styles/admin.css +85 -85
  146. package/styles/animations.css +237 -237
  147. package/styles/base.css +114 -114
@@ -65,6 +65,7 @@ export const ALL_BLOCK_INFO: BlockTypeInfo[] = [
65
65
  // Section blocks — not in the content picker but still need label/icon lookup
66
66
  { type: "projectGridBlock", label: "Project Grid", description: "Staggered project showcase grid", group: "generic", icon: "⬡", category: "section" },
67
67
  { type: "projectCarouselBlock", label: "Project Carousel", description: "Horizontal carousel of projects — great for end-of-page 'keep browsing'", group: "generic", icon: "▸", category: "section" },
68
+ { type: "marqueeBlock", label: "Marquee", description: "Horizontal scrolling ticker of text and images", group: "generic", icon: "⇄", category: "section" },
68
69
  ];
69
70
 
70
71
  // Parallax group info — used by BuilderCanvas/SortableRow for label/icon lookup (not a block)
@@ -75,12 +76,13 @@ export const PARALLAX_GROUP_INFO = { label: "Parallax Showcase", icon: "▽" };
75
76
  // ============================================
76
77
 
77
78
  /** Section block types that create a full-width row with a pre-populated block */
78
- export type SectionBlockType = "projectGridBlock" | "projectCarouselBlock";
79
+ export type SectionBlockType = "projectGridBlock" | "projectCarouselBlock" | "marqueeBlock";
79
80
 
80
81
  /** Set for fast lookup — used by SortableBlock, ColumnDropZone, SortableRow to suppress inner chrome */
81
82
  const SECTION_BLOCK_TYPES: ReadonlySet<string> = new Set<string>([
82
83
  "projectGridBlock",
83
84
  "projectCarouselBlock",
85
+ "marqueeBlock",
84
86
  ]);
85
87
 
86
88
  /** Check if a block type is a section-level block (should render without block/column chrome) */
@@ -114,6 +116,7 @@ export const SECTION_TYPE_REGISTRY: SectionTypeInfo[] = [
114
116
  { type: "coverSection", label: "Cover Section", description: "Full-viewport section with background and proportional rows", icon: "◆" },
115
117
  { type: "projectGridBlock", label: "Project Grid", description: "Staggered project showcase grid", icon: "⬡", blockType: "projectGridBlock" },
116
118
  { type: "projectCarouselBlock", label: "Project Carousel", description: "Horizontal 'keep browsing' carousel of projects", icon: "▸", blockType: "projectCarouselBlock" },
119
+ { type: "marqueeBlock", label: "Marquee", description: "Horizontal scrolling ticker of text and images", icon: "⇄", blockType: "marqueeBlock" },
117
120
  { type: "parallaxGroup", label: "Parallax Section", description: "Full-screen parallax showcase with V2 slides", icon: "▽" },
118
121
  ];
119
122
 
@@ -1,28 +1,28 @@
1
- /**
2
- * Site configuration accessor.
3
- *
4
- * Uses globalThis + Symbol.for to guarantee a true singleton even when
5
- * the bundler creates multiple module instances of this file.
6
- */
7
-
8
- import type { SiteConfig } from "./types";
9
-
10
- const CONFIG_KEY = Symbol.for("@morphika/andami/siteConfig");
11
- const g = globalThis as unknown as Record<symbol, SiteConfig | undefined>;
12
-
13
- export function registerConfig(config: SiteConfig): void {
14
- g[CONFIG_KEY] = config;
15
- }
16
-
17
- export function getSiteConfig(): SiteConfig {
18
- const cfg = g[CONFIG_KEY];
19
- if (!cfg) {
20
- throw new Error(
21
- "SiteConfig not registered. Call registerConfig(config) in your root layout before using getSiteConfig().\n" +
22
- "See: https://github.com/MorphikaStudio/Morphika_Andami#quick-start",
23
- );
24
- }
25
- return cfg;
26
- }
27
-
1
+ /**
2
+ * Site configuration accessor.
3
+ *
4
+ * Uses globalThis + Symbol.for to guarantee a true singleton even when
5
+ * the bundler creates multiple module instances of this file.
6
+ */
7
+
8
+ import type { SiteConfig } from "./types";
9
+
10
+ const CONFIG_KEY = Symbol.for("@morphika/andami/siteConfig");
11
+ const g = globalThis as unknown as Record<symbol, SiteConfig | undefined>;
12
+
13
+ export function registerConfig(config: SiteConfig): void {
14
+ g[CONFIG_KEY] = config;
15
+ }
16
+
17
+ export function getSiteConfig(): SiteConfig {
18
+ const cfg = g[CONFIG_KEY];
19
+ if (!cfg) {
20
+ throw new Error(
21
+ "SiteConfig not registered. Call registerConfig(config) in your root layout before using getSiteConfig().\n" +
22
+ "See: https://github.com/MorphikaStudio/Morphika_Andami#quick-start",
23
+ );
24
+ }
25
+ return cfg;
26
+ }
27
+
28
28
  export type { SiteConfig } from "./types";
@@ -20,6 +20,18 @@ const blockExpansion = `
20
20
  grid_row,
21
21
  span,
22
22
  enter_animation,
23
+ // Column-level background + border (Session 184)
24
+ background_color,
25
+ background_opacity,
26
+ background_image,
27
+ background_size,
28
+ background_position,
29
+ background_repeat,
30
+ border_color,
31
+ border_width,
32
+ border_style,
33
+ border_sides,
34
+ border_radius,
23
35
  blocks[] {
24
36
  _type,
25
37
  _key,
@@ -60,6 +72,18 @@ const blockExpansion = `
60
72
  grid_column,
61
73
  grid_row,
62
74
  span,
75
+ // Column-level background + border (Session 184)
76
+ background_color,
77
+ background_opacity,
78
+ background_image,
79
+ background_size,
80
+ background_position,
81
+ background_repeat,
82
+ border_color,
83
+ border_width,
84
+ border_style,
85
+ border_sides,
86
+ border_radius,
63
87
  blocks[] {
64
88
  _type,
65
89
  _key,
@@ -429,6 +453,18 @@ export const customSectionBySlugQuery = groq`
429
453
  grid_column,
430
454
  grid_row,
431
455
  span,
456
+ enter_animation,
457
+ background_color,
458
+ background_opacity,
459
+ background_image,
460
+ background_size,
461
+ background_position,
462
+ background_repeat,
463
+ border_color,
464
+ border_width,
465
+ border_style,
466
+ border_sides,
467
+ border_radius,
432
468
  blocks[] {
433
469
  _type,
434
470
  _key,
@@ -456,6 +492,18 @@ export const customSectionByIdQuery = groq`
456
492
  grid_column,
457
493
  grid_row,
458
494
  span,
495
+ enter_animation,
496
+ background_color,
497
+ background_opacity,
498
+ background_image,
499
+ background_size,
500
+ background_position,
501
+ background_repeat,
502
+ border_color,
503
+ border_width,
504
+ border_style,
505
+ border_sides,
506
+ border_radius,
459
507
  blocks[] {
460
508
  _type,
461
509
  _key,
@@ -368,6 +368,102 @@ export interface ProjectGridBlock {
368
368
  responsive?: ResponsiveOverrides<ProjectGridBlock>;
369
369
  }
370
370
 
371
+ // ============================================
372
+ // Marquee Block — horizontal infinite-scroll ticker (Session 184)
373
+ // ============================================
374
+
375
+ /** Plain-text item in a marquee. */
376
+ export interface MarqueeTextItem {
377
+ _key: string;
378
+ _type: "marqueeText";
379
+ text: string;
380
+ }
381
+
382
+ /** Image item in a marquee — asset_path is relative (resolved by the asset proxy). */
383
+ export interface MarqueeImageItem {
384
+ _key: string;
385
+ _type: "marqueeImage";
386
+ asset_path: string;
387
+ alt?: string;
388
+ /** Per-item override, 0–200 px. */
389
+ border_radius?: number;
390
+ /** Fixed width in px. When absent, derived from row_height × natural aspect ratio. */
391
+ width?: number;
392
+ }
393
+
394
+ /** Separator item — inherits block-level typography (size/color/weight/style). */
395
+ export interface MarqueeSeparatorItem {
396
+ _key: string;
397
+ _type: "marqueeSeparator";
398
+ /** 1–4 chars — glyph or emoji. Common: • · — / ▸ ★ */
399
+ character: string;
400
+ }
401
+
402
+ export type MarqueeItem =
403
+ | MarqueeTextItem
404
+ | MarqueeImageItem
405
+ | MarqueeSeparatorItem;
406
+
407
+ /**
408
+ * Font-size scale for text/separator items in a marquee.
409
+ * Mapped to concrete rem/px values by the renderer.
410
+ */
411
+ export type MarqueeFontSize =
412
+ | "s"
413
+ | "base"
414
+ | "l"
415
+ | "xl"
416
+ | "2xl"
417
+ | "3xl"
418
+ | "4xl"
419
+ | "5xl"
420
+ | "6xl";
421
+
422
+ /**
423
+ * Marquee Block — horizontal infinite-scroll ticker.
424
+ *
425
+ * Section-level block: lives inside a full-width column of a `PageSectionV2`.
426
+ * Available on both pages and projects (unlike projectGrid/projectCarousel
427
+ * which are pages-only) — makes sense as an end-of-project "keep browsing"
428
+ * style band.
429
+ *
430
+ * Motion is driven by CSS `@keyframes` with an IntersectionObserver pause
431
+ * when off-screen. Respects `prefers-reduced-motion`. Hover effects are
432
+ * deliberately not supported — a block that is already animating fights
433
+ * scale/tilt hover presets.
434
+ */
435
+ export interface MarqueeBlock {
436
+ _type: "marqueeBlock";
437
+ _key: string;
438
+
439
+ // ─── Content ───
440
+ items: MarqueeItem[];
441
+
442
+ // ─── Motion ───
443
+ direction?: "left" | "right"; // default "left"
444
+ speed?: number; // px/s, 5–600, default 60
445
+ pause_on_hover?: boolean; // default true
446
+
447
+ // ─── Typography (applies to text + separator items) ───
448
+ font_size?: MarqueeFontSize; // default "3xl"
449
+ font_weight?: "400" | "500" | "700" | "900"; // default "700"
450
+ color?: string; // hex or palette token, default "#111111"
451
+ text_style?: "solid" | "outline" | "italic-outline"; // default "solid"
452
+ letter_spacing?: number; // em, default 0
453
+ text_transform?: "none" | "uppercase" | "lowercase"; // default "uppercase"
454
+
455
+ // ─── Layout ───
456
+ gap?: number; // px between items, default 48
457
+ row_height?: number; // px, default 120 (controls image height)
458
+ padding_y?: number; // px, default 16
459
+ background_color?: string; // hex or palette; empty = transparent
460
+
461
+ // ─── Standard block fields ───
462
+ enter_animation?: import("../../lib/animation/enter-types").EnterAnimationConfig;
463
+ layout?: BlockLayout;
464
+ responsive?: ResponsiveOverrides<MarqueeBlock>;
465
+ }
466
+
371
467
  // ============================================
372
468
  // Parallax V2 — Group + Slide types (Session 123)
373
469
  // ============================================
@@ -463,6 +559,20 @@ export interface SectionColumn {
463
559
  blocks: ContentBlock[]; // same block types as today
464
560
  // NEW (Session 116) — column-level enter animation for 4-level cascade
465
561
  enter_animation?: import("../../lib/animation/enter-types").EnterAnimationConfig;
562
+ // Column-level layout — desktop-only for now. Background + border only
563
+ // (no spacing — section row_gap/col_gap + block padding cover that).
564
+ // Viewport overrides are TBD: the current ColumnOverride type is position-only.
565
+ background_color?: string;
566
+ background_opacity?: number;
567
+ background_image?: string;
568
+ background_size?: string;
569
+ background_position?: string;
570
+ background_repeat?: string;
571
+ border_color?: string;
572
+ border_width?: string;
573
+ border_style?: string;
574
+ border_sides?: string;
575
+ border_radius?: string;
466
576
  }
467
577
 
468
578
  export interface ColumnOverride {
@@ -694,7 +804,8 @@ export type ContentBlock =
694
804
  | BeforeAfterBlock
695
805
  | AudioBlock
696
806
  | ProjectGridBlock
697
- | ProjectCarouselBlock;
807
+ | ProjectCarouselBlock
808
+ | MarqueeBlock;
698
809
 
699
810
  // ============================================
700
811
  // Structural types
package/lib/version.ts CHANGED
@@ -6,4 +6,4 @@
6
6
  * Exposed as a plain constant so it can be imported without reading
7
7
  * package.json at runtime.
8
8
  */
9
- export const ANDAMI_VERSION = "0.5.1";
9
+ export const ANDAMI_VERSION = "0.5.3";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morphika/andami",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "Visual Page Builder — core library. A reusable website builder with visual editing, CMS integration, and asset management.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -193,7 +193,6 @@
193
193
  "react-dom": ">=19.0.0"
194
194
  },
195
195
  "dependencies": {
196
- "archiver": "^7.0.1",
197
196
  "@aws-sdk/client-s3": "^3.1021.0",
198
197
  "@aws-sdk/s3-request-presigner": "^3.1021.0",
199
198
  "@dnd-kit/core": "^6.3.1",
@@ -210,10 +209,11 @@
210
209
  "@tiptap/starter-kit": "^2.12.0",
211
210
  "@types/archiver": "^6.0.3",
212
211
  "@types/unzipper": "^0.10.10",
212
+ "archiver": "^7.0.1",
213
213
  "jszip": "^3.10.1",
214
- "next-sanity": "^12.1.5",
214
+ "next-sanity": "^12.3.0",
215
215
  "ogl": "^1.0.8",
216
- "sanity": "^5.17.1",
216
+ "sanity": "^5.21.0",
217
217
  "unzipper": "^0.12.3",
218
218
  "zustand": "^5.0.12"
219
219
  },
@@ -228,10 +228,12 @@
228
228
  "@types/react-dom": "^19",
229
229
  "eslint": "^9",
230
230
  "eslint-config-next": "16.2.1",
231
+ "framer-motion": "^12.38.0",
231
232
  "jsdom": "^26.1.0",
232
- "next": "16.2.1",
233
+ "next": "^16.2.4",
233
234
  "react": "19.2.4",
234
235
  "react-dom": "19.2.4",
236
+ "styled-components": "^6.4.0",
235
237
  "tailwindcss": "^4",
236
238
  "typescript": "^5",
237
239
  "vitest": "^4.1.2"
@@ -1,69 +1,69 @@
1
- import { defineField, defineType } from "sanity";
2
- import { blockLayoutField, blockAnimationFields } from "./blockLayout";
3
-
4
- export const audioBlock = defineType({
5
- name: "audioBlock",
6
- title: "Audio Block",
7
- type: "object",
8
- fields: [
9
- // ── Source ──
10
- defineField({
11
- name: "asset_path",
12
- title: "Audio File",
13
- type: "string",
14
- description: "Relative path to the audio file (mp3, wav, ogg, m4a, aac, flac)",
15
- validation: (Rule) => Rule.required(),
16
- }),
17
- defineField({ name: "alt", title: "Alt Text", type: "string" }),
18
-
19
- // ── Metadata ──
20
- defineField({ name: "title", title: "Title", type: "string" }),
21
- defineField({ name: "artist", title: "Artist", type: "string" }),
22
- defineField({
23
- name: "cover_path",
24
- title: "Cover Art",
25
- type: "string",
26
- description: "Optional relative path to a cover image",
27
- }),
28
-
29
- // ── Appearance ──
30
- defineField({
31
- name: "accent_color",
32
- title: "Accent Color",
33
- type: "string",
34
- description: "Hex color for the play button + progress fill",
35
- initialValue: "#4794E2",
36
- }),
37
- defineField({
38
- name: "width",
39
- title: "Width",
40
- type: "string",
41
- options: {
42
- list: [
43
- { title: "Full", value: "full" },
44
- { title: "Contained", value: "contained" },
45
- { title: "Small", value: "small" },
46
- { title: "Fill", value: "fill" },
47
- ],
48
- },
49
- initialValue: "contained",
50
- }),
51
- defineField({ name: "border_radius", title: "Border Radius", type: "string" }),
52
- defineField({ name: "shadow", title: "Shadow", type: "boolean", initialValue: false }),
53
-
54
- // ── Playback ──
55
- defineField({ name: "autoplay", title: "Autoplay", type: "boolean", initialValue: false }),
56
- defineField({ name: "loop", title: "Loop", type: "boolean", initialValue: false }),
57
- defineField({ name: "muted", title: "Muted", type: "boolean", initialValue: false }),
58
-
59
- ...blockAnimationFields,
60
- blockLayoutField,
61
- ],
62
- preview: {
63
- select: { path: "asset_path", title: "title", artist: "artist" },
64
- prepare({ path, title, artist }) {
65
- const label = title ? (artist ? `${title} — ${artist}` : title) : path || "Audio block";
66
- return { title: label };
67
- },
68
- },
69
- });
1
+ import { defineField, defineType } from "sanity";
2
+ import { blockLayoutField, blockAnimationFields } from "./blockLayout";
3
+
4
+ export const audioBlock = defineType({
5
+ name: "audioBlock",
6
+ title: "Audio Block",
7
+ type: "object",
8
+ fields: [
9
+ // ── Source ──
10
+ defineField({
11
+ name: "asset_path",
12
+ title: "Audio File",
13
+ type: "string",
14
+ description: "Relative path to the audio file (mp3, wav, ogg, m4a, aac, flac)",
15
+ validation: (Rule) => Rule.required(),
16
+ }),
17
+ defineField({ name: "alt", title: "Alt Text", type: "string" }),
18
+
19
+ // ── Metadata ──
20
+ defineField({ name: "title", title: "Title", type: "string" }),
21
+ defineField({ name: "artist", title: "Artist", type: "string" }),
22
+ defineField({
23
+ name: "cover_path",
24
+ title: "Cover Art",
25
+ type: "string",
26
+ description: "Optional relative path to a cover image",
27
+ }),
28
+
29
+ // ── Appearance ──
30
+ defineField({
31
+ name: "accent_color",
32
+ title: "Accent Color",
33
+ type: "string",
34
+ description: "Hex color for the play button + progress fill",
35
+ initialValue: "#3580f9",
36
+ }),
37
+ defineField({
38
+ name: "width",
39
+ title: "Width",
40
+ type: "string",
41
+ options: {
42
+ list: [
43
+ { title: "Full", value: "full" },
44
+ { title: "Contained", value: "contained" },
45
+ { title: "Small", value: "small" },
46
+ { title: "Fill", value: "fill" },
47
+ ],
48
+ },
49
+ initialValue: "contained",
50
+ }),
51
+ defineField({ name: "border_radius", title: "Border Radius", type: "string" }),
52
+ defineField({ name: "shadow", title: "Shadow", type: "boolean", initialValue: false }),
53
+
54
+ // ── Playback ──
55
+ defineField({ name: "autoplay", title: "Autoplay", type: "boolean", initialValue: false }),
56
+ defineField({ name: "loop", title: "Loop", type: "boolean", initialValue: false }),
57
+ defineField({ name: "muted", title: "Muted", type: "boolean", initialValue: false }),
58
+
59
+ ...blockAnimationFields,
60
+ blockLayoutField,
61
+ ],
62
+ preview: {
63
+ select: { path: "asset_path", title: "title", artist: "artist" },
64
+ prepare({ path, title, artist }) {
65
+ const label = title ? (artist ? `${title} — ${artist}` : title) : path || "Audio block";
66
+ return { title: label };
67
+ },
68
+ },
69
+ });
@@ -1,11 +1,12 @@
1
- // Block schemas (10)
2
- export { textBlock } from "./textBlock";
3
- export { imageBlock } from "./imageBlock";
4
- export { imageGridBlock } from "./imageGridBlock";
5
- export { videoBlock } from "./videoBlock";
6
- export { spacerBlock } from "./spacerBlock";
7
- export { buttonBlock } from "./buttonBlock";
8
- export { beforeAfterBlock } from "./beforeAfterBlock";
9
- export { audioBlock } from "./audioBlock";
10
- export { projectGridBlock } from "./projectGridBlock";
11
- export { projectCarouselBlock } from "./projectCarouselBlock";
1
+ // Block schemas (11)
2
+ export { textBlock } from "./textBlock";
3
+ export { imageBlock } from "./imageBlock";
4
+ export { imageGridBlock } from "./imageGridBlock";
5
+ export { videoBlock } from "./videoBlock";
6
+ export { spacerBlock } from "./spacerBlock";
7
+ export { buttonBlock } from "./buttonBlock";
8
+ export { beforeAfterBlock } from "./beforeAfterBlock";
9
+ export { audioBlock } from "./audioBlock";
10
+ export { projectGridBlock } from "./projectGridBlock";
11
+ export { projectCarouselBlock } from "./projectCarouselBlock";
12
+ export { marqueeBlock } from "./marqueeBlock";